Merge branch 'iipbs_cs' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
This commit is contained in:
@@ -21,20 +21,28 @@ import {
|
||||
Skeleton,
|
||||
} from "@common/components";
|
||||
import { AttachmentApi } from "@/entities/attachment";
|
||||
import { PostApi, type PostParams } from "@/entities/post";
|
||||
import {
|
||||
PostApi,
|
||||
deleteReasonMessages,
|
||||
type PostParams,
|
||||
} from "@/entities/post";
|
||||
import { AuthorityButton } from "@/entities/menu-authority";
|
||||
import { ENDPOINT } from "@/shared/config";
|
||||
import {
|
||||
useAlertStore,
|
||||
useMenuStore,
|
||||
usePrompt,
|
||||
useSelectPrompt,
|
||||
useMutationWrapper,
|
||||
} from "@/shared/lib";
|
||||
|
||||
const selectData = Object.entries(deleteReasonMessages).map(
|
||||
([label, value]) => ({ label, value }),
|
||||
);
|
||||
|
||||
function FaqPage({ bbsId }: { bbsId: string }) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const { prompt } = usePrompt();
|
||||
const { prompt } = useSelectPrompt(selectData);
|
||||
const alert = useAlertStore((state) => state.setMessage);
|
||||
const bbsInfo = useMenuStore((state) => state.bbsInfo);
|
||||
|
||||
@@ -75,11 +83,13 @@ function FaqPage({ bbsId }: { bbsId: string }) {
|
||||
|
||||
const handleDeletePost = async (pstNo: number) => {
|
||||
const delRsn = await getPromptValue("삭제 사유를 입력해주세요.");
|
||||
if (!delRsn) return;
|
||||
deletePost({ pstNo, delRsn });
|
||||
};
|
||||
|
||||
const onSelectedPostDelete = async () => {
|
||||
const delRsn = await getPromptValue("삭제 사유를 입력해주세요.");
|
||||
if (!delRsn) return;
|
||||
await Promise.all(
|
||||
selectedPostNumbers.map((pstNo) =>
|
||||
PostApi.deletePost(parseInt(bbsId), pstNo, delRsn),
|
||||
|
||||
@@ -26,7 +26,7 @@ export const columns: ColumnDef<GroupUserListResponseDto>[] = [
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
accessorKey: "groupId",
|
||||
accessorKey: "userId",
|
||||
header: "사용자ID",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -57,7 +57,6 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
getRowId: (row) => row.userUnqId,
|
||||
},
|
||||
dataTableOptions: {
|
||||
viewPagination: true,
|
||||
@@ -65,15 +64,18 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
});
|
||||
|
||||
const handleDeleteSelected = async () => {
|
||||
const selectedRows = Object.keys(rowSelection);
|
||||
const selectedRows = table.getSelectedRowModel().rows.map((row) => ({
|
||||
groupId: row.original.groupId,
|
||||
userUnqId: row.original.userUnqId,
|
||||
}));
|
||||
if (selectedRows.length === 0 || !groupId) return;
|
||||
if (!(await confirm("사용자를 삭제하시겠습니까?"))) return;
|
||||
|
||||
try {
|
||||
const usersToDelete = selectedRows.map((userUnqId) => ({
|
||||
const usersToDelete = selectedRows.map((row) => ({
|
||||
mode: "D",
|
||||
groupId,
|
||||
userUnqId,
|
||||
groupId: row.groupId,
|
||||
userUnqId: row.userUnqId,
|
||||
}));
|
||||
|
||||
await GroupUsersApi.saveGroupUser(usersToDelete);
|
||||
@@ -90,6 +92,7 @@ function GroupUsersSection({ groupId }: GroupUsersSectionProps) {
|
||||
|
||||
useEffect(() => {
|
||||
setPagination({ ...pagination, pageIndex: 0 });
|
||||
setRowSelection({});
|
||||
}, [groupId]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export { LoginlogPage } from "./ui/loginlog-page";
|
||||
@@ -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",
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -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<HTMLDivElement>(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 (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button disabled={!url}>QR 조회</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>QR 코드 스캔</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex items-center justify-center p-4">
|
||||
<div ref={canvasRef}>
|
||||
<Canvas
|
||||
text={url}
|
||||
options={{
|
||||
errorCorrectionLevel: "M",
|
||||
margin: 1,
|
||||
width: 200,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-sm bg-accent p-2 text-[1.3rem] text-accent-foreground">
|
||||
<p className="break-all px-2">{qrCodeUrl}</p>
|
||||
<Tooltip open={isCopied}>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
onClick={() => copyText(qrCodeUrl)}
|
||||
className="size-10 shrink-0 rounded-sm hover:bg-[rgba(0,0,0,0.1)]"
|
||||
>
|
||||
<i className="diveicon di-clipboard" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<span className="text-base">복사완료</span>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Button onClick={handleDownload}>QR 코드 다운로드</Button>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export { QRCodeDialog };
|
||||
@@ -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<MenuFormData>;
|
||||
@@ -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 (
|
||||
<Table variant="secondary" className="-mt-[0.1rem]">
|
||||
<colgroup>
|
||||
@@ -91,7 +105,10 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
{(menuType === "INSIDE" || menuType === "OUTSIDE") && (
|
||||
{(menuType === "CONTENTS" ||
|
||||
menuType === "BOARD" ||
|
||||
menuType === "INSIDE" ||
|
||||
menuType === "OUTSIDE") && (
|
||||
<TableRow>
|
||||
<TableHead>연결 URL</TableHead>
|
||||
<TableCell className="border">
|
||||
@@ -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"
|
||||
}
|
||||
/>
|
||||
</FormControl>
|
||||
@@ -293,25 +309,30 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
|
||||
control={form.control}
|
||||
name="qrcd"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
value={field.value ?? ""}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="Y" id="qrcdY" />
|
||||
<Label htmlFor="qrcdY">사용</Label>
|
||||
<div className="flex items-center gap-4">
|
||||
<FormItem>
|
||||
<FormControl>
|
||||
<RadioGroup
|
||||
value={field.value ?? ""}
|
||||
onValueChange={field.onChange}
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="Y" id="qrcdY" />
|
||||
<Label htmlFor="qrcdY">사용</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="N" id="qrcdN" />
|
||||
<Label htmlFor="qrcdN">사용안함</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<RadioGroupItem value="N" id="qrcdN" />
|
||||
<Label htmlFor="qrcdN">사용안함</Label>
|
||||
</div>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</FormItem>
|
||||
{field.value === "Y" && (
|
||||
<QRCodeDialog url={form.watch("urlPath") ?? ""} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
@@ -23,13 +23,14 @@ import {
|
||||
} from "@common/components";
|
||||
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,9 +40,13 @@ 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<DateRange>();
|
||||
|
||||
@@ -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<SiteFormData>({
|
||||
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 (
|
||||
|
||||
@@ -82,6 +82,7 @@ function SiteForm({ form, siteId, ...props }: SiteFormProps) {
|
||||
<CodeSelect
|
||||
{...field}
|
||||
upCd="SITEKNDCD"
|
||||
value={field.value ?? ""}
|
||||
onValueChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
Reference in New Issue
Block a user