214 lines
8.5 KiB
TypeScript
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="사용자 검색…" 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>
|
|
</>
|
|
)
|
|
}
|