docs: Add documentation for category tree modal updates with continuous registration mode
- Introduced new documents detailing the modifications made to the category tree modal for continuous registration mode. - Updated the functionality to allow the modal to close after saving or remain open based on user preference via a checkbox. - Enhanced the user experience by aligning the modal behavior with existing patterns in the project. - Included a checklist to track implementation progress and ensure thorough testing. These changes aim to improve the usability and consistency of the category management feature in the application.
This commit is contained in:
parent
d9611f234e
commit
634f0cae18
|
|
@ -0,0 +1,199 @@
|
|||
# [계획서] 카테고리 트리 대분류 추가 모달 - 연속 등록 모드 수정
|
||||
|
||||
> 관련 문서: [맥락노트](./CCA[맥락]-카테고리-연속등록모드.md) | [체크리스트](./CCA[체크]-카테고리-연속등록모드.md)
|
||||
|
||||
## 개요
|
||||
|
||||
기준정보 - 옵션설정 화면에서 트리 구조 카테고리(예: 품목정보 > 재고단위)의 "대분류 추가" 모달이 저장 후 닫히지 않는 버그를 수정합니다.
|
||||
평면 목록용 추가 모달(`CategoryValueAddDialog.tsx`)과 동일한 연속 입력 패턴을 적용합니다.
|
||||
|
||||
---
|
||||
|
||||
## 현재 동작
|
||||
|
||||
- 대분류 추가 모달에서 값 입력 후 "추가" 클릭 시 **값은 정상 저장됨**
|
||||
- 저장 후 **모달이 닫히지 않고** 폼만 초기화됨 (항상 연속 입력 상태)
|
||||
- "연속 입력" 체크박스 UI가 **없음** → 사용자가 모드를 끌 수 없음
|
||||
- 모달을 닫으려면 "닫기" 버튼 또는 외부 클릭을 해야 함
|
||||
|
||||
### 현재 코드 (CategoryValueManagerTree.tsx - handleAdd, 512~530행)
|
||||
|
||||
```tsx
|
||||
if (response.success) {
|
||||
toast.success("카테고리가 추가되었습니다");
|
||||
// 폼 초기화 (모달은 닫지 않고 연속 입력)
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
valueCode: "",
|
||||
valueLabel: "",
|
||||
description: "",
|
||||
color: "",
|
||||
}));
|
||||
setTimeout(() => addNameRef.current?.focus(), 50);
|
||||
await loadTree(true);
|
||||
if (parentValue) {
|
||||
setExpandedNodes((prev) => new Set([...prev, parentValue.valueId]));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 현재 DialogFooter (809~821행)
|
||||
|
||||
```tsx
|
||||
<DialogFooter className="gap-2 sm:gap-0">
|
||||
<Button variant="outline" onClick={() => setIsAddModalOpen(false)} ...>
|
||||
닫기
|
||||
</Button>
|
||||
<Button onClick={handleAdd} ...>
|
||||
추가
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 변경 후 동작
|
||||
|
||||
### 1. 기본 동작: 저장 후 모달 닫힘
|
||||
|
||||
- "추가" 클릭 → 저장 성공 → 모달 닫힘 + 트리 새로고침
|
||||
- `CategoryValueAddDialog.tsx`(평면 목록 추가 모달)와 동일한 기본 동작
|
||||
|
||||
### 2. 연속 입력 체크박스 추가
|
||||
|
||||
- DialogFooter 좌측에 "연속 입력" 체크박스 표시
|
||||
- 기본값: 체크 해제 (OFF)
|
||||
- 체크 시: 저장 후 폼만 초기화, 모달 유지, 이름 필드에 포커스
|
||||
- 체크 해제 시: 저장 후 모달 닫힘
|
||||
|
||||
---
|
||||
|
||||
## 시각적 예시
|
||||
|
||||
| 상태 | 연속 입력 체크 | 추가 버튼 클릭 후 |
|
||||
|------|---------------|-----------------|
|
||||
| 기본 (체크 해제) | [ ] 연속 입력 | 저장 → 모달 닫힘 → 트리 갱신 |
|
||||
| 연속 모드 (체크) | [x] 연속 입력 | 저장 → 폼 초기화 → 모달 유지 → 이름 필드 포커스 |
|
||||
|
||||
### 모달 하단 레이아웃 (ScreenModal.tsx 패턴)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ [닫기] [추가] │ ← DialogFooter (버튼만)
|
||||
├─────────────────────────────────────────┤
|
||||
│ [x] 저장 후 계속 입력 (연속 등록 모드) │ ← border-t 구분선 아래 별도 영역
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 아키텍처
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["사용자: '추가' 클릭"] --> B["handleAdd()"]
|
||||
B --> C{"API 호출 성공?"}
|
||||
C -- 실패 --> D["toast.error → 모달 유지"]
|
||||
C -- 성공 --> E["toast.success + loadTree"]
|
||||
E --> F{"continuousAdd?"}
|
||||
F -- true --> G["폼 초기화 + 이름 필드 포커스\n모달 유지"]
|
||||
F -- false --> H["폼 초기화 + 모달 닫힘"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 변경 대상 파일
|
||||
|
||||
| 파일 | 역할 | 변경 내용 |
|
||||
|------|------|----------|
|
||||
| `frontend/components/table-category/CategoryValueManagerTree.tsx` | 트리형 카테고리 값 관리 | 상태 추가, handleAdd 분기, DialogFooter UI |
|
||||
|
||||
- **변경 규모**: 약 20줄 내외 소규모 변경
|
||||
- **참고 파일**: `frontend/components/table-category/CategoryValueAddDialog.tsx` (동일 패턴)
|
||||
|
||||
---
|
||||
|
||||
## 코드 설계
|
||||
|
||||
### 1. 상태 추가 (286행 근처, 모달 상태 선언부)
|
||||
|
||||
```tsx
|
||||
const [continuousAdd, setContinuousAdd] = useState(false);
|
||||
```
|
||||
|
||||
### 2. handleAdd 성공 분기 수정 (512~530행 대체)
|
||||
|
||||
```tsx
|
||||
if (response.success) {
|
||||
toast.success("카테고리가 추가되었습니다");
|
||||
await loadTree(true);
|
||||
if (parentValue) {
|
||||
setExpandedNodes((prev) => new Set([...prev, parentValue.valueId]));
|
||||
}
|
||||
|
||||
if (continuousAdd) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
valueCode: "",
|
||||
valueLabel: "",
|
||||
description: "",
|
||||
color: "",
|
||||
}));
|
||||
setTimeout(() => addNameRef.current?.focus(), 50);
|
||||
} else {
|
||||
setFormData({ valueCode: "", valueLabel: "", description: "", color: "", isActive: true });
|
||||
setIsAddModalOpen(false);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. DialogFooter + 연속 등록 체크박스 수정 (809~821행 대체)
|
||||
|
||||
DialogFooter는 버튼만 유지하고, 그 아래에 `border-t` 구분선과 체크박스를 별도 영역으로 배치합니다.
|
||||
`ScreenModal.tsx` (1287~1303행) 패턴 그대로입니다.
|
||||
|
||||
```tsx
|
||||
<DialogFooter className="gap-2 sm:gap-0">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => setIsAddModalOpen(false)}
|
||||
className="h-9 flex-1 text-sm sm:flex-none"
|
||||
>
|
||||
닫기
|
||||
</Button>
|
||||
<Button onClick={handleAdd} className="h-9 flex-1 text-sm sm:flex-none">
|
||||
추가
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
{/* 연속 등록 모드 체크박스 - ScreenModal.tsx 패턴 */}
|
||||
<div className="border-t px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="tree-continuous-add"
|
||||
checked={continuousAdd}
|
||||
onCheckedChange={(checked) => setContinuousAdd(checked as boolean)}
|
||||
/>
|
||||
<Label htmlFor="tree-continuous-add" className="cursor-pointer text-sm font-normal select-none">
|
||||
저장 후 계속 입력 (연속 등록 모드)
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 예상 문제 및 대응
|
||||
|
||||
`CategoryValueAddDialog.tsx`와 동일한 패턴이므로 별도 예상 문제 없음.
|
||||
|
||||
---
|
||||
|
||||
## 설계 원칙
|
||||
|
||||
- `CategoryValueAddDialog.tsx`(같은 폴더, 같은 목적)의 패턴을 그대로 따름
|
||||
- 기존 수정/삭제 모달 동작은 변경하지 않음
|
||||
- 하위 추가(중분류/소분류) 모달도 동일한 `handleAdd`를 사용하므로 자동 적용
|
||||
- `Checkbox` import는 이미 존재 (24행)하므로 추가 import 불필요
|
||||
- `Label` import는 이미 존재 (53행)하므로 추가 import 불필요
|
||||
- 체크박스 위치/라벨/className 모두 `ScreenModal.tsx` (1287~1303행)과 동일
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
# [맥락노트] 카테고리 트리 대분류 추가 모달 - 연속 등록 모드 수정
|
||||
|
||||
> 관련 문서: [계획서](./CCA[계획]-카테고리-연속등록모드.md) | [체크리스트](./CCA[체크]-카테고리-연속등록모드.md)
|
||||
|
||||
---
|
||||
|
||||
## 왜 이 작업을 하는가
|
||||
|
||||
- 기준정보 - 옵션설정에서 트리 구조 카테고리(품목정보 > 재고단위 등)의 "대분류 추가" 모달이 저장 후 닫히지 않음
|
||||
- 연속 등록 모드가 하드코딩되어 항상 ON 상태이고, 끌 수 있는 UI가 없음
|
||||
- 같은 폴더의 평면 목록 모달(`CategoryValueAddDialog.tsx`)은 이미 올바르게 구현되어 있음
|
||||
- 동일 패턴을 적용하여 일관성 확보
|
||||
|
||||
---
|
||||
|
||||
## 핵심 결정 사항과 근거
|
||||
|
||||
### 1. 기본값: 연속 등록 OFF (모달 닫힘)
|
||||
|
||||
- **결정**: `continuousAdd` 초기값을 `false`로 설정
|
||||
- **근거**: 대부분의 사용자는 한 건 추가 후 결과를 확인하려 함. 연속 입력은 선택적 기능
|
||||
|
||||
### 2. 체크박스 위치: DialogFooter 아래, border-t 구분선 별도 영역
|
||||
|
||||
- **결정**: `ScreenModal.tsx` (1287~1303행) 패턴 그대로 적용
|
||||
- **근거**: "기준정보 - 부서관리" 추가 모달과 동일한 디자인. 프로젝트 관행 준수
|
||||
- **대안 검토**: `CategoryValueAddDialog.tsx`는 DialogFooter 안에 체크박스 배치 → 부서 모달과 다른 디자인이므로 기각
|
||||
|
||||
### 3. 라벨: "저장 후 계속 입력 (연속 등록 모드)"
|
||||
|
||||
- **결정**: `ScreenModal.tsx`와 동일한 라벨 텍스트 사용
|
||||
- **근거**: 부서 추가 모달과 동일한 문구로 사용자 혼란 방지
|
||||
|
||||
### 4. localStorage 미사용
|
||||
|
||||
- **결정**: 컴포넌트 state만 사용, localStorage 영속화 안 함
|
||||
- **근거**: `CategoryValueAddDialog.tsx`(같은 폴더 형제 컴포넌트)가 localStorage를 쓰지 않음. `ScreenModal.tsx`는 사용하지만 동적 화면 모달 전용 기능이므로 범위가 다름
|
||||
|
||||
### 5. 수정 대상: handleAdd 함수만
|
||||
|
||||
- **결정**: 저장 성공 분기에서만 `continuousAdd` 체크
|
||||
- **근거**: 실패 시에는 원래대로 모달 유지 + 에러 표시. 분기가 필요한 건 성공 시뿐
|
||||
|
||||
---
|
||||
|
||||
## 관련 파일 위치
|
||||
|
||||
| 구분 | 파일 경로 | 설명 |
|
||||
|------|----------|------|
|
||||
| 수정 대상 | `frontend/components/table-category/CategoryValueManagerTree.tsx` | 트리형 카테고리 값 관리 (대분류/중분류/소분류) |
|
||||
| 참고 패턴 (로직) | `frontend/components/table-category/CategoryValueAddDialog.tsx` | 평면 목록 추가 모달 - continuousAdd 분기 로직 |
|
||||
| 참고 패턴 (UI) | `frontend/components/common/ScreenModal.tsx` | 동적 화면 모달 - 체크박스 위치/라벨/스타일 |
|
||||
|
||||
---
|
||||
|
||||
## 기술 참고
|
||||
|
||||
### 현재 handleAdd 흐름
|
||||
|
||||
```
|
||||
handleAdd() → API 호출 → 성공 시:
|
||||
1. toast.success
|
||||
2. 폼 초기화 (모달 유지 - 하드코딩)
|
||||
3. addNameRef 포커스
|
||||
4. loadTree(true) - 펼침 상태 유지
|
||||
5. parentValue 있으면 해당 노드 펼침
|
||||
```
|
||||
|
||||
### 변경 후 handleAdd 흐름
|
||||
|
||||
```
|
||||
handleAdd() → API 호출 → 성공 시:
|
||||
1. toast.success
|
||||
2. loadTree(true) + parentValue 펼침
|
||||
3. continuousAdd 체크:
|
||||
- true: 폼 초기화 + addNameRef 포커스 (모달 유지)
|
||||
- false: 폼 초기화 + setIsAddModalOpen(false) (모달 닫힘)
|
||||
```
|
||||
|
||||
### import 현황
|
||||
|
||||
- `Checkbox`: 24행에서 이미 import (`@/components/ui/checkbox`)
|
||||
- `Label`: 53행에서 이미 import (`@/components/ui/label`)
|
||||
- 추가 import 불필요
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
# [체크리스트] 카테고리 트리 대분류 추가 모달 - 연속 등록 모드 수정
|
||||
|
||||
> 관련 문서: [계획서](./CCA[계획]-카테고리-연속등록모드.md) | [맥락노트](./CCA[맥락]-카테고리-연속등록모드.md)
|
||||
|
||||
---
|
||||
|
||||
## 공정 상태
|
||||
|
||||
- 전체 진행률: **100%** (구현 완료)
|
||||
- 현재 단계: 완료
|
||||
|
||||
---
|
||||
|
||||
## 구현 체크리스트
|
||||
|
||||
### 1단계: 상태 추가
|
||||
|
||||
- [x] `CategoryValueManagerTree.tsx` 모달 상태 선언부(286행 근처)에 `continuousAdd` 상태 추가
|
||||
|
||||
### 2단계: handleAdd 분기 수정
|
||||
|
||||
- [x] `handleAdd` 성공 분기(512~530행)에서 `continuousAdd` 체크 분기 추가
|
||||
- [x] `continuousAdd === true`: 폼 초기화 + addNameRef 포커스 (모달 유지)
|
||||
- [x] `continuousAdd === false`: 폼 초기화 + `setIsAddModalOpen(false)` (모달 닫힘)
|
||||
|
||||
### 3단계: DialogFooter UI 수정
|
||||
|
||||
- [x] DialogFooter(809~821행)는 버튼만 유지
|
||||
- [x] DialogFooter 아래에 `border-t px-4 py-3` 영역 추가
|
||||
- [x] "저장 후 계속 입력 (연속 등록 모드)" 체크박스 배치
|
||||
- [x] ScreenModal.tsx (1287~1303행) 패턴과 동일한 className/라벨 사용
|
||||
|
||||
### 4단계: 검증
|
||||
|
||||
- [ ] 대분류 추가: 체크 해제 상태에서 추가 → 모달 닫힘 확인
|
||||
- [ ] 대분류 추가: 체크 상태에서 추가 → 모달 유지 + 폼 초기화 + 포커스 확인
|
||||
- [ ] 하위 추가(중분류/소분류): 동일하게 동작하는지 확인
|
||||
- [ ] 수정/삭제 모달: 기존 동작 변화 없음 확인
|
||||
|
||||
### 5단계: 정리
|
||||
|
||||
- [x] 린트 에러 없음 확인
|
||||
- [x] 이 체크리스트 완료 표시 업데이트
|
||||
|
||||
---
|
||||
|
||||
## 변경 이력
|
||||
|
||||
| 날짜 | 내용 |
|
||||
|------|------|
|
||||
| 2026-03-11 | 계획서, 맥락노트, 체크리스트 작성 완료 |
|
||||
| 2026-03-11 | 구현 완료 (1~3단계, 5단계 정리). 4단계 검증은 수동 테스트 필요 |
|
||||
|
|
@ -288,6 +288,7 @@ export const CategoryValueManagerTree: React.FC<CategoryValueManagerTreeProps> =
|
|||
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
|
||||
const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false);
|
||||
const [parentValue, setParentValue] = useState<CategoryValue | null>(null);
|
||||
const [continuousAdd, setContinuousAdd] = useState(false);
|
||||
const [editingValue, setEditingValue] = useState<CategoryValue | null>(null);
|
||||
const [deletingValue, setDeletingValue] = useState<CategoryValue | null>(null);
|
||||
|
||||
|
|
@ -512,21 +513,24 @@ export const CategoryValueManagerTree: React.FC<CategoryValueManagerTreeProps> =
|
|||
const response = await createCategoryValue(input);
|
||||
if (response.success) {
|
||||
toast.success("카테고리가 추가되었습니다");
|
||||
// 폼 초기화 (모달은 닫지 않고 연속 입력)
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
valueCode: "",
|
||||
valueLabel: "",
|
||||
description: "",
|
||||
color: "",
|
||||
}));
|
||||
setTimeout(() => addNameRef.current?.focus(), 50);
|
||||
// 기존 펼침 상태 유지하면서 데이터 새로고침
|
||||
await loadTree(true);
|
||||
// 부모 노드만 펼치기 (하위 추가 시)
|
||||
if (parentValue) {
|
||||
setExpandedNodes((prev) => new Set([...prev, parentValue.valueId]));
|
||||
}
|
||||
|
||||
if (continuousAdd) {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
valueCode: "",
|
||||
valueLabel: "",
|
||||
description: "",
|
||||
color: "",
|
||||
}));
|
||||
setTimeout(() => addNameRef.current?.focus(), 50);
|
||||
} else {
|
||||
setFormData({ valueCode: "", valueLabel: "", description: "", color: "", isActive: true });
|
||||
setIsAddModalOpen(false);
|
||||
}
|
||||
} else {
|
||||
toast.error(response.error || "추가 실패");
|
||||
}
|
||||
|
|
@ -818,6 +822,19 @@ export const CategoryValueManagerTree: React.FC<CategoryValueManagerTreeProps> =
|
|||
추가
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
|
||||
<div className="border-t px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Checkbox
|
||||
id="tree-continuous-add"
|
||||
checked={continuousAdd}
|
||||
onCheckedChange={(checked) => setContinuousAdd(checked as boolean)}
|
||||
/>
|
||||
<Label htmlFor="tree-continuous-add" className="cursor-pointer text-sm font-normal select-none">
|
||||
저장 후 계속 입력 (연속 등록 모드)
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue