-
로그인로그 목록
+
+
로그인로그 목록
+
+
+
+
-
-
+
+ {/* Debug: 로딩 상태 표시 */}
+ {(query.isLoading || query.isPending || isManualLoading) && (
+
+ )}
+ {tableComponent}
diff --git a/apps/cs/src/pages/menu/ui/menu-page.tsx b/apps/cs/src/pages/menu/ui/menu-page.tsx
index 6ff6f89bc..d212dc970 100644
--- a/apps/cs/src/pages/menu/ui/menu-page.tsx
+++ b/apps/cs/src/pages/menu/ui/menu-page.tsx
@@ -75,6 +75,9 @@ function MenuPage() {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId],
});
+ queryClient.invalidateQueries({
+ queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
+ });
},
});
@@ -91,6 +94,9 @@ function MenuPage() {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId],
});
+ queryClient.invalidateQueries({
+ queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
+ });
},
});
@@ -101,6 +107,9 @@ function MenuPage() {
queryClient.invalidateQueries({
queryKey: [ENDPOINT.CMS_SERVICE.MENUS, siteId],
});
+ queryClient.invalidateQueries({
+ queryKey: [ENDPOINT.CMS_SERVICE.MENU_ROLES],
+ });
},
});
@@ -118,8 +127,10 @@ function MenuPage() {
menuLnkgTypeCd: menuInfo?.menuLnkgTypeCd ?? MENU_TYPE.EMPTY,
iconNm: menuInfo?.iconName,
otptYn: menuInfo?.otptYn ?? "Y",
- evlUseYn: menuInfo?.evlUseYn ?? "Y",
+ evlUseYn: menuInfo?.evlUseYn ?? "N",
+ qrcd: menuInfo?.qrcd ?? "N",
useYn: menuInfo?.useYn ?? "Y",
+ newWndwYn: menuInfo?.newWndwYn ?? "N",
},
});
diff --git a/apps/cs/src/pages/menu/ui/qrcode-dialog.tsx b/apps/cs/src/pages/menu/ui/qrcode-dialog.tsx
new file mode 100644
index 000000000..042c48911
--- /dev/null
+++ b/apps/cs/src/pages/menu/ui/qrcode-dialog.tsx
@@ -0,0 +1,81 @@
+import React, { useEffect, useRef, useState } from "react";
+import { useQRCode } from "next-qrcode";
+import {
+ Button,
+ Dialog,
+ DialogContent,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from "@common/components";
+import { useClipboard } from "@frontend-opensource/use-react-hooks";
+
+function QRCodeDialog({ url }: { url: string }) {
+ const { Canvas } = useQRCode();
+ const { copied: isCopied, copyText } = useClipboard();
+ const canvasRef = useRef
(null);
+ const [originUrl, setOriginUrl] = useState("");
+ const qrCodeUrl = originUrl + url;
+
+ const handleDownload = () => {
+ const canvas = canvasRef.current?.querySelector("canvas");
+ if (!canvas) return;
+ const url = canvas.toDataURL("image/png");
+ const link = document.createElement("a");
+ link.href = url;
+ link.download = "qr-code.png";
+ link.click();
+ };
+
+ useEffect(() => {
+ if (typeof window === "undefined") return;
+ setOriginUrl(window.location.origin);
+ }, []);
+
+ return (
+
+ );
+}
+
+export { QRCodeDialog };
diff --git a/apps/cs/src/pages/menu/ui/type-menu-form.tsx b/apps/cs/src/pages/menu/ui/type-menu-form.tsx
index 1f0483d1b..ac62e2520 100644
--- a/apps/cs/src/pages/menu/ui/type-menu-form.tsx
+++ b/apps/cs/src/pages/menu/ui/type-menu-form.tsx
@@ -1,5 +1,5 @@
import dayjs from "dayjs";
-import { type UseFormReturn } from "react-hook-form";
+import type { UseFormReturn } from "react-hook-form";
import {
DatePicker,
IconFinder,
@@ -21,6 +21,7 @@ import type { MenuFormData, MenuType } from "../model/menu-interface";
import { OrganizationSearchDialog } from "./organization-search-dialog";
import { ContentSearchDialog } from "./content-search-dialog";
import { BoardSearchDialog } from "./board-search-dialog";
+import { QRCodeDialog } from "./qrcode-dialog";
type TypeMenuFormProps = {
form: UseFormReturn;
@@ -33,6 +34,7 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
};
const handleContentSelect = (content: Contents) => {
+ form.setValue("urlPath", `/cms/cntns/${content.contsSn}`);
form.setValue("menuLnkgSn", content.contsSn);
form.setValue("menuLnkgSnNm", content.contsTtl);
};
@@ -43,6 +45,10 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
};
const handleBoardSelect = (board: BoardPopup) => {
+ form.setValue(
+ "urlPath",
+ `/cms/${board.bbsTypeCd.toLowerCase()}/${board.bbsId}`,
+ );
form.setValue("menuLnkgSn", board.bbsId);
form.setValue("menuLnkgSnNm", board.bbsNm);
};
@@ -52,6 +58,14 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
form.setValue("menuPicDsgnYmd", dayjs(date).format("YYYYMMDD"));
};
+ const getUrlPathPlaceholder = () => {
+ if (menuType === "CONTENTS") return "콘텐츠 선택 시 자동으로 입력됩니다.";
+ if (menuType === "BOARD") return "게시판 선택 시 자동으로 입력됩니다.";
+ if (menuType === "INSIDE")
+ return "메뉴에 연결된 페이지의 경로를 입력하십시오.";
+ return "'http://' 또는 'https://'를 포함하여 입력하십시오.";
+ };
+
return (
@@ -91,7 +105,10 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
)}
- {(menuType === "INSIDE" || menuType === "OUTSIDE") && (
+ {(menuType === "CONTENTS" ||
+ menuType === "BOARD" ||
+ menuType === "INSIDE" ||
+ menuType === "OUTSIDE") && (
연결 URL
@@ -105,10 +122,9 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
- placeholder={
- menuType === "INSIDE"
- ? "메뉴에 연결된 페이지의 경로를 입력하십시오."
- : "'http://' 또는 'https://'를 포함하여 입력하십시오."
+ placeholder={getUrlPathPlaceholder()}
+ disabled={
+ menuType === "CONTENTS" || menuType === "BOARD"
}
/>
@@ -293,25 +309,30 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
control={form.control}
name="qrcd"
render={({ field }) => (
-
-
-
-
-
-
-
+
-
-
-
+
+
+
+ {field.value === "Y" && (
+
+ )}
+
)}
/>
diff --git a/apps/cs/src/pages/privacylog/index.ts b/apps/cs/src/pages/privacylog/index.ts
new file mode 100644
index 000000000..7bbd75042
--- /dev/null
+++ b/apps/cs/src/pages/privacylog/index.ts
@@ -0,0 +1 @@
+export { PrivacyLogPage } from "./ui/privacylog-page";
diff --git a/apps/cs/src/pages/privacylog/model/table-columns.tsx b/apps/cs/src/pages/privacylog/model/table-columns.tsx
new file mode 100644
index 000000000..a5b52d796
--- /dev/null
+++ b/apps/cs/src/pages/privacylog/model/table-columns.tsx
@@ -0,0 +1,41 @@
+import dayjs from "dayjs";
+import type { ColumnDef } from "@tanstack/react-table";
+import type { PrivacyLog } from "@/entities/privacylog";
+
+
+export const columns: ColumnDef
[] = [
+ {
+ id: "no",
+ header: "NO",
+ cell: ({ row, table }) => {
+ const totalRows = table.getRowCount();
+ const { pageIndex, pageSize } = table.getState().pagination;
+ return totalRows - (pageIndex * pageSize + row.index);
+ },
+ },
+ {
+ accessorKey: "inqDt",
+ header: "요청일시",
+ cell: ({ row }) => {
+ const date = dayjs(row.original.inqDt);
+ return date.format("YYYY-MM-DD HH:mm:ss");
+ },
+ },
+ {
+ accessorKey: "siteNm",
+ header: "사이트명",
+ },
+ {
+ accessorKey: "rqstrNm",
+ header: "조회자명",
+ },
+ {
+ accessorKey: "rqstrIp",
+ header: "조회IP",
+ },
+ {
+ accessorKey: "inqInfoSummary",
+ header: "조회정보",
+ }
+];
+
diff --git a/apps/cs/src/pages/privacylog/ui/privacylog-page.tsx b/apps/cs/src/pages/privacylog/ui/privacylog-page.tsx
new file mode 100644
index 000000000..5cb8bf13d
--- /dev/null
+++ b/apps/cs/src/pages/privacylog/ui/privacylog-page.tsx
@@ -0,0 +1,206 @@
+"use client";
+
+import dayjs from "dayjs";
+import { useState } from "react";
+import { keepPreviousData, useQuery } from "@tanstack/react-query";
+import {
+ getCoreRowModel,
+ type PaginationState,
+} from "@tanstack/react-table";
+import {
+ Button,
+ DateRangePicker,
+ Input,
+ Label,
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+ } from "@common/components";
+import { PrivacyLogPrivacy } from "@/entities/privacylog";
+import { type PrivacyLogParams} from "@/entities/privacylog/model/privacylog-interface";
+import { useDataTable } from "@/shared/lib";
+import { ENDPOINT } from "@/shared/config";
+import { columns } from "@/pages/privacylog/model/table-columns";
+
+
+
+type DateRange = {
+ from: Date | undefined;
+ to?: Date | undefined;
+};
+
+function PrivacyLogPage({ }) {
+
+ // 1주일 전부터 오늘까지 기본 날짜 설정
+ const defaultDateRange: DateRange = {
+ from: dayjs().subtract(7, "day").toDate(),
+ to: dayjs().toDate(),
+ };
+
+ const [dateRange, setDateRange] = useState(defaultDateRange);
+
+ const [pagination, setPagination] = useState({
+ pageIndex: 0,
+ pageSize: 10,
+ });
+
+ const [privacylogPostQuery, setprivacylogPostQuery] = useState< PrivacyLogParams>({
+ siteId: "",
+ inqInfoKeywordType: "userName",
+ inqInfoKeyword: "",
+ rqstrIp: "",
+ inqDtStart: dayjs().subtract(7, "day").format("YYYY-MM-DD"),
+ inqDtEnd: dayjs().format("YYYY-MM-DD"),
+ sort:"inqDt,asc"
+ });
+
+
+ const query = useQuery({
+ queryKey: [ENDPOINT.USER_SERVICE.PRIVACYLOG, pagination],
+ queryFn: () =>
+ PrivacyLogPrivacy.getPrivacyLogList({
+ ...privacylogPostQuery,
+ page: pagination.pageIndex,
+ size: pagination.pageSize,
+ }),
+ placeholderData: keepPreviousData,
+ });
+
+ const { tableComponent, tableInstance: table } = useDataTable({
+ query,
+ tableOptions: {
+ columns,
+ state: {
+ pagination,
+ },
+ manualPagination: true,
+ enableMultiRowSelection: true,
+ getCoreRowModel: getCoreRowModel(),
+ onPaginationChange: setPagination,
+ },
+ dataTableOptions: {
+ viewPagination: true,
+ },
+ });
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ query.refetch();
+ };
+
+ const handleprivacylogParamsChange =
+ (key: keyof PrivacyLogParams) => (value: unknown) => {
+ setprivacylogPostQuery({ ...privacylogPostQuery, [key]: value });
+ };
+
+ const handlePresetDateRange = (value: string) => {
+ let presetDateRange: DateRange | undefined;
+ switch (value) {
+ case "1W":
+ presetDateRange = {
+ from: dayjs().subtract(7, "day").toDate(),
+ to: dayjs().toDate(),
+ };
+ break;
+ case "1M":
+ presetDateRange = {
+ from: dayjs().subtract(30, "day").toDate(),
+ to: dayjs().toDate(),
+ };
+ break;
+ case "3M":
+ presetDateRange = {
+ from: dayjs().subtract(90, "day").toDate(),
+ to: dayjs().toDate(),
+ };
+ break;
+ default:
+ presetDateRange = undefined;
+ }
+ handleDateRangeChange(presetDateRange);
+ };
+
+
+
+ const handleDateRangeChange = (value: DateRange | undefined) => {
+ setDateRange(value || defaultDateRange);
+ setprivacylogPostQuery({
+ ...privacylogPostQuery,
+ inqDtStart: dayjs(value?.from).format("YYYY-MM-DD") ?? dayjs().subtract(7, "day").format("YYYY-MM-DD"),
+ inqDtEnd: dayjs(value?.to).format("YYYY-MM-DD") ?? dayjs().format("YYYY-MM-DD"),
+ });
+ };
+
+
+
+
+ return (
+
+
+
+
+
+
로그인로그 목록
+
+
+
+
+
+
{tableComponent}
+
+
+ );
+}
+
+export { PrivacyLogPage };
diff --git a/apps/cs/src/pages/recent/ui/recent-page.tsx b/apps/cs/src/pages/recent/ui/recent-page.tsx
index f99ee2339..26cf3af85 100644
--- a/apps/cs/src/pages/recent/ui/recent-page.tsx
+++ b/apps/cs/src/pages/recent/ui/recent-page.tsx
@@ -21,15 +21,17 @@ import {
SelectTrigger,
SelectValue,
} from "@common/components";
+import { SortSelect } from "@/widgets/post";
import {
PostApi,
+ deleteReasonMessages,
type DeletePost,
type RecentPost,
type RecentPostParams,
} from "@/entities/post";
import { BoardApi } from "@/entities/board";
import { AuthorityButton } from "@/entities/menu-authority";
-import { useAlertStore, usePrompt, useDataTable } from "@/shared/lib";
+import { useAlertStore, useSelectPrompt, useDataTable } from "@/shared/lib";
import { ENDPOINT } from "@/shared/config";
import { columns } from "../model/table-columns";
import { MovePostDialog } from "./move-post-dialog";
@@ -39,12 +41,18 @@ type DateRange = {
to?: Date | undefined;
};
+const selectData = Object.entries(deleteReasonMessages).map(
+ ([label, value]) => ({ label, value }),
+);
+
function RecentPage() {
const router = useRouter();
- const { prompt } = usePrompt();
+ const { prompt } = useSelectPrompt(selectData);
const { confirm } = useConfirm();
const alert = useAlertStore((state) => state.setMessage);
+
const [dateRange, setDateRange] = useState();
+ const [tableSort, setTableSort] = useState("regDt,desc");
const [pagination, setPagination] = useState({
pageIndex: 0,
pageSize: 10,
@@ -61,12 +69,13 @@ function RecentPage() {
const [isDeletePostData, setIsDeletePostData] = useState(false);
const query = useQuery({
- queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, pagination],
+ queryKey: [ENDPOINT.BOARD_SERVICE.POSTS, pagination, tableSort],
queryFn: () =>
PostApi.getRecentPostList({
...recentPostQuery,
page: pagination.pageIndex,
size: pagination.pageSize,
+ sort: tableSort,
}),
placeholderData: keepPreviousData,
});
@@ -336,6 +345,7 @@ function RecentPage() {
))}
+
diff --git a/apps/cs/src/pages/schedule/ui/date-time-picker.tsx b/apps/cs/src/pages/schedule/ui/date-time-picker.tsx
index 0302253b9..8906585a0 100644
--- a/apps/cs/src/pages/schedule/ui/date-time-picker.tsx
+++ b/apps/cs/src/pages/schedule/ui/date-time-picker.tsx
@@ -64,12 +64,7 @@ function DateTimePicker({ date, classes, onDateChange }: DateTimePickerProps) {
-
+
diff --git a/apps/cs/src/pages/site/ui/ip-management-dialog.tsx b/apps/cs/src/pages/site/ui/ip-management-dialog.tsx
index c2a2c2702..43e2c2155 100644
--- a/apps/cs/src/pages/site/ui/ip-management-dialog.tsx
+++ b/apps/cs/src/pages/site/ui/ip-management-dialog.tsx
@@ -46,7 +46,7 @@ function IPManagementDialog({
const { data: ipList } = useQuery({
queryKey: [ENDPOINT.CMS_SERVICE.IPS, props.open],
- queryFn: () => IpApi.getIpList({ siteId, sort: [] }),
+ queryFn: () => IpApi.getIpList({ siteId, sort: "" }),
select: (data) => {
const ips = data.content.map((item) => ({
...item,
diff --git a/apps/cs/src/pages/site/ui/modify-page.tsx b/apps/cs/src/pages/site/ui/modify-page.tsx
index 0f36bc58d..fdc40c477 100644
--- a/apps/cs/src/pages/site/ui/modify-page.tsx
+++ b/apps/cs/src/pages/site/ui/modify-page.tsx
@@ -18,12 +18,8 @@ function ModifyPage({ siteId }: { siteId: string }) {
queryKey: [ENDPOINT.CMS_SERVICE.SITES, siteId],
queryFn: () => SiteApi.siteDetail(siteId),
select: (data) => {
- const keys = Object.keys(siteFormSchema.shape);
- const filteredData = Object.fromEntries(
- Object.entries(data).filter(([key]) => keys.includes(key)),
- );
return {
- ...filteredData,
+ ...data,
faxNo: data.faxNumber,
bscSiteYn: data.basicSiteYn,
};
@@ -50,7 +46,14 @@ function ModifyPage({ siteId }: { siteId: string }) {
const form = useForm
({
resolver: zodResolver(siteFormSchema),
- values: siteDetail as SiteFormData,
+ values: {
+ ...siteDetail,
+ siteId: siteDetail?.siteId ?? "",
+ siteNm: siteDetail?.siteNm ?? "",
+ siteKndCd: siteDetail?.siteKndCd ?? "",
+ bscSiteYn: siteDetail?.bscSiteYn ?? "",
+ useYn: siteDetail?.useYn ?? "",
+ },
});
return (
diff --git a/apps/cs/src/pages/site/ui/site-form.tsx b/apps/cs/src/pages/site/ui/site-form.tsx
index c76f23ae5..ffd03e7f4 100644
--- a/apps/cs/src/pages/site/ui/site-form.tsx
+++ b/apps/cs/src/pages/site/ui/site-form.tsx
@@ -82,6 +82,7 @@ function SiteForm({ form, siteId, ...props }: SiteFormProps) {
diff --git a/apps/cs/src/pages/site/ui/site-page.tsx b/apps/cs/src/pages/site/ui/site-page.tsx
index 6d0041a65..734ba7658 100644
--- a/apps/cs/src/pages/site/ui/site-page.tsx
+++ b/apps/cs/src/pages/site/ui/site-page.tsx
@@ -17,6 +17,7 @@ function SitePage() {
const [siteQuery, setSiteQuery] = useState({
siteNm: "",
+ sort:"regDt,desc"
});
const [pagination, setPagination] = useState({
pageIndex: 0,
diff --git a/apps/cs/src/pages/template/ui/template-page.tsx b/apps/cs/src/pages/template/ui/template-page.tsx
index dc90b69ff..59e8c8b01 100644
--- a/apps/cs/src/pages/template/ui/template-page.tsx
+++ b/apps/cs/src/pages/template/ui/template-page.tsx
@@ -18,6 +18,7 @@ function TemplatePage() {
const [templateQuery, setTemplateQuery] = useState({
tmpltSeCd: "",
keyword: "",
+ sort:"regDt,desc"
});
const [pagination, setPagination] = useState({
pageIndex: 0,
diff --git a/apps/cs/src/pages/terms/ui/terms-page.tsx b/apps/cs/src/pages/terms/ui/terms-page.tsx
index 8cb6a911e..617d4eff6 100644
--- a/apps/cs/src/pages/terms/ui/terms-page.tsx
+++ b/apps/cs/src/pages/terms/ui/terms-page.tsx
@@ -17,7 +17,8 @@ function TermsPage() {
const [termsQuery, setTermsQuery] = useState({
trmsSeCd: "",
- keyword: "",
+ keyword: "",
+ sort:"regDt,desc"
});
const [pagination, setPagination] = useState({
pageIndex: 0,
diff --git a/apps/cs/src/pages/users/ui/users-page.tsx b/apps/cs/src/pages/users/ui/users-page.tsx
index d6f77deb5..2ffe05eb4 100644
--- a/apps/cs/src/pages/users/ui/users-page.tsx
+++ b/apps/cs/src/pages/users/ui/users-page.tsx
@@ -51,7 +51,7 @@ function UsersPage() {
grpId: "",
frDateYmd: "",
toDateYmd: "",
- sort: ["aprvYmd,desc"],
+ sort: "regDt,desc"
},
});
@@ -288,9 +288,9 @@ function UsersPage() {
회원 목록