리포트 디자이너에 바코드/QR코드 컴포넌트 추가
This commit is contained in:
parent
932eb288c6
commit
ea01309158
|
|
@ -12,6 +12,7 @@
|
||||||
"@types/mssql": "^9.1.8",
|
"@types/mssql": "^9.1.8",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bwip-js": "^4.8.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"docx": "^9.5.1",
|
"docx": "^9.5.1",
|
||||||
|
|
@ -4540,6 +4541,15 @@
|
||||||
"node": ">=10.16.0"
|
"node": ">=10.16.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/bwip-js": {
|
||||||
|
"version": "4.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/bwip-js/-/bwip-js-4.8.0.tgz",
|
||||||
|
"integrity": "sha512-gUDkDHSTv8/DJhomSIbO0fX/Dx0MO/sgllLxJyJfu4WixCQe9nfGJzmHm64ZCbxo+gUYQEsQcRmqcwcwPRwUkg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"bwip-js": "bin/bwip-js.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
"@types/mssql": "^9.1.8",
|
"@types/mssql": "^9.1.8",
|
||||||
"axios": "^1.11.0",
|
"axios": "^1.11.0",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
"bwip-js": "^4.8.0",
|
||||||
"compression": "^1.7.4",
|
"compression": "^1.7.4",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"docx": "^9.5.1",
|
"docx": "^9.5.1",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import {
|
||||||
PageOrientation,
|
PageOrientation,
|
||||||
convertMillimetersToTwip,
|
convertMillimetersToTwip,
|
||||||
} from "docx";
|
} from "docx";
|
||||||
|
import bwipjs from "bwip-js";
|
||||||
|
|
||||||
export class ReportController {
|
export class ReportController {
|
||||||
/**
|
/**
|
||||||
|
|
@ -1326,6 +1327,43 @@ export class ReportController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Barcode 컴포넌트 (바코드 이미지가 미리 생성되어 전달된 경우)
|
||||||
|
else if (component.type === "barcode" && component.barcodeImageBase64) {
|
||||||
|
try {
|
||||||
|
const base64Data =
|
||||||
|
component.barcodeImageBase64.split(",")[1] || component.barcodeImageBase64;
|
||||||
|
const imageBuffer = Buffer.from(base64Data, "base64");
|
||||||
|
result.push(
|
||||||
|
new ParagraphRef({
|
||||||
|
children: [
|
||||||
|
new ImageRunRef({
|
||||||
|
data: imageBuffer,
|
||||||
|
transformation: {
|
||||||
|
width: Math.round(component.width * 0.75),
|
||||||
|
height: Math.round(component.height * 0.75),
|
||||||
|
},
|
||||||
|
type: "png",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
// 바코드 이미지 생성 실패 시 텍스트로 대체
|
||||||
|
const barcodeValue = component.barcodeValue || "BARCODE";
|
||||||
|
result.push(
|
||||||
|
new ParagraphRef({
|
||||||
|
children: [
|
||||||
|
new TextRunRef({
|
||||||
|
text: `[${barcodeValue}]`,
|
||||||
|
size: pxToHalfPtFn(12),
|
||||||
|
font: "맑은 고딕",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Divider - 테이블 셀로 감싸서 정확한 너비 적용
|
// Divider - 테이블 셀로 감싸서 정확한 너비 적용
|
||||||
else if (
|
else if (
|
||||||
component.type === "divider" &&
|
component.type === "divider" &&
|
||||||
|
|
@ -1354,6 +1392,82 @@ export class ReportController {
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 바코드 이미지 생성 헬퍼 함수
|
||||||
|
const generateBarcodeImage = async (
|
||||||
|
component: any,
|
||||||
|
queryResultsMapRef: Record<string, { fields: string[]; rows: Record<string, unknown>[] }>
|
||||||
|
): Promise<string | null> => {
|
||||||
|
try {
|
||||||
|
const barcodeType = component.barcodeType || "CODE128";
|
||||||
|
const barcodeColor = (component.barcodeColor || "#000000").replace("#", "");
|
||||||
|
const barcodeBackground = (component.barcodeBackground || "#ffffff").replace("#", "");
|
||||||
|
|
||||||
|
// 바코드 값 결정 (쿼리 바인딩 또는 고정값)
|
||||||
|
let barcodeValue = component.barcodeValue || "SAMPLE123";
|
||||||
|
if (component.barcodeFieldName && component.queryId && queryResultsMapRef[component.queryId]) {
|
||||||
|
const qResult = queryResultsMapRef[component.queryId];
|
||||||
|
if (qResult.rows && qResult.rows.length > 0) {
|
||||||
|
const row = qResult.rows[0];
|
||||||
|
const val = row[component.barcodeFieldName];
|
||||||
|
if (val !== null && val !== undefined) {
|
||||||
|
barcodeValue = String(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bwip-js 바코드 타입 매핑
|
||||||
|
const bcidMap: Record<string, string> = {
|
||||||
|
"CODE128": "code128",
|
||||||
|
"CODE39": "code39",
|
||||||
|
"EAN13": "ean13",
|
||||||
|
"EAN8": "ean8",
|
||||||
|
"UPC": "upca",
|
||||||
|
"QR": "qrcode",
|
||||||
|
};
|
||||||
|
|
||||||
|
const bcid = bcidMap[barcodeType] || "code128";
|
||||||
|
const isQR = barcodeType === "QR";
|
||||||
|
|
||||||
|
// 바코드 옵션 설정
|
||||||
|
const options: any = {
|
||||||
|
bcid: bcid,
|
||||||
|
text: barcodeValue,
|
||||||
|
scale: 3,
|
||||||
|
includetext: !isQR && component.showBarcodeText !== false,
|
||||||
|
textxalign: "center",
|
||||||
|
barcolor: barcodeColor,
|
||||||
|
backgroundcolor: barcodeBackground,
|
||||||
|
};
|
||||||
|
|
||||||
|
// QR 코드 옵션
|
||||||
|
if (isQR) {
|
||||||
|
options.eclevel = component.qrErrorCorrectionLevel || "M";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 바코드 이미지 생성
|
||||||
|
const png = await bwipjs.toBuffer(options);
|
||||||
|
const base64 = png.toString("base64");
|
||||||
|
return `data:image/png;base64,${base64}`;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("바코드 생성 오류:", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 모든 페이지의 바코드 컴포넌트에 대해 이미지 생성
|
||||||
|
for (const page of layoutConfig.pages) {
|
||||||
|
if (page.components) {
|
||||||
|
for (const component of page.components) {
|
||||||
|
if (component.type === "barcode") {
|
||||||
|
const barcodeImage = await generateBarcodeImage(component, queryResultsMap);
|
||||||
|
if (barcodeImage) {
|
||||||
|
component.barcodeImageBase64 = barcodeImage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 섹션 생성 (페이지별)
|
// 섹션 생성 (페이지별)
|
||||||
const sortedPages = layoutConfig.pages.sort(
|
const sortedPages = layoutConfig.pages.sort(
|
||||||
(a: any, b: any) => a.page_order - b.page_order
|
(a: any, b: any) => a.page_order - b.page_order
|
||||||
|
|
@ -2624,6 +2738,77 @@ export class ReportController {
|
||||||
lastBottomY = adjustedY + component.height;
|
lastBottomY = adjustedY + component.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Barcode 컴포넌트
|
||||||
|
else if (component.type === "barcode") {
|
||||||
|
if (component.barcodeImageBase64) {
|
||||||
|
try {
|
||||||
|
const base64Data =
|
||||||
|
component.barcodeImageBase64.split(",")[1] || component.barcodeImageBase64;
|
||||||
|
const imageBuffer = Buffer.from(base64Data, "base64");
|
||||||
|
|
||||||
|
// spacing을 위한 빈 paragraph
|
||||||
|
if (spacingBefore > 0) {
|
||||||
|
children.push(
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { before: spacingBefore, after: 0 },
|
||||||
|
children: [],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
children.push(
|
||||||
|
new Paragraph({
|
||||||
|
indent: { left: indentLeft },
|
||||||
|
children: [
|
||||||
|
new ImageRun({
|
||||||
|
data: imageBuffer,
|
||||||
|
transformation: {
|
||||||
|
width: Math.round(component.width * 0.75),
|
||||||
|
height: Math.round(component.height * 0.75),
|
||||||
|
},
|
||||||
|
type: "png",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
} catch (imgError) {
|
||||||
|
console.error("바코드 이미지 오류:", imgError);
|
||||||
|
// 바코드 이미지 생성 실패 시 텍스트로 대체
|
||||||
|
const barcodeValue = component.barcodeValue || "BARCODE";
|
||||||
|
children.push(
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { before: spacingBefore, after: 0 },
|
||||||
|
indent: { left: indentLeft },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: `[${barcodeValue}]`,
|
||||||
|
size: pxToHalfPt(12),
|
||||||
|
font: "맑은 고딕",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 바코드 이미지가 없는 경우 텍스트로 대체
|
||||||
|
const barcodeValue = component.barcodeValue || "BARCODE";
|
||||||
|
children.push(
|
||||||
|
new Paragraph({
|
||||||
|
spacing: { before: spacingBefore, after: 0 },
|
||||||
|
indent: { left: indentLeft },
|
||||||
|
children: [
|
||||||
|
new TextRun({
|
||||||
|
text: `[${barcodeValue}]`,
|
||||||
|
size: pxToHalfPt(12),
|
||||||
|
font: "맑은 고딕",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
lastBottomY = adjustedY + component.height;
|
||||||
|
}
|
||||||
|
|
||||||
// Table 컴포넌트
|
// Table 컴포넌트
|
||||||
else if (component.type === "table" && component.queryId) {
|
else if (component.type === "table" && component.queryId) {
|
||||||
const queryResult = queryResultsMap[component.queryId];
|
const queryResult = queryResultsMap[component.queryId];
|
||||||
|
|
|
||||||
|
|
@ -166,3 +166,98 @@ export interface CreateTemplateRequest {
|
||||||
layoutConfig?: any;
|
layoutConfig?: any;
|
||||||
defaultQueries?: any;
|
defaultQueries?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 컴포넌트 설정 (프론트엔드와 동기화)
|
||||||
|
export interface ComponentConfig {
|
||||||
|
id: string;
|
||||||
|
type: string;
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
zIndex: number;
|
||||||
|
fontSize?: number;
|
||||||
|
fontFamily?: string;
|
||||||
|
fontWeight?: string;
|
||||||
|
fontColor?: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
borderWidth?: number;
|
||||||
|
borderColor?: string;
|
||||||
|
borderRadius?: number;
|
||||||
|
textAlign?: string;
|
||||||
|
padding?: number;
|
||||||
|
queryId?: string;
|
||||||
|
fieldName?: string;
|
||||||
|
defaultValue?: string;
|
||||||
|
format?: string;
|
||||||
|
visible?: boolean;
|
||||||
|
printable?: boolean;
|
||||||
|
conditional?: string;
|
||||||
|
locked?: boolean;
|
||||||
|
groupId?: string;
|
||||||
|
// 이미지 전용
|
||||||
|
imageUrl?: string;
|
||||||
|
objectFit?: "contain" | "cover" | "fill" | "none";
|
||||||
|
// 구분선 전용
|
||||||
|
orientation?: "horizontal" | "vertical";
|
||||||
|
lineStyle?: "solid" | "dashed" | "dotted" | "double";
|
||||||
|
lineWidth?: number;
|
||||||
|
lineColor?: string;
|
||||||
|
// 서명/도장 전용
|
||||||
|
showLabel?: boolean;
|
||||||
|
labelText?: string;
|
||||||
|
labelPosition?: "top" | "left" | "bottom" | "right";
|
||||||
|
showUnderline?: boolean;
|
||||||
|
personName?: string;
|
||||||
|
// 테이블 전용
|
||||||
|
tableColumns?: Array<{
|
||||||
|
field: string;
|
||||||
|
header: string;
|
||||||
|
width?: number;
|
||||||
|
align?: "left" | "center" | "right";
|
||||||
|
}>;
|
||||||
|
headerBackgroundColor?: string;
|
||||||
|
headerTextColor?: string;
|
||||||
|
showBorder?: boolean;
|
||||||
|
rowHeight?: number;
|
||||||
|
// 페이지 번호 전용
|
||||||
|
pageNumberFormat?: "number" | "numberTotal" | "koreanNumber";
|
||||||
|
// 카드 컴포넌트 전용
|
||||||
|
cardTitle?: string;
|
||||||
|
cardItems?: Array<{
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
fieldName?: string;
|
||||||
|
}>;
|
||||||
|
labelWidth?: number;
|
||||||
|
showCardBorder?: boolean;
|
||||||
|
showCardTitle?: boolean;
|
||||||
|
titleFontSize?: number;
|
||||||
|
labelFontSize?: number;
|
||||||
|
valueFontSize?: number;
|
||||||
|
titleColor?: string;
|
||||||
|
labelColor?: string;
|
||||||
|
valueColor?: string;
|
||||||
|
// 계산 컴포넌트 전용
|
||||||
|
calcItems?: Array<{
|
||||||
|
label: string;
|
||||||
|
value: number | string;
|
||||||
|
operator: "+" | "-" | "x" | "÷";
|
||||||
|
fieldName?: string;
|
||||||
|
}>;
|
||||||
|
resultLabel?: string;
|
||||||
|
resultColor?: string;
|
||||||
|
resultFontSize?: number;
|
||||||
|
showCalcBorder?: boolean;
|
||||||
|
numberFormat?: "none" | "comma" | "currency";
|
||||||
|
currencySuffix?: string;
|
||||||
|
// 바코드 컴포넌트 전용
|
||||||
|
barcodeType?: "CODE128" | "CODE39" | "EAN13" | "EAN8" | "UPC" | "QR";
|
||||||
|
barcodeValue?: string;
|
||||||
|
barcodeFieldName?: string;
|
||||||
|
showBarcodeText?: boolean;
|
||||||
|
barcodeColor?: string;
|
||||||
|
barcodeBackground?: string;
|
||||||
|
barcodeMargin?: number;
|
||||||
|
qrErrorCorrectionLevel?: "L" | "M" | "Q" | "H";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,143 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useRef, useState, useEffect } from "react";
|
import { useRef, useState, useEffect, useCallback } from "react";
|
||||||
import { ComponentConfig } from "@/types/report";
|
import { ComponentConfig } from "@/types/report";
|
||||||
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
|
import { useReportDesigner } from "@/contexts/ReportDesignerContext";
|
||||||
import { getFullImageUrl } from "@/lib/api/client";
|
import { getFullImageUrl } from "@/lib/api/client";
|
||||||
|
import JsBarcode from "jsbarcode";
|
||||||
|
import QRCode from "qrcode";
|
||||||
|
|
||||||
|
// 1D 바코드 렌더러 컴포넌트
|
||||||
|
interface BarcodeRendererProps {
|
||||||
|
value: string;
|
||||||
|
format: string;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
displayValue: boolean;
|
||||||
|
lineColor: string;
|
||||||
|
background: string;
|
||||||
|
margin: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function BarcodeRenderer({ value, format, width, height, displayValue, lineColor, background, margin }: BarcodeRendererProps) {
|
||||||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!svgRef.current || !value) return;
|
||||||
|
|
||||||
|
// 매번 에러 상태 초기화 후 재검사
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 바코드 형식에 따른 유효성 검사
|
||||||
|
let isValid = true;
|
||||||
|
let errorMsg = "";
|
||||||
|
const trimmedValue = value.trim();
|
||||||
|
|
||||||
|
if (format === "EAN13" && !/^\d{12,13}$/.test(trimmedValue)) {
|
||||||
|
isValid = false;
|
||||||
|
errorMsg = "EAN-13: 12~13자리 숫자 필요";
|
||||||
|
} else if (format === "EAN8" && !/^\d{7,8}$/.test(trimmedValue)) {
|
||||||
|
isValid = false;
|
||||||
|
errorMsg = "EAN-8: 7~8자리 숫자 필요";
|
||||||
|
} else if (format === "UPC" && !/^\d{11,12}$/.test(trimmedValue)) {
|
||||||
|
isValid = false;
|
||||||
|
errorMsg = "UPC: 11~12자리 숫자 필요";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
setError(errorMsg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// JsBarcode는 format을 소문자로 받음
|
||||||
|
const barcodeFormat = format.toLowerCase();
|
||||||
|
|
||||||
|
JsBarcode(svgRef.current, trimmedValue, {
|
||||||
|
format: barcodeFormat,
|
||||||
|
width: 2,
|
||||||
|
height: Math.max(30, height - (displayValue ? 30 : 10)),
|
||||||
|
displayValue: displayValue,
|
||||||
|
lineColor: lineColor,
|
||||||
|
background: background,
|
||||||
|
margin: margin,
|
||||||
|
fontSize: 12,
|
||||||
|
textMargin: 2,
|
||||||
|
});
|
||||||
|
} catch (err: any) {
|
||||||
|
// JsBarcode 체크섬 오류 등
|
||||||
|
setError(err?.message || "바코드 생성 실패");
|
||||||
|
}
|
||||||
|
}, [value, format, width, height, displayValue, lineColor, background, margin]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative h-full w-full">
|
||||||
|
{/* SVG는 항상 렌더링 (에러 시 숨김) */}
|
||||||
|
<svg
|
||||||
|
ref={svgRef}
|
||||||
|
className={`max-h-full max-w-full ${error ? "hidden" : ""}`}
|
||||||
|
/>
|
||||||
|
{/* 에러 메시지 오버레이 */}
|
||||||
|
{error && (
|
||||||
|
<div className="absolute inset-0 flex flex-col items-center justify-center text-xs text-red-500">
|
||||||
|
<span>{error}</span>
|
||||||
|
<span className="mt-1 text-gray-400">{value}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// QR코드 렌더러 컴포넌트
|
||||||
|
interface QRCodeRendererProps {
|
||||||
|
value: string;
|
||||||
|
size: number;
|
||||||
|
fgColor: string;
|
||||||
|
bgColor: string;
|
||||||
|
level: "L" | "M" | "Q" | "H";
|
||||||
|
}
|
||||||
|
|
||||||
|
function QRCodeRenderer({ value, size, fgColor, bgColor, level }: QRCodeRendererProps) {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (canvasRef.current && value) {
|
||||||
|
QRCode.toCanvas(
|
||||||
|
canvasRef.current,
|
||||||
|
value,
|
||||||
|
{
|
||||||
|
width: Math.max(50, size),
|
||||||
|
margin: 2,
|
||||||
|
color: {
|
||||||
|
dark: fgColor,
|
||||||
|
light: bgColor,
|
||||||
|
},
|
||||||
|
errorCorrectionLevel: level,
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
if (err) {
|
||||||
|
setError("QR코드 생성 실패");
|
||||||
|
} else {
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [value, size, fgColor, bgColor, level]);
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col items-center justify-center text-xs text-red-500">
|
||||||
|
<span>{error}</span>
|
||||||
|
<span className="mt-1 text-gray-400">{value}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <canvas ref={canvasRef} className="max-h-full max-w-full" />;
|
||||||
|
}
|
||||||
|
|
||||||
interface CanvasComponentProps {
|
interface CanvasComponentProps {
|
||||||
component: ComponentConfig;
|
component: ComponentConfig;
|
||||||
|
|
@ -804,6 +938,70 @@ export function CanvasComponent({ component }: CanvasComponentProps) {
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
case "barcode":
|
||||||
|
// 바코드/QR코드 컴포넌트 렌더링
|
||||||
|
const barcodeType = component.barcodeType || "CODE128";
|
||||||
|
const showBarcodeText = component.showBarcodeText !== false;
|
||||||
|
const barcodeColor = component.barcodeColor || "#000000";
|
||||||
|
const barcodeBackground = component.barcodeBackground || "#ffffff";
|
||||||
|
const barcodeMargin = component.barcodeMargin ?? 10;
|
||||||
|
const qrErrorLevel = component.qrErrorCorrectionLevel || "M";
|
||||||
|
|
||||||
|
// 바코드 값 결정 (쿼리 바인딩 또는 고정값)
|
||||||
|
const getBarcodeValue = (): string => {
|
||||||
|
if (component.barcodeFieldName && component.queryId) {
|
||||||
|
const queryResult = getQueryResult(component.queryId);
|
||||||
|
if (queryResult && queryResult.rows && queryResult.rows.length > 0) {
|
||||||
|
const row = queryResult.rows[0];
|
||||||
|
const val = row[component.barcodeFieldName];
|
||||||
|
if (val !== null && val !== undefined) {
|
||||||
|
return String(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return `{${component.barcodeFieldName}}`;
|
||||||
|
}
|
||||||
|
return component.barcodeValue || "SAMPLE123";
|
||||||
|
};
|
||||||
|
|
||||||
|
const barcodeValue = getBarcodeValue();
|
||||||
|
const isQR = barcodeType === "QR";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-full w-full flex-col">
|
||||||
|
<div className="mb-1 flex items-center justify-between text-xs text-gray-500">
|
||||||
|
<span>{isQR ? "QR코드" : `바코드 (${barcodeType})`}</span>
|
||||||
|
{component.barcodeFieldName && component.queryId && (
|
||||||
|
<span className="text-blue-600">● 연결됨</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className="flex flex-1 items-center justify-center overflow-hidden"
|
||||||
|
style={{ backgroundColor: barcodeBackground }}
|
||||||
|
>
|
||||||
|
{isQR ? (
|
||||||
|
<QRCodeRenderer
|
||||||
|
value={barcodeValue}
|
||||||
|
size={Math.min(component.width - 20, component.height - 40)}
|
||||||
|
fgColor={barcodeColor}
|
||||||
|
bgColor={barcodeBackground}
|
||||||
|
level={qrErrorLevel}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<BarcodeRenderer
|
||||||
|
value={barcodeValue}
|
||||||
|
format={barcodeType}
|
||||||
|
width={component.width - 20}
|
||||||
|
height={component.height - 40}
|
||||||
|
displayValue={showBarcodeText}
|
||||||
|
lineColor={barcodeColor}
|
||||||
|
background={barcodeBackground}
|
||||||
|
margin={barcodeMargin}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return <div>알 수 없는 컴포넌트</div>;
|
return <div>알 수 없는 컴포넌트</div>;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useDrag } from "react-dnd";
|
import { useDrag } from "react-dnd";
|
||||||
import { Type, Table, Image, Minus, PenLine, Stamp as StampIcon, Hash, CreditCard, Calculator } from "lucide-react";
|
import { Type, Table, Image, Minus, PenLine, Stamp as StampIcon, Hash, CreditCard, Calculator, Barcode } from "lucide-react";
|
||||||
|
|
||||||
interface ComponentItem {
|
interface ComponentItem {
|
||||||
type: string;
|
type: string;
|
||||||
|
|
@ -19,6 +19,7 @@ const COMPONENTS: ComponentItem[] = [
|
||||||
{ type: "pageNumber", label: "페이지번호", icon: <Hash className="h-4 w-4" /> },
|
{ type: "pageNumber", label: "페이지번호", icon: <Hash className="h-4 w-4" /> },
|
||||||
{ type: "card", label: "정보카드", icon: <CreditCard className="h-4 w-4" /> },
|
{ type: "card", label: "정보카드", icon: <CreditCard className="h-4 w-4" /> },
|
||||||
{ type: "calculation", label: "계산", icon: <Calculator className="h-4 w-4" /> },
|
{ type: "calculation", label: "계산", icon: <Calculator className="h-4 w-4" /> },
|
||||||
|
{ type: "barcode", label: "바코드/QR", icon: <Barcode className="h-4 w-4" /> },
|
||||||
];
|
];
|
||||||
|
|
||||||
function DraggableComponentItem({ type, label, icon }: ComponentItem) {
|
function DraggableComponentItem({ type, label, icon }: ComponentItem) {
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ export function ReportDesignerCanvas() {
|
||||||
} else if (item.componentType === "pageNumber") {
|
} else if (item.componentType === "pageNumber") {
|
||||||
width = 100;
|
width = 100;
|
||||||
height = 30;
|
height = 30;
|
||||||
|
} else if (item.componentType === "barcode") {
|
||||||
|
width = 200;
|
||||||
|
height = 80;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 여백을 px로 변환 (1mm ≈ 3.7795px)
|
// 여백을 px로 변환 (1mm ≈ 3.7795px)
|
||||||
|
|
@ -204,6 +207,17 @@ export function ReportDesignerCanvas() {
|
||||||
showBorder: true,
|
showBorder: true,
|
||||||
rowHeight: 32,
|
rowHeight: 32,
|
||||||
}),
|
}),
|
||||||
|
// 바코드 컴포넌트 전용
|
||||||
|
...(item.componentType === "barcode" && {
|
||||||
|
barcodeType: "CODE128" as const,
|
||||||
|
barcodeValue: "SAMPLE123",
|
||||||
|
barcodeFieldName: "",
|
||||||
|
showBarcodeText: true,
|
||||||
|
barcodeColor: "#000000",
|
||||||
|
barcodeBackground: "#ffffff",
|
||||||
|
barcodeMargin: 10,
|
||||||
|
qrErrorCorrectionLevel: "M" as const,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
addComponent(newComponent);
|
addComponent(newComponent);
|
||||||
|
|
|
||||||
|
|
@ -1631,10 +1631,214 @@ export function ReportDesignerRightPanel() {
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 데이터 바인딩 (텍스트/라벨/테이블 컴포넌트) */}
|
{/* 바코드 컴포넌트 설정 */}
|
||||||
|
{selectedComponent.type === "barcode" && (
|
||||||
|
<Card className="mt-4 border-cyan-200 bg-cyan-50">
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<CardTitle className="text-sm text-cyan-900">바코드 설정</CardTitle>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-3">
|
||||||
|
{/* 바코드 타입 */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">바코드 타입</Label>
|
||||||
|
<Select
|
||||||
|
value={selectedComponent.barcodeType || "CODE128"}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeType: value as "CODE128" | "CODE39" | "EAN13" | "EAN8" | "UPC" | "QR",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="CODE128">CODE128 (범용)</SelectItem>
|
||||||
|
<SelectItem value="CODE39">CODE39 (산업용)</SelectItem>
|
||||||
|
<SelectItem value="EAN13">EAN-13 (상품)</SelectItem>
|
||||||
|
<SelectItem value="EAN8">EAN-8 (소형상품)</SelectItem>
|
||||||
|
<SelectItem value="UPC">UPC (북미상품)</SelectItem>
|
||||||
|
<SelectItem value="QR">QR코드</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 바코드 값 입력 (쿼리 연결 없을 때) */}
|
||||||
|
{!selectedComponent.queryId && (
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">바코드 값</Label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
value={selectedComponent.barcodeValue || ""}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeValue: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
placeholder={
|
||||||
|
selectedComponent.barcodeType === "EAN13" ? "13자리 숫자" :
|
||||||
|
selectedComponent.barcodeType === "EAN8" ? "8자리 숫자" :
|
||||||
|
selectedComponent.barcodeType === "UPC" ? "12자리 숫자" :
|
||||||
|
"바코드에 표시할 값"
|
||||||
|
}
|
||||||
|
className="h-8"
|
||||||
|
/>
|
||||||
|
{(selectedComponent.barcodeType === "EAN13" ||
|
||||||
|
selectedComponent.barcodeType === "EAN8" ||
|
||||||
|
selectedComponent.barcodeType === "UPC") && (
|
||||||
|
<p className="mt-1 text-[10px] text-gray-500">
|
||||||
|
{selectedComponent.barcodeType === "EAN13" && "EAN-13: 12~13자리 숫자 필요"}
|
||||||
|
{selectedComponent.barcodeType === "EAN8" && "EAN-8: 7~8자리 숫자 필요"}
|
||||||
|
{selectedComponent.barcodeType === "UPC" && "UPC: 11~12자리 숫자 필요"}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 쿼리 연결 시 필드 선택 */}
|
||||||
|
{selectedComponent.queryId && (
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">바인딩 필드</Label>
|
||||||
|
<Select
|
||||||
|
value={selectedComponent.barcodeFieldName || "none"}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeFieldName: value === "none" ? "" : value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8">
|
||||||
|
<SelectValue placeholder="필드 선택" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="none">선택 안함</SelectItem>
|
||||||
|
{(() => {
|
||||||
|
const query = queries.find((q) => q.id === selectedComponent.queryId);
|
||||||
|
const result = query ? getQueryResult(query.id) : null;
|
||||||
|
if (result && result.fields) {
|
||||||
|
return result.fields.map((field: string) => (
|
||||||
|
<SelectItem key={field} value={field}>
|
||||||
|
{field}
|
||||||
|
</SelectItem>
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
})()}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 1D 바코드 전용 옵션 */}
|
||||||
|
{selectedComponent.barcodeType !== "QR" && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="showBarcodeText"
|
||||||
|
checked={selectedComponent.showBarcodeText !== false}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
showBarcodeText: e.target.checked,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="h-4 w-4 rounded border-gray-300"
|
||||||
|
/>
|
||||||
|
<Label htmlFor="showBarcodeText" className="text-xs">
|
||||||
|
바코드 아래 텍스트 표시
|
||||||
|
</Label>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* QR 오류 보정 수준 */}
|
||||||
|
{selectedComponent.barcodeType === "QR" && (
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">오류 보정 수준</Label>
|
||||||
|
<Select
|
||||||
|
value={selectedComponent.qrErrorCorrectionLevel || "M"}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
qrErrorCorrectionLevel: value as "L" | "M" | "Q" | "H",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8">
|
||||||
|
<SelectValue />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="L">L (7% 복구)</SelectItem>
|
||||||
|
<SelectItem value="M">M (15% 복구)</SelectItem>
|
||||||
|
<SelectItem value="Q">Q (25% 복구)</SelectItem>
|
||||||
|
<SelectItem value="H">H (30% 복구)</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
<p className="mt-1 text-[10px] text-gray-500">
|
||||||
|
높을수록 손상에 강하지만 크기 증가
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 색상 설정 */}
|
||||||
|
<div className="grid grid-cols-2 gap-2">
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">바코드 색상</Label>
|
||||||
|
<Input
|
||||||
|
type="color"
|
||||||
|
value={selectedComponent.barcodeColor || "#000000"}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeColor: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="h-8 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">배경 색상</Label>
|
||||||
|
<Input
|
||||||
|
type="color"
|
||||||
|
value={selectedComponent.barcodeBackground || "#ffffff"}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeBackground: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
className="h-8 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 여백 */}
|
||||||
|
<div>
|
||||||
|
<Label className="text-xs">여백 (px)</Label>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={selectedComponent.barcodeMargin ?? 10}
|
||||||
|
onChange={(e) =>
|
||||||
|
updateComponent(selectedComponent.id, {
|
||||||
|
barcodeMargin: Number(e.target.value),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
min={0}
|
||||||
|
max={50}
|
||||||
|
className="h-8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* 쿼리 연결 안내 */}
|
||||||
|
{!selectedComponent.queryId && (
|
||||||
|
<div className="rounded border border-cyan-200 bg-cyan-100 p-2 text-xs text-cyan-800">
|
||||||
|
쿼리를 연결하면 데이터베이스 값으로 바코드를 생성할 수 있습니다.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* 데이터 바인딩 (텍스트/라벨/테이블/바코드 컴포넌트) */}
|
||||||
{(selectedComponent.type === "text" ||
|
{(selectedComponent.type === "text" ||
|
||||||
selectedComponent.type === "label" ||
|
selectedComponent.type === "label" ||
|
||||||
selectedComponent.type === "table") && (
|
selectedComponent.type === "table" ||
|
||||||
|
selectedComponent.type === "barcode") && (
|
||||||
<Card className="mt-4 border-blue-200 bg-blue-50">
|
<Card className="mt-4 border-blue-200 bg-blue-50">
|
||||||
<CardHeader className="pb-3">
|
<CardHeader className="pb-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
"@turf/union": "^7.2.0",
|
"@turf/union": "^7.2.0",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/leaflet": "^1.9.21",
|
"@types/leaflet": "^1.9.21",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/react-window": "^1.8.8",
|
"@types/react-window": "^1.8.8",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"@xyflow/react": "^12.8.4",
|
"@xyflow/react": "^12.8.4",
|
||||||
|
|
@ -61,11 +62,13 @@
|
||||||
"html-to-image": "^1.11.13",
|
"html-to-image": "^1.11.13",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"isomorphic-dompurify": "^2.28.0",
|
"isomorphic-dompurify": "^2.28.0",
|
||||||
|
"jsbarcode": "^3.12.1",
|
||||||
"jspdf": "^3.0.3",
|
"jspdf": "^3.0.3",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"mammoth": "^1.11.0",
|
"mammoth": "^1.11.0",
|
||||||
"next": "^15.4.8",
|
"next": "^15.4.8",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-day-picker": "^9.11.1",
|
"react-day-picker": "^9.11.1",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
|
@ -91,6 +94,7 @@
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@tanstack/react-query-devtools": "^5.86.0",
|
"@tanstack/react-query-devtools": "^5.86.0",
|
||||||
|
"@types/jsbarcode": "^3.11.4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19.1.13",
|
"@types/react": "^19.1.13",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.1.9",
|
||||||
|
|
@ -6022,6 +6026,16 @@
|
||||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/jsbarcode": {
|
||||||
|
"version": "3.11.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/jsbarcode/-/jsbarcode-3.11.4.tgz",
|
||||||
|
"integrity": "sha512-VBcpTAnEMH0Gbh8JpV14CgOtJjCYjsvR2FoDRyoYPE0gUxtApf8N4c+HKEOyz/iiIZkMzqrzBA3XX7+KgKxxsA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/json-schema": {
|
"node_modules/@types/json-schema": {
|
||||||
"version": "7.0.15",
|
"version": "7.0.15",
|
||||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
|
||||||
|
|
@ -6071,7 +6085,6 @@
|
||||||
"version": "20.19.24",
|
"version": "20.19.24",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
|
||||||
"integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==",
|
"integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
|
|
@ -6089,6 +6102,15 @@
|
||||||
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
"integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/qrcode": {
|
||||||
|
"version": "1.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz",
|
||||||
|
"integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/raf": {
|
"node_modules/@types/raf": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz",
|
||||||
|
|
@ -6917,11 +6939,19 @@
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
"url": "https://github.com/sponsors/epoberezkin"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/ansi-regex": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||||
|
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ansi-styles": {
|
"node_modules/ansi-styles": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-convert": "^2.0.1"
|
"color-convert": "^2.0.1"
|
||||||
|
|
@ -7387,6 +7417,15 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/camelcase": {
|
||||||
|
"version": "5.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
|
||||||
|
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/camera-controls": {
|
"node_modules/camera-controls": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/camera-controls/-/camera-controls-3.1.0.tgz",
|
||||||
|
|
@ -7529,6 +7568,17 @@
|
||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/cliui": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"strip-ansi": "^6.0.0",
|
||||||
|
"wrap-ansi": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||||
|
|
@ -7580,7 +7630,6 @@
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"color-name": "~1.1.4"
|
"color-name": "~1.1.4"
|
||||||
|
|
@ -7593,7 +7642,6 @@
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/combined-stream": {
|
"node_modules/combined-stream": {
|
||||||
|
|
@ -8292,6 +8340,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/decimal.js": {
|
"node_modules/decimal.js": {
|
||||||
"version": "10.6.0",
|
"version": "10.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
|
||||||
|
|
@ -8420,6 +8477,12 @@
|
||||||
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
"integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/dijkstrajs": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/dingbat-to-unicode": {
|
"node_modules/dingbat-to-unicode": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/dingbat-to-unicode/-/dingbat-to-unicode-1.0.1.tgz",
|
||||||
|
|
@ -9606,6 +9669,15 @@
|
||||||
"quickselect": "^1.0.1"
|
"quickselect": "^1.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/get-caller-file": {
|
||||||
|
"version": "2.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||||
|
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "6.* || 8.* || >= 10.*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/get-intrinsic": {
|
"node_modules/get-intrinsic": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
|
||||||
|
|
@ -10256,6 +10328,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-fullwidth-code-point": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/is-generator-function": {
|
"node_modules/is-generator-function": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz",
|
||||||
|
|
@ -10593,6 +10674,12 @@
|
||||||
"js-yaml": "bin/js-yaml.js"
|
"js-yaml": "bin/js-yaml.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsbarcode": {
|
||||||
|
"version": "3.12.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsbarcode/-/jsbarcode-3.12.1.tgz",
|
||||||
|
"integrity": "sha512-QZQSqIknC2Rr/YOUyOkCBqsoiBAOTYK+7yNN3JsqfoUtJtkazxNw1dmPpxuv7VVvqW13kA3/mKiLq+s/e3o9hQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jsdom": {
|
"node_modules/jsdom": {
|
||||||
"version": "27.1.0",
|
"version": "27.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.1.0.tgz",
|
||||||
|
|
@ -11700,6 +11787,15 @@
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-try": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/pako": {
|
"node_modules/pako": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
|
||||||
|
|
@ -11735,7 +11831,6 @@
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
|
|
@ -11818,6 +11913,15 @@
|
||||||
"pathe": "^2.0.3"
|
"pathe": "^2.0.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pngjs": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/point-in-polygon": {
|
"node_modules/point-in-polygon": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
|
||||||
|
|
@ -12348,6 +12452,23 @@
|
||||||
],
|
],
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/qrcode": {
|
||||||
|
"version": "1.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
|
||||||
|
"integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"dijkstrajs": "^1.0.1",
|
||||||
|
"pngjs": "^5.0.0",
|
||||||
|
"yargs": "^15.3.1"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"qrcode": "bin/qrcode"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/queue-microtask": {
|
"node_modules/queue-microtask": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
||||||
|
|
@ -12873,6 +12994,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-directory": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/require-from-string": {
|
"node_modules/require-from-string": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
|
||||||
|
|
@ -12882,6 +13012,12 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/require-main-filename": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/reselect": {
|
"node_modules/reselect": {
|
||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
|
||||||
|
|
@ -13110,6 +13246,12 @@
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/set-blocking": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
|
|
@ -13451,6 +13593,26 @@
|
||||||
"safe-buffer": "~5.1.0"
|
"safe-buffer": "~5.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/string-width": {
|
||||||
|
"version": "4.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||||
|
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"emoji-regex": "^8.0.0",
|
||||||
|
"is-fullwidth-code-point": "^3.0.0",
|
||||||
|
"strip-ansi": "^6.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string-width/node_modules/emoji-regex": {
|
||||||
|
"version": "8.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||||
|
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/string.prototype.includes": {
|
"node_modules/string.prototype.includes": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
|
||||||
|
|
@ -13564,6 +13726,18 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/strip-ansi": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-regex": "^5.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/strip-bom": {
|
"node_modules/strip-bom": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
|
||||||
|
|
@ -14191,7 +14365,6 @@
|
||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"devOptional": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unrs-resolver": {
|
"node_modules/unrs-resolver": {
|
||||||
|
|
@ -14511,6 +14684,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/which-module": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/which-typed-array": {
|
"node_modules/which-typed-array": {
|
||||||
"version": "1.1.19",
|
"version": "1.1.19",
|
||||||
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
|
||||||
|
|
@ -14561,6 +14740,20 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/wrap-ansi": {
|
||||||
|
"version": "6.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
|
||||||
|
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"string-width": "^4.1.0",
|
||||||
|
"strip-ansi": "^6.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ws": {
|
"node_modules/ws": {
|
||||||
"version": "8.18.3",
|
"version": "8.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
|
||||||
|
|
@ -14675,6 +14868,99 @@
|
||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/y18n": {
|
||||||
|
"version": "4.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/yargs": {
|
||||||
|
"version": "15.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
|
||||||
|
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cliui": "^6.0.0",
|
||||||
|
"decamelize": "^1.2.0",
|
||||||
|
"find-up": "^4.1.0",
|
||||||
|
"get-caller-file": "^2.0.1",
|
||||||
|
"require-directory": "^2.1.1",
|
||||||
|
"require-main-filename": "^2.0.0",
|
||||||
|
"set-blocking": "^2.0.0",
|
||||||
|
"string-width": "^4.2.0",
|
||||||
|
"which-module": "^2.0.0",
|
||||||
|
"y18n": "^4.0.0",
|
||||||
|
"yargs-parser": "^18.1.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs-parser": {
|
||||||
|
"version": "18.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
|
||||||
|
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"camelcase": "^5.0.0",
|
||||||
|
"decamelize": "^1.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/find-up": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"locate-path": "^5.0.0",
|
||||||
|
"path-exists": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/locate-path": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-locate": "^4.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/p-limit": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-try": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/yargs/node_modules/p-locate": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"p-limit": "^2.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yocto-queue": {
|
"node_modules/yocto-queue": {
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@
|
||||||
"@turf/union": "^7.2.0",
|
"@turf/union": "^7.2.0",
|
||||||
"@types/d3": "^7.4.3",
|
"@types/d3": "^7.4.3",
|
||||||
"@types/leaflet": "^1.9.21",
|
"@types/leaflet": "^1.9.21",
|
||||||
|
"@types/qrcode": "^1.5.6",
|
||||||
"@types/react-window": "^1.8.8",
|
"@types/react-window": "^1.8.8",
|
||||||
"@types/three": "^0.180.0",
|
"@types/three": "^0.180.0",
|
||||||
"@xyflow/react": "^12.8.4",
|
"@xyflow/react": "^12.8.4",
|
||||||
|
|
@ -69,11 +70,13 @@
|
||||||
"html-to-image": "^1.11.13",
|
"html-to-image": "^1.11.13",
|
||||||
"html2canvas": "^1.4.1",
|
"html2canvas": "^1.4.1",
|
||||||
"isomorphic-dompurify": "^2.28.0",
|
"isomorphic-dompurify": "^2.28.0",
|
||||||
|
"jsbarcode": "^3.12.1",
|
||||||
"jspdf": "^3.0.3",
|
"jspdf": "^3.0.3",
|
||||||
"leaflet": "^1.9.4",
|
"leaflet": "^1.9.4",
|
||||||
"lucide-react": "^0.525.0",
|
"lucide-react": "^0.525.0",
|
||||||
"mammoth": "^1.11.0",
|
"mammoth": "^1.11.0",
|
||||||
"next": "^15.4.8",
|
"next": "^15.4.8",
|
||||||
|
"qrcode": "^1.5.4",
|
||||||
"react": "19.1.0",
|
"react": "19.1.0",
|
||||||
"react-day-picker": "^9.11.1",
|
"react-day-picker": "^9.11.1",
|
||||||
"react-dnd": "^16.0.1",
|
"react-dnd": "^16.0.1",
|
||||||
|
|
@ -99,6 +102,7 @@
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
"@tanstack/react-query-devtools": "^5.86.0",
|
"@tanstack/react-query-devtools": "^5.86.0",
|
||||||
|
"@types/jsbarcode": "^3.11.4",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "^19.1.13",
|
"@types/react": "^19.1.13",
|
||||||
"@types/react-dom": "^19.1.9",
|
"@types/react-dom": "^19.1.9",
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,15 @@ export interface ComponentConfig {
|
||||||
showCalcBorder?: boolean; // 테두리 표시 여부
|
showCalcBorder?: boolean; // 테두리 표시 여부
|
||||||
numberFormat?: "none" | "comma" | "currency"; // 숫자 포맷 (없음, 천단위, 원화)
|
numberFormat?: "none" | "comma" | "currency"; // 숫자 포맷 (없음, 천단위, 원화)
|
||||||
currencySuffix?: string; // 통화 접미사 (예: "원")
|
currencySuffix?: string; // 통화 접미사 (예: "원")
|
||||||
|
// 바코드 컴포넌트 전용
|
||||||
|
barcodeType?: "CODE128" | "CODE39" | "EAN13" | "EAN8" | "UPC" | "QR"; // 바코드 타입
|
||||||
|
barcodeValue?: string; // 고정값
|
||||||
|
barcodeFieldName?: string; // 쿼리 필드 바인딩
|
||||||
|
showBarcodeText?: boolean; // 바코드 아래 텍스트 표시 (1D만)
|
||||||
|
barcodeColor?: string; // 바코드 색상
|
||||||
|
barcodeBackground?: string; // 배경 색상
|
||||||
|
barcodeMargin?: number; // 여백
|
||||||
|
qrErrorCorrectionLevel?: "L" | "M" | "Q" | "H"; // QR 오류 보정 수준
|
||||||
}
|
}
|
||||||
|
|
||||||
// 리포트 상세
|
// 리포트 상세
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue