diff --git a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx index 3bbdf039..48c58392 100644 --- a/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx +++ b/frontend/lib/registry/components/repeat-screen-modal/RepeatScreenModalComponent.tsx @@ -9,7 +9,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@ import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Loader2, Save, X, Layers, Table as TableIcon, Plus, Trash2, RotateCcw } from "lucide-react"; +import { Loader2, Save, X, Layers, Table as TableIcon, Plus, Trash2, RotateCcw, Pencil } from "lucide-react"; import { AlertDialog, AlertDialogAction, @@ -344,6 +344,8 @@ export function RepeatScreenModalComponent({ _originalData: { ...row }, _isDirty: false, _isNew: false, + _isEditing: false, // ๐Ÿ†• v3.8: ๋กœ๋“œ๋œ ๋ฐ์ดํ„ฐ๋Š” ์ฝ๊ธฐ ์ „์šฉ + _isDeleted: false, ...row, })); @@ -733,11 +735,14 @@ export function RepeatScreenModalComponent({ }) ); } else if (_originalData?.id) { - // UPDATE - /edit ์—”๋“œํฌ์ธํŠธ ์‚ฌ์šฉ (id๋ฅผ body์— ํฌํ•จ) - const updateData = { ...dataToSave, id: _originalData.id }; - console.log(`[RepeatScreenModal] UPDATE ์š”์ฒญ: /table-management/tables/${targetTable}/edit`, updateData); + // UPDATE - /edit ์—”๋“œํฌ์ธํŠธ ์‚ฌ์šฉ (originalData์™€ updatedData ํ˜•์‹) + const updatePayload = { + originalData: _originalData, + updatedData: { ...dataToSave, id: _originalData.id }, + }; + console.log(`[RepeatScreenModal] UPDATE ์š”์ฒญ: /table-management/tables/${targetTable}/edit`, updatePayload); savePromises.push( - apiClient.put(`/table-management/tables/${targetTable}/edit`, updateData).then((res) => { + apiClient.put(`/table-management/tables/${targetTable}/edit`, updatePayload).then((res) => { console.log("[RepeatScreenModal] UPDATE ์‘๋‹ต:", res.data); savedIds.push(_originalData.id); return res; @@ -752,7 +757,7 @@ export function RepeatScreenModalComponent({ try { await Promise.all(savePromises); - // ์ €์žฅ ํ›„: ์‚ญ์ œ๋œ ํ–‰์€ ์ œ๊ฑฐ, ๋‚˜๋จธ์ง€๋Š” dirty ํ”Œ๋ž˜๊ทธ ์ดˆ๊ธฐํ™” + // ์ €์žฅ ํ›„: ์‚ญ์ œ๋œ ํ–‰์€ ์ œ๊ฑฐ, ๋‚˜๋จธ์ง€๋Š” dirty/editing ํ”Œ๋ž˜๊ทธ ์ดˆ๊ธฐํ™” setExternalTableData((prev) => { const updated = { ...prev }; if (updated[key]) { @@ -763,7 +768,8 @@ export function RepeatScreenModalComponent({ ...row, _isDirty: false, _isNew: false, - _originalData: { ...row, _rowId: undefined, _originalData: undefined, _isDirty: undefined, _isNew: undefined, _isDeleted: undefined }, + _isEditing: false, // ๐Ÿ†• v3.8: ์ˆ˜์ • ๋ชจ๋“œ ํ•ด์ œ + _originalData: { ...row, _rowId: undefined, _originalData: undefined, _isDirty: undefined, _isNew: undefined, _isDeleted: undefined, _isEditing: undefined }, })); } return updated; @@ -856,6 +862,40 @@ export function RepeatScreenModalComponent({ }); }; + // ๐Ÿ†• v3.8: ์ˆ˜์ • ๋ชจ๋“œ ์ „ํ™˜ + const handleEditExternalRow = (cardId: string, rowId: string, contentRowId: string) => { + const key = `${cardId}-${contentRowId}`; + setExternalTableData((prev) => ({ + ...prev, + [key]: (prev[key] || []).map((row) => + row._rowId === rowId + ? { ...row, _isEditing: true } + : row + ), + })); + }; + + // ๐Ÿ†• v3.8: ์ˆ˜์ • ์ทจ์†Œ + const handleCancelEditExternalRow = (cardId: string, rowId: string, contentRowId: string) => { + const key = `${cardId}-${contentRowId}`; + setExternalTableData((prev) => ({ + ...prev, + [key]: (prev[key] || []).map((row) => + row._rowId === rowId + ? { + ...row._originalData, + _rowId: row._rowId, + _originalData: row._originalData, + _isEditing: false, + _isDirty: false, + _isNew: false, + _isDeleted: false, + } + : row + ), + })); + }; + // ๐Ÿ†• v3.1: ์™ธ๋ถ€ ํ…Œ์ด๋ธ” ํ–‰ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ const handleExternalRowDataChange = (cardId: string, contentRowId: string, rowId: string, field: string, value: any) => { const key = `${cardId}-${contentRowId}`; @@ -1742,8 +1782,8 @@ export function RepeatScreenModalComponent({ {col.label} ))} - {contentRow.tableCrud?.allowDelete && ( - ์‚ญ์ œ + {(contentRow.tableCrud?.allowUpdate || contentRow.tableCrud?.allowDelete) && ( + ์ž‘์—… )} @@ -1777,33 +1817,66 @@ export function RepeatScreenModalComponent({ row._isDeleted && "line-through text-muted-foreground" )} > - {renderTableCell(col, row, (value) => - handleExternalRowDataChange(card._cardId, contentRow.id, row._rowId, col.field, value) + {renderTableCell( + col, + row, + (value) => handleExternalRowDataChange(card._cardId, contentRow.id, row._rowId, col.field, value), + row._isNew || row._isEditing // ์‹ ๊ทœ ํ–‰์ด๊ฑฐ๋‚˜ ์ˆ˜์ • ๋ชจ๋“œ์ผ ๋•Œ๋งŒ ํŽธ์ง‘ ๊ฐ€๋Šฅ )} ))} - {contentRow.tableCrud?.allowDelete && ( + {(contentRow.tableCrud?.allowUpdate || contentRow.tableCrud?.allowDelete) && ( - {row._isDeleted ? ( - - ) : ( - - )} +
+ {/* ์ˆ˜์ • ๋ฒ„ํŠผ: ์ €์žฅ๋œ ํ–‰(isNew๊ฐ€ ์•„๋‹Œ)์ด๊ณ  ํŽธ์ง‘ ๋ชจ๋“œ๊ฐ€ ์•„๋‹ ๋•Œ๋งŒ ํ‘œ์‹œ */} + {contentRow.tableCrud?.allowUpdate && !row._isNew && !row._isEditing && !row._isDeleted && ( + + )} + {/* ์ˆ˜์ • ์ทจ์†Œ ๋ฒ„ํŠผ: ํŽธ์ง‘ ๋ชจ๋“œ์ผ ๋•Œ๋งŒ ํ‘œ์‹œ */} + {row._isEditing && !row._isNew && ( + + )} + {/* ์‚ญ์ œ/๋ณต์› ๋ฒ„ํŠผ */} + {contentRow.tableCrud?.allowDelete && ( + row._isDeleted ? ( + + ) : ( + + ) + )} +
)} @@ -1869,8 +1942,11 @@ export function RepeatScreenModalComponent({ key={`${row._rowId}-${col.id}`} className={cn("text-sm", col.align && `text-${col.align}`)} > - {renderTableCell(col, row, (value) => - handleRowDataChange(card._cardId, row._rowId, col.field, value) + {renderTableCell( + col, + row, + (value) => handleRowDataChange(card._cardId, row._rowId, col.field, value), + row._isNew || row._isEditing )} ))} @@ -2184,8 +2260,11 @@ function renderContentRow( key={`${row._rowId}-${col.id}`} className={cn("text-sm", col.align && `text-${col.align}`)} > - {renderTableCell(col, row, (value) => - onRowDataChange(card._cardId, row._rowId, col.field, value) + {renderTableCell( + col, + row, + (value) => onRowDataChange(card._cardId, row._rowId, col.field, value), + row._isNew || row._isEditing )} ))} @@ -2459,7 +2538,8 @@ function renderHeaderColumn( } // ํ…Œ์ด๋ธ” ์…€ ๋ Œ๋”๋ง -function renderTableCell(col: TableColumnConfig, row: CardRowData, onChange: (value: any) => void) { +// ๐Ÿ†• v3.8: isRowEditable ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€ - ํ–‰์ด ํŽธ์ง‘ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ์ธ์ง€ (์‹ ๊ทœ ํ–‰์ด๊ฑฐ๋‚˜ ์ˆ˜์ • ๋ชจ๋“œ) +function renderTableCell(col: TableColumnConfig, row: CardRowData, onChange: (value: any) => void, isRowEditable?: boolean) { const value = row[col.field]; // Badge ํƒ€์ž… @@ -2468,11 +2548,20 @@ function renderTableCell(col: TableColumnConfig, row: CardRowData, onChange: (va return {value || "-"}; } + // ๐Ÿ†• v3.8: ํ–‰ ์ˆ˜์ค€ ํŽธ์ง‘ ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ์ฒดํฌ + // isRowEditable์ด false์ด๋ฉด ์ปฌ๋Ÿผ ์„ค์ •๊ณผ ๊ด€๊ณ„์—†์ด ์ฝ๊ธฐ ์ „์šฉ + const canEdit = col.editable && (isRowEditable !== false); + // ์ฝ๊ธฐ ์ „์šฉ - if (!col.editable) { + if (!canEdit) { if (col.type === "number") { return {typeof value === "number" ? value.toLocaleString() : value || "-"}; } + if (col.type === "date") { + // ISO 8601 ํ˜•์‹์„ ํ‘œ์‹œ์šฉ์œผ๋กœ ๋ณ€ํ™˜ + const displayDate = value ? (typeof value === 'string' && value.includes('T') ? value.split('T')[0] : value) : "-"; + return {displayDate}; + } return {value || "-"}; }