Skip to content

Commit c1ab84c

Browse files
feat: scheduled grants and loading page
1 parent 37af9e6 commit c1ab84c

6 files changed

Lines changed: 169 additions & 17 deletions

File tree

package.json

Lines changed: 1 addition & 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.17",
19+
"@polinetwork/backend": "^0.15.19",
2020
"@radix-ui/react-dialog": "^1.1.15",
2121
"@t3-oss/env-nextjs": "^0.13.10",
2222
"@tanstack/react-table": "^8.21.2",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/app/dashboard/(active)/telegram/grants/grant-list.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { DeleteGrant } from "./delete-grant"
55

66
type Grants = NonNullable<ApiOutput["tg"]["grants"]["getOngoing"]["grants"]>
77

8-
export function GrantList({ grants }: { grants: Grants }) {
8+
export function GrantList({ grants, isScheduled }: { grants: Grants; isScheduled?: boolean }) {
99
return (
1010
<div className="flex flex-col w-full items-start justify-start py-4">
11-
<div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2 font-bold">
11+
<div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2 font-semibold">
1212
<p>Telegram ID</p>
1313
<p>Username</p>
1414
<p>Start Date</p>
@@ -18,7 +18,11 @@ export function GrantList({ grants }: { grants: Grants }) {
1818
{grants.map((r) => (
1919
<GrantRow row={r} key={r.grant.id} />
2020
))}
21-
{grants.length === 0 && <div className="w-full text-center py-2 italic">There are no ongoing grants</div>}
21+
{grants.length === 0 && (
22+
<div className="w-full text-center py-2 italic">
23+
There are no {isScheduled ? "scheduled" : "ongoing"} grants
24+
</div>
25+
)}
2226
</div>
2327
)
2428
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { Skeleton } from "@/components/ui/skeleton"
2+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
3+
import { NewGrant } from "./new-grant"
4+
5+
export default async function Loading() {
6+
return (
7+
<div className="container p-8 h-full">
8+
<div className="py-4 flex gap-4 justify-start items-center">
9+
<p>Telegram Grants</p>
10+
<NewGrant />
11+
</div>
12+
13+
<Tabs defaultValue="all">
14+
<TabsList>
15+
<TabsTrigger className="w-30" value="all">
16+
All
17+
</TabsTrigger>
18+
<TabsTrigger value="ongoing">Ongoing</TabsTrigger>
19+
<TabsTrigger value="scheduled">Scheduled</TabsTrigger>
20+
</TabsList>
21+
22+
<TabsContent value="all">
23+
<Content />
24+
</TabsContent>
25+
<TabsContent value="ongoing">
26+
<Content />
27+
</TabsContent>
28+
<TabsContent value="scheduled">
29+
<Content />
30+
</TabsContent>
31+
</Tabs>
32+
</div>
33+
)
34+
}
35+
36+
function Content() {
37+
return (
38+
<div className="flex flex-col w-full items-start justify-start py-4">
39+
<div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2 font-semibold">
40+
<p>Telegram ID</p>
41+
<p>Username</p>
42+
<p>Start Date</p>
43+
<p>End Date</p>
44+
<p>Interrupt</p>
45+
</div>
46+
{new Array(6).fill(0).map((_, i) => (
47+
<Skeleton key={i} className="h-10 w-full my-2" />
48+
))}
49+
</div>
50+
)
51+
}
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,37 @@
1-
import { ArrowLeft } from "lucide-react"
2-
import Link from "next/link"
1+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
32
import { trpc } from "@/server/trpc"
43
import { GrantList } from "./grant-list"
54
import { NewGrant } from "./new-grant"
65

76
export default async function GrantsPage() {
8-
const { grants } = await trpc.tg.grants.getOngoing.query()
7+
const { grants: ongoing } = await trpc.tg.grants.getOngoing.query()
8+
const { grants: scheduled } = await trpc.tg.grants.getScheduled.query()
99

1010
return (
11-
<div className="container p-8">
12-
<Link href="/dashboard/telegram" className="flex gap-1 items-center text-muted-foreground mb-2 hover:underline">
13-
<ArrowLeft size={16} /> Back
14-
</Link>
11+
<div className="container p-8 h-full">
1512
<div className="py-4 flex gap-4 justify-start items-center">
1613
<p>Telegram Grants</p>
1714
<NewGrant />
1815
</div>
19-
<GrantList grants={grants} />
16+
17+
<Tabs defaultValue="all">
18+
<TabsList>
19+
<TabsTrigger className="w-30" value="all">
20+
All
21+
</TabsTrigger>
22+
<TabsTrigger value="ongoing">Ongoing</TabsTrigger>
23+
<TabsTrigger value="scheduled">Scheduled</TabsTrigger>
24+
</TabsList>
25+
<TabsContent value="all">
26+
<GrantList grants={[...ongoing, ...scheduled]} />
27+
</TabsContent>
28+
<TabsContent value="ongoing">
29+
<GrantList grants={ongoing} />
30+
</TabsContent>
31+
<TabsContent value="scheduled">
32+
<GrantList grants={scheduled} />
33+
</TabsContent>
34+
</Tabs>
2035
</div>
2136
)
2237
}

src/components/ui/tabs.tsx

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
"use client"
2+
3+
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs"
4+
import { cva, type VariantProps } from "class-variance-authority"
5+
6+
import { cn } from "@/lib/utils/shadcn"
7+
8+
function Tabs({
9+
className,
10+
orientation = "horizontal",
11+
...props
12+
}: TabsPrimitive.Root.Props) {
13+
return (
14+
<TabsPrimitive.Root
15+
data-slot="tabs"
16+
data-orientation={orientation}
17+
className={cn(
18+
"group/tabs flex gap-2 data-horizontal:flex-col",
19+
className
20+
)}
21+
{...props}
22+
/>
23+
)
24+
}
25+
26+
const tabsListVariants = cva(
27+
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-8 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none",
28+
{
29+
variants: {
30+
variant: {
31+
default: "bg-background border",
32+
line: "gap-1 bg-transparent",
33+
},
34+
},
35+
defaultVariants: {
36+
variant: "default",
37+
},
38+
}
39+
)
40+
41+
function TabsList({
42+
className,
43+
variant = "default",
44+
...props
45+
}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
46+
return (
47+
<TabsPrimitive.List
48+
data-slot="tabs-list"
49+
data-variant={variant}
50+
className={cn(tabsListVariants({ variant }), className)}
51+
{...props}
52+
/>
53+
)
54+
}
55+
56+
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
57+
return (
58+
<TabsPrimitive.Tab
59+
data-slot="tabs-trigger"
60+
className={cn(
61+
"relative not-data-active:cursor-pointer inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-1.5 py-0.5 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1 has-data-[icon=inline-start]:pl-1 aria-disabled:pointer-events-none aria-disabled:opacity-50 dark:text-muted-foreground/80 dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
62+
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
63+
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-accent dark:data-active:text-foreground",
64+
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
65+
className
66+
)}
67+
{...props}
68+
/>
69+
)
70+
}
71+
72+
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
73+
return (
74+
<TabsPrimitive.Panel
75+
data-slot="tabs-content"
76+
className={cn("flex-1 text-sm outline-none", className)}
77+
{...props}
78+
/>
79+
)
80+
}
81+
82+
export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants }

0 commit comments

Comments
 (0)