Merge branch 'iipbs_cs' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
This commit is contained in:
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# 디폴트 무시된 파일
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 에디터 기반 HTTP 클라이언트 요청
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
PORT=13000
|
||||
LOG_FILE="start_13000.log"
|
||||
|
||||
# 1. 기존 포트 사용 프로세스 종료
|
||||
PID=$(lsof -t -i :$PORT)
|
||||
if [ -n "$PID" ]; then
|
||||
echo "Killing process on port $PORT (PID: $PID)"
|
||||
kill -9 $PID
|
||||
fi
|
||||
|
||||
# 2. 백그라운드로 앱 시작 (로그 파일로 리디렉션)
|
||||
echo "Starting app on port $PORT..."
|
||||
nohup pnpm start > $LOG_FILE 2>&1 &
|
||||
|
||||
# 3. PID 확인
|
||||
sleep 1
|
||||
NEW_PID=$(lsof -t -i :$PORT)
|
||||
if [ -n "$NEW_PID" ]; then
|
||||
echo "App started successfully (PID: $NEW_PID)"
|
||||
echo "Logs: tail -f $LOG_FILE"
|
||||
else
|
||||
echo "Failed to start app. Check logs: $LOG_FILE"
|
||||
fi
|
||||
@@ -3,6 +3,7 @@ import type { PageableList, PageableParams } from "@/shared/model";
|
||||
// 그룹 사용자 검색 파라미터 인터페이스
|
||||
export interface GroupUserSearchParams extends PageableParams {
|
||||
keyword?: string;
|
||||
userNm?: string;
|
||||
}
|
||||
|
||||
// 그룹 사용자 목록 응답 인터페이스
|
||||
|
||||
@@ -3,4 +3,5 @@ export {
|
||||
groupFormSchema,
|
||||
type GroupFormData,
|
||||
type GroupListResponseDto,
|
||||
type GroupSearchParams,
|
||||
} from "./model/group-interface";
|
||||
|
||||
@@ -5,56 +5,62 @@ import type { LwpoCode } from "@/entities/code";
|
||||
import { LwpoCodeManagementDialog } from "../ui/lwpocode-dialog";
|
||||
import { useState } from "react";
|
||||
|
||||
export const columns: ColumnDef<LwpoCode>[] = [
|
||||
{
|
||||
id: "no",
|
||||
header: "NO",
|
||||
cell: ({ row, table }) => {
|
||||
const totalRows = table.getRowCount();
|
||||
const { pageIndex, pageSize } = table.getState().pagination;
|
||||
return totalRows - (pageIndex * pageSize + row.index);
|
||||
// columns를 함수로 변경하여 onAfterSave를 주입받음
|
||||
export function getColumns(onAfterSave: () => void) : ColumnDef<LwpoCode>[] {
|
||||
return [
|
||||
{
|
||||
id: "no",
|
||||
header: "NO",
|
||||
cell: ({ row, table }) => {
|
||||
const totalRows = table.getRowCount();
|
||||
const { pageIndex, pageSize } = table.getState().pagination;
|
||||
return totalRows - (pageIndex * 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");
|
||||
{
|
||||
accessorKey: "lwpoCd",
|
||||
header: "하위코드",
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "관리",
|
||||
cell: ({ row }) => (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<LwpoCodeManageButton
|
||||
lwpoCd={row.original.lwpoCd}
|
||||
upCd={row.original.upCd}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
{
|
||||
accessorKey: "lwpoCdNm",
|
||||
header: "하위코드명",
|
||||
},
|
||||
{
|
||||
accessorKey: "useYn",
|
||||
header: "사용 여부",
|
||||
},
|
||||
{
|
||||
accessorKey: "regDt",
|
||||
header: "등록일",
|
||||
cell: ({ row }) => {
|
||||
const date = dayjs(row.original.regDt);
|
||||
return date.format("YYYY-MM-DD");
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "관리",
|
||||
cell: ({ row }) => (
|
||||
<div onClick={(e) => e.stopPropagation()}>
|
||||
<LwpoCodeManageButton
|
||||
lwpoCd={row.original.lwpoCd}
|
||||
upCd={row.original.upCd}
|
||||
onAfterSave={onAfterSave}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const LwpoCodeManageButton = ({
|
||||
lwpoCd,
|
||||
upCd,
|
||||
onAfterSave,
|
||||
}: {
|
||||
lwpoCd: string;
|
||||
upCd: string;
|
||||
onAfterSave: () => void;
|
||||
}) => {
|
||||
const [dialogOpen, setDialogOpen] = useState(false);
|
||||
return (
|
||||
@@ -63,6 +69,7 @@ const LwpoCodeManageButton = ({
|
||||
upCd={upCd}
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
onAfterSave={onAfterSave}
|
||||
>
|
||||
<Button size="sm">관리</Button>
|
||||
</LwpoCodeManagementDialog>
|
||||
|
||||
@@ -38,6 +38,7 @@ type ManagementLwpoCodeDialogProps = {
|
||||
children?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onAfterSave?: () => void; // 콜백 추가
|
||||
};
|
||||
|
||||
const LwpoCodeManagementDialog = memo(
|
||||
@@ -47,6 +48,7 @@ const LwpoCodeManagementDialog = memo(
|
||||
children,
|
||||
open,
|
||||
onOpenChange,
|
||||
onAfterSave, // 콜백 사용
|
||||
...props
|
||||
}: ManagementLwpoCodeDialogProps) => {
|
||||
const isRegistration = !lwpoCd;
|
||||
@@ -57,7 +59,7 @@ const LwpoCodeManagementDialog = memo(
|
||||
const { data: detailData } = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, upCd, lwpoCd],
|
||||
queryFn: () => LwpoCodeApi.lwpoCodeDetail(upCd!, lwpoCd!),
|
||||
enabled: !!upCd && !isRegistration,
|
||||
enabled: !!upCd && !isRegistration && open,
|
||||
});
|
||||
|
||||
const { data: upCodeData } = useQuery({
|
||||
@@ -69,7 +71,10 @@ const LwpoCodeManagementDialog = memo(
|
||||
const { mutateAsync: createLwpoCode } = useMutationWrapper({
|
||||
mutationFn: (data: LwpoCodeFormData) => LwpoCodeApi.createLwpoCode(data),
|
||||
onSuccess: () => {
|
||||
alert("등록되었습니다.", () => onOpenChange?.(false));
|
||||
alert("등록되었습니다.", () => {
|
||||
onOpenChange?.(false);
|
||||
onAfterSave?.();
|
||||
});
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, upCd],
|
||||
});
|
||||
@@ -80,7 +85,10 @@ const LwpoCodeManagementDialog = memo(
|
||||
mutationFn: (data: LwpoCodeFormData) =>
|
||||
LwpoCodeApi.updateLwpoCode(upCd!, lwpoCd!, data),
|
||||
onSuccess: () => {
|
||||
alert("수정되었습니다.", () => onOpenChange?.(false));
|
||||
alert("수정되었습니다.", () => {
|
||||
onOpenChange?.(false);
|
||||
onAfterSave?.();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -99,15 +107,38 @@ const LwpoCodeManagementDialog = memo(
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmit =
|
||||
(callback: (data: LwpoCodeFormData) => void) =>
|
||||
async (data: LwpoCodeFormData) => {
|
||||
const confirmMessage = isRegistration
|
||||
? "등록하시겠습니까?"
|
||||
: "수정하시겠습니까?";
|
||||
|
||||
if (await confirm(confirmMessage)) callback(data);
|
||||
};
|
||||
const handleRegister = async () => {
|
||||
if (!upCd) {
|
||||
alert("상위코드가 없습니다.");
|
||||
return;
|
||||
}
|
||||
const confirmMessage = "등록하시겠습니까?";
|
||||
if (await confirm(confirmMessage)) {
|
||||
const rawValues = form.getValues();
|
||||
const values = {
|
||||
...rawValues,
|
||||
upCd: upCd,
|
||||
sortSeq: Number(rawValues.sortSeq) || 0,
|
||||
};
|
||||
await createLwpoCode(values);
|
||||
}
|
||||
};
|
||||
const handleSave = async () => {
|
||||
if (!upCd) {
|
||||
alert("상위코드가 없습니다.");
|
||||
return;
|
||||
}
|
||||
const confirmMessage = "수정하시겠습니까?";
|
||||
if (await confirm(confirmMessage)) {
|
||||
const rawValues = form.getValues();
|
||||
const values = {
|
||||
...rawValues,
|
||||
upCd: upCd,
|
||||
sortSeq: Number(rawValues.sortSeq) || 0,
|
||||
};
|
||||
await updateLwpoCode(values);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange} {...props}>
|
||||
@@ -120,9 +151,14 @@ const LwpoCodeManagementDialog = memo(
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form
|
||||
onSubmit={form.handleSubmit(
|
||||
onSubmit(isRegistration ? createLwpoCode : updateLwpoCode),
|
||||
)}
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
if (isRegistration) {
|
||||
handleRegister();
|
||||
} else {
|
||||
handleSave();
|
||||
}
|
||||
}}
|
||||
className="space-y-4"
|
||||
>
|
||||
<Table variant="secondary">
|
||||
@@ -298,7 +334,11 @@ const LwpoCodeManagementDialog = memo(
|
||||
</TableBody>
|
||||
</Table>
|
||||
<DialogFooter className="!justify-center">
|
||||
<Button size="lg" type="submit">
|
||||
<Button
|
||||
size="lg"
|
||||
type="submit"
|
||||
onClick={isRegistration ? handleRegister : handleSave}
|
||||
>
|
||||
{isRegistration ? "등록" : "저장"}
|
||||
</Button>
|
||||
<DialogClose asChild>
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Button, Input, Label } from "@common/components";
|
||||
import { LwpoCodeApi, type LwpoCodeParams } from "@/entities/code";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore, useDataTable } from "@/shared/lib";
|
||||
import { columns } from "../model/lwpocode-table-columns";
|
||||
import { getColumns } from "../model/lwpocode-table-columns";
|
||||
import { LwpoCodeManagementDialog } from "./lwpocode-dialog";
|
||||
|
||||
type LwpoCodeSectionProps = {
|
||||
@@ -24,7 +24,7 @@ function LwpoCodeSection({ upCd }: LwpoCodeSectionProps) {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.LWPO_CODE, upCd, searchParams, pagination],
|
||||
queryFn: () =>
|
||||
@@ -34,10 +34,17 @@ function LwpoCodeSection({ upCd }: LwpoCodeSectionProps) {
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
enabled: !!upCd,
|
||||
enabled: !!upCd ,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
});
|
||||
|
||||
// 등록/수정 후 목록 재조회 콜백
|
||||
const handleAfterSave = () => {
|
||||
query.refetch();
|
||||
};
|
||||
const columns = getColumns(handleAfterSave);
|
||||
|
||||
|
||||
const { mutateAsync: codeRefreshAll } = useMutation({
|
||||
mutationFn: LwpoCodeApi.codeRefreshAll,
|
||||
onSuccess: () => {
|
||||
@@ -118,7 +125,9 @@ function LwpoCodeSection({ upCd }: LwpoCodeSectionProps) {
|
||||
<LwpoCodeManagementDialog
|
||||
upCd={upCd}
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
upCd={upCd}
|
||||
onOpenChange={setDialogOpen}
|
||||
onAfterSave={handleAfterSave}
|
||||
>
|
||||
<Button>등록</Button>
|
||||
</LwpoCodeManagementDialog>
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
type RowSelectionState,
|
||||
} from "@tanstack/react-table";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { Button } from "@common/components";
|
||||
import { Button,Input, Label } from "@common/components";
|
||||
import { GroupUsersApi } from "@/entities/group-user";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useAlertStore, useDataTable } from "@/shared/lib";
|
||||
@@ -32,13 +32,17 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
pageIndex: 0,
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [userNm, setUserNm] = useState("");
|
||||
|
||||
const [rowSelection, setRowSelection] = useState<RowSelectionState>({});
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.GROUP_USERS, groupId, pagination],
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.GROUP_USERS, groupId, pagination, userNm],
|
||||
queryFn: () =>
|
||||
GroupUsersApi.findPageGroupUser({
|
||||
keyword: groupId,
|
||||
userNm: userNm,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
@@ -46,6 +50,20 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
});
|
||||
// userNm이 이미 state에 반영되어 있으므로 별도 처리 불필요
|
||||
// 쿼리키에 userNm이 포함되어 있어 자동 refetch됨
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setUserNm("");
|
||||
};
|
||||
|
||||
const { tableComponent, tableInstance: table } = useDataTable({
|
||||
query,
|
||||
tableOptions: {
|
||||
@@ -97,6 +115,29 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label htmlFor="" className="mr-2 text-label">
|
||||
이름
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
|
||||
value={userNm}
|
||||
onChange={(e) => setUserNm(e.target.value)}
|
||||
/>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="white"
|
||||
className="h-[3.2rem]"
|
||||
onClick={handleReset}
|
||||
>
|
||||
초기화
|
||||
</Button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">그룹 사용자 목록</h3>
|
||||
|
||||
@@ -7,14 +7,14 @@ import {
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { getCoreRowModel, type PaginationState } from "@tanstack/react-table";
|
||||
import { Button } from "@common/components";
|
||||
import { GroupApi } from "@/entities/group";
|
||||
import { Button, Input, Label } from "@common/components";
|
||||
import { GroupApi , type GroupSearchParams} from "@/entities/group";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import { useDataTable } from "@/shared/lib";
|
||||
import { createColumns } from "../model/groups-table-columns";
|
||||
import { GroupsDialog } from "./groups-dialog";
|
||||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||||
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
|
||||
import { useAlertStore, useMutationWrapper } from "@/shared/lib";
|
||||
|
||||
type GroupsSectionProps = {
|
||||
handleSelectedGroup: (groupId: string) => void;
|
||||
@@ -26,10 +26,20 @@ function GroupsSection({ handleSelectedGroup }: GroupsSectionProps) {
|
||||
pageSize: 10,
|
||||
});
|
||||
|
||||
const [groupNm, setGroupNm] = useState("");
|
||||
const [searchParams, setSearchParams] = useState<GroupSearchParams>({
|
||||
groupNm: "",
|
||||
delYn :"N",
|
||||
sort : "groupId,desc",
|
||||
});
|
||||
|
||||
|
||||
|
||||
const query = useQuery({
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.GROUPS, pagination],
|
||||
queryKey: [ENDPOINT.CMS_SERVICE.GROUPS,searchParams, pagination],
|
||||
queryFn: () =>
|
||||
GroupApi.findPageGroup({
|
||||
...searchParams,
|
||||
page: pagination.pageIndex,
|
||||
size: pagination.pageSize,
|
||||
}),
|
||||
@@ -53,6 +63,27 @@ function GroupsSection({ handleSelectedGroup }: GroupsSectionProps) {
|
||||
await deleteGroup(groupId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
setPagination({
|
||||
...pagination,
|
||||
pageIndex: 0,
|
||||
});
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
groupNm,
|
||||
});
|
||||
};
|
||||
|
||||
const handleReset = () => {
|
||||
setGroupNm("");
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
groupNm: "",
|
||||
delYn:"N"
|
||||
});
|
||||
};
|
||||
const handleRowClick = (row: { groupId: string }) => {
|
||||
table.setRowSelection({ [row.groupId]: true });
|
||||
handleSelectedGroup(row.groupId); // 1. 리스트에서 선택된 groupId 저장
|
||||
@@ -77,6 +108,43 @@ function GroupsSection({ handleSelectedGroup }: GroupsSectionProps) {
|
||||
|
||||
return (
|
||||
<div className="flex-1">
|
||||
<div className="card card-border">
|
||||
<form onSubmit={handleSubmit} className="flex items-center gap-2 p-2">
|
||||
<Label htmlFor="" className="mr-2 text-label">
|
||||
그룹명
|
||||
</Label>
|
||||
<Input
|
||||
className="h-[3.2rem] w-[20rem] rounded-sm !text-[1.3rem]"
|
||||
value={groupNm}
|
||||
onChange={(e) => setGroupNm(e.target.value)}
|
||||
/>
|
||||
<Button className="h-[3.2rem]" type="submit">
|
||||
조회
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
color="white"
|
||||
className="h-[3.2rem]"
|
||||
onClick={handleReset}
|
||||
>
|
||||
초기화
|
||||
</Button>
|
||||
<Label htmlFor="delYn" className="ml-4 mr-2 text-label">
|
||||
삭제여부
|
||||
</Label>
|
||||
<input
|
||||
id="delYn"
|
||||
type="checkbox"
|
||||
checked={searchParams.delYn === "Y"}
|
||||
onChange={(e) =>
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
delYn: e.target.checked ? "Y" : "N",
|
||||
})
|
||||
}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
<div className="card card-border">
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium text-label">그룹 목록</h3>
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
|
||||
PORT=3000
|
||||
LOG_FILE="start_cs.log"
|
||||
|
||||
# 1. 기존 포트 사용 프로세스 종료
|
||||
PID=$(lsof -t -i :$PORT)
|
||||
if [ -n "$PID" ]; then
|
||||
echo "Killing process on port $PORT (PID: $PID)"
|
||||
kill -9 $PID
|
||||
fi
|
||||
|
||||
# 2. 백그라운드로 앱 시작 (로그 파일로 리디렉션)
|
||||
echo "Starting app on port $PORT..."
|
||||
nohup pnpm start > $LOG_FILE 2>&1 &
|
||||
|
||||
# 3. PID 확인
|
||||
sleep 1
|
||||
NEW_PID=$(lsof -t -i :$PORT)
|
||||
if [ -n "$NEW_PID" ]; then
|
||||
echo "App started successfully (PID: $NEW_PID)"
|
||||
echo "Logs: tail -f $LOG_FILE"
|
||||
else
|
||||
echo "Failed to start app. Check logs: $LOG_FILE"
|
||||
fi
|
||||
Reference in New Issue
Block a user