diff --git a/package.json b/package.json index 7140bef..b4e204c 100644 --- a/package.json +++ b/package.json @@ -16,15 +16,12 @@ "@base-ui/react": "^1.3.0", "@better-auth/passkey": "^1.5.5", "@hookform/resolvers": "^3.9.1", - "@polinetwork/backend": "^0.15.15", + "@polinetwork/backend": "^0.15.16", "@radix-ui/react-dialog": "^1.1.15", "@t3-oss/env-nextjs": "^0.13.10", - "@tanstack/react-query": "^5.90.19", "@tanstack/react-table": "^8.21.2", "@trpc/client": "11.5.1", "@trpc/next": "11.5.1", - "@trpc/react-query": "11.5.1", - "@trpc/tanstack-react-query": "11.5.1", "babel-plugin-react-compiler": "1.0.0", "better-auth": "^1.5.5", "class-variance-authority": "^0.7.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 56e4e8e..aef74f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,17 +18,14 @@ importers: specifier: ^3.9.1 version: 3.10.0(react-hook-form@7.55.0(react@18.3.1)) '@polinetwork/backend': - specifier: ^0.15.15 - version: 0.15.15 + specifier: ^0.15.16 + version: 0.15.16 '@radix-ui/react-dialog': specifier: ^1.1.15 version: 1.1.15(@types/react-dom@18.3.5(@types/react@18.3.18))(@types/react@18.3.18)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@t3-oss/env-nextjs': specifier: ^0.13.10 version: 0.13.10(typescript@5.7.3)(zod@4.3.5) - '@tanstack/react-query': - specifier: ^5.90.19 - version: 5.90.19(react@18.3.1) '@tanstack/react-table': specifier: ^8.21.2 version: 8.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -38,12 +35,6 @@ importers: '@trpc/next': specifier: 11.5.1 version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(next@15.5.9(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) - '@trpc/react-query': - specifier: 11.5.1 - version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) - '@trpc/tanstack-react-query': - specifier: 11.5.1 - version: 11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3) babel-plugin-react-compiler: specifier: 1.0.0 version: 1.0.0 @@ -850,8 +841,8 @@ packages: resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} engines: {node: '>=20.0.0'} - '@polinetwork/backend@0.15.15': - resolution: {integrity: sha512-cgWO5YOeEBl08EWvf+FpsJlEWHkSxsYUqTHjO3+p58VHggkpbXbF+qqbTUhCmfCUek765ZVxO/08VGOXvoM2QQ==} + '@polinetwork/backend@0.15.16': + resolution: {integrity: sha512-wGJZ3FsJfiYqh0S/pF726aFVNQE+u+ZiN6zUpgjT5D+NaZv3orYwRf5DXvKRm7GjGf+PX/BwXPgTRKnnauv1Ew==} '@radix-ui/number@1.1.1': resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} @@ -1795,16 +1786,6 @@ packages: peerDependencies: typescript: '>=5.7.2' - '@trpc/tanstack-react-query@11.5.1': - resolution: {integrity: sha512-1irzKOXhasMq09pHvLqJPTTwaEULIoNfFtoeLNkLnOVHLGvfHkS9qvpVjinRyW1aiQi7OqFDeaUDrQhtbP6tVA==} - peerDependencies: - '@tanstack/react-query': ^5.80.3 - '@trpc/client': 11.5.1 - '@trpc/server': 11.5.1 - react: '>=18.2.0' - react-dom: '>=18.2.0' - typescript: '>=5.7.2' - '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} @@ -4198,7 +4179,7 @@ snapshots: tslib: 2.8.1 tsyringe: 4.10.0 - '@polinetwork/backend@0.15.15': {} + '@polinetwork/backend@0.15.16': {} '@radix-ui/number@1.1.1': {} @@ -5085,12 +5066,14 @@ snapshots: postcss: 8.5.2 tailwindcss: 4.1.4 - '@tanstack/query-core@5.90.19': {} + '@tanstack/query-core@5.90.19': + optional: true '@tanstack/react-query@5.90.19(react@18.3.1)': dependencies: '@tanstack/query-core': 5.90.19 react: 18.3.1 + optional: true '@tanstack/react-table@8.21.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: @@ -5125,20 +5108,12 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) typescript: 5.7.3 + optional: true '@trpc/server@11.5.1(typescript@5.7.3)': dependencies: typescript: 5.7.3 - '@trpc/tanstack-react-query@11.5.1(@tanstack/react-query@5.90.19(react@18.3.1))(@trpc/client@11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3))(@trpc/server@11.5.1(typescript@5.7.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.3)': - dependencies: - '@tanstack/react-query': 5.90.19(react@18.3.1) - '@trpc/client': 11.5.1(@trpc/server@11.5.1(typescript@5.7.3))(typescript@5.7.3) - '@trpc/server': 11.5.1(typescript@5.7.3) - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - typescript: 5.7.3 - '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 diff --git a/src/app/(auth)/onboarding/no-role/page.tsx b/src/app/(auth)/onboarding/no-role/page.tsx index c890e55..7146387 100644 --- a/src/app/(auth)/onboarding/no-role/page.tsx +++ b/src/app/(auth)/onboarding/no-role/page.tsx @@ -2,7 +2,7 @@ import Image from "next/image" import { redirect } from "next/navigation" import loginSvg2 from "@/assets/svg/login-2.svg" import { Card } from "@/components/ui/card" -import { getQueryClient, trpc } from "@/lib/trpc/server" +import { getUserRoles } from "@/server/actions/users" import { getServerSession } from "@/server/auth" import { Logout } from "../link/logout" @@ -11,8 +11,7 @@ export default async function OnboardingNoRole() { if (!session) redirect("/login") if (!session.user.telegramId) redirect("/onboarding/link") - const qc = getQueryClient() - const { roles } = await qc.fetchQuery(trpc.tg.permissions.getRoles.queryOptions({ userId: session.user.telegramId })) + const { roles } = await getUserRoles(session.user.telegramId) if (roles?.includes("creator")) redirect("/onboarding/unauthorized") if (roles && roles.length > 0) redirect("/dashboard") diff --git a/src/app/dashboard/(active)/account/telegram.tsx b/src/app/dashboard/(active)/account/telegram.tsx index f5261e1..d573433 100644 --- a/src/app/dashboard/(active)/account/telegram.tsx +++ b/src/app/dashboard/(active)/account/telegram.tsx @@ -1,27 +1,17 @@ -"use client" -import { useQuery } from "@tanstack/react-query" -import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" +import { getUserRoles } from "@/server/actions/users" +import { getServerSession } from "@/server/auth" -export function Telegram() { - const { data: session, isPending } = useSession() - if (isPending || !session) return null +export async function Telegram() { + const { data } = await getServerSession() + if (!data || !data.user.telegramId) return null - const { user } = session - if (!user.telegramUsername || !user.telegramId) return null + const { roles } = await getUserRoles(data.user.telegramId) + const length = roles?.length ?? 0 - return -} - -function ShowTelegram({ username, userId }: { username: string; userId: number }) { - const trpc = useTRPC() - const { data, isLoading } = useQuery(trpc.tg.permissions.getRoles.queryOptions({ userId })) return ( <> - @{username} - {!isLoading && data?.roles?.length && ( - (roles: {data.roles.join(", ")}) - )} + {data.user.telegramUsername ? `@${data.user.telegramUsername}` : data.user.id} + {roles && length > 0 && (roles: {roles.join(", ")})} ) } diff --git a/src/app/dashboard/(active)/azure/members/columns.tsx b/src/app/dashboard/(active)/azure/members/columns.tsx index 7476cc2..0673bbf 100644 --- a/src/app/dashboard/(active)/azure/members/columns.tsx +++ b/src/app/dashboard/(active)/azure/members/columns.tsx @@ -1,11 +1,10 @@ "use client" import { createColumnHelper, type Row } from "@tanstack/react-table" import { Badge } from "@/components/ui/badge" -import type { ApiOutput } from "@/lib/trpc/types" -import { SetAssocNumberDialog } from "./_components/set-assoc-number-dialog" +import type { AzureMember } from "@/server/trpc/types" +import { SetAssocNumberDialog } from "./set-assoc-number-dialog" -type ParsedUser = ApiOutput["azure"]["members"]["getAll"][0] -const ch = createColumnHelper() +const ch = createColumnHelper() export const columns = [ ch.accessor("employeeId", { @@ -46,6 +45,6 @@ export const columns = [ }), ] -function RowActions({ row: _ }: { row: Row }) { +function RowActions({ row: _ }: { row: Row }) { return
} diff --git a/src/components/create-assoc-member.tsx b/src/app/dashboard/(active)/azure/members/create-assoc-member.tsx similarity index 81% rename from src/components/create-assoc-member.tsx rename to src/app/dashboard/(active)/azure/members/create-assoc-member.tsx index faba22b..834a927 100644 --- a/src/components/create-assoc-member.tsx +++ b/src/app/dashboard/(active)/azure/members/create-assoc-member.tsx @@ -1,8 +1,10 @@ "use client" -import { useMutation } from "@tanstack/react-query" import { useRouter } from "next/navigation" -import { useState } from "react" +import { useState, useTransition } from "react" import { toast } from "sonner" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" import { Sheet, SheetClose, @@ -12,10 +14,7 @@ import { SheetTitle, SheetTrigger, } from "@/components/ui/sheet" -import { useTRPC } from "@/lib/trpc/client" -import { Button } from "./ui/button" -import { Input } from "./ui/input" -import { Label } from "./ui/label" +import { createAzureMember } from "@/server/actions/azure" export function CreateAssocUser() { const [open, setOpen] = useState(false) @@ -23,11 +22,8 @@ export function CreateAssocUser() { const [lastName, setLastName] = useState("") const [assocNumber, setAssocNumber] = useState("") const [sendTo, setSendTo] = useState("") - const trpc = useTRPC() const router = useRouter() - const { mutateAsync, isPending } = useMutation(trpc.azure.members.create.mutationOptions()) - function handleOpenChange(v: boolean): void { setOpen(v) setFirstName("") @@ -36,11 +32,23 @@ export function CreateAssocUser() { setSendTo("") } - async function handleSubmit(e: React.FormEvent) { - e.preventDefault() + const [pending, startTransition] = useTransition() + + async function handleSubmit() { if (!assocNumber || Number.isNaN(parseInt(assocNumber, 10))) return - const res = await mutateAsync({ assocNumber: parseInt(assocNumber, 10), firstName, lastName, sendEmailTo: sendTo }) + const res = await createAzureMember({ + assocNumber: parseInt(assocNumber, 10), + firstName, + lastName, + sendEmailTo: sendTo, + }).catch(() => null) + + if (!res) { + toast.error("There was an error") + return + } + if (res.error !== null) { toast.error(res.error) return @@ -59,7 +67,7 @@ export function CreateAssocUser() { Create new member account -
+ startTransition(handleSubmit)}>
@@ -117,11 +125,11 @@ export function CreateAssocUser() {
- - + diff --git a/src/app/dashboard/(active)/azure/members/page.tsx b/src/app/dashboard/(active)/azure/members/page.tsx index 84e7e68..1b38126 100644 --- a/src/app/dashboard/(active)/azure/members/page.tsx +++ b/src/app/dashboard/(active)/azure/members/page.tsx @@ -3,12 +3,12 @@ import Link from "next/link" import { Suspense } from "react" import { ErrorBoundary } from "react-error-boundary" import { Spinner } from "@/components/spinner" -import { getQueryClient, trpc } from "@/lib/trpc/server" +import { getAzureMembers } from "@/server/actions/azure" import { AssocTable } from "./table" export default async function AssocMembers() { - const qc = getQueryClient() - void qc.prefetchQuery(trpc.azure.members.getAll.queryOptions()) + const members = await getAzureMembers() + return (
@@ -16,7 +16,7 @@ export default async function AssocMembers() { Something went wrong
}> }> - +
diff --git a/src/app/dashboard/(active)/azure/members/_components/set-assoc-number-dialog.tsx b/src/app/dashboard/(active)/azure/members/set-assoc-number-dialog.tsx similarity index 68% rename from src/app/dashboard/(active)/azure/members/_components/set-assoc-number-dialog.tsx rename to src/app/dashboard/(active)/azure/members/set-assoc-number-dialog.tsx index ace742d..ae3e7dc 100644 --- a/src/app/dashboard/(active)/azure/members/_components/set-assoc-number-dialog.tsx +++ b/src/app/dashboard/(active)/azure/members/set-assoc-number-dialog.tsx @@ -1,5 +1,6 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query" -import { useState } from "react" +"use client" +import { useRouter } from "next/navigation" +import { useState, useTransition } from "react" import { toast } from "sonner" import { Button } from "@/components/ui/button" import { @@ -14,29 +15,26 @@ import { } from "@/components/ui/dialog" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" -import { useTRPC } from "@/lib/trpc/client" import { wait } from "@/lib/utils" +import { setAzureMemberNumber } from "@/server/actions/azure" export function SetAssocNumberDialog({ userId }: { userId: string }) { + const router = useRouter() const [value, setValue] = useState("") const [open, setOpen] = useState(false) - const trpc = useTRPC() - - const qc = useQueryClient() - const { mutateAsync, isPending } = useMutation(trpc.azure.members.setAssocNumber.mutationOptions()) function handleOpenChange(v: boolean): void { setOpen(v) setValue("") } - async function handleSubmit(e: React.FormEvent) { - console.log("submit") - e.preventDefault() - if (isPending || !value || Number.isNaN(parseInt(value, 10))) return + const [pending, startTransition] = useTransition() + + async function handleSubmit() { + if (pending || !value || Number.isNaN(parseInt(value, 10))) return const loading = toast.loading(`Updating...`) - const res = await mutateAsync({ userId, assocNumber: parseInt(value, 10) }) + const res = await setAzureMemberNumber({ userId, assocNumber: parseInt(value, 10) }) handleOpenChange(false) if (res.error !== null) { toast.error("There was an error") @@ -45,8 +43,8 @@ export function SetAssocNumberDialog({ userId }: { userId: string }) { } await wait(2000) - await qc.refetchQueries({ queryKey: trpc.azure.members.getAll.queryKey() }) toast.success(`Updated successfully!`, { id: loading }) + router.refresh() console.log("Updated user assocNumber", userId, value) } @@ -64,7 +62,7 @@ export function SetAssocNumberDialog({ userId }: { userId: string }) { Set Assoc Number This changes the `employeeId` field in the Azure User properties. -
+ startTransition(handleSubmit)}>
- - - - + } + > + diff --git a/src/app/dashboard/(active)/azure/members/table.tsx b/src/app/dashboard/(active)/azure/members/table.tsx index c2d8b65..2282f5d 100644 --- a/src/app/dashboard/(active)/azure/members/table.tsx +++ b/src/app/dashboard/(active)/azure/members/table.tsx @@ -1,16 +1,13 @@ "use client" -import { useSuspenseQuery } from "@tanstack/react-query" import { useState } from "react" -import { CreateAssocUser } from "@/components/create-assoc-member" import { DataTable } from "@/components/data-table" import { Badge } from "@/components/ui/badge" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiOutput } from "@/lib/trpc/types" +import type { AzureMember } from "@/server/trpc/types" import { columns } from "./columns" +import { CreateAssocUser } from "./create-assoc-member" -type ParsedUser = ApiOutput["azure"]["members"]["getAll"][0] -function sortByAssocNumber(users: ParsedUser[]) { +function sortByAssocNumber(users: AzureMember[]) { if (!users || users.length === 0) return users if (users.every((u) => !u.employeeId)) return users.sort((a, b) => (a.displayName ?? "").localeCompare(b.displayName ?? "")) @@ -30,18 +27,16 @@ function sortByAssocNumber(users: ParsedUser[]) { }) } -export function AssocTable() { - const trpc = useTRPC() - const { data } = useSuspenseQuery(trpc.azure.members.getAll.queryOptions()) +export function AssocTable({ members }: { members: AzureMember[] }) { const [sociFilter, setSociFilter] = useState(false) - const users = sociFilter ? data.filter((v) => v.isMember) : data + const users = sociFilter ? members.filter((v) => v.isMember) : members return (

Utenti MS @polinetwork.org

- setSociFilter((v) => !v)}>{data.filter((d) => d.isMember).length} Soci - {data.filter((d) => !d.isMember).length} fuori + setSociFilter((v) => !v)}>{members.filter((d) => d.isMember).length} Soci + {members.filter((d) => !d.isMember).length} fuori {users.filter((u) => u.assignedLicensesIds.includes("OFFICE_365")).length} licenze Office
diff --git a/src/app/dashboard/(active)/telegram/grants/delete-grant.tsx b/src/app/dashboard/(active)/telegram/grants/delete-grant.tsx index 286f0b2..8743e65 100644 --- a/src/app/dashboard/(active)/telegram/grants/delete-grant.tsx +++ b/src/app/dashboard/(active)/telegram/grants/delete-grant.tsx @@ -1,7 +1,7 @@ "use client" -import { useMutation, useQueryClient } from "@tanstack/react-query" -import { OctagonX, Square, Trash2, Trash2Icon } from "lucide-react" +import { OctagonX } from "lucide-react" +import { useRouter } from "next/navigation" import { useState } from "react" import { toast } from "sonner" import { @@ -17,37 +17,34 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" -import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" - -export function DeleteGrant({ userId }: { userId: number }) { - const sesh = useSession() - - const qc = useQueryClient() - const trpc = useTRPC() - const removerId = sesh.data?.user.telegramId - const { mutateAsync } = useMutation(trpc.tg.grants.interrupt.mutationOptions()) +import { interruptGrant } from "@/server/actions/grants" +export function DeleteGrant({ userId, onDelete }: { userId: number; onDelete(): void }) { + const router = useRouter() const [open, setOpen] = useState(false) async function interrupt() { - if (!removerId) return toast.error("Invalid session, try to reload the page") - const { error } = await mutateAsync({ userId, interruptedById: removerId, sendTgLog: true }) - - if (error === "NOT_FOUND") toast.info("The grant was expired or already interrupted") - else if (error === "UNAUTHORIZED") toast.error("You don't have enought permission") - else if (error === "INTERNAL_SERVER_ERROR") toast.error("There was an internal server error") - else toast.success("Grant interrupted successfully") + try { + const { error } = await interruptGrant(userId) - handleOpenChange(false) + if (error === "NOT_FOUND") toast.info("The grant was expired or already interrupted") + else if (error === "UNAUTHORIZED") toast.error("You don't have enough permission") + else if (error === "INTERNAL_SERVER_ERROR") toast.error("There was an internal server error") + else { + toast.success("Grant interrupted successfully") + router.refresh() + onDelete() + } + } catch (err) { + toast.error("There was an error") + console.error(err) + } finally { + handleOpenChange(false) + } } function handleOpenChange(v: boolean) { setOpen(v) - if (v === false) { - qc.invalidateQueries(trpc.tg.grants.getOngoing.queryOptions()) - qc.invalidateQueries(trpc.tg.grants.checkUser.queryOptions({ userId })) - } } return ( diff --git a/src/app/dashboard/(active)/telegram/grants/grant-list.tsx b/src/app/dashboard/(active)/telegram/grants/grant-list.tsx index 0516719..328e6aa 100644 --- a/src/app/dashboard/(active)/telegram/grants/grant-list.tsx +++ b/src/app/dashboard/(active)/telegram/grants/grant-list.tsx @@ -1,16 +1,11 @@ "use client" -import { useQuery } from "@tanstack/react-query" import { format } from "date-fns" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiOutput } from "@/lib/trpc/types" +import type { ApiOutput } from "@/server/trpc/types" import { DeleteGrant } from "./delete-grant" type Grants = NonNullable -export function GrantList() { - const trpc = useTRPC() - const { data } = useQuery(trpc.tg.grants.getOngoing.queryOptions()) - +export function GrantList({ grants }: { grants: Grants }) { return (
@@ -20,10 +15,10 @@ export function GrantList() {

End Date

Interrupt

- {data?.grants?.map((r) => ( + {grants.map((r) => ( ))} - {data?.grants.length === 0 &&
There are no ongoing grants
} + {grants.length === 0 &&
There are no ongoing grants
}
) } @@ -37,7 +32,7 @@ function GrantRow({ row: r }: { row: Grants[number] }) {

{format(r.grant.validSince, "yyyy/MM/dd HH:mm")}

{format(r.grant.validUntil, "yyyy/MM/dd HH:mm")}

- + null} />
) } diff --git a/src/app/dashboard/(active)/telegram/grants/new-grant.tsx b/src/app/dashboard/(active)/telegram/grants/new-grant.tsx index 34649e8..3f839aa 100644 --- a/src/app/dashboard/(active)/telegram/grants/new-grant.tsx +++ b/src/app/dashboard/(active)/telegram/grants/new-grant.tsx @@ -1,8 +1,8 @@ "use client" -import { useMutation, useQueryClient } from "@tanstack/react-query" import { format } from "date-fns" import { ChevronDownIcon, Plus } from "lucide-react" +import { useRouter } from "next/navigation" import { useCallback, useEffect, useState } from "react" import { toast } from "sonner" import { Button } from "@/components/ui/button" @@ -33,17 +33,14 @@ import { } from "@/components/ui/select" import { UserSelect } from "@/components/user-select" import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" -import type { TgUser } from "@/lib/trpc/types" +import { createGrant } from "@/server/actions/grants" +import type { TgUser } from "@/server/trpc/types" export function NewGrant() { const sesh = useSession() + const router = useRouter() const adderId = sesh.data?.user.telegramId - const trpc = useTRPC() - const qc = useQueryClient() - const mutation = useMutation(trpc.tg.grants.create.mutationOptions()) - const [open, setOpen] = useState(false) const [user, setUser] = useState(null) const [startDate, setStartDate] = useState() @@ -59,8 +56,6 @@ export function NewGrant() { setStartDate(undefined) setEndDate(undefined) setReason("") - qc.invalidateQueries(trpc.tg.grants.getOngoing.queryOptions()) - if (user) qc.invalidateQueries(trpc.tg.grants.checkUser.queryOptions({ userId: user.id })) } } @@ -81,19 +76,21 @@ export function NewGrant() { async function create() { if (!user || !startDate || !endDate || !adderId) return - const res = await mutation.mutateAsync({ + const res = await createGrant({ userId: user.id, - adderId, since: startDate, until: endDate, reason: reason || undefined, - sendTgLog: true, }) if (res.error === "UNAUTHORIZED") toast.error("You don't have permission to create grants.") else if (res.error === "ALREADY_EXISTING") toast.error("This user already has an ongoing grant.") else if (res.error === "INTERNAL_SERVER_ERROR") toast.error("There was a server error.") - else toast.success("Grant created successfully!") + else { + toast.success("Grant created successfully!") + router.refresh() + } + handleOpenChange(false) } diff --git a/src/app/dashboard/(active)/telegram/grants/page.tsx b/src/app/dashboard/(active)/telegram/grants/page.tsx index d76971b..39537ee 100644 --- a/src/app/dashboard/(active)/telegram/grants/page.tsx +++ b/src/app/dashboard/(active)/telegram/grants/page.tsx @@ -1,9 +1,12 @@ import { ArrowLeft } from "lucide-react" import Link from "next/link" +import { trpc } from "@/server/trpc" import { GrantList } from "./grant-list" import { NewGrant } from "./new-grant" -export default function GrantsPage() { +export default async function GrantsPage() { + const { grants } = await trpc.tg.grants.getOngoing.query() + return (
@@ -13,7 +16,7 @@ export default function GrantsPage() {

Telegram Grants

- +
) } diff --git a/src/app/dashboard/(active)/telegram/groups/group-row.tsx b/src/app/dashboard/(active)/telegram/groups/group-row.tsx new file mode 100644 index 0000000..db84982 --- /dev/null +++ b/src/app/dashboard/(active)/telegram/groups/group-row.tsx @@ -0,0 +1,67 @@ +"use client" +import { Copy, Pen } from "lucide-react" +import { useRouter } from "next/navigation" +import { toast } from "sonner" +import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" +import { setGroupHide } from "@/server/actions/groups" +import type { TgGroup } from "@/server/trpc/types" + +export function GroupRow({ row: r }: { row: TgGroup }) { + const router = useRouter() + + async function toggleHide() { + const ok = await setGroupHide(r.telegramId, !r.hide).catch(() => false) + if (!ok) { + toast.error("The field cannot be modified") + } else { + toast.success("Hide option toggled!") + router.refresh() + } + } + + return ( +
+

{r.telegramId}

+

{r.title}

+

{r.tag ? `@${r.tag}` : ``}

+
+ {r.link && ( + + {r.link} + + )} + +
+
+

{r.hide ? HIDDEN : Visibile}

+ +
+
+ ) +} diff --git a/src/app/dashboard/(active)/telegram/groups/page.tsx b/src/app/dashboard/(active)/telegram/groups/page.tsx index 914fd63..68366b8 100644 --- a/src/app/dashboard/(active)/telegram/groups/page.tsx +++ b/src/app/dashboard/(active)/telegram/groups/page.tsx @@ -1,92 +1,27 @@ -"use client" -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query" -import { ArrowLeft, Copy, Pen, RefreshCcw, Search, X } from "lucide-react" +import { ArrowLeft } from "lucide-react" import Link from "next/link" -import { useState } from "react" -import { toast } from "sonner" -import { Badge } from "@/components/ui/badge" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiOutput } from "@/lib/trpc/types" +import { trpc } from "@/server/trpc" +import { GroupRow } from "./group-row" +import { SearchInput } from "./search-input" -type Groups = ApiOutput["tg"]["groups"]["search"]["groups"] +export default async function TgGroups({ searchParams }: { searchParams: Promise<{ q?: string }> }) { + const { q } = await searchParams -export default function TgGroups() { - const [query, setQuery] = useState("") + const rows = q + ? (await trpc.tg.groups.search.query({ limit: 20, query: q, showHidden: true })).groups + : await trpc.tg.groups.getAll.query() - const trpc = useTRPC() - const qc = useQueryClient() - const queryOpts = trpc.tg.groups.search.queryOptions({ query, limit: 20, showHidden: true }) - const { data: allGroups, isLoading, refetch } = useQuery(trpc.tg.groups.getAll.queryOptions()) - - const [rows, setRows] = useState(!isLoading ? (allGroups ?? []) : []) - - async function search() { - const res = await qc.fetchQuery(queryOpts) - setRows(res.groups) - return res - } - - async function invalidate() { - await qc.invalidateQueries(queryOpts) - refetch() - await search() - } - - async function submit(e: React.FormEvent) { - e.preventDefault() - - const res = await search() - if (res.count === 0) toast.warning("No groups found with this query") - else toast.info(`Found ${res.count} groups`) - } - - function reset() { - setRows(allGroups ?? []) - setQuery("") - } + const sorted = rows.sort((a, b) => a.title.localeCompare(b.title)) return (
Back - -
- -
- { - setQuery(e.target.value) - }} - value={query} - /> - - {rows.length > 0 && ( - - )} - -
- Max results: 20 -
- + +

+ Count: {rows.length} +

telegram ID

@@ -95,68 +30,10 @@ export default function TgGroups() {

Invite Link

Hide

- {rows.map((r) => ( - + {sorted.map((r) => ( + ))}
) } - -function GroupRow({ row: r, invalidate }: { row: Groups[number]; invalidate: () => void }) { - const trpc = useTRPC() - const { mutateAsync: hideMutate } = useMutation(trpc.tg.groups.setHide.mutationOptions()) - - async function toggleHide() { - const ok = await hideMutate({ telegramId: r.telegramId, hide: !r.hide }).catch(() => false) - if (!ok) toast.error("The field cannot be modified") - - toast.success("Hide option toggled!") - invalidate() - } - - return ( -
-

{r.telegramId}

-

{r.title}

-

{r.tag ? `@${r.tag}` : ``}

-
- {r.link && ( - - {r.link} - - )} - -
-
-

{r.hide ? HIDDEN : Visibile}

- -
-
- ) -} diff --git a/src/app/dashboard/(active)/telegram/groups/search-input.tsx b/src/app/dashboard/(active)/telegram/groups/search-input.tsx new file mode 100644 index 0000000..82ad573 --- /dev/null +++ b/src/app/dashboard/(active)/telegram/groups/search-input.tsx @@ -0,0 +1,46 @@ +"use client" + +import { RefreshCcw, Search } from "lucide-react" +import { useRouter, useSearchParams } from "next/navigation" +import { useEffect, useState } from "react" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" + +export function SearchInput() { + const router = useRouter() + const searchParams = useSearchParams() + const q = searchParams.get("q") ?? "" + const [value, setValue] = useState(q) + + const handleSearch = () => { + const params = new URLSearchParams(searchParams.toString()) + if (value) { + params.set("q", value) + } else { + params.delete("q") + } + router.replace(`?${params.toString()}`) + } + useEffect(() => { + setValue(q) + }, [q]) + + return ( +
+ setValue(e.target.value)} + onKeyDown={(e) => e.key === "Enter" && handleSearch()} + placeholder="Search..." + /> + + +
+ ) +} diff --git a/src/app/dashboard/(active)/telegram/user-details/add-role.tsx b/src/app/dashboard/(active)/telegram/user-details/add-role.tsx index b9a3997..dfd5685 100644 --- a/src/app/dashboard/(active)/telegram/user-details/add-role.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/add-role.tsx @@ -1,10 +1,9 @@ "use client" import { USER_ROLE } from "@polinetwork/backend" -import { useMutation, useQueryClient } from "@tanstack/react-query" -import { Plus, Search, X } from "lucide-react" -import { useRouter } from "next/navigation" +import { Plus } from "lucide-react" import { useState } from "react" import { toast } from "sonner" +import { Spinner } from "@/components/spinner" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { @@ -18,12 +17,8 @@ import { DialogTrigger, } from "@/components/ui/dialog" import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" -import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiInput, ApiOutput } from "@/lib/trpc/types" - -type User = ApiOutput["tg"]["users"]["getByUsername"]["user"] -type Roles = NonNullable +import { addUserRole } from "@/server/actions/users" +import type { TgUser, TgUserRole } from "@/server/trpc/types" const ARRAY_USER_ROLES = [ USER_ROLE.ADMIN, @@ -34,46 +29,45 @@ const ARRAY_USER_ROLES = [ USER_ROLE.PRESIDENT, ] as const -export function AddRole({ user, alreadyRoles }: { user: User; alreadyRoles: Roles }) { - const sesh = useSession() - const adderId = sesh.data?.user.telegramId - +export function AddRole({ user, alreadyRoles, onAdd }: { user: TgUser; alreadyRoles: TgUserRole[]; onAdd(): void }) { const availableRoles = ARRAY_USER_ROLES.filter((r) => !alreadyRoles.includes(r)).map((g) => ({ value: g, label: `${g.slice(0, 1).toUpperCase()}${g.slice(1)}`, })) - const trpc = useTRPC() - const qc = useQueryClient() - const router = useRouter() - const [open, setOpen] = useState(false) - const [selectedRole, setSelectedRole] = useState(null) - - const submitMutation = useMutation(trpc.tg.permissions.addRole.mutationOptions()) + const [pending, setPending] = useState(false) + const [selectedRole, setSelectedRole] = useState(null) async function submit() { - if (!adderId) return toast.warning("Invalid session, try reloading the page") if (!selectedRole) return toast.warning("No group selected, cannot proceed") if (!user) return toast.warning("Invalid user, try restarting the dialog") + setPending(true) + try { - await submitMutation.mutateAsync({ adderId, userId: user.id, role: selectedRole }) - toast.info(`Role added`) + const { error } = await addUserRole(user.id, selectedRole) + + if (error === "UNAUTHORIZED") toast.error("You don't have enough permission") + else if (error === "INTERNAL_SERVER_ERROR") toast.error("There was an internal server error") + else if (error === "UNAUTHORIZED_SELF_ASSIGN") toast.error("You cannot add roles to yourself") + else { + toast.success(`Role added!`) + onAdd() + } handleOpenChange(false) - router.refresh() } catch (err) { console.error(err) - handleOpenChange(false) toast.error("There was an error, check logs") + } finally { + setPending(false) + handleOpenChange(false) } } function handleOpenChange(v: boolean) { setOpen(v) if (v === false) { - // closing - qc.invalidateQueries(trpc.tg.permissions.getRoles.queryOptions({ userId: user?.id ?? 0 })) setSelectedRole(null) } } @@ -125,9 +119,15 @@ export function AddRole({ user, alreadyRoles }: { user: User; alreadyRoles: Role - Cancel} /> - + } + /> + diff --git a/src/app/dashboard/(active)/telegram/user-details/card-audit-log.tsx b/src/app/dashboard/(active)/telegram/user-details/card-audit-log.tsx index 70cb5a5..681216e 100644 --- a/src/app/dashboard/(active)/telegram/user-details/card-audit-log.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/card-audit-log.tsx @@ -1,15 +1,11 @@ -import { useQuery } from "@tanstack/react-query" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiOutput } from "@/lib/trpc/types" import { fmtUser } from "@/lib/utils/telegram" +import type { searchUser } from "@/server/actions/users" -type Log = NonNullable[number] -export function AuditLogCard({ log: m }: { log: Log }) { - const trpc = useTRPC() - - const { data: admin } = useQuery(trpc.tg.users.get.queryOptions({ userId: m.adminId })) +type Data = Awaited> +type Log = NonNullable["audits"][number] +export function AuditLogCard({ log: m }: { log: Log }) { return ( @@ -21,7 +17,7 @@ export function AuditLogCard({ log: m }: { log: Log }) { {m.groupTitle && {m.groupTitle}} [{m.groupId}]

Admin ID: -

{admin?.user && fmtUser(admin.user)}

+

{m.admin && fmtUser(m.admin)}

{m.createdAt && ( <> diff --git a/src/app/dashboard/(active)/telegram/user-details/card-group-admin.tsx b/src/app/dashboard/(active)/telegram/user-details/card-group-admin.tsx index 693195a..e098242 100644 --- a/src/app/dashboard/(active)/telegram/user-details/card-group-admin.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/card-group-admin.tsx @@ -1,7 +1,7 @@ import { Code } from "@/components/code" import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" -import type { ApiOutput } from "@/lib/trpc/types" import { stripChatId } from "@/lib/utils/telegram" +import type { ApiOutput } from "@/server/trpc/types" import { DeleteGroupAdmin } from "./delete-group-admin" type User = ApiOutput["tg"]["users"]["getByUsername"]["user"] @@ -10,9 +10,11 @@ type GroupAdminSingle = NonNullable groupAdminInfo: GroupAdminSingle + onDelete(): void }) { return ( @@ -30,7 +32,7 @@ export function GroupAdminCard({

- +
) diff --git a/src/app/dashboard/(active)/telegram/user-details/card-message.tsx b/src/app/dashboard/(active)/telegram/user-details/card-message.tsx index b2828da..46603cc 100644 --- a/src/app/dashboard/(active)/telegram/user-details/card-message.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/card-message.tsx @@ -1,8 +1,8 @@ import { ExternalLinkIcon } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardFooter } from "@/components/ui/card" -import type { ApiOutput } from "@/lib/trpc/types" import { stripChatId } from "@/lib/utils/telegram" +import type { ApiOutput } from "@/server/trpc/types" type Message = NonNullable[number] export function MessageCard({ message: m }: { message: Message }) { diff --git a/src/app/dashboard/(active)/telegram/user-details/card-user-grant.tsx b/src/app/dashboard/(active)/telegram/user-details/card-user-grant.tsx index 5809b79..05a80ab 100644 --- a/src/app/dashboard/(active)/telegram/user-details/card-user-grant.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/card-user-grant.tsx @@ -1,34 +1,26 @@ "use client" -import { useQuery } from "@tanstack/react-query" import { format } from "date-fns" -import { Sparkle, Star } from "lucide-react" +import { Sparkle } from "lucide-react" import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" -import { useTRPC } from "@/lib/trpc/client" -import type { TgUser } from "@/lib/trpc/types" +import type { TgGrant, TgUser } from "@/server/trpc/types" import { DeleteGrant } from "../grants/delete-grant" -export function UserGrantCard({ user }: { user: TgUser }) { - const trpc = useTRPC() - const { data } = useQuery(trpc.tg.grants.checkUser.queryOptions({ userId: user.id })) - - if (!data) return null +export function UserGrantCard({ user, grant, onDelete }: { user: TgUser; grant: TgGrant; onDelete(): void }) { return ( - data.grant && ( - - - - Ongoing Grant - - This user has an ongoing grant - - -

Start: {format(data.grant.validSince, "yyyy/MM/dd HH:mm")}

-

End: {format(data.grant.validUntil, "yyyy/MM/dd HH:mm")}

-
- - - -
- ) + + + + Ongoing Grant + + This user has an ongoing grant + + +

Start: {format(grant.validSince, "yyyy/MM/dd HH:mm")}

+

End: {format(grant.validUntil, "yyyy/MM/dd HH:mm")}

+
+ + + +
) } diff --git a/src/app/dashboard/(active)/telegram/user-details/card-user-info.tsx b/src/app/dashboard/(active)/telegram/user-details/card-user-info.tsx index 70e7034..ca2362a 100644 --- a/src/app/dashboard/(active)/telegram/user-details/card-user-info.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/card-user-info.tsx @@ -2,20 +2,28 @@ import { Star } from "lucide-react" import { Badge } from "@/components/ui/badge" import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" import { useSession } from "@/lib/auth" -import type { ApiOutput } from "@/lib/trpc/types" +import type { ApiOutput } from "@/server/trpc/types" import { AddRole } from "./add-role" import { RemoveRole } from "./remove-role" type User = ApiOutput["tg"]["users"]["getByUsername"]["user"] type UserRoles = ApiOutput["tg"]["permissions"]["getRoles"]["roles"] -export function UserInfoCard({ user, roles }: { user: NonNullable; roles: UserRoles }) { +export function UserInfoCard({ + user, + roles, + onUpdate, +}: { + user: NonNullable + roles: UserRoles + onUpdate(): void +}) { const sesh = useSession() const seshUserId = sesh.data?.user.telegramId const isSelf = seshUserId && seshUserId === user.id return ( - + # User ID: {user.id}{" "} @@ -41,8 +49,8 @@ export function UserInfoCard({ user, roles }: { user: NonNullable; roles:
- - + + ) diff --git a/src/app/dashboard/(active)/telegram/user-details/delete-group-admin.tsx b/src/app/dashboard/(active)/telegram/user-details/delete-group-admin.tsx index 86bb9c7..3a2cdcc 100644 --- a/src/app/dashboard/(active)/telegram/user-details/delete-group-admin.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/delete-group-admin.tsx @@ -1,10 +1,9 @@ "use client" -import { useMutation, useQueryClient } from "@tanstack/react-query" import { Trash2, Trash2Icon } from "lucide-react" -import { useRouter } from "next/navigation" import { useState } from "react" import { toast } from "sonner" +import { Spinner } from "@/components/spinner" import { AlertDialog, AlertDialogAction, @@ -18,43 +17,37 @@ import { AlertDialogTrigger, } from "@/components/ui/alert-dialog" import { Button } from "@/components/ui/button" -import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" - -export function DeleteGroupAdmin({ userId, chatId }: { userId: number; chatId: number }) { - const router = useRouter() - const sesh = useSession() - - const qc = useQueryClient() - const trpc = useTRPC() - const removerId = sesh.data?.user.telegramId - const { mutateAsync } = useMutation(trpc.tg.permissions.removeGroup.mutationOptions()) +import { delGroupAdmin } from "@/server/actions/users" +export function DeleteGroupAdmin({ userId, chatId, onDelete }: { userId: number; chatId: number; onDelete(): void }) { const [open, setOpen] = useState(false) + const [pending, setPending] = useState(false) async function deleteGroupAdmin() { - if (!removerId) return toast.error("Invalid session, try to reload the page") - const { error } = await mutateAsync({ removerId, userId, groupId: chatId }) - if (error) { - toast.error("There was an error") - console.error(error) - } else { - toast.success("Group Admin deleted!") - } + setPending(true) - handleOpenChange(false) - } + try { + const { error } = await delGroupAdmin(userId, chatId) - function handleOpenChange(v: boolean) { - setOpen(v) - if (v === false) { - qc.invalidateQueries(trpc.tg.permissions.getRoles.queryOptions({ userId })) - router.refresh() + if (error === "NOT_FOUND") toast.info("User or group admin not found") + else if (error === "UNAUTHORIZED") toast.error("You don't have enough permission") + else if (error === "INTERNAL_SERVER_ERROR") toast.error("There was an internal server error") + else if (error === "UNAUTHORIZED_SELF_ASSIGN") toast.error("You cannot delete on yourself") + else { + toast.success("Group Admin deleted!") + onDelete() + } + } catch (err) { + toast.error("There was an error") + console.error(err) + } finally { + setOpen(false) + setPending(false) } } return ( - + @@ -73,8 +66,8 @@ export function DeleteGroupAdmin({ userId, chatId }: { userId: number; chatId: n Cancel - - Confirm + + {pending ? : "Confirm"} diff --git a/src/app/dashboard/(active)/telegram/user-details/new-group-admin.tsx b/src/app/dashboard/(active)/telegram/user-details/new-group-admin.tsx index 066658a..58763cc 100644 --- a/src/app/dashboard/(active)/telegram/user-details/new-group-admin.tsx +++ b/src/app/dashboard/(active)/telegram/user-details/new-group-admin.tsx @@ -1,7 +1,5 @@ "use client" -import { useMutation, useQueryClient } from "@tanstack/react-query" import { Plus, Search, X } from "lucide-react" -import { useRouter } from "next/navigation" import { useState } from "react" import { toast } from "sonner" import { Button } from "@/components/ui/button" @@ -18,51 +16,41 @@ import { import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger } from "@/components/ui/select" -import { useSession } from "@/lib/auth" -import { useTRPC } from "@/lib/trpc/client" -import type { ApiOutput } from "@/lib/trpc/types" +import { searchGroup } from "@/server/actions/groups" +import { addGroupAdmin } from "@/server/actions/users" +import type { ApiOutput } from "@/server/trpc/types" type Groups = ApiOutput["tg"]["groups"]["search"]["groups"] type User = ApiOutput["tg"]["users"]["getByUsername"]["user"] -export function NewGroupAdmin({ user, alreadyIn }: { user: User; alreadyIn: number[] }) { - const sesh = useSession() - const adderId = sesh.data?.user.telegramId - - const trpc = useTRPC() - const qc = useQueryClient() - const router = useRouter() - +export function NewGroupAdmin({ user, alreadyIn, onConfirm }: { user: User; alreadyIn: number[]; onConfirm(): void }) { const [open, setOpen] = useState(false) const [groupQuery, setGroupQuery] = useState("") const [groups, setGroups] = useState([]) const [selectedGroup, setSelectedGroup] = useState(null) - const queryOpts = trpc.tg.groups.search.queryOptions({ query: groupQuery, limit: 20, showHidden: true }) - - async function searchGroup(e: React.FormEvent) { - e.preventDefault() - - const res = await qc.fetchQuery(queryOpts) - setGroups(res.groups.filter((g) => !alreadyIn.includes(g.telegramId))) + async function search() { + const { groups } = await searchGroup(groupQuery) + setGroups(groups.filter((g) => !alreadyIn.includes(g.telegramId))) } - const submitMutation = useMutation(trpc.tg.permissions.addGroup.mutationOptions()) - async function submit() { - if (!adderId) return toast.warning("Invalid session, try reloading the page") if (!selectedGroup) return toast.warning("No group selected, cannot proceed") if (!user) return toast.warning("Invalid user, try restarting the dialog") try { - await submitMutation.mutateAsync({ adderId, groupId: selectedGroup?.telegramId, userId: user.id }) - toast.info(`Group admin added`) - handleOpenChange(false) - router.refresh() + const { error } = await addGroupAdmin(user.id, selectedGroup.telegramId) + if (error) { + toast.error("You don't have enough permissions") + } else { + toast.info(`Group admin added`) + onConfirm() + } } catch (err) { console.error(err) - handleOpenChange(false) toast.error("There was an error, check logs") + } finally { + handleOpenChange(false) } } @@ -75,8 +63,6 @@ export function NewGroupAdmin({ user, alreadyIn }: { user: User; alreadyIn: numb function handleOpenChange(v: boolean) { setOpen(v) if (v === false) { - // closing - qc.invalidateQueries(trpc.tg.permissions.getRoles.queryOptions({ userId: user?.id ?? 0 })) reset() } } @@ -101,7 +87,7 @@ export function NewGroupAdmin({ user, alreadyIn }: { user: User; alreadyIn: numb

)} -
+