"use client"; import clsx from "clsx"; import { useOptimistic, useRef, useTransition } from "react"; import { saveFeature, upvote } from "./actions"; import { Feature } from "./types"; function Item({ isFirst, isLast, isReleased, hasVoted, feature, pending, mutate, }: { isFirst: boolean; isLast: boolean; isReleased: boolean; hasVoted: boolean; feature: Feature; pending: boolean; mutate: any; }) { let upvoteWithId = upvote.bind(null, feature); // eslint-disable-next-line @typescript-eslint/no-unused-vars let [isPending, startTransition] = useTransition(); return (
{ event.preventDefault(); startTransition(async () => { mutate({ updatedFeature: { ...feature, score: Number(feature.score) + 1, }, pending: true, }); await upvote(feature); }); }} className={clsx( "p-6 mx-8 flex items-center border-t border-l border-r", isFirst && "rounded-t-md", isLast && "border-b rounded-b-md", )} >

{feature.title}

{feature.score}
); } type FeatureState = { newFeature: Feature; updatedFeature?: Feature; pending: boolean; }; export default function FeatureForm({ features }: { features: Feature[] }) { let formRef = useRef(null); let [state, mutate] = useOptimistic( { features, pending: false }, function createReducer(state, newState: FeatureState) { if (newState.newFeature) { return { features: [...state.features, newState.newFeature], pending: newState.pending, }; } else { return { features: [ ...state.features.filter( (f) => f.id !== newState.updatedFeature!.id, ), newState.updatedFeature, ] as Feature[], pending: newState.pending, }; } }, ); let sortedFeatures = state.features.sort((a, b) => { // First, compare by score in descending order if (Number(a.score) > Number(b.score)) return -1; if (Number(a.score) < Number(b.score)) return 1; // If scores are equal, then sort by created_at in ascending order return new Date(a.created_at).getTime() - new Date(b.created_at).getTime(); }); let featureStub = { id: crypto.randomUUID(), title: "", // will used value from form created_at: new Date().toISOString(), score: "1", }; let saveWithNewFeature = saveFeature.bind(null, featureStub); // eslint-disable-next-line @typescript-eslint/no-unused-vars let [isPending, startTransition] = useTransition(); return ( <>
{ event.preventDefault(); let formData = new FormData(event.currentTarget); let newFeature = { ...featureStub, title: formData.get("feature") as string, }; formRef.current?.reset(); startTransition(async () => { mutate({ newFeature, pending: true, }); await saveFeature(newFeature, formData); }); }} >
{sortedFeatures.map((feature: any, index: number) => ( ))}
); }