feat(WEB): Vercel 배포 대응 및 타입 안정성 개선
- puppeteer → puppeteer-core + @sparticuz/chromium 전환 (Vercel 서버리스 호환) - PDF 생성 API 로컬/Vercel 환경 분기 처리 - next.config.ts: ignoreBuildErrors false로 전환 - WorkOrder items에 orderNodeId/orderNodeName 필드 추가 - 결재선 데이터에 name/dept 필드 추가 - OrderSalesDetailView 타입 캐스팅 안전하게 수정 - vercel.json 설정 추가 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,15 @@ API_KEY=your-secret-api-key-here
|
||||
# 주의: 운영 환경에서는 반드시 false로 설정!
|
||||
NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=false
|
||||
|
||||
# ==============================================
|
||||
# Puppeteer (로컬 PDF 생성용)
|
||||
# ==============================================
|
||||
# puppeteer-core는 Chromium을 번들하지 않으므로 로컬 Chrome 경로 필요
|
||||
# macOS: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
||||
# Linux: /usr/bin/google-chrome-stable
|
||||
# Vercel에서는 @sparticuz/chromium이 자동 처리하므로 설정 불필요
|
||||
PUPPETEER_EXECUTABLE_PATH=/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
|
||||
|
||||
# ==============================================
|
||||
# Development Notes
|
||||
# ==============================================
|
||||
|
||||
81
claudedocs/vercel/vercel-env-setup-guide.md
Normal file
81
claudedocs/vercel/vercel-env-setup-guide.md
Normal file
@@ -0,0 +1,81 @@
|
||||
# Vercel 환경변수 설정 가이드
|
||||
|
||||
## 설정 위치
|
||||
Vercel Dashboard → Project → Settings → Environment Variables
|
||||
|
||||
---
|
||||
|
||||
## 필수 환경변수
|
||||
|
||||
### 서버 전용 (Server-side Only)
|
||||
|
||||
| 변수명 | 설명 | 예시 값 | 환경 |
|
||||
|--------|------|---------|------|
|
||||
| `API_KEY` | 백엔드 API 인증 키 (X-API-KEY 헤더) | `42Jfwc6Ea...` | Production, Preview |
|
||||
| `API_URL` | 백엔드 API 내부 URL (서버 사이드 전용) | `https://api.codebridge-x.com` | Production, Preview |
|
||||
|
||||
> `API_KEY`는 절대 `NEXT_PUBLIC_` 접두사를 붙이지 말 것. 클라이언트에 노출되면 안 됨.
|
||||
|
||||
### 클라이언트 공개 (NEXT_PUBLIC_*)
|
||||
|
||||
| 변수명 | 설명 | Production 값 | Preview 값 |
|
||||
|--------|------|--------------|------------|
|
||||
| `NEXT_PUBLIC_API_URL` | 프론트엔드에서 호출하는 API URL | `https://api.codebridge-x.com` | `https://api.codebridge-x.com` |
|
||||
| `NEXT_PUBLIC_FRONTEND_URL` | 프론트엔드 자체 URL (CORS 용) | `https://dev.codebridge-x.com` | Vercel 자동 할당 URL |
|
||||
| `NEXT_PUBLIC_AUTH_MODE` | 인증 모드 | `sanctum` | `sanctum` |
|
||||
| `NEXT_PUBLIC_GOOGLE_MAPS_API_KEY` | Google Maps API 키 | `AIzaSy...` | `AIzaSy...` |
|
||||
| `NEXT_PUBLIC_DEV_TOOLBAR_ENABLED` | 개발 도구 툴바 | `false` | `true` |
|
||||
|
||||
### 선택 환경변수
|
||||
|
||||
| 변수명 | 설명 | 기본값 |
|
||||
|--------|------|--------|
|
||||
| `NEXT_PUBLIC_API_LOGGING` | API 로깅 활성화 | 미설정 (비활성) |
|
||||
| `NEXT_PUBLIC_APP_VERSION` | 앱 버전 표시 | 미설정 |
|
||||
| `PUPPETEER_EXECUTABLE_PATH` | Chromium 경로 (Vercel에서는 불필요) | 미설정 |
|
||||
|
||||
---
|
||||
|
||||
## 환경별 설정 방법
|
||||
|
||||
### 1. Vercel Dashboard에서 설정
|
||||
|
||||
1. [Vercel Dashboard](https://vercel.com) 접속
|
||||
2. 프로젝트 선택 → **Settings** → **Environment Variables**
|
||||
3. 각 변수 추가 시 적용 환경 선택:
|
||||
- **Production**: 운영 배포에만 적용
|
||||
- **Preview**: PR/브랜치 배포에 적용
|
||||
- **Development**: `vercel dev` 로컬 실행 시 적용
|
||||
|
||||
### 2. 환경별 권장 설정
|
||||
|
||||
#### Production
|
||||
```
|
||||
API_KEY=<실제 운영 API 키>
|
||||
API_URL=https://api.codebridge-x.com
|
||||
NEXT_PUBLIC_API_URL=https://api.codebridge-x.com
|
||||
NEXT_PUBLIC_FRONTEND_URL=https://<운영 도메인>
|
||||
NEXT_PUBLIC_AUTH_MODE=sanctum
|
||||
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=<Google Maps 키>
|
||||
NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=false
|
||||
```
|
||||
|
||||
#### Preview
|
||||
```
|
||||
API_KEY=<개발용 API 키>
|
||||
API_URL=https://api.codebridge-x.com
|
||||
NEXT_PUBLIC_API_URL=https://api.codebridge-x.com
|
||||
NEXT_PUBLIC_FRONTEND_URL=<Vercel Preview URL - 자동>
|
||||
NEXT_PUBLIC_AUTH_MODE=sanctum
|
||||
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=<Google Maps 키>
|
||||
NEXT_PUBLIC_DEV_TOOLBAR_ENABLED=true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 주의사항
|
||||
|
||||
1. **API_KEY 보안**: Sensitive 체크박스를 반드시 활성화하여 Dashboard에서도 값이 마스킹되도록 설정
|
||||
2. **NEXT_PUBLIC_ 접두사**: 이 접두사가 붙은 변수는 클라이언트 번들에 포함되므로 민감 정보 절대 금지
|
||||
3. **VERCEL 환경변수**: `VERCEL=1`은 Vercel이 자동 주입하므로 별도 설정 불필요 (PDF 생성 분기에 사용)
|
||||
4. **NODE_ENV**: Vercel이 자동 설정 (`production` for Production, `development` for Preview)
|
||||
@@ -6,7 +6,7 @@ const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
|
||||
const nextConfig: NextConfig = {
|
||||
reactStrictMode: false, // 🧪 TEST: Strict Mode 비활성화로 중복 요청 테스트
|
||||
turbopack: {}, // ✅ CRITICAL: Next.js 15 + next-intl compatibility
|
||||
serverExternalPackages: ['puppeteer'], // puppeteer는 Node.js 전용 - Webpack 번들 제외
|
||||
serverExternalPackages: ['puppeteer-core', '@sparticuz/chromium'], // Vercel 서버리스 PDF 생성용 - Webpack 번들 제외
|
||||
images: {
|
||||
remotePatterns: [
|
||||
{
|
||||
@@ -21,13 +21,9 @@ const nextConfig: NextConfig = {
|
||||
},
|
||||
},
|
||||
typescript: {
|
||||
// ⚠️ WARNING: This allows production builds to complete even with TypeScript errors
|
||||
// Only use during development. Remove for production deployments.
|
||||
ignoreBuildErrors: true,
|
||||
ignoreBuildErrors: false,
|
||||
},
|
||||
eslint: {
|
||||
// ⚠️ WARNING: Temporarily ignore ESLint during builds for migration
|
||||
// TODO: Fix ESLint errors after migration is complete
|
||||
ignoreDuringBuilds: true,
|
||||
},
|
||||
// Capacitor 패키지는 모바일 앱 전용 - 웹 빌드에서 제외
|
||||
|
||||
305
package-lock.json
generated
305
package-lock.json
generated
@@ -29,6 +29,7 @@
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@sparticuz/chromium": "^143.0.4",
|
||||
"@tiptap/extension-image": "^3.13.0",
|
||||
"@tiptap/extension-link": "^3.13.0",
|
||||
"@tiptap/extension-placeholder": "^3.13.0",
|
||||
@@ -48,7 +49,7 @@
|
||||
"lucide-react": "^0.552.0",
|
||||
"next": "^15.5.9",
|
||||
"next-intl": "^4.4.0",
|
||||
"puppeteer": "^23.11.1",
|
||||
"puppeteer-core": "^24.37.2",
|
||||
"react": "^19.2.3",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "^19.2.3",
|
||||
@@ -87,29 +88,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/code-frame": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz",
|
||||
"integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.28.5",
|
||||
"js-tokens": "^4.0.0",
|
||||
"picocolors": "^1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-validator-identifier": {
|
||||
"version": "7.28.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
||||
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.28.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
|
||||
@@ -1536,18 +1514,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@puppeteer/browsers": {
|
||||
"version": "2.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.6.1.tgz",
|
||||
"integrity": "sha512-aBSREisdsGH890S2rQqK82qmQYU3uFpSH8wcZWHgHzl3LfzsxAKbLNiAG9mO8v1Y0UICBeClICxPJvyr0rcuxg==",
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.12.0.tgz",
|
||||
"integrity": "sha512-Xuq42yxcQJ54ti8ZHNzF5snFvtpgXzNToJ1bXUGQRaiO8t+B6UM8sTUJfvV+AJnqtkJU/7hdy6nbKyA12aHtRw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "^4.4.0",
|
||||
"debug": "^4.4.3",
|
||||
"extract-zip": "^2.0.1",
|
||||
"progress": "^2.0.3",
|
||||
"proxy-agent": "^6.5.0",
|
||||
"semver": "^7.6.3",
|
||||
"tar-fs": "^3.0.6",
|
||||
"unbzip2-stream": "^1.4.3",
|
||||
"semver": "^7.7.3",
|
||||
"tar-fs": "^3.1.1",
|
||||
"yargs": "^17.7.2"
|
||||
},
|
||||
"bin": {
|
||||
@@ -2862,6 +2839,19 @@
|
||||
"integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@sparticuz/chromium": {
|
||||
"version": "143.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@sparticuz/chromium/-/chromium-143.0.4.tgz",
|
||||
"integrity": "sha512-/6I7uQTRhRDD2/gGPQ1Gkf+Dqk0RYDACPJDZfSzz0OWk4JmUTonNHPXbrn6UIklOHlnDLf8xAAzkOZKB/cJpLA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.11",
|
||||
"tar-fs": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@standard-schema/spec": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
|
||||
@@ -4977,26 +4967,6 @@
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/basic-ftp": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.1.0.tgz",
|
||||
@@ -5029,30 +4999,6 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
|
||||
"integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.1.13"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-crc32": {
|
||||
"version": "0.2.13",
|
||||
"resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
||||
@@ -5116,6 +5062,7 @@
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -5192,22 +5139,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/chromium-bidi": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-0.11.0.tgz",
|
||||
"integrity": "sha512-6CJWHkNRoyZyjV9Rwv2lYONZf1Xm0IuDyNq97nwSsxxP3wf5Bwy15K5rOvVKMtJ127jJBmxFUanSAOjgFRxgrA==",
|
||||
"version": "13.1.1",
|
||||
"resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-13.1.1.tgz",
|
||||
"integrity": "sha512-zB9MpoPd7VJwjowQqiW3FKOvQwffFMjQ8Iejp5ZW+sJaKLRhZX1sTxzl3Zt22TDB4zP0OOqs8lRoY7eAW5geyQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"mitt": "3.0.1",
|
||||
"zod": "3.23.8"
|
||||
"mitt": "^3.0.1",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"devtools-protocol": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/chromium-bidi/node_modules/zod": {
|
||||
"version": "3.23.8",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
|
||||
"integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
|
||||
"version": "3.25.76",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
|
||||
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
@@ -5316,32 +5263,6 @@
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
|
||||
"integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"env-paths": "^2.2.1",
|
||||
"import-fresh": "^3.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"parse-json": "^5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/d-fischer"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.9.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/crc-32": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
|
||||
@@ -5700,9 +5621,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/devtools-protocol": {
|
||||
"version": "0.0.1367902",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1367902.tgz",
|
||||
"integrity": "sha512-XxtPuC3PGakY6PD7dG66/o8KwJ/LkH2/EKe19Dcw58w53dv4/vSQEkn/SzuyhHE2q4zPgCkxQBxus3VV4ql+Pg==",
|
||||
"version": "0.0.1566079",
|
||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1566079.tgz",
|
||||
"integrity": "sha512-MJfAEA1UfVhSs7fbSQOG4czavUp1ajfg6prlAN0+cmfa2zNjaIbvq8VneP7do1WAQQIvgNJWSMeP6UyI90gIlQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/doctrine": {
|
||||
@@ -5791,24 +5712,6 @@
|
||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/env-paths": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
|
||||
"integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
|
||||
"integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.24.1",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
|
||||
@@ -6674,6 +6577,26 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.11",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
|
||||
"integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"debug": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/for-each": {
|
||||
"version": "0.3.5",
|
||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||
@@ -7078,26 +7001,6 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
|
||||
@@ -7122,6 +7025,7 @@
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
@@ -7213,12 +7117,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-async-function": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
|
||||
@@ -7655,12 +7553,14 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
|
||||
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"argparse": "^2.0.1"
|
||||
@@ -7676,12 +7576,6 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
@@ -8047,12 +7941,6 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/lines-and-columns": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/linkify-it": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||
@@ -8702,6 +8590,7 @@
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
@@ -8710,24 +8599,6 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
@@ -9150,40 +9021,19 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer": {
|
||||
"version": "23.11.1",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-23.11.1.tgz",
|
||||
"integrity": "sha512-53uIX3KR5en8l7Vd8n5DUv90Ae9QDQsyIthaUFVzwV6yU750RjqRznEtNMBT20VthqAdemnJN+hxVdmMHKt7Zw==",
|
||||
"deprecated": "< 24.15.0 is no longer supported",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "2.6.1",
|
||||
"chromium-bidi": "0.11.0",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
"devtools-protocol": "0.0.1367902",
|
||||
"puppeteer-core": "23.11.1",
|
||||
"typed-query-selector": "^2.12.0"
|
||||
},
|
||||
"bin": {
|
||||
"puppeteer": "lib/cjs/puppeteer/node/cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/puppeteer-core": {
|
||||
"version": "23.11.1",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-23.11.1.tgz",
|
||||
"integrity": "sha512-3HZ2/7hdDKZvZQ7dhhITOUg4/wOrDRjyK2ZBllRB0ZCOi9u0cwq1ACHDjBB+nX+7+kltHjQvBRdeY7+W0T+7Gg==",
|
||||
"version": "24.37.2",
|
||||
"resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-24.37.2.tgz",
|
||||
"integrity": "sha512-nN8qwE3TGF2vA/+xemPxbesntTuqD9vCGOiZL2uh8HES3pPzLX20MyQjB42dH2rhQ3W3TljZ4ZaKZ0yX/abQuw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@puppeteer/browsers": "2.6.1",
|
||||
"chromium-bidi": "0.11.0",
|
||||
"debug": "^4.4.0",
|
||||
"devtools-protocol": "0.0.1367902",
|
||||
"@puppeteer/browsers": "2.12.0",
|
||||
"chromium-bidi": "13.1.1",
|
||||
"debug": "^4.4.3",
|
||||
"devtools-protocol": "0.0.1566079",
|
||||
"typed-query-selector": "^2.12.0",
|
||||
"ws": "^8.18.0"
|
||||
"webdriver-bidi-protocol": "0.4.0",
|
||||
"ws": "^8.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -9523,6 +9373,7 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
@@ -10277,12 +10128,6 @@
|
||||
"utrie": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/through": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
|
||||
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tiny-invariant": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
|
||||
@@ -10517,16 +10362,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/unbzip2-stream": {
|
||||
"version": "1.4.3",
|
||||
"resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz",
|
||||
"integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer": "^5.2.1",
|
||||
"through": "^2.3.8"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
@@ -10695,6 +10530,12 @@
|
||||
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/webdriver-bidi-protocol": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-0.4.0.tgz",
|
||||
"integrity": "sha512-U9VIlNRrq94d1xxR9JrCEAx5Gv/2W7ERSv8oWRoNe/QYbfccS0V3h/H6qeNeCRJxXGMhhnkqvwNrvPAYeuP9VA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"@radix-ui/react-switch": "^1.2.6",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tooltip": "^1.2.8",
|
||||
"@sparticuz/chromium": "^143.0.4",
|
||||
"@tiptap/extension-image": "^3.13.0",
|
||||
"@tiptap/extension-link": "^3.13.0",
|
||||
"@tiptap/extension-placeholder": "^3.13.0",
|
||||
@@ -54,7 +55,7 @@
|
||||
"lucide-react": "^0.552.0",
|
||||
"next": "^15.5.9",
|
||||
"next-intl": "^4.4.0",
|
||||
"puppeteer": "^23.11.1",
|
||||
"puppeteer-core": "^24.37.2",
|
||||
"react": "^19.2.3",
|
||||
"react-day-picker": "^9.11.1",
|
||||
"react-dom": "^19.2.3",
|
||||
|
||||
@@ -210,9 +210,9 @@ const createQmsMockWorkOrder = (subType?: string): WorkOrder => ({
|
||||
shutterCount: 5,
|
||||
department: '생산부',
|
||||
items: [
|
||||
{ id: '1', no: 1, status: 'completed', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '3000×2500', quantity: 2, unit: 'EA' },
|
||||
{ id: '2', no: 2, status: 'in_progress', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '3000×2500', quantity: 3, unit: 'EA' },
|
||||
{ id: '3', no: 3, status: 'waiting', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12000×4500', quantity: 1, unit: 'EA' },
|
||||
{ id: '1', no: 1, status: 'completed', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '3000×2500', quantity: 2, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '2', no: 2, status: 'in_progress', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '3000×2500', quantity: 3, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '3', no: 3, status: 'waiting', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12000×4500', quantity: 1, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
],
|
||||
currentStep: 2,
|
||||
issues: [],
|
||||
@@ -314,7 +314,7 @@ export const InspectionModalV2 = ({
|
||||
// 저장된 측정값을 initialValues로 변환
|
||||
const docData = result.resolveData?.document?.data;
|
||||
if (docData && docData.length > 0) {
|
||||
const values = parseSavedDataToInitialValues(tmpl, docData);
|
||||
const values = parseSavedDataToInitialValues(tmpl, docData.map((d: { field_key: string; field_value?: string | null }) => ({ field_key: d.field_key, field_value: d.field_value ?? null })));
|
||||
setImportInitialValues(values);
|
||||
} else {
|
||||
setImportInitialValues(undefined);
|
||||
|
||||
@@ -13,6 +13,7 @@ export const MOCK_WORK_ORDER: WorkOrder = {
|
||||
projectName: '강남 아파트 단지',
|
||||
assignees: ['김작업', '이생산'],
|
||||
quantity: 5,
|
||||
shutterCount: 3,
|
||||
dueDate: '2024-10-05',
|
||||
priority: 1,
|
||||
status: 'inProgress',
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import puppeteer from 'puppeteer';
|
||||
import puppeteer from 'puppeteer-core';
|
||||
import chromium from '@sparticuz/chromium';
|
||||
|
||||
/**
|
||||
* PDF 생성 API
|
||||
@@ -35,17 +36,20 @@ export async function POST(request: NextRequest) {
|
||||
);
|
||||
}
|
||||
|
||||
// Puppeteer 브라우저 실행 (Docker Alpine에서는 시스템 Chromium 사용)
|
||||
// 로컬 개발 vs Vercel 환경 분기
|
||||
const isVercel = process.env.VERCEL === '1';
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
executablePath: process.env.PUPPETEER_EXECUTABLE_PATH || undefined,
|
||||
args: [
|
||||
args: isVercel ? chromium.args : [
|
||||
'--no-sandbox',
|
||||
'--disable-setuid-sandbox',
|
||||
'--disable-dev-shm-usage',
|
||||
'--disable-gpu',
|
||||
'--disable-software-rasterizer',
|
||||
],
|
||||
executablePath: isVercel
|
||||
? await chromium.executablePath()
|
||||
: process.env.PUPPETEER_EXECUTABLE_PATH || '/usr/bin/google-chrome-stable',
|
||||
headless: true,
|
||||
});
|
||||
|
||||
const page = await browser.newPage();
|
||||
|
||||
@@ -1325,10 +1325,10 @@ export async function getInspectionTemplate(params: {
|
||||
},
|
||||
// 결재선 데이터
|
||||
approvalLines: [
|
||||
{ id: 1, role: '작성', sortOrder: 1 },
|
||||
{ id: 2, role: '검토', sortOrder: 2 },
|
||||
{ id: 3, role: '승인', sortOrder: 3 },
|
||||
{ id: 4, role: '승인', sortOrder: 4 },
|
||||
{ id: 1, name: '', dept: '', role: '작성', sortOrder: 1 },
|
||||
{ id: 2, name: '', dept: '', role: '검토', sortOrder: 2 },
|
||||
{ id: 3, name: '', dept: '', role: '승인', sortOrder: 3 },
|
||||
{ id: 4, name: '', dept: '', role: '승인', sortOrder: 4 },
|
||||
],
|
||||
},
|
||||
inspectionItems: [
|
||||
|
||||
@@ -133,16 +133,16 @@ function OrderNodeCard({ node, depth = 0 }: { node: OrderNode; depth?: number })
|
||||
)}
|
||||
<MapPin className="h-4 w-4 text-blue-500" />
|
||||
<span className="font-semibold text-sm">{node.name}</span>
|
||||
{options.product_name && (
|
||||
{options.product_name ? (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
({options.product_name as string})
|
||||
({String(options.product_name)})
|
||||
</span>
|
||||
)}
|
||||
{(options.open_width || options.open_height) && (
|
||||
) : null}
|
||||
{(options.open_width || options.open_height) ? (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{options.open_width as string}x{options.open_height as string}mm
|
||||
{String(options.open_width ?? '')}x{String(options.open_height ?? '')}mm
|
||||
</span>
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<BadgeSm className={statusConfig.className}>
|
||||
|
||||
@@ -231,6 +231,8 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
|
||||
specification: '8,260 X 8,350 mm',
|
||||
quantity: 500,
|
||||
unit: 'm',
|
||||
orderNodeId: null,
|
||||
orderNodeName: '',
|
||||
},
|
||||
{
|
||||
id: 'mock-2',
|
||||
@@ -241,6 +243,8 @@ export function WorkOrderDetail({ orderId }: WorkOrderDetailProps) {
|
||||
specification: '1,200 X 2,400 mm',
|
||||
quantity: 100,
|
||||
unit: 'EA',
|
||||
orderNodeId: null,
|
||||
orderNodeName: '',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
@@ -164,6 +164,8 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
|
||||
specification: '8,260 X 8,350 mm',
|
||||
quantity: 500,
|
||||
unit: 'm',
|
||||
orderNodeId: null,
|
||||
orderNodeName: '',
|
||||
},
|
||||
{
|
||||
id: 'mock-2',
|
||||
@@ -174,6 +176,8 @@ export function WorkOrderEdit({ orderId }: WorkOrderEditProps) {
|
||||
specification: '1,200 X 2,400 mm',
|
||||
quantity: 100,
|
||||
unit: 'EA',
|
||||
orderNodeId: null,
|
||||
orderNodeName: '',
|
||||
},
|
||||
]);
|
||||
} else {
|
||||
|
||||
@@ -96,9 +96,9 @@ export function InspectionReportModal({
|
||||
shutterCount: 12,
|
||||
department: '생산부',
|
||||
items: [
|
||||
{ id: '1', no: 1, status: 'in_progress', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '8,260 X 8,350', quantity: 2, unit: 'EA' },
|
||||
{ id: '2', no: 2, status: 'waiting', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '6,400 X 5,200', quantity: 4, unit: 'EA' },
|
||||
{ id: '3', no: 3, status: 'completed', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12,000 X 4,500', quantity: 1, unit: 'EA' },
|
||||
{ id: '1', no: 1, status: 'in_progress', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '8,260 X 8,350', quantity: 2, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '2', no: 2, status: 'waiting', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '6,400 X 5,200', quantity: 4, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '3', no: 3, status: 'completed', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12,000 X 4,500', quantity: 1, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
],
|
||||
currentStep: 2,
|
||||
issues: [],
|
||||
|
||||
@@ -60,9 +60,9 @@ export function WorkLogModal({ open, onOpenChange, workOrderId, processType }: W
|
||||
shutterCount: 12,
|
||||
department: '생산부',
|
||||
items: [
|
||||
{ id: '1', no: 1, status: 'in_progress', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '8,260 X 8,350', quantity: 2, unit: 'EA' },
|
||||
{ id: '2', no: 2, status: 'waiting', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '6,400 X 5,200', quantity: 4, unit: 'EA' },
|
||||
{ id: '3', no: 3, status: 'completed', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12,000 X 4,500', quantity: 1, unit: 'EA' },
|
||||
{ id: '1', no: 1, status: 'in_progress', productName: '와이어 스크린', floorCode: '1층/FSS-01', specification: '8,260 X 8,350', quantity: 2, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '2', no: 2, status: 'waiting', productName: '메쉬 스크린', floorCode: '2층/FSS-03', specification: '6,400 X 5,200', quantity: 4, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
{ id: '3', no: 3, status: 'completed', productName: '광폭 와이어', floorCode: '3층/FSS-05', specification: '12,000 X 4,500', quantity: 1, unit: 'EA', orderNodeId: null, orderNodeName: '' },
|
||||
],
|
||||
currentStep: 2,
|
||||
issues: [],
|
||||
|
||||
@@ -663,6 +663,7 @@ export default function WorkerScreen() {
|
||||
projectName: '-',
|
||||
assignees: [],
|
||||
quantity: mockItem.quantity,
|
||||
shutterCount: 0,
|
||||
dueDate: '',
|
||||
priority: 5,
|
||||
status: 'waiting',
|
||||
@@ -789,6 +790,7 @@ export default function WorkerScreen() {
|
||||
projectName: '-',
|
||||
assignees: [],
|
||||
quantity: mockItem.quantity,
|
||||
shutterCount: 0,
|
||||
dueDate: '',
|
||||
priority: 5,
|
||||
status: 'waiting',
|
||||
@@ -814,6 +816,7 @@ export default function WorkerScreen() {
|
||||
projectName: '-',
|
||||
assignees: [],
|
||||
quantity: item.quantity,
|
||||
shutterCount: 0,
|
||||
dueDate: '',
|
||||
priority: 5,
|
||||
status: 'waiting',
|
||||
|
||||
@@ -154,9 +154,10 @@ export function LocationDetailPanel({
|
||||
|
||||
Object.entries(subtotals).forEach(([key, value]) => {
|
||||
if (typeof value === "object" && value !== null) {
|
||||
const obj = value as { name?: string };
|
||||
tabs.push({
|
||||
value: key,
|
||||
label: value.name || key,
|
||||
label: obj.name || key,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import type { QuoteFormDataV2 } from './QuoteRegistrationV2';
|
||||
import type { BomCalculationResultItem } from './types';
|
||||
|
||||
// 양식 타입
|
||||
type TemplateType = 'vendor' | 'calculation';
|
||||
@@ -327,7 +328,7 @@ export function QuotePreviewContent({
|
||||
|
||||
{/* BOM 품목 상세 */}
|
||||
{bomItems.length > 0 ? (
|
||||
bomItems.map((item, itemIndex) => (
|
||||
bomItems.map((item: BomCalculationResultItem, itemIndex: number) => (
|
||||
<tr key={`${loc.id}-${itemIndex}`} className="border-b border-gray-200">
|
||||
<td className="border-r border-gray-300 px-2 py-1 text-center text-gray-500">
|
||||
{itemIndex === 0 ? locationSymbol : ''}
|
||||
|
||||
@@ -108,10 +108,11 @@ export function QuoteSummaryPanel({
|
||||
unitPrice: item.unit_price || 0,
|
||||
totalPrice: item.total_price || 0,
|
||||
}));
|
||||
const obj = value as { name?: string; count?: number; subtotal?: number };
|
||||
result.push({
|
||||
label: value.name || key,
|
||||
count: value.count || 0,
|
||||
amount: value.subtotal || 0,
|
||||
label: obj.name || key,
|
||||
count: obj.count || 0,
|
||||
amount: obj.subtotal || 0,
|
||||
items: groupItems,
|
||||
});
|
||||
} else if (typeof value === "number") {
|
||||
|
||||
@@ -32,6 +32,7 @@ import type {
|
||||
BomCalculationResultItem,
|
||||
BomCalculationResult,
|
||||
} from './types';
|
||||
export type { BomCalculationResult, BomCalculationResultItem };
|
||||
import { transformApiToFrontend, transformFrontendToApi } from './types';
|
||||
|
||||
// ===== 페이지네이션 타입 =====
|
||||
|
||||
9
vercel.json
Normal file
9
vercel.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"regions": ["icn1"],
|
||||
"functions": {
|
||||
"src/app/api/pdf/generate/route.ts": {
|
||||
"memory": 1024,
|
||||
"maxDuration": 30
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user