feat: 콘텐츠 및 게시판 메뉴 qrcode 조회 기능 추가

This commit is contained in:
2025-06-26 14:12:11 +09:00
parent 749176a37b
commit c667d5b0a1
8 changed files with 263 additions and 44 deletions
+3 -1
View File
@@ -127,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 };
+42 -32
View File
@@ -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", `/cntns/${content.contsSn}`);
form.setValue("menuLnkgSn", content.contsSn);
form.setValue("menuLnkgSnNm", content.contsTtl);
};
@@ -56,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>
@@ -95,7 +105,8 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
</TableCell>
</TableRow>
)}
{(menuType === "BOARD" ||
{(menuType === "CONTENTS" ||
menuType === "BOARD" ||
menuType === "INSIDE" ||
menuType === "OUTSIDE") && (
<TableRow>
@@ -111,14 +122,10 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
{...field}
value={field.value ?? ""}
className="!text-[1.3rem]"
placeholder={
menuType === "BOARD"
? "게시판 선택 시 자동으로 입력됩니다."
: menuType === "INSIDE"
? "메뉴에 연결된 페이지의 경로를 입력하십시오."
: "'http://' 또는 'https://'를 포함하여 입력하십시오."
placeholder={getUrlPathPlaceholder()}
disabled={
menuType === "CONTENTS" || menuType === "BOARD"
}
disabled={menuType === "BOARD"}
/>
</FormControl>
</FormItem>
@@ -298,31 +305,34 @@ function TypeMenuForm({ form, menuType }: TypeMenuFormProps) {
<TableRow>
<TableHead>QR코드</TableHead>
<TableCell className="border">
<FormField
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">
<FormField
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>
<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>
)}
/>
<QRCodeDialog url={form.watch("urlPath") ?? ""} />
</div>
</TableCell>
</TableRow>
<TableRow>