konobangu/apps/webui/src/presentation/routes/_app/credential3rd/detail.$id.tsx

215 lines
6.8 KiB
TypeScript

import { DetailCardSkeleton } from '@/components/detail-card-skeleton';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import { DetailEmptyView } from '@/components/ui/detail-empty-view';
import { Label } from '@/components/ui/label';
import { QueryErrorView } from '@/components/ui/query-error-view';
import { Separator } from '@/components/ui/separator';
import { GET_CREDENTIAL_3RD_DETAIL } from '@/domains/recorder/schema/credential3rd';
import type { GetCredential3rdDetailQuery } from '@/infra/graphql/gql/graphql';
import type { RouteStateDataOption } from '@/infra/routes/traits';
import { useQuery } from '@apollo/client';
import { createFileRoute, useNavigate } from '@tanstack/react-router';
import { format } from 'date-fns/format';
import { ArrowLeft, Edit, Eye, EyeOff } from 'lucide-react';
import { useState } from 'react';
export const Route = createFileRoute('/_app/credential3rd/detail/$id')({
component: Credential3rdDetailRouteComponent,
staticData: {
breadcrumb: { label: 'Detail' },
} satisfies RouteStateDataOption,
});
function Credential3rdDetailRouteComponent() {
const { id } = Route.useParams();
const navigate = useNavigate();
const [showPassword, setShowPassword] = useState(false);
const { loading, error, data } = useQuery<GetCredential3rdDetailQuery>(
GET_CREDENTIAL_3RD_DETAIL,
{
variables: {
id: Number.parseInt(id),
},
fetchPolicy: 'cache-and-network',
}
);
const handleBack = () => {
navigate({
to: '/credential3rd/manage',
});
};
const handleEnterEditMode = () => {
navigate({
to: '/credential3rd/edit/$id',
params: {
id,
},
});
};
const togglePasswordVisibility = () => {
setShowPassword((prev) => !prev);
};
const credential = data?.credential3rd?.nodes?.[0];
if (loading) {
return <DetailCardSkeleton />;
}
if (error) {
return <QueryErrorView message={error.message} />;
}
if (!credential) {
return <DetailEmptyView message="Not found certain credential" />;
}
return (
<div className="container mx-auto max-w-4xl py-6">
<div className="mb-6 flex items-center justify-between">
<div className="flex items-center gap-4">
<Button
variant="ghost"
size="sm"
onClick={handleBack}
className="h-8 w-8 p-0"
>
<ArrowLeft className="h-4 w-4" />
</Button>
<div>
<h1 className="font-bold text-2xl">Credential detail</h1>
<p className="mt-1 text-muted-foreground">
View credential #{credential.id}
</p>
</div>
</div>
<div className="flex gap-2">
<Button onClick={handleEnterEditMode}>
<Edit className="mr-2 h-4 w-4" />
Edit
</Button>
</div>
</div>
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle>Credential information</CardTitle>
<CardDescription className="mt-2">
View credential detail
</CardDescription>
</div>
<Badge variant="secondary" className="capitalize">
{credential.credentialType}
</Badge>
</div>
</CardHeader>
<CardContent>
<div className="space-y-6">
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="space-y-2">
<Label className="font-medium text-sm">ID</Label>
<div className="rounded-md bg-muted p-3">
<code className="text-sm">{credential.id}</code>
</div>
</div>
<div className="space-y-2">
<Label className="font-medium text-sm">Credential type</Label>
<div className="rounded-md bg-muted p-3">
<Badge variant="secondary" className="capitalize">
{credential.credentialType}
</Badge>
</div>
</div>
<div className="space-y-2">
<Label className="font-medium text-sm">Username</Label>
<div className="rounded-md bg-muted p-3">
<span className="text-sm">
{credential.username || 'Not set'}
</span>
</div>
</div>
<div className="space-y-2">
<Label className="font-medium text-sm">Password</Label>
<div className="flex items-center justify-between rounded-md bg-muted p-3">
<span className="font-mono text-sm">
{showPassword ? credential.password || '-' : '••••••••'}
</span>
{credential.password && (
<Button
variant="ghost"
size="sm"
className="h-6 w-6 p-0"
onClick={togglePasswordVisibility}
>
{showPassword ? (
<EyeOff className="h-3 w-3" />
) : (
<Eye className="h-3 w-3" />
)}
</Button>
)}
</div>
</div>
</div>
<div className="space-y-2">
<Label className="font-medium text-sm">User Agent</Label>
<div className="rounded-md bg-muted p-3">
<span className="break-all text-sm">
{credential.userAgent || '-'}
</span>
</div>
</div>
<Separator />
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
<div className="space-y-2">
<Label className="font-medium text-sm">Created at</Label>
<div className="rounded-md bg-muted p-3">
<span className="text-sm">
{format(
new Date(credential.createdAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span>
</div>
</div>
<div className="space-y-2">
<Label className="font-medium text-sm">Updated at</Label>
<div className="rounded-md bg-muted p-3">
<span className="text-sm">
{format(
new Date(credential.updatedAt),
'yyyy-MM-dd HH:mm:ss'
)}
</span>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
);
}