코드 관리 페이지 추가

This commit is contained in:
2025-05-20 10:18:23 +09:00
parent 0d9d3c2fc5
commit b4f4839f99
21 changed files with 1269 additions and 3 deletions
@@ -1,2 +1,3 @@
export { SiteApi } from "../api/site-service";
export type { Site } from "../model/site-interface";
export { SiteSelect } from "../ui/site-select";
@@ -5,3 +5,5 @@ export {
type Site,
type SiteFormData,
} from "./model/site-interface";
export { SiteSelect } from "./ui/site-select";
@@ -0,0 +1,48 @@
import { useQuery } from "@tanstack/react-query";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { SiteApi } from "../api/site-service";
type SiteSelectProps = {
siteId: string;
placeholder?: string;
} & React.ComponentPropsWithoutRef<typeof Select>;
function SiteSelect({
siteId,
defaultValue,
placeholder = "선택",
...props
}: SiteSelectProps) {
const { data: sites } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES],
queryFn: () => SiteApi.getSiteList(),
enabled: !!siteId,
});
const defaultValueText =
sites?.content.find((item) => item.siteId === defaultValue)?.siteNm ?? placeholder;
return (
<Select {...props}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder={defaultValueText} />
</SelectTrigger>
<SelectContent>
{sites?.content.map((item) => (
<SelectItem key={item.siteId} value={item.siteId}>
{item.siteNm}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
export { SiteSelect };
@@ -0,0 +1,2 @@
import { CodePage } from "@/pages/code";
export default CodePage;
@@ -0,0 +1,61 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import {
LwpoCodeFormSchema,
type LwpoCodeService,
type LwpoCodeList,
type LwpoCodeDetail,
type LwpoCodeParams,
type LwpoCodeFormData,
} from "../model/lwpocode-interface";
export class LwpoCodeApiService implements LwpoCodeService {
public async getLwpoCodeList(params?: LwpoCodeParams): Promise<LwpoCodeList> {
const response = await client.get<LwpoCodeList>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}`,
{ params },
);
return response.data;
}
public async getUpCodeLwpoCodeList(upCd: string): Promise<LwpoCodeList> {
const response = await client.get<LwpoCodeList>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}/up-cd/${upCd}`,
);
return response.data;
}
public async LwpoCodeDetail(upCd: string, LwpoCd: string): Promise<LwpoCodeDetail> {
const response = await client.get<LwpoCodeDetail>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${LwpoCd}/up-cd/${upCd}`,
);
return response.data;
}
public async createLwpoCode(data: LwpoCodeFormData): Promise<void> {
const validatedData = LwpoCodeFormSchema.parse(data);
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}`,
validatedData,
);
return response.data;
}
public async updateLwpoCode(upCd: string, LwpoCd: string, data: LwpoCodeFormData): Promise<void> {
const validatedData = LwpoCodeFormSchema.parse(data);
const response = await client.put<void>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${LwpoCd}/up-cd/${upCd}`,
validatedData,
);
return response.data;
}
public async deleteLwpoCode(upCd: string, LwpoCd: string): Promise<void> {
await client.delete<void>(
`${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${LwpoCd}/up-cd/${upCd}`,
);
}
}
export const lwpoCodeApi = new LwpoCodeApiService();
@@ -0,0 +1,48 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import {
upCodeFormSchema,
type UpCodeService,
type UpCodeList,
type UpCodeDetail,
type UpCodeParams,
type UpCodeFormData,
} from "../model/upcode-interface";
export class UpCodeApiService implements UpCodeService {
public async getUpCodeList(params?: UpCodeParams): Promise<UpCodeList> {
const response = await client.get<UpCodeList>(
`${ENDPOINT.CMS_SERVICE.UP_CODE}`,
{ params },
);
return response.data;
}
public async upCodeDetail(upCd: string): Promise<UpCodeDetail> {
const response = await client.get<UpCodeDetail>(
`${ENDPOINT.CMS_SERVICE.UP_CODE}/${upCd}`,
);
return response.data;
}
public async createUpCode(data: UpCodeFormData): Promise<void> {
const validatedData = upCodeFormSchema.parse(data);
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.UP_CODE}`,
validatedData,
);
return response.data;
}
public async updateUpCode(upCd: string, data: UpCodeFormData): Promise<void> {
const validatedData = upCodeFormSchema.parse(data);
const response = await client.put<void>(
`${ENDPOINT.CMS_SERVICE.UP_CODE}/${upCd}`,
validatedData,
);
return response.data;
}
}
export const upCodeApi = new UpCodeApiService();
+11
View File
@@ -1,3 +1,14 @@
export { CodeApi } from "./api/code-service";
export type { ICode } from "./model/code-interface";
export { CodeSelect } from "./ui/code-select";
export { upCodeApi } from "./api/upcode-service";
export type { UpCode } from "./model/upcode-interface";
export { upCodeFormSchema, type UpCodeFormData } from "./model/upcode-interface";
export { lwpoCodeApi} from "./api/lwpocode-service";
export type { LwpoCode } from "./model/lwpocode-interface";
export { LwpoCodeFormSchema, type LwpoCodeFormData } from "./model/lwpocode-interface";
@@ -0,0 +1,61 @@
import { z } from "zod";
import type { PageableParams, PageableList } from "@/shared/model";
export interface LwpoCodeParams extends PageableParams {
upCd?: string;
lwpoCd?: string;
lwpoCdNm?: string;
useYn?: string;
}
export interface LwpoCode {
lwpoCd: string;
upCd: string;
lwpoCdNm: string;
lwpoCdExpln: string;
unit: string;
prntLwpoCd: string;
useYn: string;
sortSeq: number;
regDt: string;
rgTr: string;
rgTrNm: string;
mdfcnDt: string;
mdFr: string;
}
export interface LwpoCodeDetail extends LwpoCodeFormData {
lwpoCd: string;
upCd: string;
lwpoCdNm: string;
lwpoCdExpln: string;
unit: string;
prntLwpoCd: string;
useYn: string;
sortSeq: 0;
}
export const LwpoCodeFormSchema = z.object({
lwpoCd: z.string().max(10).nonempty(),
upCd: z.string().max(10).nonempty(),
lwpoCdNm: z.string().max(100).nonempty(),
lwpoCdExpln: z.string().max(1000).nullish(),
unit: z.string().max(20).nullish(),
prntLwpoCd: z.string().max(10).nullish(),
useYn: z.string(),
sortSeq: z.number(),
delYn: z.string(),
});
export type LwpoCodeFormData = z.infer<typeof LwpoCodeFormSchema>;
export type LwpoCodeList = PageableList<LwpoCode>;
export interface LwpoCodeService {
getLwpoCodeList(params: LwpoCodeParams): Promise<LwpoCodeList>;
getUpCodeLwpoCodeList(upCd: string): Promise<LwpoCodeList>;
LwpoCodeDetail(upCd: string, LwpoCode: string): Promise<LwpoCodeDetail>;
updateLwpoCode(upCd: string, LwpoCode: string, data: LwpoCodeFormData): Promise<void>;
createLwpoCode(data: LwpoCodeFormData): Promise<void>;
deleteLwpoCode(upCd: string, LwpoCode: string, data: LwpoCodeFormData): Promise<void>;
}
@@ -0,0 +1,53 @@
import { z } from "zod";
import type { PageableParams, PageableList } from "@/shared/model";
export interface UpCodeParams extends PageableParams {
keyword?: string;
upCd?: string;
cdSe?: string;
useYn?: string;
siteId?: string;
}
export interface UpCode {
upCd: string;
upCdNm: string;
upCdExpln: string;
cdSe: string;
siteId: string;
useYn: string;
regDt: string;
}
export interface UpCodeDetail extends UpCodeFormData {
upCd: string;
upCdNm: string;
upCdExpln: string;
cdSe: string;
siteId: string;
useYn: string;
delYn: string;
regDt: string;
rgTrNm: string;
}
export const upCodeFormSchema = z.object({
upCd: z.string().max(10).nonempty(),
upCdNm: z.string().max(100).nonempty(),
upCdExpln: z.string().max(1000).nullish(),
cdSe: z.string().max(10).nonempty(),
siteId: z.string().max(2).nullish(),
useYn: z.string(),
delYn: z.string(),
});
export type UpCodeFormData = z.infer<typeof upCodeFormSchema>;
export type UpCodeList = PageableList<UpCode>;
export interface UpCodeService {
getUpCodeList(params: UpCodeParams): Promise<UpCodeList>;
upCodeDetail(upCodeId: string): Promise<UpCodeDetail>;
updateUpCode(upCodeId: string, data: UpCodeFormData): Promise<void>;
createUpCode(data: UpCodeFormData): Promise<void>;
}
+2
View File
@@ -1,2 +1,4 @@
export { SiteApi } from "../api/site-service";
export type { Site } from "../model/site-interface";
export { SiteSelect } from "../ui/site-select";
+2
View File
@@ -5,3 +5,5 @@ export {
type Site,
type SiteFormData,
} from "./model/site-interface";
export { SiteSelect } from "./ui/site-select";
@@ -0,0 +1,48 @@
import { useQuery } from "@tanstack/react-query";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { SiteApi } from "../api/site-service";
type SiteSelectProps = {
siteId: string;
placeholder?: string;
} & React.ComponentPropsWithoutRef<typeof Select>;
function SiteSelect({
siteId,
defaultValue,
placeholder = "선택",
...props
}: SiteSelectProps) {
const { data: sites } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.SITES],
queryFn: () => SiteApi.getSiteList(),
enabled: true,
});
const defaultValueText =
sites?.content.find((item) => item.siteId === defaultValue)?.siteNm ?? placeholder;
return (
<Select {...props}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder={defaultValueText} />
</SelectTrigger>
<SelectContent>
{sites?.content.map((item) => (
<SelectItem key={item.siteId} value={item.siteId}>
{item.siteNm}
</SelectItem>
))}
</SelectContent>
</Select>
);
}
export { SiteSelect };
@@ -10,7 +10,12 @@ export const columns: ColumnDef<Board>[] = [
{
id: "no",
header: "NO",
cell: ({ row }) => row.index + 1,
cell: ({ row, table }) => {
const totalRows = table.getRowCount();
const currentPage = table.getState().pagination.pageIndex;
const pageSize = table.getState().pagination.pageSize;
return totalRows - (currentPage * pageSize + row.index);
},
},
{
accessorKey: "bbsNm",
+1
View File
@@ -0,0 +1 @@
export { CodePage } from "./ui/code-page";
@@ -0,0 +1,123 @@
import dayjs from "dayjs";
import { type ColumnDef} from "@tanstack/react-table";
import { Button } from "@common/components";
import type { UpCode } from "@/entities/code/model/upcode-interface";
import type { LwpoCode } from "@/entities/code/model/lwpocode-interface";
type UpCodeTableMeta = {
onManagementClick?: (row: UpCode) => void;
};
type LwpoCodeTableMeta = {
onManagementClick?: (row: LwpoCode) => void;
};
export const upCodecolumns: ColumnDef<UpCode, UpCodeTableMeta>[] = [
{
id: "no",
header: "NO",
cell: ({ row, table }) => {
const isSelected = row.getIsSelected();
if (isSelected) {
return "▶";
}
const totalRows = table.getRowCount();
const currentPage = table.getState().pagination.pageIndex;
const pageSize = table.getState().pagination.pageSize;
return totalRows - (currentPage * pageSize + row.index);
},
},
{
accessorKey: "upCd",
header: "상위코드",
},
{
accessorKey: "upCdNm",
header: "상위코드명",
},
{
accessorKey: "cdSe",
header: "코드구분",
},
{
accessorKey: "useYn",
header: "사용여부",
},
{
accessorKey: "regDt",
header: "등록일",
cell: ({ row }) => {
const date = dayjs(row.original.regDt);
return date.format("YYYY-MM-DD");
},
},
{
id: "management",
header: "관리",
cell: ({ row, table }) => {
const onManagementClick = (table.options.meta as UpCodeTableMeta)?.onManagementClick;
return (
<Button
size="sm"
onClick={(e) => {
e.stopPropagation();
onManagementClick?.(row.original);
}}
>
관리
</Button>
);
},
},
];
export const lowCodecolumns: ColumnDef<LwpoCode, LwpoCodeTableMeta>[] = [
{
id: "no",
header: "NO",
cell: ({ row, table }) => {
const totalRows = table.getRowCount();
const currentPage = table.getState().pagination.pageIndex;
const pageSize = table.getState().pagination.pageSize;
return totalRows - (currentPage * pageSize + row.index);
},
},
{
accessorKey: "lwpoCd",
header: "하위코드",
},
{
accessorKey: "lwpoCdNm",
header: "하위코드명",
},
{
accessorKey: "useYn",
header: "사용 여부",
},
{
accessorKey: "regDt",
header: "등록일",
cell: ({ row }) => {
const date = dayjs(row.original.regDt);
return date.format("YYYY-MM-DD");
},
},
{
id: "management",
header: "관리",
cell: ({ row, table }) => {
const onManagementClick = (table.options.meta as LwpoCodeTableMeta)?.onManagementClick;
return (
<Button
size="sm"
onClick={(e) => {
e.stopPropagation();
onManagementClick?.(row.original);
}}
>
관리
</Button>
);
},
},
];
+248
View File
@@ -0,0 +1,248 @@
"use client";
import "@common/assets/styles/grid.css";
import { useEffect, useState } from "react";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
import {
getCoreRowModel,
useReactTable,
type PaginationState,
} from "@tanstack/react-table";
import { Button, DataTable, Input, Label } from "@common/components";
import { upCodeApi } from "@/entities/code/api/upcode-service";
import { type UpCode, type UpCodeParams } from "@/entities/code/model/upcode-interface";
import { type LwpoCode, type LwpoCodeParams } from "@/entities/code/model/lwpocode-interface";
import { ENDPOINT } from "@/shared/config";
import { upCodecolumns, lowCodecolumns } from "../model/table-columns";
import { lwpoCodeApi } from "@/entities/code/api/lwpocode-service";
import { ManagementUpCodeDialog } from "./upcode-dialog";
import { ManagementLwpoCodeDialog } from "./lowcode-dialog";
function CodePage() {
const [selectedUpCode, setSelectedUpCode] = useState<UpCode | null>(null);
const [isManagementDialogOpen, setIsManagementDialogOpen] = useState(false);
const [isRegistrationMode, setIsRegistrationMode] = useState(false);
const [isLwpoCodeRegistrationMode, setIsLwpoCodeRegistrationMode] = useState(false);
const [selectedLwpoCode, setSelectedLwpoCode] = useState<LwpoCode | null>(null);
const [isLwpoCodeManagementDialogOpen, setIsLwpoCodeManagementDialogOpen] = useState(false);
const [upCodeQuery, setUpCodeQuery] = useState<UpCodeParams>({
upCd: "",
});
const [lwpoCodeQuery, setLwpoCodeQuery] = useState<LwpoCodeParams>({
upCd: "",
lwpoCd: "",
});
const [upCodePagination, setUpCodePagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 10,
});
const [lwpoCodePagination, setLwpoCodePagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 1000,
});
const { data: upCodeList, refetch: refetchUpCode } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.UP_CODE],
queryFn: () =>
upCodeApi.getUpCodeList({
...upCodeQuery,
page: upCodePagination.pageIndex,
size: upCodePagination.pageSize,
}),
placeholderData: keepPreviousData,
});
const { data: lwpoCodeList, refetch: refetchLwpoCode } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, lwpoCodeQuery],
queryFn: () =>
lwpoCodeApi.getLwpoCodeList({
...lwpoCodeQuery,
page: lwpoCodePagination.pageIndex,
size: lwpoCodePagination.pageSize,
}),
enabled: !!lwpoCodeQuery.upCd,
});
const upCodehandleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
refetchUpCode();
};
const upCodehandleRowClick = (row: UpCode) => {
setSelectedUpCode(row);
setLwpoCodeQuery({ ...lwpoCodeQuery, upCd: row.upCd });
refetchLwpoCode();
};
const handleManagementClick = (row: UpCode) => {
setSelectedUpCode(row);
setIsRegistrationMode(false);
setIsManagementDialogOpen(true);
};
const handleRegistrationClick = () => {
setSelectedUpCode(null);
setIsRegistrationMode(true);
setIsManagementDialogOpen(true);
};
const handleLwpoCodeManagementClick = (row: LwpoCode) => {
setSelectedLwpoCode(row);
setIsLwpoCodeRegistrationMode(false);
setIsLwpoCodeManagementDialogOpen(true);
};
const handleLwpoCodeRegistrationClick = () => {
if (!selectedUpCode?.upCd) {
alert("상위코드를 선택해주세요.");
return;
}
setSelectedLwpoCode(null);
setIsLwpoCodeRegistrationMode(true);
setIsLwpoCodeManagementDialogOpen(true);
};
const upCodeTable = useReactTable({
data: upCodeList?.content ?? [],
columns: upCodecolumns,
rowCount: upCodeList?.totalElements ?? 0,
state: {
pagination: upCodePagination,
rowSelection: selectedUpCode ? { [selectedUpCode.upCd]: true } : {},
},
onPaginationChange: setUpCodePagination,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
meta: {
onManagementClick: handleManagementClick,
},
getRowId: (row) => row.upCd,
});
const lwpoCodeTable = useReactTable({
data: lwpoCodeList?.content ?? [],
columns: lowCodecolumns,
rowCount: lwpoCodeList?.totalElements ?? 0,
state: {
pagination: lwpoCodePagination,
rowSelection: selectedLwpoCode ? { [selectedLwpoCode.lwpoCd]: true } : {},
},
onPaginationChange: setLwpoCodePagination,
getCoreRowModel: getCoreRowModel(),
manualPagination: true,
meta: {
onManagementClick: handleLwpoCodeManagementClick,
},
getRowId: (row) => row.lwpoCd,
});
const lwpoCodehandleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
refetchLwpoCode();
};
useEffect(() => {
refetchUpCode();
}, [upCodePagination]);
return (
<div className="card card-border !mt-6">
<div className="flex gap-4">
<div className="flex-1">
<div className="card card-border">
<form onSubmit={upCodehandleSubmit} className="flex items-center gap-2 p-2">
<Label htmlFor="" className="mr-2 text-label">
상위코드명
</Label>
<Input
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
value={upCodeQuery.keyword}
onChange={(e) =>
setUpCodeQuery({
...upCodeQuery,
keyword: e.target.value,
})
}
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</div>
<div className="card card-border">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">상위코드 목록</h3>
<Button onClick={handleRegistrationClick}>등록</Button>
</div>
<div className="card !m-4 h-[calc(100vh-30rem)] overflow-auto !p-0">
<DataTable
table={upCodeTable}
onRowClick={upCodehandleRowClick}
viewPagination
/>
</div>
</div>
</div>
<div className="flex-1">
<div className="card card-border">
<form onSubmit={lwpoCodehandleSubmit} className="flex items-center gap-2 p-2">
<Label htmlFor="" className="mr-2 text-label">
하위코드명
</Label>
<Input
className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]"
value={lwpoCodeQuery.lwpoCdNm}
onChange={(e) => setLwpoCodeQuery({
...lwpoCodeQuery,
lwpoCdNm: e.target.value
})
}
/>
<Button className="h-[3.2rem]" type="submit">
조회
</Button>
</form>
</div>
<div className="card card-border">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">하위코드 목록</h3>
<Button onClick={handleLwpoCodeRegistrationClick}>등록</Button>
</div>
<div className="card !m-4 h-[calc(100vh-30rem)] overflow-auto !p-0">
<DataTable
table={lwpoCodeTable}
/>
</div>
</div>
</div>
</div>
{isManagementDialogOpen && (
<ManagementUpCodeDialog
open={isManagementDialogOpen}
onOpenChange={setIsManagementDialogOpen}
upCd={selectedUpCode?.upCd}
isRegistration={isRegistrationMode}
/>
)}
{isLwpoCodeManagementDialogOpen && (
<ManagementLwpoCodeDialog
open={isLwpoCodeManagementDialogOpen}
onOpenChange={setIsLwpoCodeManagementDialogOpen}
upCd={isLwpoCodeRegistrationMode ? selectedUpCode?.upCd : selectedLwpoCode?.upCd}
lwpoCd={selectedLwpoCode?.lwpoCd}
isRegistration={isLwpoCodeRegistrationMode}
/>
)}
</div>
);
}
export { CodePage };
@@ -0,0 +1,292 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Input,
RadioGroup,
RadioGroupItem,
Label,
} from "@common/components";
import { Form, FormField, FormItem, FormControl } from "@/shared/ui/form";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import { lwpoCodeApi } from "@/entities/code/api/lwpocode-service";
import { LwpoCodeFormSchema, type LwpoCodeFormData } from "@/entities/code/model/lwpocode-interface";
import { ENDPOINT } from "@/shared/config";
import { upCodeApi } from "@/entities/code/api/upcode-service";
type ManagementLwpoCodeDialogProps = {
upCd?: string;
lwpoCd?: string;
open?: boolean;
onOpenChange?: (open: boolean) => void;
isRegistration?: boolean;
} & Omit<React.ComponentProps<typeof Dialog>, 'open' | 'onOpenChange'>;
function ManagementLwpoCodeDialog({
lwpoCd,
upCd,
open,
onOpenChange,
isRegistration = false,
...props
}: ManagementLwpoCodeDialogProps) {
const { confirm } = useConfirm();
const { setMessage: alert } = useAlertStore((state) => state);
const { data: detailData } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, upCd, lwpoCd],
queryFn: () => lwpoCodeApi.LwpoCodeDetail(upCd as string, lwpoCd as string),
enabled: !!upCd && !!lwpoCd && !isRegistration,
});
const { data: upCodeData } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.UP_CODE, upCd],
queryFn: () => upCodeApi.upCodeDetail(upCd as string),
enabled: !!upCd,
});
const { mutateAsync: saveLwpoCode } = useMutation({
mutationFn: (data: LwpoCodeFormData) =>
isRegistration ? lwpoCodeApi.createLwpoCode(data) : lwpoCodeApi.updateLwpoCode(upCd as string, lwpoCd as string, data),
onSuccess: () => {
alert(isRegistration ? "등록되었습니다." : "수정되었습니다.");
onOpenChange?.(false);
},
});
const form = useForm<LwpoCodeFormData>({
resolver: zodResolver(LwpoCodeFormSchema),
values: {
upCd: upCd ?? "",
lwpoCd: detailData?.lwpoCd ?? "",
lwpoCdNm: detailData?.lwpoCdNm ?? "",
lwpoCdExpln: detailData?.lwpoCdExpln ?? "",
useYn: detailData?.useYn ?? "Y",
delYn: "N",
unit: detailData?.unit ?? "",
prntLwpoCd: detailData?.prntLwpoCd ?? "",
sortSeq: detailData?.sortSeq ?? 0,
},
});
const handleSave = async () => {
if (await confirm(isRegistration ? "등록하시겠습니까?" : "수정하시겠습니까?")) {
const data = form.getValues();
await saveLwpoCode(data);
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange} {...props}>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>{isRegistration ? "하위코드 등록" : "하위코드 관리"}</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={(e) => {
e.preventDefault();
handleSave();
}} className="space-y-4">
<Table variant="secondary">
<colgroup>
<col width="20%" />
<col width="80%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>하위코드</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="lwpoCd"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="하위코드를 입력해주세요"
disabled={!isRegistration}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>하위코드명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="lwpoCdNm"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="하위코드명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>하위코드설명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="lwpoCdExpln"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="하위코드 설명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
{upCodeData?.cdSe === "CODECLUNIT" && (
<TableRow>
<TableHead>단위</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="unit"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="단위를 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
)}
{upCodeData?.cdSe === "CODECLTREE" && (
<TableRow>
<TableHead>상위하위코드</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="prntLwpoCd"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위하위코드를 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
)}
<TableRow>
<TableHead>정렬순서</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="sortSeq"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
type="number"
value={field.value ?? 0}
className="!text-[1.3rem]"
placeholder="정렬순서를 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사용여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="useYn"
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
<DialogFooter className="!justify-center">
<Button size="lg" type="submit" onClick={(e) => {
e.preventDefault();
handleSave();
}}>
{isRegistration ? "등록" : "저장"}
</Button>
<DialogClose asChild>
<Button size="lg" color="white">
취소
</Button>
</DialogClose>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}
export { ManagementLwpoCodeDialog };
+256
View File
@@ -0,0 +1,256 @@
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { useMutation, useQuery } from "@tanstack/react-query";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { ENDPOINT } from "@/shared/config";
import {
Button,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
Input,
RadioGroup,
RadioGroupItem,
Label,
} from "@common/components";
import { Form, FormField, FormItem, FormControl } from "@/shared/ui/form";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import { upCodeApi } from "@/entities/code/api/upcode-service";
import { upCodeFormSchema, type UpCodeFormData } from "@/entities/code/model/upcode-interface";
import { CodeSelect } from "@/entities/code";
import { SiteSelect } from "@/entities/site";
type ManagementUpCodeDialogProps = {
upCd?: string;
open?: boolean;
onOpenChange?: (open: boolean) => void;
isRegistration?: boolean;
} & Omit<React.ComponentProps<typeof Dialog>, 'open' | 'onOpenChange'>;
function ManagementUpCodeDialog({
upCd,
open,
onOpenChange,
isRegistration = false,
...props
}: ManagementUpCodeDialogProps) {
const { confirm } = useConfirm();
const { setMessage: alert } = useAlertStore((state) => state);
const { data: detailData } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.UP_CODE, upCd],
queryFn: () => upCodeApi.upCodeDetail(upCd as string),
enabled: !!upCd && !isRegistration,
});
const { mutateAsync: saveUpCode } = useMutation({
mutationFn: (data: UpCodeFormData) =>
isRegistration ? upCodeApi.createUpCode(data) : upCodeApi.updateUpCode(upCd as string, data),
onSuccess: () => {
alert(isRegistration ? "등록되었습니다." : "수정되었습니다.");
onOpenChange?.(false);
},
});
const form = useForm<UpCodeFormData>({
resolver: zodResolver(upCodeFormSchema),
values: {
upCd: detailData?.upCd ?? "",
upCdNm: detailData?.upCdNm ?? "",
cdSe: detailData?.cdSe ?? "",
siteId: detailData?.siteId ?? "",
upCdExpln: detailData?.upCdExpln ?? "",
useYn: detailData?.useYn ?? "Y",
delYn: "N",
},
});
const handleSave = async () => {
if (await confirm(isRegistration ? "등록하시겠습니까?" : "수정하시겠습니까?")) {
const data = form.getValues();
await saveUpCode(data);
}
};
return (
<Dialog open={open} onOpenChange={onOpenChange} {...props}>
<DialogContent className="max-w-[60rem]" aria-describedby={undefined}>
<DialogHeader>
<DialogTitle>{isRegistration ? "상위코드 등록" : "상위코드 관리"}</DialogTitle>
</DialogHeader>
<Form {...form}>
<form onSubmit={(e) => {
e.preventDefault();
handleSave();
}} className="space-y-4">
<Table variant="secondary">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<TableBody>
<TableRow>
<TableHead>상위코드</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCd"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드를를 입력해주세요"
disabled={!isRegistration}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>상위코드명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCdNm"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>상위코드설명</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="upCdExpln"
render={({ field }) => (
<FormItem>
<FormControl>
<Input
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder="상위코드설명을 입력해주세요"
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>코드구분</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="cdSe"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormControl>
<CodeSelect
upCd="CODECL"
value={field.value || ""}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사이트</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="siteId"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormControl>
<SiteSelect
siteId={field.value || ""}
value={field.value || ""}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
<TableRow>
<TableHead>사용여부</TableHead>
<TableCell className="border">
<FormField
control={form.control}
name="useYn"
render={({ field }) => (
<FormItem>
<FormControl>
<RadioGroup
{...field}
name="useYn"
onValueChange={field.onChange}
>
<div className="flex items-center space-x-2">
<RadioGroupItem value="Y" id="useY" />
<Label htmlFor="useY">사용</Label>
</div>
<div className="flex items-center space-x-2">
<RadioGroupItem value="N" id="useN" />
<Label htmlFor="useN">사용안함</Label>
</div>
</RadioGroup>
</FormControl>
</FormItem>
)}
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
<DialogFooter className="!justify-center">
<Button size="lg" type="submit" onClick={(e) => {
e.preventDefault();
handleSave();
}}>
{isRegistration ? "등록" : "저장"}
</Button>
<DialogClose asChild>
<Button size="lg" color="white">
취소
</Button>
</DialogClose>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}
export { ManagementUpCodeDialog };
+1 -1
View File
@@ -23,7 +23,7 @@ function SitePage() {
});
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 5,
pageSize: 10,
});
const { data: siteList, refetch } = useQuery({
+2
View File
@@ -36,6 +36,8 @@ export const ENDPOINT = {
BANNERS: "/cms-service/api/v1/bnrs",
TEMPLATE: "/cms-service/api/v1/tmplt",
TERMS: "/cms-service/api/v1/trms",
LWPO_CODE: "/cms-service/api/v1/lwpo-cds",
UP_CODE: "/cms-service/api/v1/up-codes",
},
BOARD_SERVICE: {
BBS: "/board-service/api/v1/bbs",
+1 -1
View File
@@ -42,6 +42,6 @@
"**/*.jsx",
"**/*.json",
".next/types/**/*.ts"
],
, "../common/features/src/entities/site/ui/site-select.tsx" ],
"exclude": ["**/node_modules", "**/.*/*"]
}