[as] rename: steiger 적용

This commit is contained in:
2026-05-19 16:01:38 +09:00
parent c5a5b3bde3
commit 3f75d2751a
20 changed files with 52 additions and 77 deletions
+25 -24
View File
@@ -239,27 +239,27 @@ END
#### 인라인 편집 정책
| 항목 | 내용 |
| ------------------- | -------------------------------------------------------------------------------------------------------- |
| **단일 행 편집** | **행 추가 또는 한 행 수정 중에는 다른 행을 편집할 수 없다.** `editingRowId` 로 현재 편집 행 1건만 허용. |
| 편집 잠금 시 비활성 | 다른 행: 셀·[수정]·[삭제] 비활성 · `[행 추가]` 비활성 · 행 드래그 비활성 · (선택) `[결재선 저장]` 비활성 |
| 편집 해제 | 행별 **[저장]** 성공 또는 **[취소]** 시 `editingRowId` 초기화 후 전체 조회 모드 복귀 |
| 조회 행 | 편집 중이 아닐 때 읽기 전용. **[수정]** 클릭 시 해당 행만 편집 모드 진입 |
| 신규 행 | `[행 추가]` 시 맨 아래 편집 행 1건 생성 (`editingRowId` = 임시 키) |
| 필수값 | `atrzSeComCd`, `aprvrUuid`, `atrzSeqNo` |
| 순서 중복 | 동일 서식 내 `atrzSeqNo` 중복 시 저장 전 검증·안내 |
| 결재구분 코드 | `ATRZSECD` — 예: `ATRZ01` 검토자, `ATRZ02` 승인자, `ATRZ03` 최종승인자 |
| 항목 | 내용 |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| **단일 행 편집** | **행 추가 또는 한 행 수정 중에는 다른 행을 편집할 수 없다.** `editingRowId` 로 현재 편집 행 1건만 허용. |
| 편집 잠금 시 비활성 | 다른 행: 셀·[수정]·[삭제] 비활성 · `[행 추가]` 비활성 · 행 드래그 비활성 · (선택) `[결재선 저장]` 비활성 |
| 편집 해제 | 행별 **[저장]** 성공 또는 **[취소]** 시 `editingRowId` 초기화 후 전체 조회 모드 복귀 |
| 조회 행 | 편집 중이 아닐 때 읽기 전용. **[수정]** 클릭 시 해당 행만 편집 모드 진입 |
| 신규 행 | `[행 추가]` 시 맨 아래 편집 행 1건 생성 (`editingRowId` = 임시 키) |
| 필수값 | `atrzSeComCd`, `aprvrUuid`, `atrzSeqNo` |
| 순서 중복 | 동일 서식 내 `atrzSeqNo` 중복 시 저장 전 검증·안내 |
| 결재구분 코드 | `ATRZSECD` — 예: `ATRZ01` 검토자, `ATRZ02` 승인자, `ATRZ03` 최종승인자 |
| **순서 제약** | `ATRZ01`은 최상단 블록, `ATRZ03`은 최하단 블록. 드래그·결재구분 변경·저장 시 `validateAtrzOrder`로 검증, 위반 시 적용/저장 불가 |
| 저장 단위 | 행별 **[저장]** (단건 POST/PUT) 또는 툴바 **[결재선 저장]** (일괄) |
| 저장 단위 | 행별 **[저장]** (단건 POST/PUT) 또는 툴바 **[결재선 저장]** (일괄) |
#### 행 드래그 정렬 (`@dnd-kit` + `useSortable`)
| 항목 | 내용 |
| ---------- | ------------------------------------------------------------------------------------------- |
| 목적 | 결재 단계 순서를 그리드에서 직접 변경 |
| 라이브러리 | `@dnd-kit/core`, `@dnd-kit/sortable` — 테이블 행 POC 후 확정 |
| 저장 시점 | 드래그는 클라이언트 상태만 변경 → **[결재선 저장]** 또는 행별 **[저장]** 시 `atrzSeqNo` PUT |
| 편집 중 | `editingRowKey != null` 이면 드래그 **불가** |
| 항목 | 내용 |
| ---------- | ------------------------------------------------------------------------------------------------------- |
| 목적 | 결재 단계 순서를 그리드에서 직접 변경 |
| 라이브러리 | `@dnd-kit/core`, `@dnd-kit/sortable` — 테이블 행 POC 후 확정 |
| 저장 시점 | 드래그는 클라이언트 상태만 변경 → **[결재선 저장]** 또는 행별 **[저장]** 시 `atrzSeqNo` PUT |
| 편집 중 | `editingRowKey != null` 이면 드래그 **불가** |
| 순서 제약 | 드래그 종료 시 `validateAtrzOrder` — 실패하면 순서 변경 미적용 + 안내. 결재구분 미선택 행은 드래그 불가 |
### 5-3. 보조 다이얼로그 (검토인력만)
@@ -371,13 +371,14 @@ END
### 7-2. 모듈·파일 배치(제안)
| 구분 | 경로 |
| ------ | ------------------------------------------------------------------ |
| 라우트 | `apps/as/app/(with-base-layout)/approval-line-management/page.tsx` |
| 페이지 | `apps/as/src/pages/approval-line-management/` |
| 위젯 | `widgets/elapr-tmplt-list/`, `widgets/elapr-apln-grid/` |
| 피처 | `features/aprvr-picker/` (검토인력 [선택]만) |
| 엔티티 | `apps/as/src/entities/elapr/` |
| 구분 | 경로 |
| ------ | -------------------------------------------------------------------------------------- |
| 라우트 | `apps/as/app/(with-base-layout)/approval-line-management/page.tsx` |
| 페이지 | `apps/as/src/pages/approval-line-management/` (Public API: page만 export) |
| UI | `.../ui/elapr-tmplt-list.tsx`, `elapr-tmplt-form-dialog.tsx`, `elapr-apln-grid.tsx` 등 |
| model | `.../model/columns.tsx`, `apln-row-types.ts`, `mock-reviewers.ts` |
| lib | `.../lib/use-apln-rows.ts`, `validators.ts`, `use-dnd-sensors.ts` |
| 엔티티 | `apps/as/src/entities/elapr/` |
### 7-3. 영역별 UI 매핑
@@ -1,4 +1,4 @@
import { client } from "@/shared/api/client";
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type { Code, CodeService } from "../model/code-interface";
@@ -9,9 +9,7 @@ async function getList(upCd: string): Promise<Code[]> {
}
async function getListByUseYn(upCd: string, useYn: string): Promise<Code[]> {
const response = await client.get<Code[]>(
`${ENDPOINT.CMS_SERVICE.CODE}/${upCd}/${useYn}`,
);
const response = await client.get<Code[]>(`${ENDPOINT.CMS_SERVICE.CODE}/${upCd}/${useYn}`);
return response.data;
}
@@ -1,4 +1,4 @@
import { client } from "@/shared/api/client";
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type { PageResponse } from "@/shared/model";
@@ -11,17 +11,14 @@ import type {
} from "../model/elapr-apln-interface";
async function findPage(params: ElaprAplnListParams): Promise<PageResponse<ElaprApln>> {
const response = await client.get<PageResponse<ElaprApln>>(
ENDPOINT.AS_ELAPR_SERVICE.ELAPR_APLN,
{ params },
);
const response = await client.get<PageResponse<ElaprApln>>(ENDPOINT.AS_ELAPR_SERVICE.ELAPR_APLN, {
params,
});
return response.data;
}
async function findById(aplnId: string): Promise<ElaprApln> {
const response = await client.get<ElaprApln>(
`${ENDPOINT.AS_ELAPR_SERVICE.ELAPR_APLN}/${aplnId}`,
);
const response = await client.get<ElaprApln>(`${ENDPOINT.AS_ELAPR_SERVICE.ELAPR_APLN}/${aplnId}`);
return response.data;
}
@@ -1,4 +1,4 @@
import { client } from "@/shared/api/client";
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type { PageResponse } from "@/shared/model";
@@ -26,17 +26,11 @@ async function findById(elaprTmpltId: string): Promise<ElaprTmplt> {
}
async function create(body: ElaprTmpltSaveRequest): Promise<ElaprTmplt> {
const response = await client.post<ElaprTmplt>(
ENDPOINT.AS_ELAPR_SERVICE.ELAPR_TMPLT,
body,
);
const response = await client.post<ElaprTmplt>(ENDPOINT.AS_ELAPR_SERVICE.ELAPR_TMPLT, body);
return response.data;
}
async function update(
elaprTmpltId: string,
body: ElaprTmpltUpdateRequest,
): Promise<ElaprTmplt> {
async function update(elaprTmpltId: string, body: ElaprTmpltUpdateRequest): Promise<ElaprTmplt> {
const response = await client.put<ElaprTmplt>(
`${ENDPOINT.AS_ELAPR_SERVICE.ELAPR_TMPLT}/${elaprTmpltId}`,
body,
@@ -1,2 +0,0 @@
export { AprvrPickerDialog } from "./ui/aprvr-picker-dialog";
export type { ReviewerOption } from "./model/mock-reviewers";
@@ -1,2 +0,0 @@
export { ElaprTmpltFormDialog } from "./ui/elapr-tmplt-form-dialog";
export type { ElaprTmpltFormValues } from "./ui/elapr-tmplt-form-dialog";
@@ -6,7 +6,7 @@ import { arrayMove } from "@dnd-kit/sortable";
import type { ElaprApln } from "@/entities/elapr";
import type { ElaprAplnRow } from "../model/types";
import type { ElaprAplnRow } from "../model/apln-row-types";
import { validateAtrzOrder } from "./validators";
@@ -1,9 +1,4 @@
import {
KeyboardSensor,
PointerSensor,
useSensor,
useSensors,
} from "@dnd-kit/core";
import { KeyboardSensor, PointerSensor, useSensor, useSensors } from "@dnd-kit/core";
import { sortableKeyboardCoordinates } from "@dnd-kit/sortable";
export function useDndSensors() {
@@ -1,4 +1,4 @@
import type { ElaprAplnRow } from "../model/types";
import type { ElaprAplnRow } from "../model/apln-row-types";
export const ATRZ_REVIEWER_CD = "ATRZ01";
export const ATRZ_FINAL_CD = "ATRZ03";
@@ -3,8 +3,9 @@
import { useState } from "react";
import type { ElaprTmplt } from "@/entities/elapr";
import { ElaprAplnGrid } from "@/widgets/elapr-apln-grid";
import { ElaprTmpltList } from "@/widgets/elapr-tmplt-list";
import { ElaprAplnGrid } from "./elapr-apln-grid";
import { ElaprTmpltList } from "./elapr-tmplt-list";
function ApprovalLineManagementPage() {
const [selectedTmplt, setSelectedTmplt] = useState<ElaprTmplt | null>(null);
@@ -1,6 +1,9 @@
"use client";
import { useMemo, useState } from "react";
import { createColumnHelper, getCoreRowModel, useReactTable } from "@tanstack/react-table";
import {
Button,
DataTable,
@@ -11,11 +14,6 @@ import {
DialogTitle,
Input,
} from "@common/components";
import {
createColumnHelper,
getCoreRowModel,
useReactTable,
} from "@tanstack/react-table";
import type { ReviewerOption } from "../model/mock-reviewers";
import { createMockReviewers } from "../model/mock-reviewers";
@@ -36,8 +34,7 @@ function AprvrPickerDialog({ open, onOpenChange, onSelect }: AprvrPickerDialogPr
const q = keyword.trim().toLowerCase();
if (!q) return reviewers;
return reviewers.filter(
(r) =>
r.name.toLowerCase().includes(q) || r.deptNm.toLowerCase().includes(q),
(r) => r.name.toLowerCase().includes(q) || r.deptNm.toLowerCase().includes(q),
);
}, [keyword, reviewers]);
@@ -12,7 +12,6 @@ import {
useElaprAplnPage,
useUpdateElaprApln,
} from "@/entities/elapr";
import { AprvrPickerDialog, type ReviewerOption } from "@/features/aprvr-picker";
import {
Button,
Skeleton,
@@ -26,8 +25,10 @@ import {
import { useAplnRows } from "../lib/use-apln-rows";
import { useDndSensors } from "../lib/use-dnd-sensors";
import { validateAplnRow, validateAtrzOrder, validateUniqueSeqNo } from "../lib/validators";
import type { ElaprAplnRow } from "../model/types";
import type { ElaprAplnRow } from "../model/apln-row-types";
import type { ReviewerOption } from "../model/mock-reviewers";
import { AprvrPickerDialog } from "./aprvr-picker-dialog";
import { SortableAplnRow } from "./sortable-apln-row";
interface ElaprAplnGridProps {
@@ -74,13 +75,9 @@ function ElaprAplnGrid({ elaprTmpltId }: ElaprAplnGridProps) {
const { active, over } = event;
if (!over || active.id === over.id) return;
const { payloadRows, orderError } = reorderRows(String(active.id), String(over.id));
const { orderError } = reorderRows(String(active.id), String(over.id));
if (orderError) {
alert(orderError);
return;
}
if (payloadRows) {
// TODO: 결재선 순서 변경 API 호출
}
};
@@ -12,11 +12,12 @@ import {
useElaprTmpltPage,
useUpdateElaprTmplt,
} from "@/entities/elapr";
import { ElaprTmpltFormDialog, type ElaprTmpltFormValues } from "@/features/elapr-tmplt-dialog";
import { Button } from "@common/components";
import { createTmpltColumns } from "../model/columns";
import { ElaprTmpltFormDialog, type ElaprTmpltFormValues } from "./elapr-tmplt-form-dialog";
interface ElaprTmpltListProps {
selectedTmpltId: string | null;
onSelectTmplt: (tmplt: ElaprTmplt | null) => void;
@@ -7,7 +7,7 @@ import { Equal } from "lucide-react";
import { CodeSelect, findCodeLabel, useCodeList } from "@/entities/code";
import { Button, Input, TableCell, TableRow } from "@common/components";
import type { ElaprAplnRow } from "../model/types";
import type { ElaprAplnRow } from "../model/apln-row-types";
interface SortableAplnRowProps {
row: ElaprAplnRow;
@@ -1 +0,0 @@
export { ElaprAplnGrid } from "./ui/elapr-apln-grid";
@@ -1 +0,0 @@
export { ElaprTmpltList } from "./ui/elapr-tmplt-list";