feat(WEB): 동적 게시판, 파트너 관리, 공지 팝업 모달 추가
- 동적 게시판 시스템 구현 (/boards/[boardCode]) - 파트너 관리 페이지 및 폼 추가 - 공지 팝업 모달 컴포넌트 (NoticePopupModal) - localStorage 기반 1일간 숨김 기능 - 테스트 페이지 (/test/popup) - IntegratedListTemplateV2 개선 - 기타 버그 수정 및 타입 개선 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { ClipboardList, ArrowLeft, Save } from 'lucide-react';
|
||||
import type { Board, BoardFormData, BoardTarget, BoardStatus } from './types';
|
||||
import { BOARD_TARGETS, BOARD_STATUS_LABELS } from './types';
|
||||
@@ -29,6 +30,14 @@ const MOCK_DEPARTMENTS = [
|
||||
{ id: 5, name: '마케팅팀' },
|
||||
];
|
||||
|
||||
// TODO: API에서 권한 목록 가져오기
|
||||
const MOCK_PERMISSIONS = [
|
||||
{ code: 'admin', name: '관리자' },
|
||||
{ code: 'manager', name: '매니저' },
|
||||
{ code: 'staff', name: '직원' },
|
||||
{ code: 'guest', name: '게스트' },
|
||||
];
|
||||
|
||||
interface BoardFormProps {
|
||||
mode: 'create' | 'edit';
|
||||
board?: Board;
|
||||
@@ -56,6 +65,7 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) {
|
||||
const [formData, setFormData] = useState<BoardFormData>({
|
||||
target: 'all',
|
||||
targetName: '',
|
||||
permissions: [],
|
||||
boardName: '',
|
||||
status: 'active',
|
||||
});
|
||||
@@ -66,6 +76,7 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) {
|
||||
setFormData({
|
||||
target: board.target,
|
||||
targetName: board.targetName || '',
|
||||
permissions: board.permissions || [],
|
||||
boardName: board.boardName,
|
||||
status: board.status,
|
||||
});
|
||||
@@ -89,7 +100,18 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
target: value,
|
||||
targetName: value === 'all' ? '' : prev.targetName,
|
||||
targetName: value === 'department' ? prev.targetName : '',
|
||||
permissions: value === 'permission' ? prev.permissions : [],
|
||||
}));
|
||||
};
|
||||
|
||||
// 권한 체크박스 핸들러
|
||||
const handlePermissionChange = (code: string, checked: boolean) => {
|
||||
setFormData(prev => ({
|
||||
...prev,
|
||||
permissions: checked
|
||||
? [...(prev.permissions || []), code]
|
||||
: (prev.permissions || []).filter(p => p !== code),
|
||||
}));
|
||||
};
|
||||
|
||||
@@ -151,6 +173,25 @@ export function BoardForm({ mode, board, onSubmit }: BoardFormProps) {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
)}
|
||||
{formData.target === 'permission' && (
|
||||
<div className="flex-1 flex flex-wrap gap-4 items-center border rounded-md px-3 py-2">
|
||||
{MOCK_PERMISSIONS.map((perm) => (
|
||||
<div key={perm.code} className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id={`perm-${perm.code}`}
|
||||
checked={(formData.permissions || []).includes(perm.code)}
|
||||
onCheckedChange={(checked) => handlePermissionChange(perm.code, checked as boolean)}
|
||||
/>
|
||||
<Label
|
||||
htmlFor={`perm-${perm.code}`}
|
||||
className="font-normal cursor-pointer text-sm"
|
||||
>
|
||||
{perm.name}
|
||||
</Label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ interface ApiResponse<T> {
|
||||
function transformApiToFrontend(apiData: BoardApiData): Board {
|
||||
const extraSettings = apiData.extra_settings || {};
|
||||
|
||||
// permissions 추출 (read 권한 기준으로 사용)
|
||||
const permissions = extraSettings.permissions?.read || [];
|
||||
|
||||
return {
|
||||
id: String(apiData.id),
|
||||
boardCode: apiData.board_code,
|
||||
@@ -35,6 +38,7 @@ function transformApiToFrontend(apiData: BoardApiData): Board {
|
||||
target: extraSettings.target || 'all',
|
||||
targetId: extraSettings.target_id,
|
||||
targetName: extraSettings.target_name,
|
||||
permissions: permissions.length > 0 ? permissions : undefined,
|
||||
boardName: apiData.name,
|
||||
description: apiData.description || undefined,
|
||||
status: apiData.is_active ? 'active' : 'inactive',
|
||||
@@ -50,14 +54,26 @@ function transformApiToFrontend(apiData: BoardApiData): Board {
|
||||
* 프론트엔드 데이터 → API 요청 형식 변환
|
||||
*/
|
||||
function transformFrontendToApi(data: BoardFormData & { boardCode?: string; description?: string }, isUpdate = false): Record<string, unknown> {
|
||||
// extra_settings 구성
|
||||
const extraSettings: Record<string, unknown> = {
|
||||
target: data.target,
|
||||
target_name: data.target === 'department' ? data.targetName : null,
|
||||
};
|
||||
|
||||
// 권한 대상인 경우 permissions 추가
|
||||
if (data.target === 'permission' && data.permissions && data.permissions.length > 0) {
|
||||
extraSettings.permissions = {
|
||||
read: data.permissions,
|
||||
write: data.permissions,
|
||||
manage: data.permissions,
|
||||
};
|
||||
}
|
||||
|
||||
const result: Record<string, unknown> = {
|
||||
name: data.boardName,
|
||||
description: data.description || null,
|
||||
is_active: data.status === 'active',
|
||||
extra_settings: {
|
||||
target: data.target,
|
||||
target_name: data.target === 'department' ? data.targetName : null,
|
||||
},
|
||||
extra_settings: extraSettings,
|
||||
};
|
||||
|
||||
// 생성 시에만 board_code 전송 (수정 시에는 코드 변경 불가)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// 게시판 상태 타입
|
||||
export type BoardStatus = 'active' | 'inactive';
|
||||
|
||||
// 대상 타입 (전사, 부서 등)
|
||||
export type BoardTarget = 'all' | 'department';
|
||||
// 대상 타입 (전사, 부서, 권한)
|
||||
export type BoardTarget = 'all' | 'department' | 'permission';
|
||||
|
||||
// 게시판 타입 (프론트엔드용)
|
||||
export interface Board {
|
||||
@@ -12,6 +12,7 @@ export interface Board {
|
||||
target: BoardTarget;
|
||||
targetId?: number;
|
||||
targetName?: string; // 부서명 (target이 department일 때)
|
||||
permissions?: string[]; // 권한 코드 목록 (target이 permission일 때)
|
||||
boardName: string;
|
||||
description?: string;
|
||||
status: BoardStatus;
|
||||
@@ -69,6 +70,7 @@ export interface BoardApiData {
|
||||
export interface BoardFormData {
|
||||
target: BoardTarget;
|
||||
targetName?: string;
|
||||
permissions?: string[]; // 권한 코드 목록 (target이 permission일 때)
|
||||
boardName: string;
|
||||
status: BoardStatus;
|
||||
}
|
||||
@@ -89,10 +91,12 @@ export const BOARD_STATUS_COLORS: Record<BoardStatus, string> = {
|
||||
export const BOARD_TARGET_LABELS: Record<BoardTarget, string> = {
|
||||
all: '전사',
|
||||
department: '부서',
|
||||
permission: '권한',
|
||||
};
|
||||
|
||||
// 대상 옵션
|
||||
export const BOARD_TARGETS = [
|
||||
{ value: 'all' as BoardTarget, label: '전사' },
|
||||
{ value: 'department' as BoardTarget, label: '부서' },
|
||||
{ value: 'permission' as BoardTarget, label: '권한' },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user