diff --git a/public/placeholder.jpeg b/public/placeholder.jpeg new file mode 100644 index 0000000..bb1655c Binary files /dev/null and b/public/placeholder.jpeg differ diff --git a/src/app/awards/AwardCard.tsx b/src/app/awards/AwardCard.tsx new file mode 100644 index 0000000..316a9d4 --- /dev/null +++ b/src/app/awards/AwardCard.tsx @@ -0,0 +1,38 @@ +"use client"; + +import Image from "next/image"; + +import { cn } from "~/utils"; + +import { type Award } from "./page"; + +export function AwardCard({ award }: { award: Award }) { + const label = award.awardType === "WORD_COUNT" ? "words" : "sentences"; + + const numberAward = ( +

+ {award.awardValue ?? 0} {label} +

+ ); + + const masteryAward = ( +

{award.word &&

{award.word?.word}

}

+ ); + + return ( +
+ {award.word ? masteryAward : numberAward} + + {"Award +
+ ); +} diff --git a/src/app/awards/AwardImageChoice.tsx b/src/app/awards/AwardImageChoice.tsx new file mode 100644 index 0000000..0306eaf --- /dev/null +++ b/src/app/awards/AwardImageChoice.tsx @@ -0,0 +1,65 @@ +"use client"; + +import Image from "next/image"; + +import { trpc } from "~/app/_trpc/client"; +import { ButtonLoading } from "~/components/ButtonLoading"; + +import { type AwardImage } from "./page"; + +export function AwardImageChoice({ + image, + shouldClickToClaim, +}: { + image: AwardImage; + shouldClickToClaim: boolean; +}) { + const utils = trpc.useContext(); + + const addImageIdToAward = trpc.awardRouter.addImageIdToAward.useMutation(); + + const handleAddImageIdToAward = async (imageId: string) => { + // confirm add + const shouldAdd = confirm( + "Are you sure you want to add this image to the award?" + ); + if (!shouldAdd) { + return; + } + + await addImageIdToAward.mutateAsync({ + imageId, + }); + + await utils.awardRouter.getAllAwardsForProfile.invalidate(); + }; + + const deleteImage = trpc.awardRouter.deleteImage.useMutation(); + + const handleDeleteImage = async (imageId: string) => { + await deleteImage.mutateAsync({ + imageId, + }); + + await utils.awardRouter.getAllAwardImages.invalidate(); + }; + + return ( +
+ {"Award shouldClickToClaim && handleAddImageIdToAward(image.id)} + /> + handleDeleteImage(image.id)} + isLoading={deleteImage.isLoading} + > + Delete + +
+ ); +} diff --git a/src/app/awards/AwardList.tsx b/src/app/awards/AwardList.tsx new file mode 100644 index 0000000..7b896a2 --- /dev/null +++ b/src/app/awards/AwardList.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { AwardCard } from "./AwardCard"; +import { type Award } from "./page"; + +export function AwardList({ awards = [] }: { awards?: Award[] }) { + return ( +
+ {awards.map((award) => ( + + ))} +
+ ); +} diff --git a/src/app/awards/page.tsx b/src/app/awards/page.tsx index ff25928..706acbe 100644 --- a/src/app/awards/page.tsx +++ b/src/app/awards/page.tsx @@ -1,16 +1,27 @@ "use client"; import { useState } from "react"; -import Image from "next/image"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; import { trpc } from "~/app/_trpc/client"; import { ButtonLoading } from "~/components/ButtonLoading"; import { Textarea } from "~/components/ui/textarea"; import { type RouterOutputs } from "~/utils/api"; -type Award = RouterOutputs["awardRouter"]["getAllAwardsForProfile"][number]; +import { AwardImageChoice } from "./AwardImageChoice"; +import { AwardList } from "./AwardList"; -type AwardImage = RouterOutputs["awardRouter"]["getAllAwardImages"][number]; +export type Award = + RouterOutputs["awardRouter"]["getAllAwardsForProfile"][number]; + +export type AwardImage = + RouterOutputs["awardRouter"]["getAllAwardImages"][number]; export default function AwardsPage() { const { data: awards } = trpc.awardRouter.getAllAwardsForProfile.useQuery(); @@ -61,139 +72,93 @@ export default function AwardsPage() { Math.ceil(((currentSentenceCount ?? 0) + 1) / 10) * 10; return ( -
+

Awards

- -

Word count awards

- -

Current word count: {currentWordCount}

-

Next award at: {nextWordAward}

- - - -

Sentence count awards

- -

Current sentence count: {currentSentenceCount}

-

Next award at: {nextSentenceAward}

- - - -

Word mastery awards

- - + + + Word count awards + + Word count awards are given every 100 correct words. + + + +

Current word count: {currentWordCount}

+

Next award at: {nextWordAward}

+ +
+
+ + + + Sentence count awards + + Awards are given every 10 sentences. + + + +

Current sentence count: {currentSentenceCount}

+

Next award at: {nextSentenceAward}

+ +
+
+ + + + Word mastery awards + + Given when the interval on a word reaches the max: 60d. + + + + + + {hasUnclaimedAwards && ( <> -

Award images

- -
- {(allAwardImages ?? []).map((image) => ( - - ))} -
+ + + Pick new awards + + Click an image to add to your awards. + + + + {" "} +
+ {(allAwardImages ?? []).map((image) => ( + + ))} +
+
+
)} -