Skip to content

Commit bdbef68

Browse files
checkpoint: groups
1 parent 4b3272d commit bdbef68

3 files changed

Lines changed: 147 additions & 162 deletions

File tree

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"use client"
2+
import { Copy, Pen } from "lucide-react"
3+
import { toast } from "sonner"
4+
import { Badge } from "@/components/ui/badge"
5+
import { Button } from "@/components/ui/button"
6+
import type { TgGroup } from "@/server/trpc/types"
7+
8+
export function GroupRow({ row: r }: { row: TgGroup }) {
9+
// const trpc = useTRPC()
10+
// const { mutateAsync: hideMutate } = useMutation(trpc.tg.groups.setHide.mutationOptions())
11+
//
12+
// async function toggleHide() {
13+
// const ok = await hideMutate({ telegramId: r.telegramId, hide: !r.hide }).catch(() => false)
14+
// if (!ok) toast.error("The field cannot be modified")
15+
//
16+
// toast.success("Hide option toggled!")
17+
// invalidate()
18+
// }
19+
20+
return (
21+
<div className="grid gap-4 items-center grid-cols-5 border-b py-2 w-full">
22+
<p>{r.telegramId}</p>
23+
<p>{r.title}</p>
24+
<p className={r.tag ? "" : "text-muted-foreground italic"}>{r.tag ? `@${r.tag}` : `<unset>`}</p>
25+
<div className="flex items-center justify-start gap-2">
26+
{r.link && (
27+
<a
28+
href={r.link}
29+
target="_blank"
30+
rel="noopener noreferrer"
31+
aria-label={`Link for group ${r.title}`}
32+
className="hover:underline text-xs"
33+
>
34+
{r.link}
35+
</a>
36+
)}
37+
<Button
38+
type="button"
39+
variant="outline"
40+
size="icon-sm"
41+
className={!r.link ? "hidden" : ""}
42+
onClick={async () => {
43+
try {
44+
if (!r.link) return
45+
await navigator.clipboard.writeText(r.link)
46+
toast.success("Link copied to clipboard!")
47+
} catch (err) {
48+
toast.error("Cannot copy link to clipboard")
49+
console.error(err)
50+
}
51+
}}
52+
>
53+
<Copy />
54+
</Button>
55+
</div>
56+
<div className="flex items-center justify-start gap-2">
57+
<p>{r.hide ? <Badge className="bg-yellow-800">HIDDEN</Badge> : <Badge variant="secondary">Visibile</Badge>}</p>
58+
<Button
59+
type="button"
60+
variant="outline"
61+
size="icon-sm"
62+
className={!r.link ? "hidden" : ""}
63+
onClick={() => null /*toggleHide*/}
64+
>
65+
<Pen />
66+
</Button>
67+
</div>
68+
</div>
69+
)
70+
}
Lines changed: 34 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,162 +1,34 @@
1-
// "use client"
2-
// import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
3-
// import { ArrowLeft, Copy, Pen, RefreshCcw, Search, X } from "lucide-react"
4-
// import Link from "next/link"
5-
// import { useState } from "react"
6-
// import { toast } from "sonner"
7-
// import { Badge } from "@/components/ui/badge"
8-
// import { Button } from "@/components/ui/button"
9-
// import { Input } from "@/components/ui/input"
10-
// import { Label } from "@/components/ui/label"
11-
// import { useTRPC } from "@/server/trpc"
12-
// import type { ApiOutput } from "@/server/trpc/types"
13-
//
14-
// type Groups = ApiOutput["tg"]["groups"]["search"]["groups"]
15-
//
16-
// export default function TgGroups() {
17-
// const [query, setQuery] = useState("")
18-
//
19-
// const trpc = useTRPC()
20-
// const qc = useQueryClient()
21-
// const queryOpts = trpc.tg.groups.search.queryOptions({ query, limit: 20, showHidden: true })
22-
// const { data: allGroups, isLoading, refetch } = useQuery(trpc.tg.groups.getAll.queryOptions())
23-
//
24-
// const [rows, setRows] = useState<Groups>(!isLoading ? (allGroups ?? []) : [])
25-
//
26-
// async function search() {
27-
// const res = await qc.fetchQuery(queryOpts)
28-
// setRows(res.groups)
29-
// return res
30-
// }
31-
//
32-
// async function invalidate() {
33-
// await qc.invalidateQueries(queryOpts)
34-
// refetch()
35-
// await search()
36-
// }
37-
//
38-
// async function submit(e: React.FormEvent<HTMLFormElement>) {
39-
// e.preventDefault()
40-
//
41-
// const res = await search()
42-
// if (res.count === 0) toast.warning("No groups found with this query")
43-
// else toast.info(`Found ${res.count} groups`)
44-
// }
45-
//
46-
// function reset() {
47-
// setRows(allGroups ?? [])
48-
// setQuery("")
49-
// }
50-
//
51-
// return (
52-
// <div className="container p-8">
53-
// <Link href="/dashboard/telegram" className="flex gap-1 items-center text-muted-foreground mb-2 hover:underline">
54-
// <ArrowLeft size={16} /> Back
55-
// </Link>
56-
// <form onSubmit={submit} className="pt-2 gap-y-4 flex flex-col justify-start items-start">
57-
// <div className="flex gap-2 flex-col items-start justify-start">
58-
// <Label htmlFor="email" className="text-base">
59-
// Search
60-
// </Label>
61-
// <div className="flex gap-2 items-center justify-start">
62-
// <Input
63-
// id="group-query"
64-
// type="text"
65-
// placeholder="Group name"
66-
// className="bg-card w-auto"
67-
// required
68-
// onChange={(e) => {
69-
// setQuery(e.target.value)
70-
// }}
71-
// value={query}
72-
// />
73-
// <Button type="submit" size="icon">
74-
// <Search />
75-
// </Button>
76-
// {rows.length > 0 && (
77-
// <Button variant="outline" onClick={reset}>
78-
// <X />
79-
// Reset
80-
// </Button>
81-
// )}
82-
// <Button variant="outline" onClick={invalidate}>
83-
// <RefreshCcw />
84-
// Refresh
85-
// </Button>
86-
// </div>
87-
// <span className="text-muted-foreground text-xs">Max results: 20</span>
88-
// </div>
89-
// </form>
90-
// <div className="flex flex-col w-full items-start justify-start py-4">
91-
// <div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2">
92-
// <p>telegram ID</p>
93-
// <p>Title</p>
94-
// <p>Tag</p>
95-
// <p>Invite Link</p>
96-
// <p>Hide</p>
97-
// </div>
98-
// {rows.map((r) => (
99-
// <GroupRow row={r} key={r.telegramId} invalidate={invalidate} />
100-
// ))}
101-
// </div>
102-
// </div>
103-
// )
104-
// }
105-
//
106-
// function GroupRow({ row: r, invalidate }: { row: Groups[number]; invalidate: () => void }) {
107-
// const trpc = useTRPC()
108-
// const { mutateAsync: hideMutate } = useMutation(trpc.tg.groups.setHide.mutationOptions())
109-
//
110-
// async function toggleHide() {
111-
// const ok = await hideMutate({ telegramId: r.telegramId, hide: !r.hide }).catch(() => false)
112-
// if (!ok) toast.error("The field cannot be modified")
113-
//
114-
// toast.success("Hide option toggled!")
115-
// invalidate()
116-
// }
117-
//
118-
// return (
119-
// <div className="grid gap-4 items-center grid-cols-5 border-b py-2 w-full">
120-
// <p>{r.telegramId}</p>
121-
// <p>{r.title}</p>
122-
// <p className={r.tag ? "" : "text-muted-foreground italic"}>{r.tag ? `@${r.tag}` : `<unset>`}</p>
123-
// <div className="flex items-center justify-start gap-2">
124-
// {r.link && (
125-
// <a
126-
// href={r.link}
127-
// target="_blank"
128-
// rel="noopener noreferrer"
129-
// aria-label={`Link for group ${r.title}`}
130-
// className="hover:underline text-xs"
131-
// >
132-
// {r.link}
133-
// </a>
134-
// )}
135-
// <Button
136-
// type="button"
137-
// variant="outline"
138-
// size="icon-sm"
139-
// className={!r.link ? "hidden" : ""}
140-
// onClick={async () => {
141-
// try {
142-
// if (!r.link) return
143-
// await navigator.clipboard.writeText(r.link)
144-
// toast.success("Link copied to clipboard!")
145-
// } catch (err) {
146-
// toast.error("Cannot copy link to clipboard")
147-
// console.error(err)
148-
// }
149-
// }}
150-
// >
151-
// <Copy />
152-
// </Button>
153-
// </div>
154-
// <div className="flex items-center justify-start gap-2">
155-
// <p>{r.hide ? <Badge className="bg-yellow-800">HIDDEN</Badge> : <Badge variant="secondary">Visibile</Badge>}</p>
156-
// <Button type="button" variant="outline" size="icon-sm" className={!r.link ? "hidden" : ""} onClick={toggleHide}>
157-
// <Pen />
158-
// </Button>
159-
// </div>
160-
// </div>
161-
// )
162-
// }
1+
"use server"
2+
import { ArrowLeft } from "lucide-react"
3+
import Link from "next/link"
4+
import { trpc } from "@/server/trpc"
5+
import { GroupRow } from "./group-row"
6+
import { SearchInput } from "./search-input"
7+
8+
export default async function TgGroups({ searchParams }: { searchParams: Promise<{ q?: string }> }) {
9+
const { q } = await searchParams
10+
11+
const all = await trpc.tg.groups.getAll.query()
12+
const rows = q ? (await trpc.tg.groups.search.query({ limit: 20, query: q, showHidden: true })).groups : all
13+
14+
return (
15+
<div className="container p-8">
16+
<Link href="/dashboard/telegram" className="flex gap-1 items-center text-muted-foreground mb-2 hover:underline">
17+
<ArrowLeft size={16} /> Back
18+
</Link>
19+
<SearchInput />
20+
<div className="flex flex-col w-full items-start justify-start py-4">
21+
<div className="grid gap-4 items-center grid-cols-5 w-full border-b py-2">
22+
<p>telegram ID</p>
23+
<p>Title</p>
24+
<p>Tag</p>
25+
<p>Invite Link</p>
26+
<p>Hide</p>
27+
</div>
28+
{rows.map((r) => (
29+
<GroupRow row={r} key={r.telegramId} />
30+
))}
31+
</div>
32+
</div>
33+
)
34+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client"
2+
3+
import { RefreshCcw, Search, X } from "lucide-react"
4+
import { useRouter, useSearchParams } from "next/navigation"
5+
import { useState } from "react"
6+
import { Button } from "@/components/ui/button"
7+
import { Input } from "@/components/ui/input"
8+
9+
export function SearchInput() {
10+
const router = useRouter()
11+
const searchParams = useSearchParams()
12+
const search = searchParams.get("q")
13+
const [value, setValue] = useState(searchParams.get("q") ?? "")
14+
15+
const handleSearch = () => {
16+
const params = new URLSearchParams(searchParams.toString())
17+
if (value) {
18+
params.set("q", value)
19+
} else {
20+
params.delete("q")
21+
}
22+
router.replace(`?${params.toString()}`)
23+
}
24+
25+
return (
26+
<div className="flex gap-2">
27+
<Input
28+
value={value}
29+
onChange={(e) => setValue(e.target.value)}
30+
onKeyDown={(e) => e.key === "Enter" && handleSearch()}
31+
placeholder="Search..."
32+
/>
33+
<Button onClick={handleSearch}>
34+
<Search />
35+
Search
36+
</Button>
37+
<Button onClick={() => router.refresh()} variant="outline">
38+
<RefreshCcw />
39+
Refresh
40+
</Button>
41+
</div>
42+
)
43+
}

0 commit comments

Comments
 (0)