[as] feat: 결재선 수정 사항 반영
This commit is contained in:
@@ -2,13 +2,7 @@ import { ACCESS_TOKEN, AUTH_USER_ID, CLAIM_NAME, ENDPOINT } from "@/shared/confi
|
||||
import { type AuthPayload, type AuthService, getRemainingMs } from "@common/auth";
|
||||
|
||||
import { clearDeviceId, getOrCreateDeviceId } from "../lib/device-id";
|
||||
import {
|
||||
clearRefreshTimer,
|
||||
removeToken,
|
||||
setToken,
|
||||
setupRefreshTimer,
|
||||
tokenManager,
|
||||
} from "../lib/token-manager";
|
||||
import { clearRefreshTimer, setToken, setupRefreshTimer, tokenManager } from "../lib/token-manager";
|
||||
|
||||
async function login(payload: AuthPayload): Promise<void> {
|
||||
const response = await fetch(`/api/proxy${ENDPOINT.USER_SERVICE.LOGIN}`, {
|
||||
@@ -79,8 +73,6 @@ async function silentRefresh(): Promise<void> {
|
||||
|
||||
async function logout(): Promise<void> {
|
||||
tokenManager.clearAuthToken();
|
||||
removeToken("site-id-key");
|
||||
removeToken("site-theme-key");
|
||||
clearDeviceId();
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { ElaprAplnRow } from "../model/elapr-apln-row-types";
|
||||
|
||||
const ATRZ_RECEIVER_CD = "ATRZ01";
|
||||
const ATRZ_FINAL_CD = "ATRZ99";
|
||||
|
||||
interface AtrzOrderValidateRow {
|
||||
@@ -16,14 +15,6 @@ export function getVisibleRows(rows: ElaprAplnRow[]): ElaprAplnRow[] {
|
||||
export function validateAtrzOrder(rows: AtrzOrderValidateRow[]): string | null {
|
||||
const visibleRows = rows.filter((row) => !row.isDeleted);
|
||||
|
||||
const receiverCount = visibleRows.filter((row) => row.atrzSeComCd === ATRZ_RECEIVER_CD).length;
|
||||
if (receiverCount === 0) {
|
||||
return "접수자는 반드시 등록해야 합니다.";
|
||||
}
|
||||
if (receiverCount > 1) {
|
||||
return "접수자는 한 명만 등록할 수 있습니다.";
|
||||
}
|
||||
|
||||
const finalCount = visibleRows.filter((row) => row.atrzSeComCd === ATRZ_FINAL_CD).length;
|
||||
if (finalCount === 0) {
|
||||
return "최종승인자는 반드시 등록해야 합니다.";
|
||||
@@ -32,10 +23,6 @@ export function validateAtrzOrder(rows: AtrzOrderValidateRow[]): string | null {
|
||||
return "최종승인자는 한 명만 등록할 수 있습니다.";
|
||||
}
|
||||
|
||||
if (receiverCount === 1 && visibleRows[0]?.atrzSeComCd !== ATRZ_RECEIVER_CD) {
|
||||
return "접수자는 결재선 첫 번째에 배치해야 합니다.";
|
||||
}
|
||||
|
||||
if (finalCount === 1) {
|
||||
const lastRow = visibleRows[visibleRows.length - 1];
|
||||
if (lastRow?.atrzSeComCd !== ATRZ_FINAL_CD) {
|
||||
|
||||
@@ -24,7 +24,7 @@ function toRow(item: ElaprDocPrgrsClientResponseDto): ElaprPrgrsSttsRow {
|
||||
elaprPrgrsId: item.elaprPrgrsId,
|
||||
elaprDmndId: item.elaprDmndId,
|
||||
aprvrUuid: item.aprvrUuid,
|
||||
aprvrNm: item.aprvrUuidNm,
|
||||
aprvrNm: item.aprvrNm,
|
||||
atrzSeComCd: item.atrzSeComCd,
|
||||
atrzSeComNm: item.atrzSeComNm,
|
||||
atrzSeqNo: item.atrzSeqNo,
|
||||
|
||||
@@ -6,14 +6,14 @@ export const ELAPR_APLN_SAVE_MODE = {
|
||||
DELETE: "D",
|
||||
} as const;
|
||||
|
||||
export type ElaprAplnSaveMode =
|
||||
(typeof ELAPR_APLN_SAVE_MODE)[keyof typeof ELAPR_APLN_SAVE_MODE];
|
||||
export type ElaprAplnSaveMode = (typeof ELAPR_APLN_SAVE_MODE)[keyof typeof ELAPR_APLN_SAVE_MODE];
|
||||
|
||||
export interface ElaprApln {
|
||||
aplnId: string;
|
||||
elaprTmpltId: string;
|
||||
aprvrUuid?: string;
|
||||
aprvrNm?: string;
|
||||
aprvrUserId?: string;
|
||||
atrzSeComCd?: string;
|
||||
atrzSeComNm?: string;
|
||||
atrzSeqNo?: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { PageableParams, PageResponse } from "@/shared/model";
|
||||
import type { ElaprDocPrgrsClientResponseDto, PageableParams, PageResponse } from "@/shared/model";
|
||||
|
||||
import type { ElaprPrgrsSttsSaveRequestDto } from "./elapr-prgrs-stts-interface";
|
||||
|
||||
@@ -33,23 +33,7 @@ export interface ElaprDmndSearchDto extends PageableParams {
|
||||
}
|
||||
|
||||
/** 백엔드 ElaprDocPrgrsResponseDto */
|
||||
export interface ElaprDocPrgrsResponseDto {
|
||||
elaprDocNo: string;
|
||||
elaprDmndId: string;
|
||||
elaprPrgrsId: string;
|
||||
atrzSeqNo?: number;
|
||||
atrzSeComCd: string;
|
||||
atrzSeComNm?: string;
|
||||
aprvrUuid?: string;
|
||||
aprvrUuidNm?: string;
|
||||
atrzPrgrsSttsComCd: string;
|
||||
atrzPrgrsSttsComNm?: string;
|
||||
rjctRsn?: string;
|
||||
atrzDt?: string;
|
||||
jbpsSeNm?: string;
|
||||
jbgdSeNm?: string;
|
||||
ognzNm?: string;
|
||||
}
|
||||
export type ElaprDocPrgrsResponseDto = ElaprDocPrgrsClientResponseDto;
|
||||
|
||||
/** Controller public 메서드명·시그니처와 동일 */
|
||||
export interface ElaprDmndService {
|
||||
|
||||
@@ -14,10 +14,6 @@ interface CreateTmpltColumnsOptions {
|
||||
|
||||
export function createTmpltColumns({ onEdit, onDelete }: CreateTmpltColumnsOptions) {
|
||||
return [
|
||||
columnHelper.accessor("elaprTmpltId", {
|
||||
header: "전자결재서식아이디",
|
||||
meta: { align: "left" },
|
||||
}),
|
||||
columnHelper.accessor("elaprTmpltNm", {
|
||||
header: "결재서식명",
|
||||
meta: { align: "left" },
|
||||
|
||||
@@ -11,7 +11,7 @@ function ApprovalLineManagementPage() {
|
||||
const [selectedTmplt, setSelectedTmplt] = useState<ElaprTmplt | null>(null);
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-[1.8rem] pb-6">
|
||||
<div className="grid grid-cols-1 gap-6 px-[1.8rem] xl:grid-cols-2">
|
||||
<ElaprTmpltList
|
||||
selectedTmpltId={selectedTmplt?.elaprTmpltId ?? null}
|
||||
onSelectTmplt={setSelectedTmplt}
|
||||
|
||||
@@ -186,20 +186,20 @@ function ElaprAplnGrid({ elaprTmpltId }: ElaprAplnGridProps) {
|
||||
<DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
|
||||
<Table className="w-full">
|
||||
<colgroup>
|
||||
<col width="10%" />
|
||||
<col width="30%" />
|
||||
<col width="5%" />
|
||||
<col width="10%" />
|
||||
<col width="10%" />
|
||||
<col width="15%" />
|
||||
<col width="15%" />
|
||||
<col width="25%" />
|
||||
<col width="20%" />
|
||||
<col width="10%" />
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-10" />
|
||||
<TableHead>결재선아이디</TableHead>
|
||||
<TableHead>결재순서</TableHead>
|
||||
<TableHead>결재구분</TableHead>
|
||||
<TableHead>결재자 ID</TableHead>
|
||||
<TableHead>결재자</TableHead>
|
||||
<TableHead>등록일시</TableHead>
|
||||
<TableHead>관리</TableHead>
|
||||
|
||||
@@ -4,9 +4,18 @@ import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { Equal } from "lucide-react";
|
||||
|
||||
import { CodeSelect, findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import { findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import type { ElaprAplnRow } from "@/entities/elapr";
|
||||
import { Button, TableCell, TableRow } from "@common/components";
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
interface SortableAplnRowProps {
|
||||
row: ElaprAplnRow;
|
||||
@@ -36,6 +45,7 @@ function SortableAplnRow({
|
||||
onPickAprvr,
|
||||
}: SortableAplnRowProps) {
|
||||
const { data: atrzSeCodes } = useCodeList("ATRZSE");
|
||||
const selectableAtrzSeCodes = atrzSeCodes?.filter((code) => code.lwpoCd !== "ATRZ01");
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: row.rowKey,
|
||||
@@ -49,7 +59,7 @@ function SortableAplnRow({
|
||||
};
|
||||
|
||||
const rowDisabled = isLocked && !isEditing;
|
||||
const atrzSeLabel = row.atrzSeComNm || findCodeLabel(atrzSeCodes, row.atrzSeComCd);
|
||||
const atrzSeLabel = row.atrzSeComNm || findCodeLabel(selectableAtrzSeCodes, row.atrzSeComCd);
|
||||
|
||||
return (
|
||||
<TableRow ref={setNodeRef} style={style} className={isEditing ? "bg-accent/30" : undefined}>
|
||||
@@ -66,31 +76,36 @@ function SortableAplnRow({
|
||||
<Equal className="!size-6" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.isNew ? "-" : (row.aplnId ?? "-")}</TableCell>
|
||||
<TableCell align="center">{displaySeqNo}</TableCell>
|
||||
<TableCell align="center">
|
||||
{isEditing ? (
|
||||
<CodeSelect
|
||||
upCd="ATRZSE"
|
||||
className="min-w-[120px]"
|
||||
<Select
|
||||
value={row.atrzSeComCd ?? ""}
|
||||
onValueChange={(v) =>
|
||||
onChange({
|
||||
atrzSeComCd: v,
|
||||
atrzSeComNm: findCodeLabel(atrzSeCodes, v),
|
||||
})
|
||||
onChange({ atrzSeComCd: v, atrzSeComNm: findCodeLabel(selectableAtrzSeCodes, v) })
|
||||
}
|
||||
placeholder="결재구분"
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="min-w-[120px]">
|
||||
<SelectValue placeholder="결재구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{selectableAtrzSeCodes?.map((code) => (
|
||||
<SelectItem key={code.lwpoCd} value={code.lwpoCd}>
|
||||
{code.lwpoCdNm}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
atrzSeLabel || "-"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.aprvrUserId ?? "-"}</TableCell>
|
||||
<TableCell align="center">
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<span>{row.aprvrNm || row.aprvrUuid || "-"}</span>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
<span>{row.aprvrNm || row.aprvrUuid || ""}</span>
|
||||
{isEditing && (
|
||||
<Button type="button" size="sm" onClick={onPickAprvr}>
|
||||
<Button type="button" size="sm" onClick={onPickAprvr} disabled={!row.atrzSeComCd}>
|
||||
선택
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -147,16 +147,14 @@ function ElaprAplnPickerGrid({
|
||||
<Table className="w-full">
|
||||
<colgroup>
|
||||
<col width="10%" />
|
||||
<col width="30%" />
|
||||
<col width="10%" />
|
||||
<col width="10%" />
|
||||
<col width="30%" />
|
||||
<col width="10%" />
|
||||
<col width="50%" />
|
||||
<col width="20%" />
|
||||
</colgroup>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="w-10" />
|
||||
<TableHead>결재선아이디</TableHead>
|
||||
<TableHead>결재순서</TableHead>
|
||||
<TableHead>결재구분</TableHead>
|
||||
<TableHead>결재자</TableHead>
|
||||
|
||||
@@ -35,18 +35,10 @@ function ElaprTmpltSelectTable({
|
||||
|
||||
const columns = useMemo(
|
||||
() => [
|
||||
columnHelper.accessor("elaprTmpltId", {
|
||||
header: "전자결재서식아이디",
|
||||
meta: { align: "left" },
|
||||
}),
|
||||
columnHelper.accessor("elaprTmpltNm", {
|
||||
header: "결재서식명",
|
||||
meta: { align: "left" },
|
||||
}),
|
||||
columnHelper.accessor("docSeComNm", {
|
||||
header: "문서구분",
|
||||
cell: ({ getValue, row }) => getValue() ?? row.original.docSeComCd ?? "—",
|
||||
}),
|
||||
],
|
||||
[],
|
||||
);
|
||||
|
||||
@@ -4,9 +4,18 @@ import { useSortable } from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import { MoveVertical } from "lucide-react";
|
||||
|
||||
import { CodeSelect, findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import { findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import type { ElaprAplnRow } from "@/entities/elapr";
|
||||
import { Button, TableCell, TableRow } from "@common/components";
|
||||
import {
|
||||
Button,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
interface SortableAplnPickerRowProps {
|
||||
row: ElaprAplnRow;
|
||||
@@ -36,6 +45,7 @@ function SortableAplnPickerRow({
|
||||
onPickAprvr,
|
||||
}: SortableAplnPickerRowProps) {
|
||||
const { data: atrzSeCodes } = useCodeList("ATRZSE");
|
||||
const selectableAtrzSeCodes = atrzSeCodes?.filter((code) => code.lwpoCd !== "ATRZ01");
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: row.rowKey,
|
||||
@@ -66,31 +76,35 @@ function SortableAplnPickerRow({
|
||||
<MoveVertical className="!size-6" />
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell align="center">{row.isNew ? "-" : (row.aplnId ?? "-")}</TableCell>
|
||||
<TableCell align="center">{displaySeqNo}</TableCell>
|
||||
<TableCell align="center">
|
||||
{isEditing ? (
|
||||
<CodeSelect
|
||||
upCd="ATRZSE"
|
||||
className="min-w-[120px]"
|
||||
<Select
|
||||
value={row.atrzSeComCd ?? ""}
|
||||
onValueChange={(v) =>
|
||||
onChange({
|
||||
atrzSeComCd: v,
|
||||
atrzSeComNm: findCodeLabel(atrzSeCodes, v),
|
||||
})
|
||||
onChange({ atrzSeComCd: v, atrzSeComNm: findCodeLabel(selectableAtrzSeCodes, v) })
|
||||
}
|
||||
placeholder="결재구분"
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="min-w-[120px]">
|
||||
<SelectValue placeholder="결재구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{selectableAtrzSeCodes?.map((code) => (
|
||||
<SelectItem key={code.lwpoCd} value={code.lwpoCd}>
|
||||
{code.lwpoCdNm}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
atrzSeLabel || "-"
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell align="center">
|
||||
<div className="flex items-center justify-center gap-4">
|
||||
<span>{row.aprvrNm || row.aprvrUuid || "-"}</span>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<span>{row.aprvrNm || row.aprvrUuid || ""}</span>
|
||||
{isEditing && (
|
||||
<Button type="button" size="sm" onClick={onPickAprvr}>
|
||||
<Button type="button" size="sm" onClick={onPickAprvr} disabled={!row.atrzSeComCd}>
|
||||
선택
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -5,9 +5,19 @@ import { CSS } from "@dnd-kit/utilities";
|
||||
import { MoveVertical } from "lucide-react";
|
||||
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/shared/ui/tooltip";
|
||||
import { CodeSelect, findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import { findCodeLabel, useCodeList } from "@/entities/code";
|
||||
import { type ElaprPrgrsSttsRow, isPrgrsSttsRowEditable } from "@/entities/elapr";
|
||||
import { Button, cn, TableCell, TableRow } from "@common/components";
|
||||
import {
|
||||
Button,
|
||||
cn,
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
TableCell,
|
||||
TableRow,
|
||||
} from "@common/components";
|
||||
|
||||
import { getApproverStatus } from "../../lib/approval-utils";
|
||||
|
||||
@@ -39,6 +49,7 @@ function SortablePrgrsSttsRow({
|
||||
onPickAprvr,
|
||||
}: SortablePrgrsSttsRowProps) {
|
||||
const { data: atrzSeCodes } = useCodeList("ATRZSE");
|
||||
const selectableAtrzSeCodes = atrzSeCodes?.filter((code) => code.lwpoCd !== "ATRZ01");
|
||||
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({
|
||||
id: row.rowKey,
|
||||
@@ -89,18 +100,23 @@ function SortablePrgrsSttsRow({
|
||||
<TableCell align="center">{displaySeqNo}</TableCell>
|
||||
<TableCell align="center">
|
||||
{isEditing ? (
|
||||
<CodeSelect
|
||||
upCd="ATRZSE"
|
||||
className="min-w-[120px]"
|
||||
<Select
|
||||
value={row.atrzSeComCd ?? ""}
|
||||
onValueChange={(v) =>
|
||||
onChange({
|
||||
atrzSeComCd: v,
|
||||
atrzSeComNm: findCodeLabel(atrzSeCodes, v),
|
||||
})
|
||||
onChange({ atrzSeComCd: v, atrzSeComNm: findCodeLabel(selectableAtrzSeCodes, v) })
|
||||
}
|
||||
placeholder="결재구분"
|
||||
/>
|
||||
>
|
||||
<SelectTrigger className="min-w-[120px]">
|
||||
<SelectValue placeholder="결재구분" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{selectableAtrzSeCodes?.map((code) => (
|
||||
<SelectItem key={code.lwpoCd} value={code.lwpoCd}>
|
||||
{code.lwpoCdNm}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
) : (
|
||||
atrzSeLabel || "-"
|
||||
)}
|
||||
|
||||
@@ -143,7 +143,8 @@ export interface ElaprDocPrgrsClientResponseDto {
|
||||
atrzSeComCd?: string;
|
||||
atrzSeComNm?: string;
|
||||
aprvrUuid?: string;
|
||||
aprvrUuidNm?: string;
|
||||
aprvrNm?: string;
|
||||
aprvrUserId?: string;
|
||||
atrzPrgrsSttsComCd?: string;
|
||||
atrzPrgrsSttsComNm?: string;
|
||||
rjctRsn?: string | null;
|
||||
|
||||
Reference in New Issue
Block a user