iipbs_cs #16

Merged
chanii8 merged 44 commits from iipbs_cs into main 2025-04-30 17:09:18 +09:00
269 changed files with 5734 additions and 661 deletions
@@ -1,2 +0,0 @@
import { BoardPage } from "@/pages/board";
export default BoardPage;
@@ -1 +0,0 @@
export { AttachmentApi } from "./api/attachment-service";
@@ -1,22 +0,0 @@
export interface Attachment {
atflCd: string;
atflSn: number;
atflId: string;
physFileNm: string;
orgnlFileNm: string;
atflFlieSz: number;
atflType: string;
dwnldNocs: number;
delYn: string;
lnkgDmnId: string;
lnkgDmnNm: string;
regDt: string;
}
export interface AttachmentService {
getAttachments: (atflCd: string) => Promise<Attachment[]>;
deleteAttachment: (atflId: string) => Promise<void>;
deleteAttachmentByAtflCd: (atflCd: string) => Promise<void>;
getImageUrl: (atflId: string) => Promise<string>;
getImageUrlByAtflCd: (atflCd: string) => Promise<string>;
}
@@ -1,2 +0,0 @@
export { LoginApi } from "./api/login-service";
export { AuthWrapper } from "./lib/auth-wrapper";
@@ -1,49 +0,0 @@
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { client } from "@/shared/api";
import type { IUserInfo } from "@/shared/store";
import { getCookie, useUserStore } from "@/shared/lib";
import { ENDPOINT, AUTH_USER_ID } from "@/shared/config";
import { LoginApi } from "../api/login-service";
const AuthWrapper = ({ children }: { children: React.ReactNode }) => {
const router = useRouter();
const { userInfo, setUserInfo } = useUserStore((state) => state);
async function authCheck() {
const userId = getCookie(AUTH_USER_ID);
if (!userId) {
throw new Error("Missing required authentication tokens");
}
const response = await client.get<IUserInfo>(
`${ENDPOINT.USER_SERVICE.USERS}/${userId}`,
);
if (!userInfo) setUserInfo(response.data);
}
useEffect(() => {
(async () => {
try {
await authCheck();
} catch (error) {
console.error(error);
try {
await LoginApi.silentRefresh();
await authCheck();
} catch (refreshError) {
console.error("Token refresh failed:", refreshError);
setUserInfo(null);
router.push("/auth/login");
}
}
})();
}, []);
return <>{children}</>;
};
export { AuthWrapper };
@@ -1,21 +0,0 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
BoardPopupList,
BoardPopupParams,
BoardService,
} from "../model/board-interface";
export class BoardApiService implements BoardService {
public async getBoardPopupList(
params: BoardPopupParams,
): Promise<BoardPopupList> {
const response = await client.get<BoardPopupList>(
`${ENDPOINT.BOARD_SERVICE.BBS}`,
{ params },
);
return response.data;
}
}
export const BoardApi = new BoardApiService();
@@ -1,2 +0,0 @@
export { BoardApi } from "./api/board-service";
export { type BoardPopup } from "./model/board-interface";
@@ -1,42 +0,0 @@
import type { PageableList, PageableParams } from "@/shared/model";
export const BOARD_TYPE = {
BBSNORMAL: "BBSNORMAL",
BBSFAQ: "BBSFAQ",
BBSIMAGE: "BBSIMAGE",
BBSMOVIE: "BBSMOVIE",
} as const;
export type BoardType = (typeof BOARD_TYPE)[keyof typeof BOARD_TYPE];
export interface BoardPopupParams extends PageableParams {
bbsNm?: string;
}
export interface BoardPopup {
bbsId: number;
bbsTypeCd: BoardType;
bbsTypeNm: string;
bbsNm: string;
bbsExpln: string;
rfrncUseYn: string;
atflUseYn: string;
atflMaxCnt: number;
atflMaxFileSz: number;
ansUseYn: string;
cmntUseYn: string;
dgstfnEvlUseYn: string;
smsUseYn: string;
upndFixUseYn: string;
pstgPrdUseYn: string;
popupUseYn: string;
delYn: string;
regDt: string;
pstCnt: number;
}
export type BoardPopupList = PageableList<BoardPopup>;
export interface BoardService {
getBoardPopupList: (params: BoardPopupParams) => Promise<BoardPopupList>;
}
@@ -1 +0,0 @@
export { CodeSelect } from "./ui/code-select";
@@ -1 +0,0 @@
export { MenuWrapper } from "./lib/menu-wrapper";
@@ -1 +0,0 @@
export { BoardPage } from "./ui/board-page";
@@ -1,5 +0,0 @@
function BoardPage() {
return <div>게시판 관리</div>;
}
export { BoardPage };
@@ -1,22 +0,0 @@
import { PageTitle } from "@common/components";
import { useMenuStore } from "../lib/use-menu-store";
function PageLayout({ children }: { children: React.ReactNode }) {
const { currentMenuInfo } = useMenuStore((state) => state);
const menuPaths = currentMenuInfo?.menuPath
?.split(">")
.map((path) => path.trim());
return (
<div className="h-auto min-h-full bg-container pb-2">
<PageTitle
title={currentMenuInfo?.menuKornNm ?? ""}
menuPaths={menuPaths ?? []}
/>
{children}
</div>
);
}
export { PageLayout };
@@ -2,10 +2,9 @@
import { usePathname, useRouter } from "next/navigation";
import { useQueryClient } from "@tanstack/react-query";
import type { MenuItem } from "@common/components/api";
import { BaseLayout } from "@common/components/ui";
import { BaseLayout } from "@common/components";
import type { MenuItem } from "@/entities/menu";
import { useMenuStore } from "@/shared/lib";
import { PageLayout } from "@/shared/ui/page-layout";
export default function WithBaseLayout({
children,
@@ -29,7 +28,7 @@ export default function WithBaseLayout({
currentPath={pathname ?? ""}
handleMenuClick={handleMenuClick}
>
<PageLayout>{children}</PageLayout>
{children}
</BaseLayout>
);
}
@@ -0,0 +1,10 @@
import { NoticePost } from "@/pages/notice";
export default async function PstBbsNoticePostPage({
params,
}: {
params: Promise<{ bbsId: string; pstNo: string }>;
}) {
const { bbsId, pstNo } = await params;
return <NoticePost bbsId={bbsId} pstNo={pstNo} />;
}
@@ -0,0 +1,10 @@
import { NoticePage } from "@/pages/notice";
export default async function PstBbsNoticePage({
params,
}: {
params: Promise<{ bbsId: string }>;
}) {
const { bbsId } = await params;
return <NoticePage bbsId={bbsId} />;
}
@@ -1,7 +1,8 @@
import { NextRequest, NextResponse } from "next/server";
import { CLAIM_NAME, AUTH_USER_ID } from "./src/shared/config";
const serverApiUrl = process.env.NEXT_PUBLIC_SERVER_API_URL;
const serverApiUrl =
process.env.NEXT_PUBLIC_SERVER_API_URL ?? "http://localhost:13000";
export function middleware(req: NextRequest) {
const pathname = req.nextUrl.pathname;
@@ -12,6 +13,7 @@ export function middleware(req: NextRequest) {
const newUrl = `${serverApiUrl}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
@@ -24,6 +26,9 @@ export function middleware(req: NextRequest) {
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
console.log(
`[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`,
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
@@ -1,5 +1,5 @@
{
"name": "feature",
"name": "features",
"version": "0.0.0",
"private": true,
"scripts": {
@@ -11,6 +11,8 @@
"dependencies": {
"@common/assets": "workspace:*",
"@common/components": "workspace:*",
"@common/config": "workspace:*",
"@common/network": "workspace:*",
"@frontend-opensource/use-react-hooks": "^1.1.4",
"@hookform/resolvers": "^4.1.3",
"@minoru/react-dnd-treeview": "^3.5.0",
@@ -23,7 +25,7 @@
"chart.js": "^4.4.8",
"dayjs": "^1.11.13",
"js-cookie": "^3.0.5",
"next": "15.1.7",
"next": "15.3.0",
"react-chartjs-2": "^5.3.0",
"react-dnd": "^16.0.1",
"react-hook-form": "^7.54.2",

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

@@ -2,23 +2,24 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ConfirmProvider } from "@frontend-opensource/use-react-hooks";
import { AuthWrapper } from "@/entities/auth";
import { MenuWrapper } from "@/entities/menu";
import { StoreProvider } from "@/shared/providers";
import { AlertDialog } from "@/shared/ui/alert-dialog";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { initializeNextClientConfig } from "@common/config";
const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => {
const queryClient = new QueryClient();
initializeNextClientConfig();
return (
<QueryClientProvider client={queryClient}>
<StoreProvider>
<ConfirmProvider>
<AuthWrapper>
<MenuWrapper>{children}</MenuWrapper>
<AlertDialog />
<ConfirmDialog />
</AuthWrapper>
<MenuWrapper>{children}</MenuWrapper>
<AlertDialog />
<ConfirmDialog />
</ConfirmProvider>
</StoreProvider>
</QueryClientProvider>
@@ -1,5 +1,6 @@
@import "@common/assets/styles/base.css";
@import "@common/assets/styles/diveicon.css";
@import "@common/components/index.css";
@tailwind base;
@tailwind utilities;
@@ -2,11 +2,23 @@ import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
Attachment,
AttachmentList,
AttachmentParams,
AttachmentService,
} from "../model/attachment-interface";
export class AttachmentApiService implements AttachmentService {
public async getAttachments(atflCd: string): Promise<Attachment[]> {
public async getAttachmentsList(
params: AttachmentParams,
): Promise<AttachmentList> {
const response = await client.get<AttachmentList>(
`${ENDPOINT.ATTACH_SERVICE.ATTACHMENTS}`,
{ params },
);
return response.data;
}
public async getAttachmentsInfo(atflCd: string): Promise<Attachment[]> {
const response = await client.get<Attachment[]>(
`${ENDPOINT.ATTACH_SERVICE.ATTACHMENTS}/${atflCd}`,
);
@@ -0,0 +1,5 @@
export { AttachmentApi } from "./api/attachment-service";
export type {
AttachmentParams,
Attachment,
} from "./model/attachment-interface";
@@ -0,0 +1,42 @@
import type { PageableList, PageableParams } from "@/shared/model";
export interface AttachmentParams extends PageableParams {
keywordType?: string;
keyword?: string;
atflCd?: string;
atflSn?: number;
atflId?: string;
orgnlFileNm?: string;
atflFlieSz?: number;
atflType?: string;
dwnldNocs?: number;
delYn?: string;
lnkgDmnId?: string;
lnkgDmnNm?: string;
}
export interface Attachment {
atflCd: string;
atflSn: number;
atflId: string;
physFileNm: string;
orgnlFileNm: string;
atflFlieSz: number;
atflType: string;
dwnldNocs: number;
delYn: string;
lnkgDmnId: string;
lnkgDmnNm: string;
regDt: string;
}
export type AttachmentList = PageableList<Attachment>;
export interface AttachmentService {
getAttachmentsList: (params: AttachmentParams) => Promise<AttachmentList>;
getAttachmentsInfo: (atflCd: string) => Promise<Attachment[]>;
deleteAttachment: (atflId: string) => Promise<void>;
deleteAttachmentByAtflCd: (atflCd: string) => Promise<void>;
getImageUrl: (atflId: string) => Promise<string>;
getImageUrlByAtflCd: (atflCd: string) => Promise<string>;
}
@@ -0,0 +1 @@
export { LoginApi } from "./api/login-service";
@@ -0,0 +1,60 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
Board,
BoardFormData,
BoardList,
BoardParams,
BoardPopupList,
BoardPopupParams,
BoardService,
} from "../model/board-interface";
export class BoardApiService implements BoardService {
public async getBoardList(params: BoardParams): Promise<BoardList> {
const response = await client.get<BoardList>(
`${ENDPOINT.BOARD_SERVICE.BBS}`,
{ params },
);
return response.data;
}
public async getBoardPopupList(
params: BoardPopupParams,
): Promise<BoardPopupList> {
const response = await client.get<BoardPopupList>(
`${ENDPOINT.BOARD_SERVICE.BBS}`,
{ params },
);
return response.data;
}
public async getBoardInfo(bbsId: string): Promise<Board> {
const response = await client.get<Board>(
`${ENDPOINT.BOARD_SERVICE.BBS}/${bbsId}`,
);
return response.data;
}
public async createBoard(data: BoardFormData): Promise<Board> {
const response = await client.post<Board>(
`${ENDPOINT.BOARD_SERVICE.BBS}`,
data,
);
return response.data;
}
public async updateBoard(bbsId: string, data: BoardFormData): Promise<Board> {
const response = await client.put<Board>(
`${ENDPOINT.BOARD_SERVICE.BBS}/${bbsId}`,
data,
);
return response.data;
}
public async deleteBoard(bbsId: string): Promise<void> {
await client.delete(`${ENDPOINT.BOARD_SERVICE.BBS}/${bbsId}`);
}
}
export const BoardApi = new BoardApiService();
@@ -0,0 +1,8 @@
export { BoardApi } from "./api/board-service";
export {
boardFormSchema,
type Board,
type BoardFormData,
type BoardParams,
type BoardPopup,
} from "./model/board-interface";
@@ -0,0 +1,96 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export const BOARD_TYPE = {
BBSNORMAL: "BBSNORMAL",
BBSFAQ: "BBSFAQ",
BBSIMAGE: "BBSIMAGE",
BBSMOVIE: "BBSMOVIE",
} as const;
export type BoardType = (typeof BOARD_TYPE)[keyof typeof BOARD_TYPE];
export interface BoardParams extends PageableParams {
keywordType?: string;
keyword?: string;
bbsId?: number;
bbsTypeCd?: string;
bbsNm?: string;
bbsExpln?: string;
rfrncUseYn?: string;
atflUseYn?: string;
atflMaxCnt?: number;
atflMaxFileSz?: number;
ansUseYn?: string;
cmntUseYn?: string;
dgstfnEvlUseYn?: string;
smsUseYn?: string;
upndFixUseYn?: string;
pstgPrdUseYn?: string;
popupUseYn?: string;
delYn?: string;
}
export interface BoardPopupParams extends PageableParams {
bbsNm?: string;
}
export interface Board {
bbsId: number;
bbsTypeCd: string;
bbsTypeNm: string;
bbsNm: string;
bbsExpln: string;
rfrncUseYn: string;
atflUseYn: string;
atflMaxCnt: number;
atflMaxFileSz: number;
ansUseYn: string;
ansUseAuthrt: string;
cmntUseYn: string;
dgstfnEvlUseYn: string;
smsUseYn: string;
upndFixUseYn: string;
pstgPrdUseYn: string;
popupUseYn: string;
delYn: string;
useYn: string;
mdfcnDt: string;
mdfrNm: string;
}
export interface BoardPopup extends Board {
pstCnt: number;
}
export const boardFormSchema = z.object({
bbsTypeCd: z.string(),
bbsNm: z.string(),
bbsExpln: z.string(),
rfrncUseYn: z.string(),
atflUseYn: z.string(),
atflMaxCnt: z.number().nullish(),
atflMaxFileSz: z.number().nullish(),
ansUseYn: z.string(),
ansUseAuthrt: z.string().nullish(),
cmntUseYn: z.string(),
dgstfnEvlUseYn: z.string(),
smsUseYn: z.string(),
upndFixUseYn: z.string(),
pstgPrdUseYn: z.string(),
popupUseYn: z.string(),
useYn: z.string(),
});
export type BoardList = PageableList<Board>;
export type BoardPopupList = PageableList<BoardPopup>;
export type BoardFormData = z.infer<typeof boardFormSchema>;
export interface BoardService {
getBoardList: (params: BoardParams) => Promise<BoardList>;
getBoardPopupList: (params: BoardPopupParams) => Promise<BoardPopupList>;
getBoardInfo: (bbsId: string) => Promise<Board>;
createBoard: (data: BoardFormData) => Promise<Board>;
updateBoard: (bbsId: string, data: BoardFormData) => Promise<Board>;
deleteBoard: (bbsId: string) => Promise<void>;
}
@@ -0,0 +1,3 @@
export { CodeApi } from "./api/code-service";
export type { ICode } from "./model/code-interface";
export { CodeSelect } from "./ui/code-select";
@@ -2,6 +2,7 @@ export interface ICode {
lwpoCd: string;
upCd: string;
lwpoCdNm: string;
unit: string;
}
export interface CodeService {
@@ -0,0 +1,90 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
ContentsService,
ContentsParams,
ContentsList,
ContentsFormData,
ContentsVersionParams,
ContentsVersionList,
ContentsDetail,
} from "../model/contents-interface";
export class ContentsApiService implements ContentsService {
public async getContentsList(params: ContentsParams): Promise<ContentsList> {
const response = await client.get<ContentsList>(
`${ENDPOINT.CMS_SERVICE.CONTENTS}`,
{ params },
);
return response.data;
}
public async getContentsVersionList(
params: ContentsVersionParams,
): Promise<ContentsVersionList> {
const response = await client.get<ContentsVersionList>(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}`,
{ params },
);
return response.data;
}
public async getLatestContentsInfo(
contsSn: number,
contsVer: number,
): Promise<ContentsDetail> {
const response = await client.get<ContentsDetail>(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}`,
);
return response.data;
}
public async createContents(data: ContentsFormData): Promise<void> {
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}`,
data,
);
return response.data;
}
public async updateContents(
contsSn: number,
contsVer: number,
data: ContentsFormData,
): Promise<void> {
const response = await client.put<void>(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}`,
data,
);
return response.data;
}
public async deleteContents(
contsSn: number,
contsVer: number,
): Promise<void> {
await client.delete(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}`,
);
}
public async requestApprove(
contsSn: number,
contsVer: number,
): Promise<void> {
await client.put(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}/approveRequest`,
);
}
public async approveContents(
contsSn: number,
contsVer: number,
): Promise<void> {
await client.put(
`${ENDPOINT.CMS_SERVICE.CONTENTS_DETAILS}/${contsSn}/${contsVer}/approve`,
);
}
}
export const ContentsApi = new ContentsApiService();
@@ -0,0 +1,8 @@
export { ContentsApi } from "./api/contents-service";
export {
CONTENTS_APPROVE_STATUS,
contentsFormSchema,
type Contents,
type ContentsParams,
type ContentsFormData,
} from "./model/contents-interface";
@@ -0,0 +1,100 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export const CONTENTS_APPROVE_STATUS = {
DRAFT: "01",
REQUESTED: "02",
APPROVED: "99",
} as const;
export type ContentsApproveStatus =
(typeof CONTENTS_APPROVE_STATUS)[keyof typeof CONTENTS_APPROVE_STATUS];
export interface ContentsParams extends PageableParams {
contsTtl?: string;
aprvContsVer?: number;
useYn?: string;
}
export interface ContentsVersionParams extends PageableParams {
contsSn?: number;
contsCn?: string;
aprvCd?: ContentsApproveStatus;
cclCd?: string;
koglCd?: string;
tag?: string;
useYn?: string;
}
export interface ContentsVersion {
contsSn: number;
contsVer: number;
contsCn: string;
useYn: string;
}
export const contentsSchema = z.object({
contsSn: z.number(),
contsTtl: z.string(),
aprvContsVer: z.number(),
useYn: z.string(),
mdfcnDt: z.string(),
mdfr: z.string(),
mdfrNm: z.string(),
aprvDt: z.string(),
autzrNm: z.string(),
workAprvCd: z.string(),
workAprvNm: z.string(),
workContsVer: z.number(),
});
export const contentsDetailSchema = z.object({
contsSn: z.number(),
contsVer: z.number(),
contsTtl: z.string(),
contsCn: z.string(),
contsRmrk: z.string().nullish(),
aprvCd: z.enum([
CONTENTS_APPROVE_STATUS.DRAFT,
CONTENTS_APPROVE_STATUS.REQUESTED,
CONTENTS_APPROVE_STATUS.APPROVED,
]),
cclCd: z.string().nullish(),
koglCd: z.string().nullish(),
tag: z.string().nullish(),
aprvDt: z.string(),
autzr: z.string(),
useYn: z.string(),
});
export const contentsFormSchema = contentsDetailSchema.omit({
contsVer: true,
aprvDt: true,
autzr: true,
});
export type Contents = z.infer<typeof contentsSchema>;
export type ContentsDetail = z.infer<typeof contentsDetailSchema>;
export type ContentsFormData = z.infer<typeof contentsFormSchema>;
export type ContentsList = PageableList<Contents>;
export type ContentsVersionList = PageableList<ContentsVersion>;
export interface ContentsService {
getContentsList(params: ContentsParams): Promise<ContentsList>;
getContentsVersionList(
params: ContentsVersionParams,
): Promise<ContentsVersionList>;
getLatestContentsInfo(
contsSn: number,
contsVer: number,
): Promise<ContentsDetail>;
createContents(data: ContentsFormData): Promise<void>;
updateContents(
contsSn: number,
contsVer: number,
data: ContentsFormData,
): Promise<void>;
deleteContents(contsSn: number, contsVer: number): Promise<void>;
requestApprove(contsSn: number, contsVer: number): Promise<void>;
approveContents(contsSn: number, contsVer: number): Promise<void>;
}
@@ -49,6 +49,7 @@ const Editor = ({ ...props }: IAllProps) => {
}}
init={{
...baseConfig.init,
disabled: props.disabled,
images_upload_handler: imagesUploadHandler,
file_picker_callback: filePickerCallback,
}}
@@ -3,7 +3,7 @@ import { ENDPOINT } from "@/shared/config";
import type {
MenuAuthorityList,
MenuAuthorityParams,
MenuAuthorityPayloadData,
MenuAuthorityPayload,
MenuAuthorityService,
} from "../model/menu-authority-interface";
@@ -18,9 +18,7 @@ export class MenuAuthorityApiService implements MenuAuthorityService {
return response.data;
}
public async saveMenuAuthority(
data: MenuAuthorityPayloadData[],
): Promise<void> {
public async saveMenuAuthority(data: MenuAuthorityPayload[]): Promise<void> {
const response = await client.post<void>(
`${ENDPOINT.CMS_SERVICE.MENU_AUTHRTS}`,
data,
@@ -30,12 +30,10 @@ export const menuAuthorityFormSchema = z.object({
export type MenuAuthority = z.infer<typeof menuAuthoritySchema>;
export type MenuAuthorityList = PageableList<MenuAuthority>;
export type MenuAuthorityPayloadData = z.infer<
typeof menuAuthorityPayloadSchema
>;
export type MenuAuthorityPayload = z.infer<typeof menuAuthorityPayloadSchema>;
export type MenuAuthorityFormData = z.infer<typeof menuAuthorityFormSchema>;
export interface MenuAuthorityService {
getMenuAuthorityList(params: MenuAuthorityParams): Promise<MenuAuthorityList>;
saveMenuAuthority(data: MenuAuthorityPayloadData[]): Promise<void>;
saveMenuAuthority(data: MenuAuthorityPayload[]): Promise<void>;
}
@@ -1,10 +1,10 @@
import type { ColumnDef } from "@tanstack/react-table";
import type { UseFormReturn } from "react-hook-form";
import { Checkbox } from "@common/components/ui";
import { Checkbox } from "@common/components";
import type {
MenuAuthority,
MenuAuthorityFormData,
MenuAuthorityPayloadData,
MenuAuthorityPayload,
} from "./menu-authority-interface";
export const createColumns = (
@@ -13,7 +13,7 @@ export const createColumns = (
const getCurrentRow = (i: number) => form.watch("menuAuthority")[i];
const handleCheckedChange =
(i: number, key: keyof MenuAuthorityPayloadData) => (checked: boolean) => {
(i: number, key: keyof MenuAuthorityPayload) => (checked: boolean) => {
if (getCurrentRow(i)?.mode === "R") {
form.setValue(`menuAuthority.${i}.mode`, "U");
}
@@ -13,13 +13,13 @@ import {
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@common/components/ui";
} from "@common/components";
import { ENDPOINT } from "@/shared/config";
import { useAlertStore } from "@/shared/lib/use-alert-store";
import {
menuAuthorityFormSchema,
type MenuAuthorityFormData,
type MenuAuthorityPayloadData,
type MenuAuthorityPayload,
} from "../model/menu-authority-interface";
import { createColumns } from "../model/menu-authority-table-columns";
import { MenuAuthorityApi } from "../api/menu-authority-service";
@@ -42,7 +42,7 @@ function ManagementAuthorityDialog({
});
const { mutateAsync: saveMenuAuthority } = useMutation({
mutationFn: (data: MenuAuthorityPayloadData[]) =>
mutationFn: (data: MenuAuthorityPayload[]) =>
MenuAuthorityApi.saveMenuAuthority(data),
onSuccess: () => {
alert("저장되었습니다.");
@@ -1,4 +1,4 @@
import { client } from "@common/components/api";
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type { MenuItem, MenuService } from "../model/menu-interface";
@@ -0,0 +1,2 @@
export { MenuWrapper } from "./lib/menu-wrapper";
export type { MenuItem } from "./model/menu-interface";
@@ -0,0 +1,66 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import {
type Organization,
type OrganizationFormData,
type OrganizationService,
type OrganizationTree,
type OrganizationTreeForDnd,
} from "../model/organization-interface";
export class OrganizationApiService implements OrganizationService {
public async getOrganizationsTreeList(): Promise<OrganizationTree[]> {
const response = await client.get<OrganizationTree[]>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/tree`,
);
return response.data;
}
public async getOrganizationInfo(ognzSn: number): Promise<Organization> {
const response = await client.get<Organization>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}`,
);
return response.data;
}
public async createOrganization(data: OrganizationFormData): Promise<void> {
await client.post<void>(`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}`, data);
}
public async deleteOrganization(ognzSn: number): Promise<void> {
await client.delete<void>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}`,
);
}
public async updateOrganizationName(
ognzSn: number,
name: string,
): Promise<Organization> {
const response = await client.put<Organization>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}/${name}`,
);
return response.data;
}
public async updateOrganization(
ognzSn: number,
data: OrganizationFormData,
): Promise<void> {
await client.put<void>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/${ognzSn}`,
data,
);
}
public async updateOrganizationByDnd(
data: OrganizationTreeForDnd[],
): Promise<void> {
await client.put<void>(
`${ENDPOINT.CMS_SERVICE.ORGANIZATIONS}/dnd/tree`,
data,
);
}
}
export const OrganizationApi = new OrganizationApiService();
@@ -0,0 +1,8 @@
export { OrganizationApi } from "./api/organization-service";
export {
organizationFormSchema,
type OrganizationTree,
type OrganizationFormData,
type OrganizationTreeForDnd,
} from "./model/organization-interface";
export { generateOrganizationTreeData, handleDropNode } from "./lib/utils";
@@ -0,0 +1,78 @@
import type { DropOptions, NodeModel } from "@minoru/react-dnd-treeview";
import type {
OrganizationTree,
OrganizationTreeForDnd,
} from "../model/organization-interface";
export const generateOrganizationTreeData = (
data: OrganizationTree[],
): NodeModel<OrganizationTree>[] => {
return data.reduce<NodeModel<OrganizationTree>[]>((acc, menu) => {
const node: NodeModel<OrganizationTree> = {
id: menu.ognzSn ?? 0,
parent: menu.upOgnzSn ?? 0,
text: menu.ognzNm,
droppable: true,
data: menu,
};
acc.push(node);
if (menu.children?.length) {
acc.push(...generateOrganizationTreeData(menu.children));
}
return acc;
}, []);
};
export const generateDndOrganizationTreeData = (
treeData: NodeModel<OrganizationTree>[],
options: DropOptions<OrganizationTree>,
): OrganizationTreeForDnd | null => {
const { dragSourceId, dragSource, dropTargetId } = options;
if (!dragSource?.data) return null;
const siblings = treeData.filter((node) => node.parent === dropTargetId);
const dragSourceIndex = siblings.findIndex(
(node) => node.id === dragSourceId,
);
const prevSibling = siblings[dragSourceIndex - 1];
const newSortSeq =
dragSourceIndex === 0 ? 1 : (prevSibling?.data?.sortSeq ?? 0) + 1;
const treeNode: OrganizationTreeForDnd = {
ognzSn: dragSource.data.ognzSn,
ognzNm: dragSource.data.ognzNm,
ognzExpln: dragSource.data.ognzExpln,
upOgnzSn: dropTargetId === 0 ? null : (dropTargetId as number),
sortSeq: newSortSeq,
};
const childNodes = treeData.filter(
(node) => node.parent === dragSourceId && node.data,
);
const result: OrganizationTreeForDnd = {
...treeNode,
children:
childNodes?.map((child) => ({
ognzSn: child.data?.ognzSn,
ognzNm: child.data?.ognzNm ?? "",
ognzExpln: child.data?.ognzExpln,
upOgnzSn: child.data?.upOgnzSn === 0 ? null : (dragSourceId as number),
sortSeq: child.data?.sortSeq ?? 1,
})) ?? [],
};
return result;
};
export const handleDropNode =
(callback: (newTree: OrganizationTreeForDnd) => void) =>
(
newTree: NodeModel<OrganizationTree>[],
options: DropOptions<OrganizationTree>,
) => {
const newTreeData = generateDndOrganizationTreeData(newTree, options);
if (!newTreeData) return;
callback(newTreeData);
};
@@ -0,0 +1,31 @@
import { z } from "zod";
export const organizationSchema = z.object({
ognzSn: z.number().nullish(),
ognzNm: z.string(),
ognzExpln: z.string().nullish(),
upOgnzSn: z.number().nullish(),
sortSeq: z.number(),
useYn: z.string().nullish(),
});
export const organizationFormSchema = organizationSchema.omit({
ognzSn: true,
});
export type Organization = z.infer<typeof organizationSchema>;
export type OrganizationFormData = z.infer<typeof organizationFormSchema>;
export type OrganizationTree = Organization & {
children?: OrganizationTree[];
};
export type OrganizationTreeForDnd = Omit<OrganizationTree, "useYn">;
export interface OrganizationService {
getOrganizationsTreeList(): Promise<OrganizationTree[]>;
getOrganizationInfo(ognzSn: number): Promise<Organization>;
createOrganization(data: OrganizationFormData): Promise<void>;
deleteOrganization(ognzSn: number): Promise<void>;
updateOrganizationName(ognzSn: number, name: string): Promise<Organization>;
updateOrganization(ognzSn: number, data: OrganizationFormData): Promise<void>;
updateOrganizationByDnd(data: OrganizationTreeForDnd[]): Promise<void>;
}
@@ -0,0 +1,30 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
PostInfo,
PostList,
PostParams,
PostService,
} from "../model/post-interface";
export class PostApiService implements PostService {
public async getPostList(params: PostParams): Promise<PostList> {
const response = await client.get<PostList>(ENDPOINT.BOARD_SERVICE.POSTS, {
params,
});
return response.data;
}
public async getPostInfo(bbsId: string, pstNo: string): Promise<PostInfo> {
const response = await client.get<PostInfo>(
`${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}/${pstNo}`,
);
return response.data;
}
public async deletePost(bbsId: string, pstNo: string): Promise<void> {
await client.delete(`${ENDPOINT.BOARD_SERVICE.POSTS}/${bbsId}/${pstNo}`);
}
}
export const PostApi = new PostApiService();
@@ -0,0 +1,7 @@
export { PostApi } from "./api/post-service";
export type {
Post,
PostList,
PostParams,
PostService,
} from "./model/post-interface";
@@ -0,0 +1,47 @@
import {
type PageableList,
type PageableParams,
} from "@/shared/model/api-interface";
export interface PostParams extends PageableParams {
keywordType?: string;
keyword?: string;
bbsId?: number;
delYn?: string;
frDateYmd?: string;
toDateYmd?: string;
}
export interface Post {
bbsId: number;
pstNo: number;
pstTtl: string;
inqNocs: number;
rfrncUrl: string;
popupYn: string;
upndFixYn: string;
prdPstgYn: string;
rootPath: string;
pstPath: string;
ansPstn: number;
wrtrNm: string;
wrtrCplc: string;
atflCd: string;
regDt: string;
rgtrNm: string;
}
export interface PostInfo extends Post {
pstCn: string;
rgtr: string;
mdfcnDt: string;
mdfr: string;
}
export type PostList = PageableList<Post>;
export interface PostService {
getPostList(params: PostParams): Promise<PostList>;
getPostInfo(bbsId: string, pstNo: string): Promise<PostInfo>;
deletePost(bbsId: string, pstNo: string): Promise<void>;
}
@@ -5,7 +5,7 @@ import type {
PostService,
PostingParams,
Post,
PostPayloadData,
PostPayload,
} from "../model/posting-interface";
export class PostingApiService implements PostService {
@@ -19,7 +19,7 @@ export class PostingApiService implements PostService {
return response.data;
}
public async savePosting(data: PostPayloadData[]): Promise<Post[]> {
public async savePosting(data: PostPayload[]): Promise<Post[]> {
const response = await client.post<Post[]>(
`${ENDPOINT.CMS_SERVICE.POSTINGS}`,
data,
@@ -2,6 +2,6 @@ export { PostingApi } from "./api/posting-service";
export {
POSTING_TYPE,
type Post,
type PostPayloadData,
type PostPayload,
} from "./model/posting-interface";
export { PostingDialog } from "./ui/posting-dialog";
@@ -41,10 +41,10 @@ export const postingFormSchema = z.object({
});
export type Post = z.infer<typeof postSchema>;
export type PostPayloadData = z.infer<typeof postPayloadSchema>;
export type PostPayload = z.infer<typeof postPayloadSchema>;
export type PostingFormData = z.infer<typeof postingFormSchema>;
export interface PostService {
getPostingHistory: (params: PostingParams) => Promise<PageableList<Post>>;
savePosting: (data: PostPayloadData[]) => Promise<Post[]>;
savePosting: (data: PostPayload[]) => Promise<Post[]>;
}
@@ -23,8 +23,8 @@ import {
DateRangePicker,
DialogClose,
DialogFooter,
} from "@common/components/ui";
import { cn } from "@common/components/lib";
cn,
} from "@common/components";
import { CodeSelect } from "@/entities/code/@x/posting";
import { SiteApi, type Site } from "@/entities/site/@x/posting";
import type { PageableList } from "@/shared/model";
@@ -34,7 +34,7 @@ import {
postingFormSchema,
type PostingFormData,
type PostingType,
type PostPayloadData,
type PostPayload,
} from "../model/posting-interface";
import { useAlertStore } from "@/shared/lib/use-alert-store";
@@ -76,7 +76,7 @@ function PostingDialog({ postingType, postingTargets }: PostingDialogProps) {
});
const { mutateAsync: savePosting } = useMutation({
mutationFn: (data: PostPayloadData[]) => PostingApi.savePosting(data),
mutationFn: (data: PostPayload[]) => PostingApi.savePosting(data),
onSuccess: () => {
alert("저장되었습니다.");
},
@@ -0,0 +1,62 @@
import { client } from "@/shared/api";
import { ENDPOINT } from "@/shared/config";
import type {
TreeUserOrganization,
UserOrganizationParams,
UserOrganizationList,
UserOrganizationService,
UserOrganizationPayload,
SearchUserParams,
SearchUserList,
User,
MoveUserOrganizationPayload,
} from "../model/user-organization-interface";
export class UserOrganizationApiService implements UserOrganizationService {
public async getUserOrganizationList(
params: UserOrganizationParams,
): Promise<UserOrganizationList> {
const response = await client.get<UserOrganizationList>(
`${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}`,
{ params },
);
return response.data;
}
public async getUserOrganizationTreeList(): Promise<TreeUserOrganization[]> {
const response = await client.get<TreeUserOrganization[]>(
`${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}/treeuser`,
);
return response.data;
}
public async searchUser(params: SearchUserParams): Promise<SearchUserList> {
const response = await client.get<SearchUserList>(
`${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}/user-popup`,
{ params },
);
return response.data;
}
public async saveUserOrganization(
data: UserOrganizationPayload[],
): Promise<User[]> {
const response = await client.post<User[]>(
`${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}`,
data,
);
return response.data;
}
public async moveUserOrganization(
data: MoveUserOrganizationPayload[],
): Promise<User[]> {
const response = await client.post<User[]>(
`${ENDPOINT.CMS_SERVICE.USER_ORGANIZATION}/move-ognz`,
data,
);
return response.data;
}
}
export const UserOrganizationApi = new UserOrganizationApiService();
@@ -0,0 +1,11 @@
export { UserOrganizationApi } from "./api/user-organization-service";
export {
userOrganizationFormSchema,
type TreeUserOrganization,
type TreeUser,
type UserOrganizationFormData,
type UserOrganizationPayload,
type User,
type SearchUser,
type MoveUserOrganizationPayload,
} from "./model/user-organization-interface";
@@ -0,0 +1,92 @@
import { z } from "zod";
import type { PageableList, PageableParams } from "@/shared/model";
export interface UserOrganizationParams extends PageableParams {
keywordType?: string;
keyword?: string;
ognzSn?: number;
userUnqId?: string;
jbpsSeCd?: string;
jbgdSeCd?: string;
}
export interface SearchUserParams extends PageableParams {
userNm?: string;
ogdpSeCd?: string;
}
export interface User {
mode: string;
ognzSn: number;
userUnqId: string;
userUnqNm?: string;
jbpsSeCd: string;
jbpsSeNm?: string;
jbgdSeCd: string;
jbgdSeNm?: string;
sortSeq: number;
mdfcnDt?: string;
mdfr?: string;
mdfrNm?: string;
}
export interface SearchUser {
userUnqId: string;
userNm: string;
ogdpSeCd: string;
ogdpSeNm: string;
regDt: string;
}
export interface TreeUser {
userUnqId: string;
userNm: string;
jbpsSeCd: string;
jbgdSeCd: string;
}
export interface TreeUserOrganization {
ognzSn: number;
upOgnzSn: number;
ognzNm: string;
users: TreeUser[];
children: TreeUserOrganization[];
}
export interface MoveUserOrganizationPayload {
ognzSn: number;
toOgnzSn: number;
userUnqId: string;
}
export const userOrganizationPayloadSchema = z.object({
mode: z.string(),
ognzSn: z.number(),
userUnqId: z.string(),
jbpsSeCd: z.string(),
jbgdSeCd: z.string(),
sortSeq: z.number(),
});
export const userOrganizationFormSchema = z.object({
userOrganization: z.array(userOrganizationPayloadSchema),
});
export type UserOrganizationList = PageableList<User>;
export type SearchUserList = PageableList<SearchUser>;
export type UserOrganizationPayload = z.infer<
typeof userOrganizationPayloadSchema
>;
export type UserOrganizationFormData = z.infer<
typeof userOrganizationFormSchema
>;
export interface UserOrganizationService {
getUserOrganizationList(
params: UserOrganizationParams,
): Promise<UserOrganizationList>;
getUserOrganizationTreeList(): Promise<TreeUserOrganization[]>;
searchUser(params: SearchUserParams): Promise<SearchUserList>;
saveUserOrganization(data: UserOrganizationPayload[]): Promise<User[]>;
moveUserOrganization(data: MoveUserOrganizationPayload[]): Promise<User[]>;
}

Some files were not shown because too many files have changed in this diff Show More