CMCH_ADMIN/src/app/(app)/users/UsersClient.tsx
2026-02-25 04:56:26 +00:00

214 lines
8.5 KiB
TypeScript

'use client'
import { Stat } from '@/app/stat'
import { Avatar } from '@/components/avatar'
import { Badge } from '@/components/badge'
import { Divider } from '@/components/divider'
import {
Dropdown,
DropdownButton,
DropdownHeading,
DropdownItem,
DropdownMenu,
DropdownSection,
} from '@/components/dropdown'
import { Heading, Subheading } from '@/components/heading'
import { Input, InputGroup } from '@/components/input'
import { Link } from '@/components/link'
import {
Pagination,
PaginationGap,
PaginationList,
PaginationNext,
PaginationPage,
PaginationPrevious,
} from '@/components/pagination'
import { Select } from '@/components/select'
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/table'
import { EllipsisHorizontalIcon, MagnifyingGlassIcon } from '@heroicons/react/16/solid'
import { useState } from 'react'
export default function UsersClient({ initialUsers }: { initialUsers: any[] }) {
const [users, setUsers] = useState(initialUsers)
// 계정 상태 업데이트
const updateUserStatus = (userId: number, newStatus: string) => {
setUsers((prev) => prev.map((user) => (user.id === userId ? { ...user, accountStatus: newStatus } : user)))
}
// 인증 상태 업데이트
const updateVerificationStatus = (userId: number, newStatus: string) => {
setUsers((prev) => prev.map((user) => (user.id === userId ? { ...user, verificationStatus: newStatus } : user)))
}
return (
<>
<div className="mt-8 flex items-end justify-between">
<Subheading>Overview</Subheading>
<div>
<Select name="period">
<option value="last_week"> 1</option>
<option value="last_month"> 1</option>
<option value="last_year"> 1</option>
<option value="last_year"> </option>
</Select>
</div>
</div>
<div className="mt-4 grid gap-8 sm:grid-cols-2 lg:grid-cols-4 xl:grid-cols-4">
<Stat title="일반" value="3,630" change="+3.5%" />
<Stat title="기관" value="1,200" change="-0.5%" />
<Stat title="기업" value="1,400" change="+4.5%" />
<Stat title="교육" value="1,030" change="+1.5%" />
</div>
<Divider className="mt-10" />
<div className="mt-14 flex items-end justify-between gap-4">
<Heading>Users</Heading>
<div className="flex max-w-xl justify-end gap-2">
<Select name="user_type" className="flex-1">
<option value="all"> </option>
<option value="normal"></option>
<option value="corp"></option>
<option value="org"></option>
<option value="edu"></option>
</Select>
<InputGroup className="flex-2">
<MagnifyingGlassIcon />
<Input name="search" placeholder="사용자 검색&hellip;" aria-label="Search" />
</InputGroup>
</div>
</div>
<Table className="mt-8 [--gutter:--spacing(6)] lg:[--gutter:--spacing(10)]">
<TableHead>
<TableRow>
<TableHeader></TableHeader>
<TableHeader> </TableHeader>
<TableHeader></TableHeader>
<TableHeader></TableHeader>
<TableHeader> </TableHeader>
<TableHeader> </TableHeader>
<TableHeader> </TableHeader>
<TableHeader> </TableHeader>
<TableHeader className="relative w-0 px-0">
<span className="sr-only">Actions</span>
</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{users.map((user) => (
<TableRow key={user.id}>
<TableCell>
<div className="flex items-center gap-3">
{/* 에리카는 이미지가 나오고, 나머지는 이니셜(Fallback)이 나옵니다. */}
<Avatar
src={user.avatar || undefined}
initials={!user.avatar ? user.name[0] : undefined}
className="size-8"
/>
<span className="font-medium text-zinc-950">{user.name}</span>
</div>
</TableCell>
<TableCell>
{user.type}
{user.subType ? `(${user.subType})` : ''}
</TableCell>
<TableCell className="text-zinc-500">{user.email}</TableCell>
<TableCell className="text-zinc-500">{user.phone}</TableCell>
<TableCell>{user.organization}</TableCell>
<TableCell>
{user.verificationStatus === '-' ? (
<span className="text-zinc-400">-</span>
) : (
<Badge
color={
user.verificationStatus === '인증 완료'
? 'lime'
: user.verificationStatus === '인증 대기'
? 'amber'
: 'rose'
}
>
{user.verificationStatus}
</Badge>
)}
</TableCell>
<TableCell>
{user.verificationDocUrl ? (
<Link href={user.verificationDocUrl} className="text-xs font-medium text-sky-600 hover:underline">
</Link>
) : (
<span className="text-zinc-400">-</span>
)}
</TableCell>
<TableCell>
<Badge color={user.accountStatus === '활동' ? 'emerald' : 'zinc'}>{user.accountStatus}</Badge>
</TableCell>
<TableCell className="text-right">
<Dropdown>
<DropdownButton plain aria-label="More options">
<EllipsisHorizontalIcon className="size-5 text-zinc-500" />
</DropdownButton>
<DropdownMenu anchor="bottom end">
{user.type !== '일반' && (
<DropdownSection>
<DropdownHeading> </DropdownHeading>
{user.verificationStatus !== '인증 완료' && (
<DropdownItem onClick={() => updateVerificationStatus(user.id, '인증 완료')}>
</DropdownItem>
)}
{user.verificationStatus !== '인증 반려' && (
<DropdownItem onClick={() => updateVerificationStatus(user.id, '인증 반려')}>
</DropdownItem>
)}
{user.verificationStatus !== '인증 대기' && (
<DropdownItem onClick={() => updateVerificationStatus(user.id, '인증 대기')}>
</DropdownItem>
)}
</DropdownSection>
)}
{user.type !== '일반' && <Divider className="my-1" />}
<DropdownSection>
<DropdownHeading> </DropdownHeading>
{user.accountStatus === '정지' ? (
<DropdownItem onClick={() => updateUserStatus(user.id, '활동')}> </DropdownItem>
) : (
<DropdownItem onClick={() => updateUserStatus(user.id, '정지')}> </DropdownItem>
)}
</DropdownSection>
</DropdownMenu>
</Dropdown>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Pagination className="mt-10">
<PaginationPrevious href="?page=2" />
<PaginationList>
<PaginationPage href="?page=1">1</PaginationPage>
<PaginationPage href="?page=2">2</PaginationPage>
<PaginationPage href="?page=3" current>
3
</PaginationPage>
<PaginationPage href="?page=4">4</PaginationPage>
<PaginationGap />
<PaginationPage href="?page=65">65</PaginationPage>
<PaginationPage href="?page=66">66</PaginationPage>
</PaginationList>
<PaginationNext href="?page=4" />
</Pagination>
</>
)
}