Skip to content

Commit b15e214

Browse files
feat: user-list, user-details, tg grants
1 parent 6d323e6 commit b15e214

26 files changed

Lines changed: 954 additions & 77 deletions

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@base-ui/react": "^1.3.0",
1717
"@better-auth/passkey": "^1.5.5",
1818
"@hookform/resolvers": "^3.9.1",
19-
"@polinetwork/backend": "^0.15.12",
19+
"@polinetwork/backend": "^0.15.14",
2020
"@radix-ui/react-dialog": "^1.1.15",
2121
"@t3-oss/env-nextjs": "^0.13.10",
2222
"@tanstack/react-query": "^5.90.19",
@@ -30,6 +30,7 @@
3030
"class-variance-authority": "^0.7.1",
3131
"clsx": "^2.1.1",
3232
"cmdk": "^1.1.1",
33+
"date-fns": "^4.1.0",
3334
"geist": "^1.3.0",
3435
"input-otp": "^1.4.2",
3536
"lucide-react": "^0.525.0",
@@ -38,6 +39,7 @@
3839
"postgres": "^3.4.4",
3940
"radix-ui": "^1.4.3",
4041
"react": "^18.3.1",
42+
"react-day-picker": "^9.14.0",
4143
"react-dom": "^18.3.1",
4244
"react-error-boundary": "^6.1.0",
4345
"react-hook-form": "^7.55.0",

pnpm-lock.yaml

Lines changed: 46 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
"use client"
2+
3+
import { useMutation, useQueryClient } from "@tanstack/react-query"
4+
import { Trash2, Trash2Icon } from "lucide-react"
5+
import { useState } from "react"
6+
import { toast } from "sonner"
7+
import {
8+
AlertDialog,
9+
AlertDialogAction,
10+
AlertDialogCancel,
11+
AlertDialogContent,
12+
AlertDialogDescription,
13+
AlertDialogFooter,
14+
AlertDialogHeader,
15+
AlertDialogMedia,
16+
AlertDialogTitle,
17+
AlertDialogTrigger,
18+
} from "@/components/ui/alert-dialog"
19+
import { Button } from "@/components/ui/button"
20+
import { useSession } from "@/lib/auth"
21+
import { useTRPC } from "@/lib/trpc/client"
22+
23+
export function DeleteGrant({ userId }: { userId: number }) {
24+
const sesh = useSession()
25+
26+
const qc = useQueryClient()
27+
const trpc = useTRPC()
28+
const removerId = sesh.data?.user.telegramId
29+
const { mutateAsync } = useMutation(trpc.tg.grants.interrupt.mutationOptions())
30+
31+
const [open, setOpen] = useState(false)
32+
33+
async function interrupt() {
34+
if (!removerId) return toast.error("Invalid session, try to reload the page")
35+
const { error } = await mutateAsync({ userId, interruptedById: removerId })
36+
37+
if (error === "NOT_FOUND") toast.info("The grant was expired or already interrupted")
38+
else if (error === "UNAUTHORIZED") toast.error("You don't have enought permission")
39+
else if (error === "INTERNAL_SERVER_ERROR") toast.error("There was an internal server error")
40+
else toast.success("Grant interrupted successfully")
41+
42+
handleOpenChange(false)
43+
}
44+
45+
function handleOpenChange(v: boolean) {
46+
setOpen(v)
47+
if (v === false) {
48+
qc.invalidateQueries(trpc.tg.grants.getOngoing.queryOptions())
49+
qc.invalidateQueries(trpc.tg.grants.checkUser.queryOptions({ userId }))
50+
}
51+
}
52+
53+
return (
54+
<AlertDialog open={open} onOpenChange={handleOpenChange}>
55+
<AlertDialogTrigger
56+
render={
57+
<Button variant="destructive">
58+
<Trash2 />
59+
Delete
60+
</Button>
61+
}
62+
></AlertDialogTrigger>
63+
<AlertDialogContent size="sm">
64+
<AlertDialogHeader>
65+
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
66+
<Trash2Icon />
67+
</AlertDialogMedia>
68+
<AlertDialogTitle>Interrupt Grant</AlertDialogTitle>
69+
<AlertDialogDescription>Are you sure you want to interrupt the grant?</AlertDialogDescription>
70+
</AlertDialogHeader>
71+
<AlertDialogFooter>
72+
<AlertDialogCancel>Cancel</AlertDialogCancel>
73+
<AlertDialogAction onClick={interrupt} variant="destructive">
74+
Confirm
75+
</AlertDialogAction>
76+
</AlertDialogFooter>
77+
</AlertDialogContent>
78+
</AlertDialog>
79+
)
80+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
"use client"
2+
import { useQuery } from "@tanstack/react-query"
3+
import { format } from "date-fns"
4+
import { useTRPC } from "@/lib/trpc/client"
5+
import type { ApiOutput } from "@/lib/trpc/types"
6+
import { DeleteGrant } from "./delete-grant"
7+
8+
type Grants = NonNullable<ApiOutput["tg"]["grants"]["getOngoing"]["grants"]>
9+
10+
export function GrantList() {
11+
const trpc = useTRPC()
12+
const { data } = useQuery(trpc.tg.grants.getOngoing.queryOptions())
13+
14+
return (
15+
<div className="flex flex-col w-full items-start justify-start py-4">
16+
<div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2 font-bold">
17+
<p>Telegram ID</p>
18+
<p>Username</p>
19+
<p>Start Date</p>
20+
<p>End Date</p>
21+
<p>Interrupt</p>
22+
</div>
23+
{data?.grants?.map((r) => (
24+
<GrantRow row={r} key={r.grant.id} />
25+
))}
26+
</div>
27+
)
28+
}
29+
30+
function GrantRow({ row: r }: { row: Grants[number] }) {
31+
return (
32+
<div className="grid gap-4 items-center grid-cols-5 border-b py-2 w-full">
33+
<p>{r.grant.userId}</p>
34+
<p className={r.user?.username ? "" : "text-muted-foreground italic"}>
35+
{r.user?.username ? `@${r.user.username}` : `<unset>`}
36+
</p>
37+
<p>{format(r.grant.validSince, "yyyy/MM/dd HH:mm")}</p>
38+
<p>{format(r.grant.validUntil, "yyyy/MM/dd HH:mm")}</p>
39+
<DeleteGrant userId={r.grant.userId} />
40+
</div>
41+
)
42+
}

0 commit comments

Comments
 (0)