diff --git a/apps/cs/app/(with-base-layout)/user-statistics/page.tsx b/apps/cs/app/(with-base-layout)/user-statistics/page.tsx new file mode 100644 index 000000000..125900022 --- /dev/null +++ b/apps/cs/app/(with-base-layout)/user-statistics/page.tsx @@ -0,0 +1,2 @@ +import { UserStatisticsPage } from "@/pages/user-statistics"; +export default UserStatisticsPage; diff --git a/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx index 327473932..859af4058 100644 --- a/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx +++ b/apps/cs/src/pages/admin/ui/add-admin-dialog.tsx @@ -47,7 +47,7 @@ function AddAdminDialog() { const [open, setOpen] = useState(false); const [userSearchQuery, setUserSearchQuery] = useState({ - keywordType: "", + keywordType: "userName", keyword: "", }); const [selectedGroupId, setSelectedGroupId] = useState(""); @@ -193,6 +193,7 @@ function AddAdminDialog() {
{ + setDateRange(dateRange); + setPresetDateRange(""); + }; + return (
@@ -148,20 +153,9 @@ function ApiLogPage() { 3개월 - { - setDateRange({ ...dateRange, from: date }); - setPresetDateRange(""); - }} - /> - ~ - { - setDateRange({ ...dateRange, to: date }); - setPresetDateRange(""); - }} +

총 회원수

- {data?.memberTotCnt ?? "-"} + {data?.memberTotCnt + ? data.memberTotCnt.toLocaleString() + : "-"}

- 금일 신규 가입자 {data?.memberTodayCnt ?? "-"}명 + 금일 신규 가입자 + {data?.memberTodayCnt + ? data.memberTodayCnt.toLocaleString() + : "-"} + 명

{/* 차트 영역 */} @@ -131,10 +137,12 @@ function DashboardPage() {

전체 게시물

- {data?.pstTotCnt ?? "-"} + {data?.pstTotCnt ? data.pstTotCnt.toLocaleString() : "-"}

- 금일 +{data?.pstTodayCnt ?? "-"} 등록 + 금일 + + {data?.pstTodayCnt ? data.pstTodayCnt.toLocaleString() : "-"}{" "} + 등록

{/* 차트 영역 */} @@ -152,10 +160,16 @@ function DashboardPage() {

오늘 접속자 수

- {data?.accessTodayCnt ?? "-"} + {data?.accessTodayCnt + ? data.accessTodayCnt.toLocaleString() + : "-"}

- 전일 대비 +{data?.accessBefDayPer ?? "-"}% + 전일 대비 + + {data?.accessBefDayPer + ? data.accessBefDayPer.toLocaleString() + : "-"} + %

{/* 차트 영역 */} @@ -173,7 +187,7 @@ function DashboardPage() {

API 호출 건수

- {data?.apiTodayCnt ?? "-"} + {data?.apiTodayCnt ? data.apiTodayCnt.toLocaleString() : "-"}

금일 누적

@@ -221,7 +235,10 @@ function DashboardPage() {
- {data?.waitMemberCnt}건 + {data?.waitMemberCnt + ? data.waitMemberCnt.toLocaleString() + : "0"} + 건
신규 가입 승인 대기 @@ -253,7 +270,9 @@ function DashboardPage() { 메뉴 승인
-
3건
+
+ {(3).toLocaleString()}건 +
매뉴 수정 승인 대기
@@ -283,7 +302,10 @@ function DashboardPage() {
- {data?.waitCntnCnt}건 + {data?.waitCntnCnt + ? data.waitCntnCnt.toLocaleString() + : "0"} + 건
컨텐츠 수정 승인 대기 @@ -303,19 +325,19 @@ function DashboardPage() {
신규 사용자 가입 - 1분 전 + {(1).toLocaleString()}분 전
메뉴 수정 요청 - 5분 전 + {(5).toLocaleString()}분 전
게시물 신고 - 10분 전 + {(10).toLocaleString()}분 전
컨텐츠 승인 요청 - 15분 전 + {(15).toLocaleString()}분 전
@@ -328,20 +350,22 @@ function DashboardPage() {
- 3월 정기 시스템 점검 + + {(3).toLocaleString()}월 정기 시스템 점검 + 내일
서버 업데이트 예정 - 3일 후 + {(3).toLocaleString()}일 후
백업 스케줄 - 5일 후 + {(5).toLocaleString()}일 후
정기 보안 점검 - 7일 후 + {(7).toLocaleString()}일 후
diff --git a/apps/cs/src/pages/groups/ui/group-form.tsx b/apps/cs/src/pages/groups/ui/group-form.tsx index 6c1d8b67f..16c620388 100644 --- a/apps/cs/src/pages/groups/ui/group-form.tsx +++ b/apps/cs/src/pages/groups/ui/group-form.tsx @@ -161,11 +161,11 @@ function GroupForm({ form }: GroupFormProps) { >
- +
- +
diff --git a/apps/cs/src/pages/groups/ui/groups-dialog.tsx b/apps/cs/src/pages/groups/ui/groups-dialog.tsx index 427ba4b1b..9fca4ebdb 100644 --- a/apps/cs/src/pages/groups/ui/groups-dialog.tsx +++ b/apps/cs/src/pages/groups/ui/groups-dialog.tsx @@ -1,6 +1,6 @@ "use client"; -import { memo, useState } from "react"; +import { memo, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { useConfirm } from "@frontend-opensource/use-react-hooks"; @@ -43,7 +43,7 @@ const GroupsDialog = memo( const { data: group } = useQuery({ queryKey: [ENDPOINT.CMS_SERVICE.GROUPS, groupId], queryFn: () => GroupApi.findGroupById(groupId!), - enabled: !!groupId && open, // open 시점 groupId가 있을 때만 상세 조회 API 호출 + enabled: !!groupId && open, // open 시점 groupId가 있을 때만 상세 조회 API 호출 }); const { mutateAsync: createGroup } = useMutationWrapper({ @@ -77,7 +77,7 @@ const GroupsDialog = memo( delYn: group?.delYn ?? "", authrtId: group?.authrtId ?? "", siteId: group?.siteId ?? "", - mngrYn: group?.mngrYn ?? "", + mngrYn: group?.mngrYn ?? "Y", }, }); @@ -91,6 +91,10 @@ const GroupsDialog = memo( if (await confirm(confirmMessage)) callback(data); }; + useEffect(() => { + if (open) form.reset(); + }, [open]); + return ( {children} diff --git a/apps/cs/src/pages/image/ui/create-page.tsx b/apps/cs/src/pages/image/ui/create-page.tsx index e7dda81d5..3bb249d25 100644 --- a/apps/cs/src/pages/image/ui/create-page.tsx +++ b/apps/cs/src/pages/image/ui/create-page.tsx @@ -23,6 +23,7 @@ function CreatePage() { resolver: zodResolver(imageFormSchema), defaultValues: { atflYn: "Y", + useYn: "Y", }, }); diff --git a/apps/cs/src/pages/image/ui/image-form.tsx b/apps/cs/src/pages/image/ui/image-form.tsx index 1de7820c5..7430a21e1 100644 --- a/apps/cs/src/pages/image/ui/image-form.tsx +++ b/apps/cs/src/pages/image/ui/image-form.tsx @@ -124,6 +124,7 @@ function ImageForm({ form, imgSn, ...props }: ImageFormProps) { {...field} type="number" min={0} + max={9999} value={field.value ?? ""} onChange={(e) => field.onChange(parseInt(e.target.value)) diff --git a/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx b/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx index a9748b28d..7ae7990a1 100644 --- a/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx +++ b/apps/cs/src/pages/loginlog/ui/loginlog-page.tsx @@ -7,7 +7,6 @@ import { getCoreRowModel, type PaginationState } from "@tanstack/react-table"; import { Button, Input, - InputDatePicker, Label, Select, SelectContent, @@ -16,10 +15,11 @@ import { SelectValue, LoadingWithImage, } from "@common/components"; +import { columns } from "@/pages/loginlog/model/table-columns"; import { LoginlogApi, type LoginlogParams } from "@/entities/loginlog"; import { useDataTable } from "@/shared/lib"; import { ENDPOINT } from "@/shared/config"; -import { columns } from "@/pages/loginlog/model/table-columns"; +import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker"; type DateRange = { from?: Date | undefined; @@ -141,6 +141,11 @@ function LoginlogPage() { setDateRange(presetDateRange || defaultDateRange); }; + const handleDateRangeChange = (dateRange: DateRange) => { + setDateRange(dateRange); + setPresetDateRange(""); + }; + return (
@@ -156,20 +161,9 @@ function LoginlogPage() { 3개월 - { - setDateRange({ ...dateRange, from: date }); - setPresetDateRange(""); - }} - /> - ~ - { - setDateRange({ ...dateRange, to: date }); - setPresetDateRange(""); - }} + { + setDateRange(dateRange); + setPresetDateRange(""); + }; + return (
@@ -147,20 +152,9 @@ function PrivacyLogPage() { 3개월 - { - setDateRange({ ...dateRange, from: date }); - setPresetDateRange(""); - }} - /> - ~ - { - setDateRange({ ...dateRange, to: date }); - setPresetDateRange(""); - }} + ({}); const [presetDateRange, setPresetDateRange] = useState(""); - const [dateRange, setDateRange] = useState({ + const [dateRange, setDateRange] = useState({ from: undefined, to: undefined, }); @@ -169,7 +169,7 @@ function RecentPage() { }; const handlePresetDateRange = (value: string) => { - let presetDateRange: DateRange | undefined; + let presetDateRange: DateRange; switch (value) { case "1W": presetDateRange = { @@ -190,12 +190,20 @@ function RecentPage() { }; break; default: - presetDateRange = undefined; + presetDateRange = { + from: undefined, + to: undefined, + }; } setPresetDateRange(value); setDateRange(presetDateRange); }; + const handleDateRangeChange = (dateRange: DateRange) => { + setDateRange(dateRange); + setPresetDateRange(""); + }; + const onSoftDeletePost = async () => { const { resolved, value: delRsn } = await prompt("삭제 사유를 입력해주세요."); @@ -260,20 +268,9 @@ function RecentPage() { 3개월 - { - setDateRange({ ...dateRange, from: date }); - setPresetDateRange(""); - }} - /> - ~ - { - setDateRange({ ...dateRange, to: date }); - setPresetDateRange(""); - }} + + + + + + 일주일 + 1개월 + 3개월 + + + + + + + + setKeyword(e.target.value)} + className="h-[3.2rem] w-[24rem] rounded-sm !text-[1.3rem]" + /> + + + +
+ + {/* 차트 영역 */} +
+ {/* 일별 접속 현황 */} +
+
+

일별 접속 현황

+
+
+ } + options={chartData.monthlyStatisticsOptions} + /> +
+
+ +
+ {/* 사이트별 접속자 */} +
+
+

사이트별 접속자

+
+
+ +
+
+ + {/* 브라우저별 접속자 */} +
+
+

브라우저별 접속자

+
+
+ +
+
+
+
+ + {/* 접속 로그 상세 */} +
+
+
+

접속 로그 상세

+
+ +
+
+
+
{tableComponent}
+
+
+ ); +} + +export { UserStatisticsPage }; diff --git a/apps/cs/src/pages/users/ui/users-page.tsx b/apps/cs/src/pages/users/ui/users-page.tsx index e119738fa..5362b8b42 100644 --- a/apps/cs/src/pages/users/ui/users-page.tsx +++ b/apps/cs/src/pages/users/ui/users-page.tsx @@ -9,7 +9,6 @@ import { Button, Checkbox, Input, - InputDatePicker, Label, Select, SelectContent, @@ -25,6 +24,7 @@ import { } from "@/entities/user"; import { ENDPOINT } from "@/shared/config"; import { useDataTable } from "@/shared/lib"; +import { InputDateRangePicker } from "@/shared/ui/input-date-range-picker"; import { columns } from "../model/table-columns"; type DateRange = { @@ -40,7 +40,7 @@ function UsersPage() { pageSize: 10, }); const [presetDateRange, setPresetDateRange] = useState(""); - const [dateRange, setDateRange] = useState({ + const [dateRange, setDateRange] = useState({ from: undefined, to: undefined, }); @@ -62,7 +62,7 @@ function UsersPage() { }); const { data: userSttsStats } = useQuery({ - queryKey: [ENDPOINT.USER_SERVICE.USERS], + queryKey: [ENDPOINT.USER_SERVICE.USERS, "userStatusStatistics"], queryFn: () => UserApi.selectUserSttsStats(), }); @@ -145,7 +145,7 @@ function UsersPage() { }; const handlePresetDateRange = (value: string) => { - let presetDateRange: DateRange | undefined; + let presetDateRange: DateRange; switch (value) { case "1M": presetDateRange = { @@ -166,15 +166,24 @@ function UsersPage() { }; break; default: - presetDateRange = undefined; + presetDateRange = { + from: undefined, + to: undefined, + }; } setPresetDateRange(value); setDateRange(presetDateRange); }; - const getUserSttsStatsCnt = (code: string): number => { - if (!Array.isArray(userSttsStats)) return 0; - return userSttsStats.find((item) => item.sttsCd === code)?.cnt ?? 0; + const handleDateRangeChange = (dateRange: DateRange) => { + setDateRange(dateRange); + setPresetDateRange(""); + }; + + const getUserSttsStatsCnt = (code: string): string => { + if (!Array.isArray(userSttsStats)) return "0"; + const count = userSttsStats.find((item) => item.sttsCd === code)?.cnt ?? 0; + return count.toLocaleString(); }; const handleUserStateCodeChange = (code: string, checked: boolean) => { @@ -292,20 +301,9 @@ function UsersPage() { 6개월 - { - setDateRange({ ...dateRange, from: date }); - setPresetDateRange(""); - }} - /> - ~ - { - setDateRange({ ...dateRange, to: date }); - setPresetDateRange(""); - }} +
diff --git a/apps/cs/src/shared/ui/input-date-range-picker.tsx b/apps/cs/src/shared/ui/input-date-range-picker.tsx new file mode 100644 index 000000000..9f574661e --- /dev/null +++ b/apps/cs/src/shared/ui/input-date-range-picker.tsx @@ -0,0 +1,47 @@ +import { InputDatePicker } from "@common/components"; +import { useEffect } from "react"; +import { useAlertStore } from "../lib"; + +type DateRange = { + from?: Date; + to?: Date; +}; + +type InputDateRangePickerProps = { + dateRange: DateRange; + setDateRange: (dateRange: DateRange) => void; +}; + +function InputDateRangePicker({ + dateRange, + setDateRange, +}: InputDateRangePickerProps) { + const alert = useAlertStore((state) => state.setMessage); + + useEffect(() => { + if (dateRange.from && dateRange.to && dateRange.from > dateRange.to) { + alert("시작일보다 종료일이 커야 합니다."); + setDateRange({ ...dateRange, to: dateRange.from }); + } + }, [dateRange]); + + return ( + <> + { + setDateRange({ ...dateRange, from: date }); + }} + /> + ~ + { + setDateRange({ ...dateRange, to: date }); + }} + /> + + ); +} + +export { InputDateRangePicker };