File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
'use client';
import { useState } from 'react';
import { ADMIN_MENU_OPTIONS } from '@/lib/domain/admin-member';
interface AdminMenuPickerProps {
/** 초기 선택 상태(수정 팝업의 기존 값). */
defaultValue: string[];
}
/**
* "메뉴 선택" 칩 목록(시안 ADM_ADM_102_p ③) — 다중 선택이며 맨 앞의 [전체]는 전체 선택/해제
* 토글이다.
*
* 선택 결과는 hidden input(`name="menuCodes"`)으로 폼에 실린다 — 칩이 `<button>`이라 그 자체로는
* 폼 값이 되지 않기 때문이다. 같은 이름의 input을 여러 개 두면 `FormData.getAll('menuCodes')`가
* 전부 읽어 간다(Server Action의 `readMenuCodes`가 그렇게 읽는다).
*
* 선택지는 아직 mock 카탈로그다 — 백엔드에 관리자 메뉴 API가 없다(`admin-member.ts` 주석 참조).
*
* 비주얼(선택 상태 칩)은 공용 컴포넌트에 아직 없어 여기서 Tailwind 토큰으로 직접 그렸다.
* 시안이 확정되면 design 레인의 공용 칩 컴포넌트로 교체할 자리다.
*/
export function AdminMenuPicker({ defaultValue }: AdminMenuPickerProps) {
const [selected, setSelected] = useState<string[]>(defaultValue);
const isAllSelected = selected.length === ADMIN_MENU_OPTIONS.length;
function toggleAll() {
setSelected(
isAllSelected ? [] : ADMIN_MENU_OPTIONS.map((option) => option.value)
);
}
function toggle(value: string) {
setSelected((prev) =>
prev.includes(value)
? prev.filter((code) => code !== value)
: [...prev, value]
);
}
return (
<div className="flex flex-wrap gap-2">
{selected.map((value) => (
<input key={value} type="hidden" name="menuCodes" value={value} />
))}
<Chip isSelected={isAllSelected} onClick={toggleAll}>
전체
</Chip>
{ADMIN_MENU_OPTIONS.map((option) => (
<Chip
key={option.value}
isSelected={selected.includes(option.value)}
onClick={() => toggle(option.value)}
>
{option.label}
</Chip>
))}
</div>
);
}
function Chip({
isSelected,
onClick,
children,
}: {
isSelected: boolean;
onClick: () => void;
children: string;
}) {
return (
<button
type="button"
aria-pressed={isSelected}
onClick={onClick}
className={`rounded-full border px-3 py-1 text-body-sm transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ${
isSelected
? 'border-primary text-primary'
: 'border-border text-foreground-muted hover:bg-surface-muted'
}`}
>
{children}
</button>
);
}