Skip to content

Commit

Permalink
feat: Add quest completion meter (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
evadecker authored Oct 20, 2024
1 parent f35921f commit 28d720f
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/fuzzy-items-grin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"namesake": minor
---

Add quest completion meter
17 changes: 16 additions & 1 deletion convex/userQuests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const getAvailableQuestsForUser = userQuery({
},
});

export const getQuestCount = query({
export const getGlobalQuestCount = query({
args: { questId: v.id("quests") },
handler: async (ctx, args) => {
const quests = await ctx.db
Expand All @@ -52,6 +52,21 @@ export const getQuestCount = query({
},
});

export const getCompletedQuestCount = userQuery({
handler: async (ctx) => {
const userQuests = await ctx.db
.query("userQuests")
.withIndex("userId", (q) => q.eq("userId", ctx.userId))
.collect();

const completedQuests = userQuests.filter(
(quest) => quest.completionTime !== undefined,
);

return completedQuests.length;
},
});

export const create = userMutation({
args: { questId: v.id("quests") },
handler: async (ctx, args) => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/ProgressBar/ProgressBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export function ProgressBar({ label, ...props }: ProgressBarProps) {
</div>
<div className="w-64 h-2 rounded-full bg-gray-4 dark:bg-graydark-4 outline outline-1 -outline-offset-1 outline-transparent relative overflow-hidden">
<div
className={`absolute top-0 h-full rounded-full bg-purple-9 dark:bg-purpledark-9 forced-colors:bg-[Highlight] ${isIndeterminate ? "left-full animate-in duration-10 [--tw-enter-translate-x:calc(-16rem-1%)] slide-out-to-right-full repeat-infinite ease-out" : "left-0"}`}
className={`absolute top-0 h-full rounded-full bg-purple-9 dark:bg-purpledark-9 transition-all forced-colors:bg-[Highlight] ${isIndeterminate ? "left-full animate-in duration-10 [--tw-enter-translate-x:calc(-16rem-1%)] slide-out-to-right-full repeat-infinite ease-out" : "left-0"}`}
style={{ width: `${isIndeterminate ? 40 : percentage}%` }}
/>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/routes/_authenticated/admin/quests/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ const QuestTableRow = ({
}: {
quest: DataModel["quests"]["document"];
}) => {
const questCount = useQuery(api.userQuests.getQuestCount, {
const questCount = useQuery(api.userQuests.getGlobalQuestCount, {
questId: quest._id,
});
const deleteQuest = useMutation(api.quests.deleteQuest);
Expand Down
69 changes: 42 additions & 27 deletions src/routes/_authenticated/quests/route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
GridList,
GridListItem,
Modal,
ProgressBar,
} from "@/components";
import { api } from "@convex/_generated/api";
import type { Id } from "@convex/_generated/dataModel";
Expand Down Expand Up @@ -113,6 +114,7 @@ function IndexRoute() {

const MyQuests = () => {
const myQuests = useQuery(api.userQuests.getQuestsForCurrentUser);
const completedQuests = useQuery(api.userQuests.getCompletedQuestCount);

if (myQuests === undefined) return;

Expand All @@ -129,34 +131,47 @@ function IndexRoute() {
/>
);

const totalQuests = myQuests.length;

return (
<GridList aria-label="My quests">
{myQuests.map((quest) => {
if (quest === null) return null;

const Icon = ICONS[quest.icon];

return (
<GridListItem
textValue={quest.title}
key={quest._id}
href={{ to: "/quests/$questId", params: { questId: quest._id } }}
>
<div className="flex items-center gap-2">
<Icon className="text-gray-dim" />
<p className="font-bold text-lg">{quest.title}</p>
{quest.jurisdiction && <Badge>{quest.jurisdiction}</Badge>}
</div>
</GridListItem>
);
})}
<GridListItem
textValue="Add quest"
onAction={() => setIsNewQuestModalOpen(true)}
>
<RiAddLine /> Add quest
</GridListItem>
</GridList>
<div className="flex flex-col gap-4">
<ProgressBar
label="Quests complete"
value={completedQuests}
maxValue={totalQuests}
valueLabel={`${completedQuests} of ${totalQuests}`}
/>
<GridList aria-label="My quests">
{myQuests.map((quest) => {
if (quest === null) return null;

const Icon = ICONS[quest.icon];

return (
<GridListItem
textValue={quest.title}
key={quest._id}
href={{
to: "/quests/$questId",
params: { questId: quest._id },
}}
>
<div className="flex items-center gap-2">
<Icon className="text-gray-dim" />
<p className="font-bold text-lg">{quest.title}</p>
{quest.jurisdiction && <Badge>{quest.jurisdiction}</Badge>}
</div>
</GridListItem>
);
})}
<GridListItem
textValue="Add quest"
onAction={() => setIsNewQuestModalOpen(true)}
>
<RiAddLine /> Add quest
</GridListItem>
</GridList>
</div>
);
};

Expand Down

0 comments on commit 28d720f

Please sign in to comment.