+ | {index + 1} |
+
+ {isEditing ? (
+ setEditWord(e.target.value)}
+ onKeyDown={handleKeyDown}
+ onBlur={handleSave}
+ autoFocus
+ className="w-full px-3 py-1.5 border border-primary rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
+ />
+ ) : (
+ setIsEditing(true)}
+ className="cursor-pointer hover:text-primary transition-colors font-medium"
+ >
+ {word}
+
+ )}
+ |
+
+
+
+
+ {showPosDropdown && (
+
+ {POS_TAGS.map((tag) => (
+
+ ))}
+
+ )}
+
+
+ |
+
+
+ |
+
+ );
+}
+
+export default WordItem;
diff --git a/frontend-temp/src/components/pc/admin/schedule/index.js b/frontend-temp/src/components/pc/admin/schedule/index.js
index e7c9d91..3151963 100644
--- a/frontend-temp/src/components/pc/admin/schedule/index.js
+++ b/frontend-temp/src/components/pc/admin/schedule/index.js
@@ -4,3 +4,4 @@ export { default as ScheduleItem, getEditPath } from './ScheduleItem';
export { default as LocationSearchDialog } from './LocationSearchDialog';
export { default as MemberSelector } from './MemberSelector';
export { default as ImageUploader } from './ImageUploader';
+export { default as WordItem, POS_TAGS } from './WordItem';
diff --git a/frontend-temp/src/pages/pc/admin/schedules/ScheduleDict.jsx b/frontend-temp/src/pages/pc/admin/schedules/ScheduleDict.jsx
index 09d0b5d..9c5cdbe 100644
--- a/frontend-temp/src/pages/pc/admin/schedules/ScheduleDict.jsx
+++ b/frontend-temp/src/pages/pc/admin/schedules/ScheduleDict.jsx
@@ -2,9 +2,10 @@ import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
import { Link } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import { useQuery } from '@tanstack/react-query';
-import { Home, ChevronRight, Book, Plus, Trash2, Search, ChevronDown } from 'lucide-react';
+import { Home, ChevronRight, Book, Plus, Search, ChevronDown } from 'lucide-react';
import { Toast } from '@/components/common';
import { AdminLayout, ConfirmDialog } from '@/components/pc/admin';
+import { WordItem, POS_TAGS } from '@/components/pc/admin/schedule';
import { useAdminAuth } from '@/hooks/pc/admin';
import { useToast } from '@/hooks/common';
import * as suggestionsApi from '@/api/admin/suggestions';
@@ -38,149 +39,6 @@ const cardVariants = {
},
};
-// 품사 태그 옵션
-const POS_TAGS = [
- {
- value: 'NNP',
- label: '고유명사 (NNP)',
- description: '사람, 그룹, 프로그램 이름 등',
- examples: '프로미스나인, 송하영, 뮤직뱅크',
- },
- { value: 'NNG', label: '일반명사 (NNG)', description: '일반적인 명사', examples: '직캠, 팬미팅, 콘서트' },
- {
- value: 'SL',
- label: '외국어 (SL)',
- description: '영어 등 외국어 단어',
- examples: 'fromis_9, YouTube, fromm',
- },
-];
-
-// 단어 항목 컴포넌트
-function WordItem({ id, word, pos, index, onUpdate, onDelete }) {
- const [isEditing, setIsEditing] = useState(false);
- const [editWord, setEditWord] = useState(word);
- const [editPos, setEditPos] = useState(pos);
- const [showPosDropdown, setShowPosDropdown] = useState(false);
- const dropdownRef = useRef(null);
-
- // 외부 클릭 시 드롭다운 닫기
- useEffect(() => {
- const handleClickOutside = (event) => {
- if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
- setShowPosDropdown(false);
- }
- };
-
- if (showPosDropdown) {
- document.addEventListener('mousedown', handleClickOutside);
- }
- return () => document.removeEventListener('mousedown', handleClickOutside);
- }, [showPosDropdown]);
-
- const handleSave = () => {
- if (editWord.trim() && (editWord.trim() !== word || editPos !== pos)) {
- onUpdate(id, editWord.trim(), editPos);
- }
- setIsEditing(false);
- };
-
- const handleKeyDown = (e) => {
- if (e.key === 'Enter') {
- handleSave();
- } else if (e.key === 'Escape') {
- setEditWord(word);
- setEditPos(pos);
- setIsEditing(false);
- }
- };
-
- return (
-
- {index + 1} |
-
- {isEditing ? (
- setEditWord(e.target.value)}
- onKeyDown={handleKeyDown}
- onBlur={handleSave}
- autoFocus
- className="w-full px-3 py-1.5 border border-primary rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20"
- />
- ) : (
- setIsEditing(true)}
- className="cursor-pointer hover:text-primary transition-colors font-medium"
- >
- {word}
-
- )}
- |
-
-
-
-
- {showPosDropdown && (
-
- {POS_TAGS.map((tag) => (
-
- ))}
-
- )}
-
-
- |
-
-
- |
-
- );
-}
-
function ScheduleDict() {
const { user, isAuthenticated } = useAdminAuth();
const { toast, setToast } = useToast();