forked from baron/baron-sso
샘플 adminfront, devfront 구성. ory-keto-migrate 오류 해결
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import type { AxiosError } from "axios";
|
||||
import {
|
||||
Activity,
|
||||
BookOpenText,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
ServerCog,
|
||||
ShieldHalf,
|
||||
} from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { Link, useNavigate } from "react-router-dom";
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
@@ -26,6 +27,7 @@ import {
|
||||
} from "../../components/ui/card";
|
||||
import { Input } from "../../components/ui/input";
|
||||
import { Separator } from "../../components/ui/separator";
|
||||
import { Switch } from "../../components/ui/switch";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -34,14 +36,29 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "../../components/ui/table";
|
||||
import { fetchClients } from "../../lib/devApi";
|
||||
import {
|
||||
deleteClient,
|
||||
fetchClients,
|
||||
updateClientStatus,
|
||||
} from "../../lib/devApi";
|
||||
import { cn } from "../../lib/utils";
|
||||
|
||||
function ClientsPage() {
|
||||
const navigate = useNavigate();
|
||||
const queryClient = useQueryClient();
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ["clients"],
|
||||
queryFn: fetchClients,
|
||||
});
|
||||
const updateStatusMutation = useMutation({
|
||||
mutationFn: (payload: { id: string; status: "active" | "inactive" }) =>
|
||||
updateClientStatus(payload.id, payload.status),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["clients"] }),
|
||||
});
|
||||
const deleteMutation = useMutation({
|
||||
mutationFn: (clientId: string) => deleteClient(clientId),
|
||||
onSuccess: () => queryClient.invalidateQueries({ queryKey: ["clients"] }),
|
||||
});
|
||||
|
||||
const clients = data?.items || [];
|
||||
const totalClients = clients.length;
|
||||
@@ -73,7 +90,8 @@ function ClientsPage() {
|
||||
|
||||
if (error) {
|
||||
const errMsg =
|
||||
(error as any).response?.data?.error || (error as Error).message;
|
||||
(error as AxiosError<{ error?: string }>).response?.data?.error ??
|
||||
(error as Error).message;
|
||||
return (
|
||||
<div className="p-8 text-center text-red-500">
|
||||
Error loading clients: {errMsg}
|
||||
@@ -102,7 +120,11 @@ function ClientsPage() {
|
||||
<Button variant="outline" size="sm">
|
||||
비밀키 재발행
|
||||
</Button>
|
||||
<Button size="sm" className="shadow-lg shadow-primary/30">
|
||||
<Button
|
||||
size="sm"
|
||||
className="shadow-lg shadow-primary/30"
|
||||
onClick={() => navigate("/clients/new")}
|
||||
>
|
||||
<Plus className="h-4 w-4" />새 클라이언트
|
||||
</Button>
|
||||
</div>
|
||||
@@ -164,7 +186,7 @@ function ClientsPage() {
|
||||
<Button variant="outline" size="sm">
|
||||
비밀키 재발행
|
||||
</Button>
|
||||
<Button size="sm">
|
||||
<Button size="sm" onClick={() => navigate("/clients/new")}>
|
||||
<Plus className="h-4 w-4" />새 클라이언트
|
||||
</Button>
|
||||
</div>
|
||||
@@ -214,9 +236,7 @@ function ClientsPage() {
|
||||
size="icon"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-primary"
|
||||
aria-label="Copy client id"
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(client.id)
|
||||
}
|
||||
onClick={() => navigator.clipboard.writeText(client.id)}
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
@@ -234,24 +254,16 @@ function ClientsPage() {
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-5 w-10 items-center rounded-full p-1",
|
||||
client.status === "active"
|
||||
? "bg-primary/40"
|
||||
: "bg-muted/50",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"h-3 w-3 rounded-full bg-background transition",
|
||||
client.status === "active"
|
||||
? "translate-x-5"
|
||||
: "translate-x-0",
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Switch
|
||||
checked={client.status === "active"}
|
||||
onCheckedChange={(checked) =>
|
||||
updateStatusMutation.mutate({
|
||||
id: client.id,
|
||||
status: checked ? "active" : "inactive",
|
||||
})
|
||||
}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
@@ -279,6 +291,7 @@ function ClientsPage() {
|
||||
size="icon"
|
||||
className="h-8 w-8 text-muted-foreground hover:text-destructive"
|
||||
aria-label="Delete client"
|
||||
onClick={() => deleteMutation.mutate(client.id)}
|
||||
>
|
||||
<Activity className="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user