Compare commits

..

No commits in common. "156e983454782791a89f4c239b56298b1f16585e" and "574de129081f62c78723495774a0a8770fad1c88" have entirely different histories.

9 changed files with 36 additions and 122 deletions

49
package-lock.json generated
View File

@ -22,7 +22,6 @@
"mysql2": "^3.14.1",
"next": "15.2.4",
"nodemailer": "^7.0.3",
"pdf-lib": "^1.17.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-to-pdf": "^2.0.0",
@ -1556,24 +1555,6 @@
"node": ">=0.10"
}
},
"node_modules/@pdf-lib/standard-fonts": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pdf-lib/standard-fonts/-/standard-fonts-1.0.0.tgz",
"integrity": "sha512-hU30BK9IUN/su0Mn9VdlVKsWBS6GyhVfqjwl1FjZN4TxP6cCw0jP2w7V3Hf5uX7M0AZJ16vey9yE0ny7Sa59ZA==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.6"
}
},
"node_modules/@pdf-lib/upng": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@pdf-lib/upng/-/upng-1.0.1.tgz",
"integrity": "sha512-dQK2FUMQtowVP00mtIksrlZhdFXQZPC+taih1q4CvPZ5vqdxR/LKBaFg0oAfzd1GlHZXXSPdQfzQnt+ViGvEIQ==",
"license": "MIT",
"dependencies": {
"pako": "^1.0.10"
}
},
"node_modules/@prisma/client": {
"version": "6.7.0",
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.7.0.tgz",
@ -2662,9 +2643,9 @@
"license": "MIT"
},
"node_modules/debug": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
"integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@ -4139,24 +4120,6 @@
"node": ">=8"
}
},
"node_modules/pdf-lib": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/pdf-lib/-/pdf-lib-1.17.1.tgz",
"integrity": "sha512-V/mpyJAoTsN4cnP31vc0wfNA1+p20evqqnap0KLoRUN0Yk/p3wN52DOEsL4oBFcLdb76hlpKPtzJIgo67j/XLw==",
"license": "MIT",
"dependencies": {
"@pdf-lib/standard-fonts": "^1.0.0",
"@pdf-lib/upng": "^1.0.1",
"pako": "^1.0.11",
"tslib": "^1.11.1"
}
},
"node_modules/pdf-lib/node_modules/tslib": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
"license": "0BSD"
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@ -4450,9 +4413,9 @@
"license": "MIT"
},
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
"license": "ISC",
"bin": {
"semver": "bin/semver.js"

View File

@ -29,7 +29,6 @@
"mysql2": "^3.14.1",
"next": "15.2.4",
"nodemailer": "^7.0.3",
"pdf-lib": "^1.17.1",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-to-pdf": "^2.0.0",

View File

@ -56,7 +56,6 @@ export async function POST(request: Request) {
session.groupId = result.data.data.groupId
session.storeLvl = result.data.data.storeLvl
session.custCd = result.data.data.custCd
session.builderId = result.data.data.builderId
session.builderNo = result.data.data.builderNo
session.builderNm = result.data.data.builderNm
session.isLoggedIn = true
@ -105,7 +104,6 @@ export async function POST(request: Request) {
GROUP_ID: result.data.data.groupId,
STORE_LVL: result.data.data.storeLvl,
CUST_CD: result.data.data.custCd,
BUILDER_ID: result.data.data.builderId,
BUILDER_NO: result.data.data.builderNo,
BUILDER_NM: result.data.data.builderNm,
IS_LOGGED_IN: true,

View File

@ -84,7 +84,6 @@ export async function POST(request: Request) {
session.groupId = null
session.storeLvl = null
session.custCd = null
session.builderId = data[0].user_seko_id
session.builderNo = data[0].user_seko_id
session.builderNm = data[0].supplier_name
session.isLoggedIn = true
@ -124,7 +123,6 @@ export async function POST(request: Request) {
GROUP_ID: null,
STORE_LVL: null,
CUST_CD: null,
BUILDER_ID: data[0].user_seko_id,
BUILDER_NO: data[0].user_seko_id,
BUILDER_NM: data[0].supplier_name,
IS_LOGGED_IN: true,

View File

@ -1,7 +1,6 @@
import React from 'react'
import { NextRequest, NextResponse } from 'next/server'
import { pdf, Document } from '@react-pdf/renderer'
import { PDFDocument } from 'pdf-lib'
import { prisma } from '@/libs/prisma'
import { type Suitable } from '@/types/Suitable'
import SuitablePdf from '@/components/pdf/SuitablePdf'
@ -66,32 +65,14 @@ export async function POST(request: NextRequest) {
// 데이터 조회
const suitable: Suitable[] = await prisma.$queryRawUnsafe(query)
// pdf 생성 : mainId 100개씩 청크로 나누기
const CHUNK_SIZE = 100
const pdfBuffers: Uint8Array[] = []
for (let i = 0; i < suitable.length; i += CHUNK_SIZE) {
const chunk = suitable.slice(i, i + CHUNK_SIZE)
const content = React.createElement(
Document,
null,
React.createElement(SuitablePdf, {
data: chunk,
fileTitle: fileTitle,
firstPage: i === 0,
}),
)
const pdfBlob = await pdf(content).toBlob()
const arrayBuffer = await pdfBlob.arrayBuffer()
pdfBuffers.push(new Uint8Array(arrayBuffer))
}
// 모든 PDF 버퍼 병합
const mergedPdfBytes = await mergePdfBuffers(pdfBuffers)
// pdf 생성
const content = React.createElement(Document, null, React.createElement(SuitablePdf, { data: suitable, fileTitle: fileTitle }))
const pdfBlob = await pdf(content).toBlob()
const fileName = `${fileTitle.replace(' ', '_')}.pdf`
const encodedFileName = encodeURIComponent(fileName)
return new NextResponse(Buffer.from(mergedPdfBytes), {
return new Response(pdfBlob, {
headers: {
'Content-Type': 'application/pdf',
'Content-Disposition': `attachment; filename="suitable.pdf"; filename*=UTF-8''${encodedFileName}`,
@ -102,16 +83,3 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 })
}
}
async function mergePdfBuffers(buffers: Uint8Array[]) {
const mergedPdf = await PDFDocument.create()
for (const buf of buffers) {
const pdf = await PDFDocument.load(buf)
const copiedPages = await mergedPdf.copyPages(pdf, pdf.getPageIndices())
copiedPages.forEach((page) => mergedPdf.addPage(page))
}
const mergedPdfBytes = await mergedPdf.save()
return mergedPdfBytes
}

View File

@ -63,42 +63,35 @@ const formatDateString = () => {
return `${year}${month}${day}${hours}:${minutes.toString().padStart(2, '0')}`
}
export default function SuitablePdf({ data, fileTitle, firstPage }: { data: Suitable[]; fileTitle: string; firstPage: boolean }) {
export default function SuitablePdf({ data, fileTitle }: { data: Suitable[]; fileTitle: string }) {
const createTime = formatDateString()
return (
<Page size="A4" orientation="landscape" style={styles.page}>
{/* Intro Section */}
{firstPage && (
<View>
<View>
<Text style={styles.text}></Text>
<Text style={styles.text}>{fileTitle}</Text>
<Text style={styles.text}>{createTime}</Text>
</View>
<View>
<Text style={styles.text}>使</Text>
<Text style={styles.text}></Text>
<Text style={styles.text}>
</Text>
<Text style={styles.text}></Text>
</View>
</View>
)}
<View>
<Text style={[styles.text]}></Text>
<Text style={[styles.text]}>{fileTitle}</Text>
<Text style={[styles.text]}>{createTime}</Text>
</View>
<View>
<Text style={[styles.text]}>使</Text>
<Text style={[styles.text]}></Text>
<Text style={[styles.text]}>
</Text>
<Text style={[styles.text]}></Text>
</View>
<View>
{/* Cards Section */}
{data?.map((item: Suitable) => (
<View key={item.id}>
{/* Table Title */}
<View>
<Text style={styles.text}>{item.productName}</Text>
<Text style={styles.text}>{item.manuFtCd}</Text>
<Text style={styles.text}>{item.roofMtCd}</Text>
</View>
<Text style={styles.text}>{item.productName}</Text>
<Text style={styles.text}>{item.manuFtCd}</Text>
<Text style={styles.text}>{item.roofMtCd}</Text>
{/* Table */}
<View style={styles.table}>
{/* Table Header */}
<View style={styles.tableRow}>
@ -109,16 +102,14 @@ export default function SuitablePdf({ data, fileTitle, firstPage }: { data: Suit
</View>
{/* Table Body */}
<View>
{JSON.parse(item.detail)?.map((subItem: SuitableDetail) => (
<View key={subItem.id} style={styles.tableRow}>
<Text style={[styles.tableCol, styles.text]}>{item.roofShCd}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.trestleMfpcCd}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.trestleManufacturerProductName}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.memo}</Text>
</View>
))}
</View>
{JSON.parse(item.detail)?.map((subItem: SuitableDetail) => (
<View key={subItem.id} style={styles.tableRow}>
<Text style={[styles.tableCol, styles.text]}>{item.roofShCd}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.trestleMfpcCd}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.trestleManufacturerProductName}</Text>
<Text style={[styles.tableCol, styles.text]}>{subItem.memo}</Text>
</View>
))}
</View>
</View>
))}

View File

@ -44,7 +44,6 @@ export const defaultSession: SessionData = {
groupId: null,
storeLvl: null,
custCd: null,
builderId: null,
builderNo: null,
builderNm: null,
isLoggedIn: false,

View File

@ -40,7 +40,6 @@ const initialState: InitialState = {
groupId: null,
storeLvl: null,
custCd: null,
builderId: null,
builderNo: null,
builderNm: null,
isLoggedIn: false,

View File

@ -26,7 +26,6 @@ export interface SessionData {
groupId: null
storeLvl: null
custCd: null
builderId: null
builderNo: null
builderNm: null | string
isLoggedIn: boolean