From 439f5bb93ecb9377383b7ba792a58327b466664c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Sans=C3=B3n?= Date: Thu, 26 Sep 2024 10:54:52 +0200 Subject: [PATCH] Rewards --- .../src/actions/rewards/claimRewardAction.ts | 27 + .../rewards/fetchClaimedRewardsAction.ts | 12 + .../AppLayout/Header/Rewards/Menu/index.tsx | 93 + .../AppLayout/Header/Rewards/index.tsx | 22 + .../layouts/AppLayout/Header/index.tsx | 2 + apps/web/src/stores/rewards.ts | 78 + .../core/drizzle/0060_lumpy_tenebrous.sql | 32 + packages/core/drizzle/meta/0060_snapshot.json | 2510 +++++++++++++++++ packages/core/drizzle/meta/_journal.json | 7 + packages/core/src/constants.ts | 16 + .../repositories/claimedRewardsRepository.ts | 41 + packages/core/src/repositories/index.ts | 1 + packages/core/src/schema/index.ts | 1 + .../core/src/schema/models/claimedRewards.ts | 49 + packages/core/src/schema/types.ts | 2 + .../core/src/services/claimedRewards/claim.ts | 39 + .../core/src/services/claimedRewards/index.ts | 1 + .../core/src/services/workspaces/usage.ts | 13 +- packages/web-ui/src/ds/atoms/Button/index.tsx | 6 +- 19 files changed, 2948 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/actions/rewards/claimRewardAction.ts create mode 100644 apps/web/src/actions/rewards/fetchClaimedRewardsAction.ts create mode 100644 apps/web/src/components/layouts/AppLayout/Header/Rewards/Menu/index.tsx create mode 100644 apps/web/src/components/layouts/AppLayout/Header/Rewards/index.tsx create mode 100644 apps/web/src/stores/rewards.ts create mode 100644 packages/core/drizzle/0060_lumpy_tenebrous.sql create mode 100644 packages/core/drizzle/meta/0060_snapshot.json create mode 100644 packages/core/src/repositories/claimedRewardsRepository.ts create mode 100644 packages/core/src/schema/models/claimedRewards.ts create mode 100644 packages/core/src/services/claimedRewards/claim.ts create mode 100644 packages/core/src/services/claimedRewards/index.ts diff --git a/apps/web/src/actions/rewards/claimRewardAction.ts b/apps/web/src/actions/rewards/claimRewardAction.ts new file mode 100644 index 000000000..3e93061b4 --- /dev/null +++ b/apps/web/src/actions/rewards/claimRewardAction.ts @@ -0,0 +1,27 @@ +'use server' + +import { ClaimedReward, RewardType } from '@latitude-data/core/browser' +import { claimReward } from '@latitude-data/core/services/claimedRewards/claim' +import { z } from 'zod' + +import { authProcedure } from '../procedures' + +export const claimRewardAction = authProcedure + .createServerAction() + .input( + z.object({ + type: z.enum(Object.values(RewardType) as [string, ...string[]]), + reference: z.string(), + }), + ) + .handler(async ({ input, ctx }) => { + const workspace = ctx.workspace + const user = ctx.user + const result = await claimReward({ + workspace, + user, + type: input.type as RewardType, + reference: input.reference, + }) + return result.unwrap() as ClaimedReward + }) diff --git a/apps/web/src/actions/rewards/fetchClaimedRewardsAction.ts b/apps/web/src/actions/rewards/fetchClaimedRewardsAction.ts new file mode 100644 index 000000000..c1600475c --- /dev/null +++ b/apps/web/src/actions/rewards/fetchClaimedRewardsAction.ts @@ -0,0 +1,12 @@ +'use server' + +import { authProcedure } from '../procedures' +import { ClaimedRewardsRepository } from '@latitude-data/core/repositories' + +export const fetchClaimedRewardsAction = authProcedure + .createServerAction() + .handler(async ({ ctx }) => { + const claimedRewardsScope = new ClaimedRewardsRepository(ctx.workspace.id) + const result = await claimedRewardsScope.findAllValidOptimistic() + return result.unwrap() + }) diff --git a/apps/web/src/components/layouts/AppLayout/Header/Rewards/Menu/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Rewards/Menu/index.tsx new file mode 100644 index 000000000..400b027aa --- /dev/null +++ b/apps/web/src/components/layouts/AppLayout/Header/Rewards/Menu/index.tsx @@ -0,0 +1,93 @@ +'use client' + +import { useMemo } from 'react' + +import { + ClaimedReward, + REWARD_VALUES, + RewardType, +} from '@latitude-data/core/browser' +import { Button, cn, Icon, Text } from '@latitude-data/web-ui' +import useRewards from '$/stores/rewards' + +function RewardItem({ + description, + type, + claimedRewards, + isLoading, +}: { + description: string + type: RewardType + claimedRewards: ClaimedReward[] + isLoading: boolean +}) { + const isClaimed = useMemo(() => { + if (isLoading || !claimedRewards) return false + return claimedRewards.some((r) => r.reward_type === type) + }, [isLoading, claimedRewards, type]) + + const runs = useMemo(() => `${REWARD_VALUES[type] / 1000}k`, [type]) + + return ( + + ) +} + +export function RewardsMenu() { + const { data: claimedRewards, isLoading } = useRewards() + + return ( +
+ + + + + +
+ ) +} diff --git a/apps/web/src/components/layouts/AppLayout/Header/Rewards/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/Rewards/index.tsx new file mode 100644 index 000000000..947a37b77 --- /dev/null +++ b/apps/web/src/components/layouts/AppLayout/Header/Rewards/index.tsx @@ -0,0 +1,22 @@ +import { Button } from '@latitude-data/web-ui' +import Popover from 'node_modules/@latitude-data/web-ui/src/ds/atoms/Popover' + +import { RewardsMenu } from './Menu' + +export function RewardsButton() { + return ( + + + + + + + + + ) +} diff --git a/apps/web/src/components/layouts/AppLayout/Header/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/index.tsx index eb2310276..1342f0459 100644 --- a/apps/web/src/components/layouts/AppLayout/Header/index.tsx +++ b/apps/web/src/components/layouts/AppLayout/Header/index.tsx @@ -11,6 +11,7 @@ import { Fragment } from 'react/jsx-runtime' import AvatarDropdown from './AvatarDropdown' import { UsageIndicator } from './UsageIndicator' +import { RewardsButton } from './Rewards' function BreadcrumbSeparator() { return ( @@ -112,6 +113,7 @@ export default function AppHeader({