254 lines
9.3 KiB
TypeScript
254 lines
9.3 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { X, Save, User, Calendar } from 'lucide-react';
|
|
import { NoteItem } from '@/types';
|
|
import { comms } from '@/communication';
|
|
|
|
interface NoteEditModalProps {
|
|
isOpen: boolean;
|
|
editingItem: NoteItem | null;
|
|
processing: boolean;
|
|
onClose: () => void;
|
|
onSave: (formData: {
|
|
pdate: string;
|
|
title: string;
|
|
uid: string;
|
|
description: string;
|
|
share: boolean;
|
|
guid: string;
|
|
}) => void;
|
|
initialEditMode?: boolean;
|
|
}
|
|
|
|
export function NoteEditModal({
|
|
isOpen,
|
|
editingItem,
|
|
processing,
|
|
onClose,
|
|
onSave,
|
|
}: NoteEditModalProps) {
|
|
const [pdate, setPdate] = useState('');
|
|
const [title, setTitle] = useState('');
|
|
const [uid, setUid] = useState('');
|
|
const [description, setDescription] = useState('');
|
|
const [share, setShare] = useState(false);
|
|
const [guid, setGuid] = useState('');
|
|
|
|
// 현재 로그인 사용자 정보 로드
|
|
const [currentUserId, setCurrentUserId] = useState('');
|
|
const [currentUserLevel, setCurrentUserLevel] = useState(0);
|
|
|
|
useEffect(() => {
|
|
const loadUserInfo = async () => {
|
|
try {
|
|
const loginStatus = await comms.checkLoginStatus();
|
|
if (loginStatus.Success && loginStatus.IsLoggedIn && loginStatus.User) {
|
|
setCurrentUserId(loginStatus.User.Id);
|
|
setCurrentUserLevel(loginStatus.User.Level);
|
|
}
|
|
} catch (error) {
|
|
console.error('로그인 정보 로드 오류:', error);
|
|
}
|
|
};
|
|
loadUserInfo();
|
|
}, []);
|
|
|
|
// 모달이 열릴 때 폼 데이터 초기화
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
if (editingItem) {
|
|
// 기존 메모
|
|
setPdate(editingItem.pdate ? editingItem.pdate.split('T')[0] : '');
|
|
setTitle(editingItem.title || '');
|
|
setUid(editingItem.uid || currentUserId);
|
|
setDescription(editingItem.description || '');
|
|
setShare(editingItem.share || false);
|
|
setGuid(editingItem.guid || '');
|
|
} else {
|
|
// 신규 메모
|
|
setPdate(new Date().toISOString().split('T')[0]);
|
|
setTitle('');
|
|
setUid(currentUserId);
|
|
setDescription('');
|
|
setShare(false);
|
|
setGuid('');
|
|
}
|
|
}
|
|
}, [isOpen, editingItem, currentUserId]);
|
|
|
|
const handleSubmit = (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
onSave({ pdate, title, uid, description, share, guid });
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
// 권한 체크: 자신의 메모이거나 관리자만 수정 가능
|
|
const canEdit = !editingItem || editingItem.uid === currentUserId || currentUserLevel >= 5;
|
|
const canChangeUser = currentUserLevel >= 5;
|
|
const canChangeShare = !editingItem || editingItem.uid === currentUserId;
|
|
|
|
return createPortal(
|
|
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50 backdrop-blur-sm">
|
|
<div className="dialog-container rounded-2xl w-full max-w-5xl max-h-[90vh] overflow-hidden transition-all duration-300">
|
|
{/* 헤더 */}
|
|
<div className="dialog-header flex items-center justify-between px-6 py-4">
|
|
<h2 className="dialog-title">
|
|
{!editingItem ? '새 메모 작성' : '메모 편집'}
|
|
</h2>
|
|
<button
|
|
onClick={onClose}
|
|
className="text-text-secondary hover:text-text-primary transition-colors"
|
|
>
|
|
<X className="w-6 h-6" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* 본문 - 좌우 레이아웃 */}
|
|
<form onSubmit={handleSubmit} className="overflow-y-auto max-h-[calc(90vh-140px)]">
|
|
<div className="flex gap-6 p-6">
|
|
{/* 좌측 - 정보 영역 */}
|
|
<div className="w-64 flex-shrink-0 space-y-4">
|
|
{/* 날짜 */}
|
|
<div>
|
|
<label className="flex items-center text-sm font-medium text-white/70 mb-2">
|
|
<Calendar className="w-4 h-4 mr-2" />
|
|
날짜
|
|
</label>
|
|
<input
|
|
type="date"
|
|
value={pdate}
|
|
onChange={(e) => setPdate(e.target.value)}
|
|
required
|
|
disabled={!canEdit}
|
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
/>
|
|
</div>
|
|
|
|
{/* 작성자 */}
|
|
<div>
|
|
<label className="flex items-center text-sm font-medium text-white/70 mb-2">
|
|
<User className="w-4 h-4 mr-2" />
|
|
작성자
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={uid}
|
|
onChange={(e) => setUid(e.target.value)}
|
|
required
|
|
disabled={!canEdit || !canChangeUser}
|
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white text-sm focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
placeholder="사용자 ID"
|
|
/>
|
|
{!canChangeUser && (
|
|
<p className="text-xs text-white/50 mt-1">
|
|
관리자만 변경 가능
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{/* 공유 여부 */}
|
|
<div>
|
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
|
공유 설정
|
|
</label>
|
|
<div className="flex items-center gap-2">
|
|
<input
|
|
type="checkbox"
|
|
id="note-share"
|
|
checked={share}
|
|
onChange={(e) => setShare(e.target.checked)}
|
|
disabled={!canEdit || !canChangeShare}
|
|
className="w-4 h-4 bg-white/20 border border-white/30 rounded focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
/>
|
|
<label htmlFor="note-share" className="text-sm text-white/70 cursor-pointer">
|
|
공유
|
|
</label>
|
|
</div>
|
|
{!canChangeShare && (
|
|
<p className="text-xs text-white/50 mt-1">
|
|
본인 메모만 변경 가능
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{!canEdit && (
|
|
<div className="bg-red-500/20 border border-red-500/50 rounded-lg p-3 text-red-300 text-xs">
|
|
타인의 자료는 수정할 수 없습니다.
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 우측 - 제목 및 내용 영역 */}
|
|
<div className="flex-1 space-y-4">
|
|
{/* 제목 */}
|
|
<div>
|
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
|
제목
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={title}
|
|
onChange={(e) => setTitle(e.target.value)}
|
|
required
|
|
disabled={!canEdit}
|
|
className="w-full h-10 bg-white/20 border border-white/30 rounded-lg px-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
placeholder="메모 제목을 입력하세요"
|
|
/>
|
|
</div>
|
|
|
|
{/* 내용 */}
|
|
<div className="flex-1">
|
|
<label className="text-sm font-medium text-white/70 mb-2 block">
|
|
내용
|
|
</label>
|
|
<textarea
|
|
value={description}
|
|
onChange={(e) => setDescription(e.target.value)}
|
|
disabled={!canEdit}
|
|
rows={18}
|
|
className="w-full bg-white/20 border border-white/30 rounded-lg p-3 text-white focus:outline-none focus:ring-2 focus:ring-primary-400 disabled:opacity-50 disabled:cursor-not-allowed resize-none"
|
|
placeholder="메모 내용을 입력하세요"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
{/* 하단 버튼 */}
|
|
<div className="dialog-footer flex items-center justify-end gap-3 px-6 py-4">
|
|
<button
|
|
type="button"
|
|
onClick={onClose}
|
|
className="px-4 py-2 rounded-lg bg-white/10 hover:bg-white/20 text-white transition-colors"
|
|
disabled={processing}
|
|
>
|
|
닫기
|
|
</button>
|
|
{canEdit && (
|
|
<button
|
|
type="submit"
|
|
onClick={handleSubmit}
|
|
disabled={processing}
|
|
className="px-4 py-2 rounded-lg bg-primary-500 hover:bg-primary-600 text-white transition-colors flex items-center disabled:opacity-50"
|
|
>
|
|
{processing ? (
|
|
<>
|
|
<div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin mr-2"></div>
|
|
처리 중...
|
|
</>
|
|
) : (
|
|
<>
|
|
<Save className="w-4 h-4 mr-2" />
|
|
저장
|
|
</>
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body
|
|
);
|
|
}
|