Compare commits

..

No commits in common. "feature/module-group" and "main" have entirely different histories.

186 changed files with 6735 additions and 20382 deletions

View File

@ -1,36 +1,11 @@
NEXT_PUBLIC_RUN_MODE="development"
NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="https://dev.hanasys.jp"
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"
#logging
NEXT_PUBLIC_ENABLE_LOGGING=true
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"

View File

@ -1,32 +0,0 @@
NEXT_PUBLIC_RUN_MODE="local.dev"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="http://1.248.227.176:5000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"

View File

@ -1,36 +0,0 @@
NEXT_PUBLIC_RUN_MODE="local"
NEXT_PUBLIC_API_SERVER_PATH="https://dev-api.hanasys.jp"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="http://localhost:3000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"
# 테스트용
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="https://interplug.s3.ap-northeast-2.amazonaws.com"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="dev"
#logging
NEXT_PUBLIC_ENABLE_LOGGING=false

View File

@ -1,35 +1,13 @@
NEXT_PUBLIC_RUN_MODE="production"
NEXT_PUBLIC_API_SERVER_PATH="https://api.hanasys.jp/"
NEXT_PUBLIC_HOST_URL="//1.248.227.176:4000"
NEXT_PUBLIC_API_HOST_URL="https://www.hanasys.jp"
NEXT_PUBLIC_HOST_URL="http://1.248.227.176:4000"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"
# NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_CONVERTER_DWG_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_CONVERTER_DXF_API_URL="https://v2.convertapi.com/convert/dxf/to/png?Secret=secret_a0FLEK6M2oTpXInK"
NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_yAS4QDalL9jgQ7vS"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"
# AWS_REGION="ap-northeast-2"
# AMPLIFY_BUCKET="interplug"
# AWS_ACCESS_KEY_ID="AKIAVWMWJCUXFHEAZ4FR"
# AWS_SECRET_ACCESS_KEY="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"
# NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
# 실제 일본 서버
AWS_REGION="ap-northeast-1"
AMPLIFY_BUCKET="files.hanasys.jp"
AWS_ACCESS_KEY_ID="AKIA3K4QWLZHFZRJOM2E"
AWS_SECRET_ACCESS_KEY="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"
NEXT_PUBLIC_AWS_S3_BASE_URL="//files.hanasys.jp"
S3_PROFILE="prd"
#logging
NEXT_PUBLIC_ENABLE_LOGGING=false
# NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="https://q-order.q-cells.jp/eos/login/autoLogin"
# NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="https://q-musubi.q-cells.jp/qm/login/autoLogin"
NEXT_PUBLIC_Q_ORDER_AUTO_LOGIN_URL="http://q-order-stg.q-cells.jp:8120/eos/login/autoLogin"
NEXT_PUBLIC_Q_MUSUBI_AUTO_LOGIN_URL="http://q-musubi-stg.q-cells.jp:8120/qm/login/autoLogin"

View File

@ -1,3 +0,0 @@
[일감번호] : [제목]
[작업내용] :

View File

@ -1,6 +1,6 @@
This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
### Getting Started
## Getting Started
First, run the development server:
@ -34,5 +34,3 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
deploy test

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'qcast-front-development',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5010,
},
},
],
}

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'qcast-front-local-development',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5000,
},
},
],
}

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'qcast-front-production',
script: 'node_modules/next/dist/bin/next',
instances: 2,
exec_mode: 'cluster',
env: {
NODE_ENV: 'production',
},
},
],
}

View File

@ -3,34 +3,27 @@
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "env-cmd -f .env.localhost next dev",
"local:dev": "env-cmd -f .env.local.dev next dev",
"build": "env-cmd -f .env.production next build",
"build:dev": "env-cmd -f .env.development next build",
"build:local.dev": "env-cmd -f .env.local.dev next build",
"start:cluster1": "env-cmd -f .env.production next start -p 5000",
"start:cluster2": "env-cmd -f .env.production next start -p 5001",
"start:dev": "env-cmd -f .env.development next start -p 5010",
"dev": "next dev",
"build": "next build",
"start": "next start -p 5000",
"start:dev": "next start -p 5010",
"lint": "next lint",
"serve": "node server.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.772.0",
"ag-grid-react": "^32.0.2",
"axios": "^1.7.8",
"big.js": "^6.2.2",
"chart.js": "^4.4.6",
"dayjs": "^1.11.13",
"env-cmd": "^10.1.0",
"fabric": "^5.3.0",
"framer-motion": "^11.2.13",
"fs": "^0.0.1-security",
"iron-session": "^8.0.2",
"jimp": "^1.6.0",
"js-cookie": "^3.0.5",
"mathjs": "^13.0.2",
"mssql": "^11.0.1",
"next": "14.2.28",
"next": "14.2.21",
"next-international": "^1.2.4",
"react": "^18",
"react-chartjs-2": "^5.2.0",
@ -44,7 +37,6 @@
"react-responsive-modal": "^6.4.2",
"react-select": "^5.8.1",
"recoil": "^0.7.7",
"sharp": "^0.33.5",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7",
"sweetalert2": "^11.14.1",

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'qcast-front-production-1',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5000,
},
},
],
}

View File

@ -1,13 +0,0 @@
module.exports = {
apps: [
{
name: 'qcast-front-production-2',
script: 'node_modules/next/dist/bin/next',
instances: 1,
exec_mode: 'fork',
env: {
PORT: 5001,
},
},
],
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -1,3 +0,0 @@
<svg width="16" height="14" viewBox="0 0 16 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8 0L15.7942 13.5H0.205771L8 0Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,4 +0,0 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="8.25" stroke="#101010" stroke-width="1.5"/>
<path d="M7.94995 16.5C10.0485 14.302 13.9289 14.1986 16.05 16.5M14.2455 9.75C14.2455 10.9926 13.2367 12 11.9923 12C10.7479 12 9.73912 10.9926 9.73912 9.75C9.73912 8.50736 10.7479 7.5 11.9923 7.5C13.2367 7.5 14.2455 8.50736 14.2455 9.75Z" stroke="#101010" stroke-width="1.5" stroke-linecap="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

View File

@ -1,13 +1,21 @@
'use client'
import { createContext, useState } from 'react'
import { createContext, useEffect, useState } from 'react'
import { useLocalStorage } from 'usehooks-ts'
export const GlobalDataContext = createContext(null)
const GlobalDataProvider = ({ children }) => {
const [managementState, setManagementState] = useState(null)
const [managementStateLoaded, setManagementStateLoaded] = useLocalStorage('managementStateLoaded', null)
return <GlobalDataContext.Provider value={{ managementState, setManagementState }}>{children}</GlobalDataContext.Provider>
useEffect(() => {
if (managementState !== null) {
setManagementStateLoaded(managementState)
}
}, [managementState])
return <GlobalDataContext.Provider value={{ managementState, setManagementState, managementStateLoaded }}>{children}</GlobalDataContext.Provider>
}
export default GlobalDataProvider

View File

@ -1,70 +0,0 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const uploadImage = async (file) => {
console.log('🚀 ~ uploadImage ~ file:', file)
const Body = Buffer.from(await file.arrayBuffer())
const Key = `cads/${file.name}`
const ContentType = 'image/png'
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
return {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const result = await uploadImage(file)
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const Key = `cads/${searchParams.get('fileName')}`
console.log('🚀 ~ DELETE ~ Key:', Key)
if (!Key) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -1,203 +0,0 @@
import { NextResponse } from 'next/server'
import { DeleteObjectCommand, GetObjectCommand, PutObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { v4 as uuidv4 } from 'uuid'
import { Jimp } from 'jimp'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const checkArea = (obj) => {
const { width, height, left, top } = obj
if (left < 0 || top < 0 || width > 1600 || height > 1000) {
return false
}
return true
}
const cropImage = async (Key, width, height, left, top) => {
try {
// Get the image from S3
const { Body } = await s3.send(
new GetObjectCommand({
Bucket,
Key,
}),
)
const chunks = []
for await (const chunk of Body) {
chunks.push(chunk)
}
const buffer = Buffer.concat(chunks)
let image = await Jimp.read(buffer)
image.autocrop({ tolerance: 0.0002, leaveBorder: 10 })
const resizedImage = await resizeImage(image).then((result) => {
return result
})
return await resizedImage.getBuffer('image/png')
// Convert stream to buffer
// const chunks = []
// for await (const chunk of Body) {
// chunks.push(chunk)
// }
// const imageBuffer = Buffer.concat(chunks)
// const image = await Jimp.read(Body)
// if (!checkResult) {
// processedImage = await image.toBuffer()
// }
//let processedImage
// if (!checkResult) {
// processedImage = await sharp(imageBuffer).toBuffer()
// } else {
// processedImage = await sharp(imageBuffer)
// .extract({
// width: parseInt(width),
// height: parseInt(height),
// left: parseInt(left),
// top: parseInt(top),
// })
// .png()
// .toBuffer()
// }
// return processedImage
} catch (error) {
console.error('Error processing image:', error)
throw error
}
}
//크롭된 이미지를 배경 크기에 맞게 리사이즈
const resizeImage = async (image) => {
//엑셀 템플릿 너비 35.4cm, 높이 12.89cm
const convertStandardWidth = Math.round((35.4 * 96) / 2.54)
const convertStandardHeight = Math.round((12.89 * 96) / 2.54)
// 이미지를 배경의 98%까지 확대 (훨씬 더 크게)
const targetImageWidth = convertStandardWidth * 0.98
const targetImageHeight = convertStandardHeight * 0.98
const scaleX = targetImageWidth / image.bitmap.width
const scaleY = targetImageHeight / image.bitmap.height
let scale = Math.min(scaleX, scaleY) // 비율 유지하면서 최대한 크게
// scale 저장 (나중에 전체 확대에 사용)
const originalScale = scale
let finalWidth = Math.round(image.bitmap.width * scale)
let finalHeight = Math.round(image.bitmap.height * scale)
if (scale >= 0.6) {
// 실제 리사이즈 실행
image.resize({ w: finalWidth, h: finalHeight })
}
//배경 이미지를 생성
const mixedImage = new Jimp({ width: convertStandardWidth, height: convertStandardHeight, color: 0xffffffff })
//이미지를 중앙에 배치
const x = Math.floor((mixedImage.bitmap.width - image.bitmap.width) / 2)
const y = Math.floor((mixedImage.bitmap.height - image.bitmap.height) / 2)
//이미지를 배경 이미지에 합성
mixedImage.composite(image, x, y, {
opacitySource: 1, // 원본 투명도 유지
opacityDest: 1,
})
// scale이 0.8 이하인 경우 완성된 이미지를 전체적으로 확대
if (originalScale <= 0.8) {
const enlargeRatio = 1.5 // 50% 확대
const newWidth = Math.round(mixedImage.bitmap.width * enlargeRatio)
const newHeight = Math.round(mixedImage.bitmap.height * enlargeRatio)
mixedImage.resize({ w: newWidth, h: newHeight })
}
return mixedImage
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const objectNo = formData.get('objectNo')
const planNo = formData.get('planNo')
const type = formData.get('type')
const width = formData.get('width')
const height = formData.get('height')
const left = formData.get('left')
const top = formData.get('top')
const OriginalKey = `Drawing/${uuidv4()}`
/**
* 원본 이미지를 우선 저장한다.
* 이미지 이름이 겹지는 현상을 방지하기 위해 uuid 사용한다.
*/
await s3.send(
new PutObjectCommand({
Bucket,
Key: OriginalKey,
Body: Buffer.from(await file.arrayBuffer()),
ContentType: 'image/png',
}),
)
/**
* 저장된 원본 이미지를 기준으로 크롭여부를 결정하여 크롭 이미지를 저장한다.
*/
const bufferImage = await cropImage(OriginalKey, width, height, left, top)
/**
* 크롭 이미지 이름을 결정한다.
*/
const Key = `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_${type}.png`
/**
* 크롭이 완료된 이미지를 업로드한다.
*/
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body: bufferImage,
ContentType: 'image/png',
}),
)
/**
* 크롭이미지 저장이 완료되면 원본 이미지를 삭제한다.
*/
await s3.send(
new DeleteObjectCommand({
Bucket,
Key: OriginalKey,
}),
)
const result = {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
return NextResponse.json(result)
} catch (error) {
console.error('Error in POST:', error)
return NextResponse.json({ error: 'Failed to process and upload image' }, { status: 500 })
}
}

View File

@ -1,59 +0,0 @@
import { NextResponse } from 'next/server'
import { S3Client, CopyObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'
import sharp from 'sharp'
import { v4 as uuidv4 } from 'uuid'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
export async function POST(req) {
const { objectNo, planNo, newObjectNo, newPlanNo } = await req.json()
const responseArray = []
//견적서1 번 이미지
const isExistImage1 = await s3.send(
new GetObjectCommand({
Bucket,
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`,
}),
)
//견적서2 번 이미지
const isExistImage2 = await s3.send(
new GetObjectCommand({
Bucket,
Key: `Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`,
}),
)
//견적서1,2 번 이미지 둘다 있어야함
if (isExistImage1.$metadata.httpStatusCode === 200 && isExistImage2.$metadata.httpStatusCode === 200) {
//견적서1 번 이미지 복사
const copyCommand = new CopyObjectCommand({
Bucket,
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_1.png`),
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_1.png`,
})
const response = await s3.send(copyCommand)
const copyCommand2 = new CopyObjectCommand({
Bucket,
CopySource: encodeURI(`${Bucket}/Drawing/${process.env.S3_PROFILE}/${objectNo}_${planNo}_2.png`),
Key: `Drawing/${process.env.S3_PROFILE}/${newObjectNo}_${newPlanNo}_2.png`,
})
const response2 = await s3.send(copyCommand2)
responseArray.push(response, response2)
return NextResponse.json({ message: '견적서 이미지 복사 성공', responseArray }, { status: 200 })
} else {
return NextResponse.json({ message: '견적서 이미지 복사 실패(존재하지 않는 이미지)', responseArray }, { status: 400 })
}
}

View File

@ -1,78 +0,0 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
export async function GET(req) {
try {
const searchParams = req.nextUrl.searchParams
const q = searchParams.get('q')
const fileNm = searchParams.get('fileNm')
const zoom = searchParams.get('zoom')
/** 구글 맵을 이미지로 변경하기 위한 API */
const API_KEY = 'AIzaSyDO7nVR1N_D2tKy60hgGFavpLaXkHpiHpc'
const targetUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${q}&zoom=${zoom}&maptype=satellite&size=640x640&scale=1&key=${API_KEY}`
const decodeUrl = decodeURIComponent(targetUrl)
/** 구글 맵을 이미지로 변경하기 위한 API 호출 */
const response = await fetch(decodeUrl)
const data = await response.arrayBuffer()
// const buffer = Buffer.from(data)
/** 변경된 이미지를 S3에 업로드 */
const Body = Buffer.from(data)
const Key = `maps/${fileNm}`
const ContentType = 'image/png'
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
const result = {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const Key = `maps/${searchParams.get('fileName')}`
console.log('🚀 ~ DELETE ~ Key:', Key)
if (!Key) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -1,72 +0,0 @@
import { NextResponse } from 'next/server'
import { S3Client, PutObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3'
const Bucket = process.env.AMPLIFY_BUCKET
const s3 = new S3Client({
region: process.env.AWS_REGION,
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
},
})
const uploadImage = async (file) => {
const Body = Buffer.from(await file.arrayBuffer())
const Key = `upload/${file.name}`
const ContentType = file.ContentType
await s3.send(
new PutObjectCommand({
Bucket,
Key,
Body,
ContentType,
}),
)
return {
filePath: `https://${process.env.AMPLIFY_BUCKET}.s3.${process.env.AWS_REGION}.amazonaws.com/${Key}`,
fileName: Key,
}
}
export async function POST(req) {
try {
const formData = await req.formData()
const file = formData.get('file')
const result = await uploadImage(file)
result.message = '이미지 업로드 성공'
return NextResponse.json(result)
} catch (error) {
console.error(error)
return NextResponse.json({ error: 'Failed to upload image' }, { status: 500 })
}
}
export async function DELETE(req) {
try {
const searchParams = req.nextUrl.searchParams
const fileName = searchParams.get('fileName')
if (!fileName) {
return NextResponse.json({ error: 'fileName parameter is required' }, { status: 400 })
}
const Key = `upload/${fileName}`
console.log('🚀 ~ DELETE ~ Key:', Key)
await s3.send(
new DeleteObjectCommand({
Bucket,
Key,
}),
)
return NextResponse.json({ message: '이미지 삭제 성공' }, { status: 200 })
} catch (error) {
console.error('S3 Delete Error:', error)
return NextResponse.json({ error: 'Failed to delete image' }, { status: 500 })
}
}

View File

@ -1,9 +0,0 @@
import Qna from '@/components/community/Qna'
export default async function CommunityQnaPage() {
return (
<>
<Qna />
</>
)
}

View File

@ -21,8 +21,6 @@ const defaultEstimateData = {
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
pricingFlag: false, // 가격 처리 플래그 추가
}
/**
@ -47,19 +45,8 @@ const FloorPlanProvider = ({ children }) => {
// const pathname = usePathname()
// const setCorrentObjectNo = useSetRecoilState(correntObjectNoState)
const searchParams = useSearchParams()
const path = usePathname()
const objectNo = searchParams.get('objectNo')
const pid = searchParams.get('pid')
useEffect(() => {
setFloorPlanState((prev) => {
return {
...prev,
objectNo,
pid,
}
})
}, [path])
// useEffect(() => {
// console.log('🚀 ~ useEffect ~ objectNo:')
// if (pathname === '/floor-plan') {

View File

@ -21,8 +21,8 @@ import GlobalLoadingProvider from './GlobalLoadingProvider'
* 서버 컴포넌트에 한해서 개별로 설정할 있음
*/
export const metadata = {
title: 'HANASYS DESIGN',
description: 'HANASYS DESIGN',
title: 'HANASYS設計',
description: 'HANASYS設計',
}
/**
@ -58,7 +58,6 @@ export default async function RootLayout({ children }) {
pwdInitYn: session.pwdInitYn,
custCd: session.custCd,
isLoggedIn: session.isLoggedIn,
builderNo: session.builderNo
}
}
if (!headerPathname.includes('/login') && !session.isLoggedIn) {

View File

@ -125,11 +125,6 @@ export const TRESTLE_MATERIAL = {
BRACKET: 'bracket',
}
export const MODULE_SETUP_TYPE = {
LAYOUT: 'layout',
AUTO: 'auto',
}
export const SAVE_KEY = [
'selectable',
'name',
@ -208,13 +203,6 @@ export const SAVE_KEY = [
'fontWeight',
'dormerAttributes',
'toFixed',
'startPoint',
'endPoint',
'editable',
'isSortedPoints',
'isMultipleOf45',
'from',
'originColor',
]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype]

View File

@ -14,7 +14,6 @@ import { sessionStore } from '@/store/commonAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import BoardDetailModal from './community/modal/BoardDetailModal'
import Config from '@/config/config.export'
export default function MainPage() {
const [sessionState, setSessionState] = useRecoilState(sessionStore)

View File

@ -2,111 +2,26 @@
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { setSession, login } from '@/lib/authActions'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState } from 'recoil'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { useRouter } from 'next/navigation'
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
export default function AutoLoginPage({ autoLoginParam }) {
const router = useRouter()
const [isLoading, setIsLoading] = useState(autoLoginParam === 'Y' ? false : true)
const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
const { promisePost } = useAxios(globalLocaleState)
export default function AutoLoginPage() {
const [isLoading, setIsLoading] = useState(true)
const { getMessage } = useMessage()
const [userId, setUserId] = useState('')
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [idFocus, setIdFocus] = useState(false)
const loginProcess = async () => {
setIsLoading(true)
await promisePost({ url: '/api/login/v1.0/user', data: { loginId: userId } }).then((response) => {
setIsLoading(false)
if (response.data) {
const res = response.data
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
setSession(result)
setSessionState(result)
login()
} else {
alert(getMessage('login.fail'))
router.push('/login?autoLoginParam1=Y')
}
})
}
return (
<>
{isLoading && <GlobalSpinner />}
{autoLoginParam !== 'Y' ? (
<>
<div className="login-input-frame">
<div className="login-frame-tit ">
<span>{getMessage('site.name')}</span>
{getMessage('site.sub_name')}
</div>
<div className="login-input-wrap">
<div className="login-area id" style={{ fontWeight: 'bolder' }}>
{getMessage('login.auto.page.text')}
</div>
</div>
<div className="login-input-frame">
<div className="login-frame-tit ">
<span>{getMessage('site.name')}</span>
{getMessage('site.sub_name')}
</div>
<div className="login-input-wrap">
<div className="login-area id" style={{ fontWeight: 'bolder' }}>
{getMessage('login.auto.page.text')}
</div>
</>
) : (
<>
<div className="login-input-frame">
<form
onSubmit={(e) => {
e.preventDefault()
loginProcess()
}}
className="space-y-6"
>
<div className="login-frame-tit">
<span>{getMessage('site.name')}</span>
{getMessage('site.sub_name')}
</div>
<div className="login-input-wrap">
<div className={`login-area id ${idFocus ? 'focus' : ''}`}>
<input
type="text"
className="login-input"
id="userId"
name="id"
required
value={userId}
placeholder={getMessage('login.id.placeholder')}
onChange={(e) => {
setUserId(e.target.value)
}}
onFocus={() => setIdFocus(true)}
onBlur={() => setIdFocus(false)}
/>
<button
type="button"
className="id-delete"
onClick={(e) => {
setUserId('')
}}
></button>
</div>
<div className="login-btn-box">
<button type="submit" className="login-btn">
{getMessage('login')}
</button>
</div>
</div>
</form>
</div>
</>
)}
</div>
</div>
</>
)
}

View File

@ -25,9 +25,7 @@ export default function Login() {
useEffect(() => {
if (autoLoginParam) {
if (autoLoginParam !== 'Y') {
autoLoginProcess(autoLoginParam)
}
autoLoginProcess(autoLoginParam)
}
// console.log('🚀 ~ checkSession ~ checkSession():', checkSession())
@ -336,7 +334,7 @@ export default function Login() {
</div>
</>
)}
{autoLoginParam && <AutoLogin autoLoginParam={autoLoginParam} />}
{autoLoginParam && <AutoLogin />}
</div>
<div className="login-copyright">COPYRIGHT©2024 Hanwha Japan All Rights Reserved.</div>
</div>

View File

@ -6,19 +6,29 @@ import { contextMenuListState, contextMenuState } from '@/store/contextMenu'
import { useTempGrid } from '@/hooks/useTempGrid'
import { useContextMenu } from '@/hooks/useContextMenu'
import { useEvent } from '@/hooks/useEvent'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { canvasState } from '@/store/canvasAtom'
export default function QContextMenu(props) {
const canvas = useRecoilValue(canvasState)
const { contextRef, canvasProps } = props
const [contextMenu, setContextMenu] = useRecoilState(contextMenuState)
const contextMenuList = useRecoilValue(contextMenuListState)
const currentObject = useRecoilValue(currentObjectState)
const activeObject = canvasProps?.getActiveObject() //
const { tempGridMode, setTempGridMode } = useTempGrid()
const { handleKeyup } = useContextMenu()
const { addDocumentEventListener, removeDocumentEvent } = useEvent()
// const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext)
let contextType = ''
if (activeObject) {
if (activeObject.initOptions && activeObject.initOptions.name) {
//
if (activeObject.initOptions?.name?.indexOf('guide') > -1) {
contextType = 'surface' //
}
}
}
const getYPosition = (e) => {
const contextLength = contextMenuList.reduce((acc, cur, index) => {
return acc + cur.length
@ -26,13 +36,11 @@ export default function QContextMenu(props) {
return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17)
}
const handleContextMenu = (e) => {
// e.preventDefault() // contextmenu
if (currentObject) {
const isArray = currentObject.hasOwnProperty('arrayData')
if (isArray && currentObject.arrayData.length === 0) return
useEffect(() => {
if (!contextRef.current) return
const handleContextMenu = (e) => {
e.preventDefault() // contextmenu
if (tempGridMode) return
const position = {
x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX,
@ -40,24 +48,21 @@ export default function QContextMenu(props) {
}
setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) })
addDocumentEventListener('keyup', document, handleKeyup)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
}
}
const handleClick = (e) => {
// e.preventDefault()
setContextMenu({ ...contextMenu, visible: false })
}
const handleOutsideClick = (e) => {
// e.preventDefault()
if (contextMenu.visible) {
const handleClick = (e) => {
// e.preventDefault()
setContextMenu({ ...contextMenu, visible: false })
removeDocumentEvent('keyup')
}
}
useEffect(() => {
if (!contextRef.current) return
const handleOutsideClick = (e) => {
// e.preventDefault()
if (contextMenu.visible) {
setContextMenu({ ...contextMenu, visible: false })
removeDocumentEvent('keyup')
}
}
canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu)
document.addEventListener('click', handleClick)
@ -67,9 +72,43 @@ export default function QContextMenu(props) {
removeDocumentEvent('keyup')
document.removeEventListener('click', handleClick)
document.removeEventListener('click', handleOutsideClick)
canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //
}
}, [contextRef, contextMenuList, currentObject])
}, [contextRef, contextMenuList])
const handleObjectMove = () => {
activeObject.set({
lockMovementX: false, // X
lockMovementY: false, // Y
})
canvasProps?.on('object:modified', function (e) {
activeObject.set({
lockMovementX: true, // X
lockMovementY: true, // Y
})
})
}
const handleObjectDelete = () => {
if (confirm('삭제하실거?')) {
canvasProps.remove(activeObject)
}
}
const handleObjectCopy = () => {
activeObject.clone((cloned) => {
cloned.set({
left: activeObject.left + activeObject.width + 20,
initOptions: { ...activeObject.initOptions },
lockMovementX: true, // X
lockMovementY: true, // Y
lockRotation: true, //
lockScalingX: true, // X
lockScalingY: true, // Y
})
canvasProps?.add(cloned)
})
}
return (
<>

View File

@ -24,8 +24,7 @@ export default function WithDraggable({ isShow, children, pos = { x: 0, y: 0 },
<Draggable
position={{ x: position.x, y: position.y }}
onDrag={(e, data) => handleOnDrag(e, data)}
handle= ''//{handle === '' ? '.modal-handle' : handle} //전체 handle
cancel="input, button, select, textarea, [contenteditable], .sort-select"
handle={handle === '' ? '.modal-handle' : handle}
>
<div className={`modal-pop-wrap ${className}`} style={{ visibility: isHidden ? 'hidden' : 'visible' }}>
{children}

View File

@ -26,7 +26,6 @@ export default function QSelectBox({
targetKey = '',
showKey = '',
params = {},
tagTitle = '',
}) {
const { getMessage } = useMessage()
@ -40,7 +39,7 @@ export default function QSelectBox({
if (showKey !== '' && !value) {
//value showKey
// return options[0][showKey]
return title !== '' ? title : getMessage('selectbox.title')
return title
} else if (showKey !== '' && value) {
//value sourceKey targetKey
@ -83,13 +82,12 @@ export default function QSelectBox({
className={`sort-select ${openSelect ? 'active' : ''} ${disabled ? 'disabled' : ''}`}
ref={ref}
onClick={disabled ? () => {} : () => setOpenSelect(!openSelect)}
title={tagTitle}
>
<p>{selected}</p>
<ul className="select-item-wrap">
{options?.length > 0 &&
options?.map((option, index) => (
<li key={option.id + '_' + index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<li key={option.id || index} className="select-item" onClick={() => handleClickSelectOption(option)}>
<button key={option.id + 'btn'}>{showKey !== '' ? option[showKey] : option.name}</button>
</li>
))}

View File

@ -1,212 +0,0 @@
'use client'
import Link from 'next/link'
import Image from 'next/image'
import Search from '@/components/community/Search'
import Pagination from '@/components/community/Pagination'
import { useContext } from 'react'
import { useEffect, useState } from 'react'
import { useResetRecoilState, useRecoilValue, useRecoilState } from 'recoil'
import { useMessage } from '@/hooks/useMessage'
import { searchState } from '@/store/boardAtom'
import { QcastContext } from '@/app/QcastProvider'
import QnaBoardDetailModal from '@/components/community/modal/QnaDetailModal'
import { sessionStore } from '@/store/commonAtom'
import { useAxios } from '@/hooks/useAxios'
import { useCommonCode } from '@/hooks/common/useCommonCode'
export default function Qna() {
const { getMessage } = useMessage()
const resetSearch = useResetRecoilState(searchState)
const [isInitialized, setIsInitialized] = useState(false)
//const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
const { findCommonCode } = useCommonCode()
const { setIsGlobalLoading } = useContext(QcastContext)
const { get } = useAxios()
const [boardList, setBoardList] = useState([])
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const [search, setSearch] = useRecoilState(searchState)
//
const [open, setOpen] = useState(false)
const [modalQnaNo, setModalQnaNo] = useState('')
const [modalQnaType, setModalQnaType] = useState('')
//
useEffect(() => {
async function fetchData() {
setIsGlobalLoading(true)
const startRow = (search.currentPage - 1) * search.pageBlock > 0 ? (search.currentPage - 1) * search.pageBlock + 1 : 1
const endRow = search.currentPage * search.pageBlock
const url = `/api/board/list`
const params = new URLSearchParams({
schNoticeTpCd : 'QC',
schNoticeClsCd: 'QNA',
compCd : 5200,
storeId : sessionState.storeId,
loginId : sessionState.userId,
schTitle : search.searchValue ? search.searchValue : '',
startRow : startRow,
endRow : endRow,
schMainYn : 'N',
siteTpCd : 'QC',
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
if (resultData.data.length > 0) {
setBoardList(resultData.data)
setSearch({ ...search, totalCount: resultData.data[0].totCnt })
} else {
setBoardList([])
setSearch({ ...search, totalCount: 0 })
}
} else {
alert(resultData.result.message)
}
}
setIsGlobalLoading(false)
}
fetchData()
}, [search.currentPage, search.searchValue, search.pageBlock, search.searchFlag])
useEffect(() => {
if (search.mainFlag === 'N') {
resetSearch()
} else {
// FAQ
setIsGlobalLoading(false)
setSearchForm({ ...searchForm, mainFlag: 'N' })
}
setIsInitialized(true)
//
// const codeL = findCommonCode(204200)
// const codeM = findCommonCode(204300)
// const codeS = findCommonCode(204400)
}, [])
if (!isInitialized) {
return null
}
const boardType = {
boardTitle: getMessage('qna.title'),
subTitle: getMessage('qna.sub.title'),
clsCode: 'QNA',
}
return (
<>
<div className="sub-header">
<div className="sub-header-inner">
<ul className="sub-header-title-wrap">
<li className="title-item">
<Link className="sub-header-title" href={'#'}>
{getMessage('qna.title')}
</Link>
</li>
</ul>
<ul className="sub-header-location">
<li className="location-item">
<span className="home">
<Image src="/static/images/main/home_icon.svg" alt="react" width={16} height={16} />
</span>
</li>
<li className="location-item">
<span>{getMessage('header.menus.community')}</span>
</li>
<li className="location-item">
<span>{getMessage('qna.title')}</span>
</li>
</ul>
</div>
</div>
<div className="sub-content">
<div className="sub-content-inner">
<div className="sub-table-box">
<Search title={boardType.boardTitle} subTitle={boardType.subTitle} isSelectUse={true} clsCode={boardType.clsCode} />
{/*<QnaTable clsCode={boardType.clsCode} />*/}
<div className="community-table">
<table>
<colgroup>
<col width={100}/>
<col width={150}/>
<col />
<col width={150}/>
<col width={150}/>
</colgroup>
<tbody>
{boardList.length > 0 ? (
boardList?.map((board) => (
<tr
key={board.qnaNo}
onClick={() => {
setOpen(true)
setModalQnaNo(board.qnaNo)
setModalQnaType("["+board?.qnaClsLrgCd+"/"+board?.qnaClsMidCd+"/"+board?.qnaClsSmlCd+"]")
}}
>
<td className="al-c">
{/* 번호 */}
{board.totCnt - board.rowNumber + 1}
</td>
{/* 답변 */}
{board?.answerYn === 'Y'? (<td className="al-c "> {getMessage('qna.list.header.answer.yes')}</td>) : (<td className="al-c org"> {getMessage('qna.list.header.answer.no')}</td>)}
<td>
<div className="mb5">[{board?.qnaClsLrgCd} / {board?.qnaClsMidCd} / {board?.qnaClsSmlCd}]</div>
{/* 제목 */}
<div className="text-frame">
<div className="text-overflow">{board.title}{board.qstTitle}</div>
{board.attachYn === 'Y' && <span className="clip"></span>}
</div>
</td>
<td>
<div className="renewal">
{/*{board.uptDt && (*/}
{/* <>*/}
{/* (<span>{getMessage('board.uptDt')}</span> : {board.uptDt})*/}
{/* </>*/}
{/*)}*/}
{board.regUserNm}
</div>
</td>
<td className="al-c">
{/* 등록일 */}
{board.regDt.split(' ')[0]}
</td>
</tr>
))
) : (
<tr>
<td className="al-c no-data" colSpan={5}>{getMessage('common.message.no.data')}</td>
</tr>
)}
</tbody>
</table>
</div>
<Pagination />
</div>
</div>
</div>
{open && <QnaBoardDetailModal qnaNo={modalQnaNo} setOpen={setOpen} qnaType = {modalQnaType} />}
</>
)
}

View File

@ -4,11 +4,10 @@ import { searchState } from '@/store/boardAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import QnaRegModal from '@/components/community/modal/QnaRegModal'
export default function Search({ title = '', subTitle = '', isSelectUse = false, clsCode = '' }) {
export default function Search({ title = '', subTitle = '', isSelectUse = false }) {
const { getMessage } = useMessage()
const [open, setOpen] = useState(false)
const search = useRecoilValue(searchState)
const [searchForm, setSearchForm] = useRecoilState(searchState)
@ -33,13 +32,7 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
} else {
setSearchView(false)
setSearchViewText('')
setSearchForm({
...searchForm,
currentPage: 1,
searchValue: '',
pageBlock : block,
searchFlag : !searchForm.searchFlag,
})
setSearchForm({ ...searchForm, currentPage: 1, searchValue: '', pageBlock: block, searchFlag: !searchForm.searchFlag })
}
//
setSearchValue('')
@ -64,10 +57,7 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
onKeyDown={handleKeyDown}
value={searchValue}
/>
<button type="button" className="community-search-ico"
onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
<button type="button" className="community-search-ico" onClick={() => handleSearch(searchValue, selectPageBlock)}></button>
</div>
{searchView && (
<div className="community-search-keyword">
@ -102,14 +92,6 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
</div>
{isSelectUse && (
<div className="left-unit-box">
{clsCode === 'QNA' &&
<div>
<button className="btn-origin navy mr10"
onClick={() => {
setOpen(true)
}}> {getMessage('qna.sub.btn.inquiry')}</button>
</div>
}
<div className="select-box" style={{ width: '80px' }}>
<select
className="select-light black"
@ -130,7 +112,6 @@ export default function Search({ title = '', subTitle = '', isSelectUse = false,
</div>
)}
</div>
{open && <QnaRegModal setOpen={setOpen} setReload={handleSearch} searchValue={searchValue ? searchValue : searchViewText} selectPageBlock = {selectPageBlock}/>}
</>
)
}

View File

@ -1,127 +0,0 @@
'use client'
import { useEffect, useState } from 'react'
import { useAxios } from '@/hooks/useAxios'
import { handleFileDown } from '@/util/board-utils'
import { useMessage } from '@/hooks/useMessage'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState } from 'recoil'
export default function QnaDetailModal({ qnaNo, setOpen, qnaType }) {
const { getMessage } = useMessage()
// api
const { get } = useAxios()
const [boardDetail, setBoardDetail] = useState({})
const [sessionState, setSessionState] = useRecoilState(sessionStore)
useEffect(() => {
//
const fetchDetail = async (qnaNo) => {
const url = `/api/board/detail`
const params = new URLSearchParams({
noticeNo : qnaNo,
qnaNo : qnaNo,
schNoticeClsCd: 'QNA',
compCd : 5200,
loginId : sessionState.userId,
langCd : 'JA',
})
const apiUrl = `${url}?${params.toString()}`
const resultData = await get({ url: apiUrl })
if (resultData) {
if (resultData.result.code === 200) {
const boardDetail = resultData.data
setBoardDetail(boardDetail)
} else {
alert(resultData.result.message)
}
}
}
fetchDetail(qnaNo)
}, [])
return (
<>
<div key={qnaNo} className="modal-popup community">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<button
type="button"
className="modal-close"
onClick={() => {
setOpen(false)
}}
>
{getMessage('board.sub.btn.close')}
</button>
</div>
<div className="modal-body">
<div className="oneonone-header-wrap">
<div className="oneonone-title"> {qnaType} {boardDetail.qstTitle}</div>
<div className="oneonone-infor">
<div className="profile">{boardDetail.regUserNm}</div>
<div className="date">{boardDetail.regDt}</div>
</div>
</div>
<div className="oneonone-detail">
{boardDetail.listFile && (
<dl className="community_detail-file-wrap">
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
{boardDetail.listFile.map((boardFile) => (
<dd key={boardFile.encodeFileNo}>
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'NO')}>
{boardFile.srcFileNm}
</button>
</dd>
))}
</dl>
)}
<div
className="community_detail-inner"
dangerouslySetInnerHTML={{
__html: boardDetail.qstContents ? boardDetail.qstContents.replaceAll('\n', '<br/>') : '',
}}
></div>
</div>
{boardDetail?.answerYn === 'Y' && (
<div className="oneonone-answer">
<div className="answer-title-wrap">
<div className="answer-title">Hanwha Japan {getMessage('qna.detail.sub.answer')}</div>
<div className="oneonone-infor">
<div className="profile">{boardDetail.ansRegNm}</div>
<div className="date">{boardDetail.ansRegDt}</div>
</div>
</div>
<div
className="community_detail-inner"
dangerouslySetInnerHTML={{
__html: boardDetail.ansContents ? boardDetail.ansContents.replaceAll('\n', '<br/>') : '',
}}
></div>
{boardDetail.ansListFile && (
<dl className="community_detail-file-wrap">
<dt>{getMessage('qna.detail.sub.fileList')}</dt>
{boardDetail.ansListFile.map((boardFile) => (
<dd key={boardFile.encodeFileNo}>
<button type="button" className="down" onClick={() => handleFileDown(boardFile.fileNo, 'N')}>
{boardFile.srcFileNm}
</button>
</dd>
))}
</dl>
)}
</div>
)}
</div>
</div>
</div>
</div>
</>
)
}

View File

@ -1,140 +0,0 @@
'use client'
import { useRef } from 'react'
import { v4 as uuidv4 } from 'uuid'
import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal'
export default function QnaFileUploader({ uploadFiles, setUploadFiles, qnaData, setQnaData }) {
const fileInputRef = useRef(null)
const { getMessage } = useMessage()
const { swalFire } = useSwal()
const handleButtonClick = (e) => {
e.preventDefault()
fileInputRef.current.click()
}
const onChangeFiles = async (e) => {
if (e.target.files.length <= 0) {
return
}
const fileList = []
let passFlag = true
const allowedFileTypes = [
'image/',
'application/pdf',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX
'application/vnd.ms-powerpoint', // PPT
]
Array.from(e.target.files).forEach((file) => {
//, pdf,
const fileType = file.type
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
passFlag = false
} else {
fileList.push({ data: file, id: uuidv4() })
}
})
if (!passFlag) {
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
}
setUploadFiles([...uploadFiles, ...fileList])
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
e.target.value = ''
}
const deleteFile = (id) => {
setUploadFiles(uploadFiles.filter((file) => file.id !== id))
setQnaData({...qnaData, files:uploadFiles.filter((file) => file.id !== id)})
}
const handleDrop = (e) => {
e.preventDefault()
e.stopPropagation()
const fileList = []
let passFlag = true
const allowedFileTypes = [
'image/',
'application/pdf',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.presentationml.presentation', // PPTX
'application/vnd.ms-powerpoint', // PPT
]
Array.from(e.dataTransfer.files).forEach((file) => {
//, pdf,
let fileType = file.type
if (!allowedFileTypes.some((type) => fileType.includes(type))) {
passFlag = false
} else {
fileList.push({ data: file, id: uuidv4() })
}
})
if (!passFlag) {
swalFire({ text: getMessage('estimate.detail.fileList.extCheck'), type: 'alert', icon: 'error' })
}
setUploadFiles([...uploadFiles, ...fileList])
setQnaData({...qnaData, files:[...uploadFiles, ...fileList]})
}
const handleDragOver = (e) => {
e.preventDefault()
e.stopPropagation()
}
const handleDragEnd = (e) => {
e.preventDefault()
e.stopPropagation()
}
const handleDragLeave = (e) => {
e.preventDefault()
e.stopPropagation()
}
return (
<div className="design-request-grid mt15">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.fileList")}</div>
<div className="btn-area one-on-one">
<label className="file-upload" htmlFor="img" onClick={handleButtonClick}>
Attach File
</label>
<input type="file" multiple name="file" ref={fileInputRef} style={{ display: 'none' }} onChange={(e) => onChangeFiles(e)} />
</div>
</div>
<div className="drag-file-box one-on-one">
<div className="drag-file-area"
draggable
onDrop={(e) => handleDrop(e)}
onDragOver={(e) => handleDragOver(e)}
onDragEnd={(e) => handleDragEnd(e)}
onDragLeave={(e) => handleDragLeave(e)}>
<p>Drag file here</p>
<ul className="file-list">
{uploadFiles.length > 0 &&
uploadFiles.map((file) => (
<li className="file-item" key={file.id}>
<span>
{file.data.name} <button className="delete" onClick={() => deleteFile(file.id)}></button>
</span>
</li>
))}
</ul>
</div>
</div>
</div>
)
}

View File

@ -1,454 +0,0 @@
'use client'
import { useMessage } from '@/hooks/useMessage'
import { sessionStore } from '@/store/commonAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import QnaFileUploader from '@/components/community/modal/QnaFileUploader'
import { useContext, useEffect, useRef, useState } from 'react'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import Select from 'react-select'
import dayjs from 'dayjs'
import { useSwal } from '@/hooks/useSwal'
import { QcastContext } from '@/app/QcastProvider'
import { useAxios } from '@/hooks/useAxios'
import { globalLocaleStore } from '@/store/localeAtom'
import { e } from 'mathjs'
import { set } from 'react-hook-form'
export default function QnaRegModal({ setOpen, setReload, searchValue, selectPageBlock }) {
const { getMessage } = useMessage()
const [fileList, setFileList] = useState([])
const [sessionState, setSessionState] = useRecoilState(sessionStore)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const [files, setFiles] = useState([])
const [qnaData, setQnaData] = useState([])
const [closeMdFlg, setCloseMdFlg] = useState(true)
const [closeSmFlg, setCloseSmFlg] = useState(true)
const [hideSmFlg, setHideSmFlg] = useState(false)
const qnaTypeLgCodeRef = useRef(null)
const qnaTypeMdCodeRef = useRef(null)
const qnaTypeSmCodeRef = useRef(null)
const qstMail = useRef(null);
const regUserNmRef = useRef(null)
const regUserTelNoRef = useRef(null)
const titleRef = useRef(null)
const contentsRef = useRef(null)
const { findCommonCode } = useCommonCode()
const [qnaTypeLgCodeList, setQnaTypeLgCodeList] = useState([])
const [qnaTypeMdCodeList, setQnaTypeMdCodeList] = useState([])
const [qnaTypeSmCodeList, setQnaTypeSmCodeList] = useState([])
const [phoneNumber, setPhoneNumber] = useState("");
const { swalFire } = useSwal()
const { setIsGlobalLoading } = useContext(QcastContext)
const [isBtnDisable, setIsBtnDisable] = useState(false);
const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
let fileCheck = false;
const regPhoneNumber = (e) => {
const result = e.target.value
.replace(/[^0-9.]/g, "")
//.replace(/^(\d{0,3})(\d{0,4})(\d{0,4})$/g, "$1-$2-$3")
//.replace(/(-{1,2})$/g, "");
//setPhoneNumber(result);
setQnaData({...qnaData, regUserTelNo: result })
}
const fileUploadProps = {
uploadFiles: files,
setUploadFiles: setFiles,
}
// const fileSave = (qnaData, fileUploadProps) => {
// return qnaData.files.push(fileUploadProps.uploadFiles)
// }
const initQnaReg = async () => {
qstMail.current.value = ''
regUserNmRef.current.value = ''
regUserTelNoRef.current.value = ''
qnaTypeLgCodeRef.current.setValue();
qnaTypeMdCodeRef.current.setValue();
qnaTypeSmCodeRef.current?.setValue();
titleRef.current.value = ''
contentsRef.current.value = ''
//setQnaData([])
setQnaData({
...qnaData,
compCd: "5200",
siteTpCd: "QC",
schNoticeClsCd: "QNA",
regId: sessionState.userId,
storeId: sessionState.userId,
qstMail : sessionState.email
})
const codeL = findCommonCode(204200)
if (codeL != null) {
setQnaTypeLgCodeList(codeL)
}
setIsGlobalLoading(false)
setIsBtnDisable(false);
}
const onChangeQnaTypeL = (e) => {
if(e === undefined || e === null) return;
const codeM = findCommonCode(204300)
if (codeM != null) {
let codeList = []
codeM.map((item) => {
if(item.clRefChr1 === e.clCode) {
codeList.push(item);
}
})
setQnaTypeMdCodeList(codeList)
setQnaData({ ...qnaData, qnaClsLrgCd:e.clCode})
setCloseMdFlg(false)
qnaTypeMdCodeRef.current.setValue();
qnaTypeSmCodeRef.current?.setValue();
}
}
const onChangeQnaTypeM = (e) => {
if(e === undefined || e === null) return;
const codeS = findCommonCode(204400)
if (codeS != null) {
let codeList = []
codeS.map((item) => {
if (item.clRefChr1 === e.clCode) {
codeList.push(item);
}
})
setQnaData({ ...qnaData, qnaClsMidCd: e.clCode })
setCloseSmFlg(false)
setQnaTypeSmCodeList(codeList)
qnaTypeSmCodeRef.current?.setValue();
if(codeList.length > 0) {
setHideSmFlg(false)
}else{
setHideSmFlg(true)
}
}
}
const onChangeQnaTypeS = (e) => {
if(e === undefined || e === null) return;
setQnaData({ ...qnaData, qnaClsSmlCd:e.clCode})
}
const onFileSave = () => {
const formData= []
if(fileUploadProps.uploadFiles.length === 0) return;
if(!fileCheck) return;
fileUploadProps.uploadFiles.forEach((file) => {
//console.log("file::::::::",file)
formData.push(file)
})
setQnaData({ ...qnaData, files:formData })
fileCheck = false;
}
const isValidEmail = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const isEmpty = (value) => {
return value === null || value === undefined || value.trim() === "";
};
const handleQnaSubmit = async () => {
//
//console.log("1::::",qnaData)
let regUserNm = qnaData?.regUserNm??'';
if (!isValidEmail(qnaData.qstMail)) {
qstMail.current.focus();
swalFire({
title: getMessage('qna.reg.alert.require.qstMail'),
icon: 'warning',
});
return;
}
if (isEmpty(regUserNm)) {
regUserNmRef.current.value = '';
regUserNmRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.regUserNm'),
icon: 'warning',
})
return false
}
let qnaClsLrgCd = qnaData?.qnaClsLrgCd??'';
let qnaClsMidCd = qnaData?.qnaClsMidCd??'';
if (isEmpty(qnaClsLrgCd) || isEmpty(qnaClsMidCd) ) {
(isEmpty(qnaClsLrgCd))?qnaTypeLgCodeRef.current.focus():qnaTypeMdCodeRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.select.type'),
icon: 'warning',
})
return false
}
let title = qnaData?.title??'';
if (isEmpty(title)) {
titleRef.current.value = '';
titleRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.title'),
icon: 'warning',
})
return false
}
//console.log("5::::",qnaData)
let contents = qnaData?.contents??'';
if (isEmpty(contents)) {
contentsRef.current.value = '';
contentsRef.current.focus()
swalFire({
title: getMessage('qna.reg.alert.require.contents'),
icon: 'warning',
})
return false
}
const formData = new FormData()
if(qnaData?.files?.length > 0) {
qnaData?.files.forEach((file) => {
formData.append('files', file.data)
})
}
formData.append("compCd", qnaData.compCd)
formData.append("siteTpCd", qnaData.siteTpCd)
formData.append("qnaClsLrgCd", qnaData.qnaClsLrgCd)
formData.append("qnaClsMidCd", qnaData.qnaClsMidCd)
formData.append("qnaClsSmlCd", qnaData.qnaClsSmlCd)
formData.append("title", qnaData.title)
formData.append("contents", qnaData.contents)
formData.append("regId", qnaData.regId)
formData.append("storeId", qnaData.storeId)
formData.append("regUserNm", qnaData.regUserNm)
formData.append("regUserTelNo", qnaData.regUserTelNo)
formData.append("qstMail", qnaData.qstMail)
formData.append("schNoticeClsCd", qnaData.schNoticeClsCd)
//console.log(Array.from(formData));
swalFire({
html: getMessage('qna.reg.confirm.save'),
type: 'confirm',
confirmFn: async () => {
setIsBtnDisable(true);
setIsGlobalLoading(true)
try {
const apiUrl = 'api/board'
//console.log("7::::",qnaData)
await post({ url: `${apiUrl}/saveQna`, data: formData }).then((res) => {
if (res?.result.code === 200) {
//qnaData.newFileList = []
setIsGlobalLoading(false)
swalFire({ text: getMessage('qna.reg.alert.save'), type: 'alert' })
setOpen(false)
setReload(searchValue, selectPageBlock);
}else{
setIsGlobalLoading(false)
swalFire({ text: getMessage('qna.reg.alert.saveFail'), type: 'alert', icon: 'error' })
console.error('error::::::::::::', res)
}
setIsBtnDisable(false)
})
} catch (e) {
setIsGlobalLoading(false)
setIsBtnDisable(false);
console.error('error::::::::::::', e.message)
swalFire({ text: e.message, type: 'alert' , icon: 'error'})
console.error('error::::::::::::', e.message)
}
}
})
}
useEffect(() => {
initQnaReg()
},[])
// useEffect(() => {
// onFileSave()
//
// }, [onFileSave])
return (
<div className="modal-popup">
<div className="modal-dialog big">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('qna.title')}</h1>
<button className="modal-close"
onClick={() => {
setOpen(false)
}}>{getMessage('board.sub.btn.close')}</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="design-request-table">
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '100px' }} />
<col />
<col style={{ width: '120px' }} />
<col />
<col style={{ width: '150px' }} />
<col />
</colgroup>
<tbody>
<tr>
<th>{getMessage('qna.list.header.regNm')}</th>
<td><input type="text" className="input-light" value={sessionState?.userNm || ''} readOnly /></td>
<th>E-Mail<span className="red">*</span></th>
<td ><input type="text" className="input-light" required
ref={qstMail}
value={qnaData?.qstMail || ''}
onChange={(e) => setQnaData({...qnaData, qstMail: e.target.value })}
onBlur={(e) => setQnaData({ ...qnaData, qstMail: e.target.value })} />
</td>
<th>{getMessage('qna.reg.header.regDt')}</th>
<td>{dayjs(new Date()).format('YYYY-MM-DD')}</td>
</tr>
<tr>
<th>{getMessage('qna.reg.header.regUserNm')}<span className="red">*</span></th>
<td ><input type="text" className="input-light" required
ref={regUserNmRef}
value={qnaData?.regUserNm || '' }
onChange={(e) => setQnaData({...qnaData, regUserNm: e.target.value })}
onBlur={(e) => setQnaData({ ...qnaData, regUserNm: e.target.value })} /> </td>
<th>{getMessage('qna.reg.header.regUserTelNo')}</th>
<td colSpan={3}><input type="text" className="input-light"
ref={regUserTelNoRef}
maxLength={13}
value={qnaData?.regUserTelNo || '' }
onChange={regPhoneNumber}
/></td>
</tr>
</tbody>
</table>
</div>
</div>
<div className="design-request-grid">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.type")}, {getMessage("qna.reg.header.title")} <span
className="red">*</span></div>
</div>
<div className="flx-box one-on-one">
<div className="select-wrap mr5" >
<Select name="" ref={qnaTypeLgCodeRef}
options={qnaTypeLgCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeL(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
/>
</div>
<div className="select-wrap mr5" >
<Select name="" ref={qnaTypeMdCodeRef}
options={qnaTypeMdCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeM(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
isDisabled={closeMdFlg}
defaultValue={''}
/>
</div>
<div className="select-wrap" >
{!hideSmFlg && (
<Select name="" ref={qnaTypeSmCodeRef}
options={qnaTypeSmCodeList}
placeholder="Select"
onChange={(e) => onChangeQnaTypeS(e)}
getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode}
isClearable={false}
isSearchable={false}
isDisabled={closeSmFlg}
/>)}
</div>
</div>
<div className="input-wrap mt5">
<input type="text" className="input-light" maxLength={200}
ref = {titleRef}
value={qnaData?.title || '' }
onChange={(e) => {setQnaData({ ...qnaData, title: e.target.value })}}
/>
</div>
</div>
<div className="design-request-grid mt15">
<div className="design-request-count">
<div className="design-request-grid-tit">{getMessage("qna.reg.header.contents")} <span className="red">*</span></div>
</div>
<div>
<textarea className="textarea-form" name="" id="" maxLength={4000}
ref={contentsRef}
value={qnaData?.contents || '' }
onChange={(e) => {setQnaData({ ...qnaData, contents: e.target.value })}} ></textarea>
</div>
</div>
<QnaFileUploader {...fileUploadProps} qnaData={qnaData} setQnaData={setQnaData} />
</div>
<div className="footer-btn-wrap">
{isBtnDisable === false && <button className="btn-origin navy mr5" onClick={handleQnaSubmit}>{getMessage("qna.reg.header.save")}</button>}
<button className="btn-origin grey" onClick={() => {
setOpen(false)
}}>{getMessage("board.sub.btn.close")}</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -13,7 +13,7 @@ import dayjs from 'dayjs'
import { useCommonCode } from '@/hooks/common/useCommonCode'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { SessionContext } from '@/app/SessionProvider'
import Select, { components } from 'react-select'
import Select from 'react-select'
import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils'
import ProductFeaturesPop from './popup/ProductFeaturesPop'
import { v4 as uuidv4 } from 'uuid'
@ -60,7 +60,7 @@ export default function Estimate({}) {
const [cableItemList, setCableItemList] = useState([]) //
const [cableItem, setCableItem] = useState('') //
const [cableDbItem, setCableDbItem] = useState('') //
const [startDate, setStartDate] = useState(new Date())
const singleDatePickerProps = {
startDate,
@ -98,7 +98,7 @@ export default function Estimate({}) {
}
const initEstimate = (currPid = currentPid) => {
// console.log('🚀 ~ initEstimate ~ currPid:', currPid)
console.log('🚀 ~ initEstimate ~ currPid:', currPid)
closeAll()
setObjectNo(objectRecoil.floorPlanObjectNo)
@ -117,7 +117,6 @@ export default function Estimate({}) {
item.value = item.clRefChr1
item.label = item.clRefChr2
})
// console.log(code2)
setCableItemList(code2)
}
@ -153,8 +152,8 @@ export default function Estimate({}) {
}
useEffect(() => {
// console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan?.planNo?? currentPid)
console.log('🚀 ~ Estimate ~ selectedPlan:', selectedPlan)
if (selectedPlan) initEstimate(selectedPlan.planNo)
}, [selectedPlan])
useEffect(() => {
@ -176,10 +175,7 @@ export default function Estimate({}) {
row.check = false
estimateOption.map((row2) => {
if (row.pkgYn === '0') {
// if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
if (row2 === row.code) {
row.check = true
}
} else {
@ -221,10 +217,7 @@ export default function Estimate({}) {
row.check = false
estimateOption.map((row2) => {
if (row.pkgYn === '0') {
// if (row2 === row.code) {
// row.check = true
// }
if (row.code.split('、').includes(row2)) {
if (row2 === row.code) {
row.check = true
}
} else {
@ -247,6 +240,7 @@ export default function Estimate({}) {
}
}
})
setSpecialNoteList(res)
setSpecialNoteFirstFlg(true)
@ -383,8 +377,8 @@ export default function Estimate({}) {
useEffect(() => {
if (estimateContextState.estimateType !== '') {
const param = {
saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: estimateContextState?.estimateType,
}
@ -393,8 +387,6 @@ export default function Estimate({}) {
if (isNotEmptyArray(res?.data)) {
setStorePriceList(res.data)
}
setItemChangeYn(true)
})
if (estimateContextState.estimateType === 'YJSS') {
@ -424,6 +416,8 @@ export default function Estimate({}) {
handlePricing('UNIT_PRICE')
}
}
setItemChangeYn(true)
}
}, [estimateContextState?.estimateType])
@ -475,21 +469,6 @@ export default function Estimate({}) {
} else {
item.check = false
}
} else {
let codes = item.code.split('、')
let flg = '0'
if (codes.length > 1) {
for (let i = 0; i < pushData.length; i++) {
if (codes.indexOf(pushData[i]) > -1) {
flg = '1'
}
}
if (flg === '1') {
item.check = true
} else {
item.check = false
}
}
}
})
@ -499,27 +478,12 @@ export default function Estimate({}) {
})
}
//Pricing confirm
const handlePricingBtn = (showPriceCd) => {
swalFire({
text: getMessage('estimate.detail.showPrice.pricingBtn.confirm'),
type: 'confirm',
icon: 'warning',
confirmFn: () => {
handlePricing(showPriceCd)
setEstimateContextState({ pricingFlag:true })
},
})
}
//Pricing
const handlePricing = async (showPriceCd) => {
const param = {
saleStoreId: estimateContextState.sapSaleStoreId,
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
docTpCd: estimateContextState.estimateType,
secSapSalesStoreCd:
estimateContextState.secSapSalesStoreCd?.length > 0 && showPriceCd === 'QSP_PRICE' ? estimateContextState.secSapSalesStoreCd : '',
priceCd: showPriceCd,
itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null),
}
@ -542,6 +506,7 @@ export default function Estimate({}) {
})
}
}
setIsGlobalLoading(true)
await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => {
let updateList = []
@ -566,7 +531,6 @@ export default function Estimate({}) {
updateList.push({
...item,
openFlg: data.data2[i].unitPrice === '0.0' ? '1' : '0',
unitOpenFlg: (showPriceCd === 'QSP_PRICE' && item.openFlg === '1') ? '1' : '0',
salePrice: data.data2[i].unitPrice === null ? '0' : data.data2[i].unitPrice,
saleTotPrice: (item.amount * data.data2[i].unitPrice).toString(),
})
@ -732,7 +696,7 @@ export default function Estimate({}) {
/* 케이블 select 변경시 */
const onChangeDisplayCableItem = (value, itemList) => {
itemList.map((item, index) => {
if (item.dispCableFlg === '1' && item.itemTpCd !== 'M12' && item.itemTpCd !== 'S13') {
if (item.dispCableFlg === '1') {
if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true)
}
@ -741,18 +705,6 @@ export default function Estimate({}) {
setCableItem(value)
}
/* 케이블 select 변경시 */
const onChangeDisplayDoubleCableItem = (value, itemList) => {
itemList.map((item, index) => {
if (item.dispCableFlg === '1' && (item.itemTpCd === 'M12' || item.itemTpCd === 'S13')) {
if (value !== '') {
onChangeDisplayItem(value, item.dispOrder, index, true)
}
}
})
setCableDbItem(value)
}
// /
const onChangeDisplayItem = (itemId, dispOrder, index, flag) => {
const param = {
@ -1102,20 +1054,15 @@ export default function Estimate({}) {
item.showSaleTotPrice = '0'
}
if (item.dispCableFlg === '1' ) {
if (item.dispCableFlg === '1') {
dispCableFlgCnt++
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
setCableItem(item.itemId)
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
let pkgAsp = estimateContextState.pkgAsp ? Number(estimateContextState.pkgAsp.replaceAll(',', '')) : 0
@ -1178,20 +1125,14 @@ export default function Estimate({}) {
dispCableFlgCnt++
}
if (item.dispCableFlg === '1'){
if(item.itemTpCd === 'M12' || item.itemTpCd === 'S13') {
setCableDbItem(item.itemId)
}else{
setCableItem(item.itemId)
}
if (item.dispCableFlg === '1') {
setCableItem(item.itemId)
}
}
})
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
totals.vatPrice = totals.supplyPrice * 0.1
@ -1252,26 +1193,10 @@ export default function Estimate({}) {
if (dispCableFlgCnt === 0) {
setCableItem('100038')
setCableDbItem('100037')
}
}
}, [estimateContextState?.itemList, cableItemList])
const [agencyCustList, setAgencyCustList] = useState([])
useEffect(() => {
// 952 - 2 sapSalesStoreCd
if (estimateContextState?.sapSalesStoreCd && session?.storeLvl === '1') {
const param = {
sapSalesStoreCd: estimateContextState.sapSalesStoreCd,
}
const apiUrl = `api/estimate/agency-cust-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => {
if (isNotEmptyArray(res?.data)) {
setAgencyCustList(res?.data)
}
})
}
}, [estimateContextState?.sapSalesStoreCd])
return (
<div className="sub-content estimate">
<div className="sub-content-inner">
@ -1282,7 +1207,7 @@ export default function Estimate({}) {
<div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.objectNo')}</div>
<div className="estimate-name">
{currentObjectNo} (Plan No: {currentPid})
{currentObjectNo} (Plan No: {planNo})
</div>
</div>
<div className="estimate-box">
@ -1416,79 +1341,36 @@ export default function Estimate({}) {
{getMessage('estimate.detail.estimateType')} <span className="important">*</span>
</th>
<td colSpan={3}>
<div className="form-flex-wrap">
<div className="radio-wrap">
{/*pkgRank is null, empty 인 경우 : 사용불가, 이전에 등록된 경우 사용가능, style로 제어*/}
<div
className="d-check-radio light mr10"
style={{
display:
(isNotEmptyArray(storePriceList) > 0 && storePriceList[0].pkgRank !== null && storePriceList[0].pkgRank !== '') ||
estimateContextState?.estimateType === 'YJSS'
? ''
: 'none',
<div className="radio-wrap">
<div className="d-check-radio light mr10">
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
//
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
>
<input
type="radio"
name="estimateType"
id="YJSS"
value={'YJSS'}
checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => {
//
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value, setEstimateContextState })
}}
/>
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
/>
<label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
name="estimateType"
id="YJOD"
value={'YJOD'}
checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => {
setHandlePricingFlag(true)
setEstimateContextState({ estimateType: e.target.value })
}}
/>
<label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div>
{session?.storeLvl === '100000' && agencyCustList.length > 0 ? ( // 1 => 100000
<div className="form-flex-select ml10">
<label htmlFor="">{getMessage('estimate.detail.agency')}</label>
<div className="select-wrap" style={{ width: '400px' }}>
<Select
id="agencyName"
instanceId="agencyName"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={agencyCustList}
onChange={(e) => {
if (isObjectNotEmpty(e)) {
setEstimateContextState({ secSapSalesStoreCd: e.sapSalesStoreCd })
} else {
setEstimateContextState({ secSapSalesStoreCd: '' })
}
}}
getOptionLabel={(x) => x.sapSalesStoreNm}
getOptionValue={(x) => x.sapSalesStoreCd}
isClearable={true}
isSearchable={true}
value={agencyCustList.filter(function (option) {
return option.sapSalesStoreCd === estimateContextState.secSapSalesStoreCd
})}
/>
</div>
</div>
) : (
''
)}
</div>
</td>
</tr>
@ -1847,17 +1729,15 @@ export default function Estimate({}) {
<button
type="button"
className="btn-origin grey ml5"
onClick={(event) => {
onClick={() => {
setHandlePricingFlag(true)
handlePricingBtn(showPriceCd)
handlePricing(showPriceCd)
}}
>
{getMessage('estimate.detail.showPrice.pricingBtn')}
</button>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.singleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
@ -1867,34 +1747,11 @@ export default function Estimate({}) {
value={cableItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => {
if(!row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</option>
}
})}
</select>
</div>
</div>
<div className="product-price-wrap ml10">
<div className="product-price-tit">{getMessage('estimate.detail.header.doubleCable')}</div>
<div className="select-wrap">
<select
className="select-light"
onChange={(e) => {
onChangeDisplayDoubleCableItem(e.target.value, estimateContextState.itemList)
}}
value={cableDbItem}
>
{cableItemList.length > 0 &&
cableItemList.map((row) => {
if(row.clRefChr2.includes('S')){
return <option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2.replace('S','')}
</option>
}
})}
cableItemList.map((row) => (
<option key={row.clRefChr1} value={row.clRefChr1}>
{row.clRefChr2}
</option>
))}
</select>
</div>
</div>
@ -1977,7 +1834,7 @@ export default function Estimate({}) {
<input
type="checkbox"
id={item?.dispOrder}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1X'}
disabled={!!item?.paDispOrder || item.dispCableFlg === '1'}
onChange={() => onChangeSelect(item.dispOrder)}
checked={!!selection.has(item.dispOrder)}
/>
@ -2002,13 +1859,8 @@ export default function Estimate({}) {
}
}}
menuPlacement={'auto'}
getOptionLabel={(x) => x.itemName + ' (' + x.itemNo + ')'}
getOptionLabel={(x) => x.itemName}
getOptionValue={(x) => x.itemNo}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{props.data.itemName}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={!!item?.paDispOrder}
value={displayItemList.filter(function (option) {
@ -2028,17 +1880,12 @@ export default function Estimate({}) {
placeholder="Select"
options={cableItemList}
menuPlacement={'auto'}
getOptionLabel={(x) => x.clRefChr3 + ' (' + x.clRefChr1 + ')'}
getOptionLabel={(x) => x.clRefChr3}
getOptionValue={(x) => x.clRefChr1}
components={{
SingleValue: ({ children, ...props }) => {
return <components.SingleValue {...props}>{(item.itemTpCd === 'M12' || item.itemTpCd === 'S13')? item.itemName : props.data.clRefChr3}</components.SingleValue>
},
}}
isClearable={false}
isDisabled={true}
value={cableItemList.filter(function (option) {
return (item.itemTpCd === 'M12' || item.itemTpCd === 'S13' )? item.itemId : option.clRefChr1 === item.itemId
return option.clRefChr1 === item.itemId
})}
/>
)}
@ -2089,11 +1936,7 @@ export default function Estimate({}) {
<input
type="text"
className="input-light al-r"
value={
item.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))
}
value={convertNumberToPriceDecimal(item?.showSalePrice === '0' ? null : item?.salePrice?.replaceAll(',', ''))}
disabled={
item.openFlg === '1'
? true
@ -2121,17 +1964,15 @@ export default function Estimate({}) {
</div>
</td>
<td className="al-r">
{item?.openFlg === '1'
? 'OPEN'
: convertNumberToPriceDecimal(
item?.showSaleTotPrice === '0'
{convertNumberToPriceDecimal(
item?.showSaleTotPrice === '0'
? null
: item?.amount === ''
? null
: item?.saleTotPrice === '0'
? null
: item?.amount === ''
? null
: item?.saleTotPrice === '0'
? null
: item?.saleTotPrice?.replaceAll(',', ''),
)}
: item?.saleTotPrice?.replaceAll(',', ''),
)}
</td>
</tr>
)

View File

@ -6,7 +6,6 @@ import { useRecoilValue } from 'recoil'
import { floorPlanObjectState, estimateState } from '@/store/floorPlanObjectAtom'
import { usePathname, useSearchParams } from 'next/navigation'
import { QcastContext } from '@/app/QcastProvider'
import { sessionStore } from '@/store/commonAtom'
export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDownPopLockFlg }) {
const { setIsGlobalLoading } = useContext(QcastContext)
@ -31,7 +30,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
// recoil
const objectRecoil = useRecoilValue(floorPlanObjectState)
const estimateRecoilState = useRecoilValue(estimateState)
const sessionState = useRecoilValue(sessionStore)
//
const handleFileDown = async () => {
const url = '/api/estimate/excel-download'
@ -68,8 +67,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
schWeightFlg: schWeightFlg,
schDrawingFlg: defaultSchDrawingFlg,
pwrGnrSimType: 'D', //default
userId: sessionState.userId ? sessionState.userId : "",
saleStoreId: sessionState.storeId ? sessionState.storeId : "",
}
const options = { responseType: 'blob' }
@ -133,7 +130,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
<div className="common-table">
<table>
<colgroup>
<col style={{ width: '220px' }} />
<col style={{ width: '260px' }} />
<col />
</colgroup>
<tbody>
@ -186,7 +183,7 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
/>
<label htmlFor="schUnitPricePdfFlg0">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg0')}</label>
</div>
<div className="d-check-radio light mr10">
<div className="d-check-radio light ">
<input
type="radio"
id="schUnitPricePdfFlg1"
@ -200,20 +197,6 @@ export default function DocDownOptionPop({ planNo, setEstimatePopupOpen, docDown
/>
<label htmlFor="schUnitPricePdfFlg1">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.pdfFlg1')}</label>
</div>
<div className="d-check-radio light">
<input
type="radio"
id="schUnitPriceExcelFlg2"
name="schUnitPriceFlg"
value={'4'}
checked={schUnitPriceFlg === '4'}
onChange={(e) => {
setSchDownload('EXCEL2')
setSchUnitPriceFlg(e.target.value)
}}
/>
<label htmlFor="schUnitPriceExcelFlg2">{getMessage('estimate.detail.docPopup.schUnitPriceFlg.excelFlg2')}</label>
</div>
</div>
</td>
</tr>

View File

@ -182,78 +182,4 @@ export const QLine = fabric.util.createClass(fabric.Line, {
}
return this
},
setCoords: function () {
// 부모 클래스의 setCoords 호출
this.callSuper('setCoords')
// QLine의 경우 추가 처리 - 항상 강제로 재계산
if (this.canvas) {
// 모든 좌표 관련 캐시 초기화
delete this.oCoords
delete this.aCoords
delete this.__corner
// 다시 부모 setCoords 호출
this.callSuper('setCoords')
// 한 번 더 강제로 bounding rect 재계산
this._clearCache && this._clearCache()
}
},
containsPoint: function (point) {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = point
if (this.canvas) {
const vpt = this.canvas.viewportTransform
if (vpt) {
// viewport transform 역변환
const inverted = fabric.util.invertTransform(vpt)
localPoint = fabric.util.transformPoint(point, inverted)
}
}
// 기본 boundingRect 사용하되 줌을 고려하여 선택 영역 조정
const boundingRect = this.getBoundingRect(true)
// 선의 방향 판단
const dx = Math.abs(this.x2 - this.x1)
const dy = Math.abs(this.y2 - this.y1)
const isVertical = dx < dy && dx < 10
const isDiagonal = dx > 10 && dy > 10
// 줌 레벨에 따른 선택 영역 조정
const zoom = this.canvas ? this.canvas.getZoom() : 1
const baseMultiplier = 1 // 줌이 클수록 선택 영역을 줄임
let reducedWidth, reducedHeight
if (isDiagonal) {
reducedWidth = Math.max(boundingRect.width / 2, 10 * baseMultiplier)
reducedHeight = Math.max(boundingRect.height / 2, 10 * baseMultiplier)
} else if (isVertical) {
reducedWidth = Math.max(boundingRect.width * 2, 20 * baseMultiplier)
reducedHeight = boundingRect.height
} else {
reducedWidth = boundingRect.width
reducedHeight = Math.max(boundingRect.height * 2, 20 * baseMultiplier)
}
// 축소된 영역의 중심점 계산
const centerX = boundingRect.left + boundingRect.width / 2
const centerY = boundingRect.top + boundingRect.height / 2
// 축소된 영역의 경계 계산
const left = centerX - reducedWidth / 2
const top = centerY - reducedHeight / 2
const right = centerX + reducedWidth / 2
const bottom = centerY + reducedHeight / 2
// 점이 축소된 영역 내에 있는지 확인
return localPoint.x >= left && localPoint.x <= right && localPoint.y >= top && localPoint.y <= bottom
},
})

View File

@ -2,7 +2,7 @@ import { fabric } from 'fabric'
import { v4 as uuidv4 } from 'uuid'
import { QLine } from '@/components/fabric/QLine'
import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util'
import { calculateAngle, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import { calculateAngle, drawGabledRoof, drawRidgeRoof, drawShedRoof, toGeoJSON } from '@/util/qpolygon-utils'
import * as turf from '@turf/turf'
import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import Big from 'big.js'
@ -29,13 +29,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.texts = []
this.hips = []
this.ridges = []
this.connectRidges = []
this.cells = []
this.innerLines = []
this.children = []
this.separatePolygon = []
this.toFixed = options.toFixed ?? 1
this.baseLines = []
// this.colorLines = []
// 소수점 전부 제거
points.forEach((point) => {
@ -46,11 +45,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
options.sort = options.sort ?? true
options.parentId = options.parentId ?? null
this.isSortedPoints = false
if (!options.sort && points.length <= 8) {
points = sortedPointLessEightPoint(points)
this.isSortedPoints = true
} else {
let isDiagonal = false
points.forEach((point, i) => {
@ -66,7 +62,6 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
if (!isDiagonal) {
points = sortedPoints(points)
this.isSortedPoints = true
}
}
@ -124,12 +119,10 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.addLengthText()
this.on('moving', () => {
this.initLines()
this.addLengthText()
})
this.on('modified', (e) => {
this.initLines()
this.addLengthText()
})
@ -190,8 +183,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.lines = []
this.getCurrentPoints().forEach((point, i) => {
const nextPoint = this.getCurrentPoints()[(i + 1) % this.points.length]
this.points.forEach((point, i) => {
const nextPoint = this.points[(i + 1) % this.points.length]
const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], {
stroke: this.stroke,
strokeWidth: this.strokeWidth,
@ -210,47 +203,14 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
line.startPoint = point
line.endPoint = nextPoint
this.lines.push(line)
this.calculateDegree()
})
},
calculateDegree() {
const degrees = []
// polygon.lines를 순회하며 각도를 구해 출력
this.lines.forEach((line, idx) => {
const dx = line.x2 - line.x1
const dy = line.y2 - line.y1
const rad = Math.atan2(dy, dx)
const degree = (rad * 180) / Math.PI
degrees.push(degree)
})
function isMultipleOf45(degree, epsilon = 1) {
return Math.abs(degree % 45) <= epsilon || Math.abs((degree % 45) - 45) <= epsilon
}
this.isMultipleOf45 = degrees.every((degree) => isMultipleOf45(degree))
},
/**
* 보조선 그리기
* @param settingModalFirstOptions
*/
drawHelpLine(settingModalFirstOptions) {
/* innerLines 초기화 */
this.canvas
.getObjects()
.filter(
(obj) =>
obj.parentId === this.id &&
obj.name !== POLYGON_TYPE.WALL &&
obj.name !== POLYGON_TYPE.ROOF &&
obj.name !== 'outerLine' &&
obj.name !== 'baseLine',
// && obj.name !== 'outerLinePoint',
)
.forEach((obj) => this.canvas.remove(obj))
this.canvas.renderAll()
let textMode = 'plane'
const dimensionDisplay = settingModalFirstOptions?.dimensionDisplay.find((opt) => opt.selected).id
@ -271,11 +231,21 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
const types = []
this.lines.forEach((line) => types.push(line.attributes.type))
const eavesType = [LINE_TYPE.WALLLINE.EAVES, LINE_TYPE.WALLLINE.HIPANDGABLE]
const gableType = [LINE_TYPE.WALLLINE.GABLE, LINE_TYPE.WALLLINE.JERKINHEAD]
// const isEaves = types.every((type) => eavesType.includes(type))
const gableOdd = types.filter((type, i) => i % 2 === 0)
const gableEven = types.filter((type, i) => i % 2 === 1)
const hasShed = types.includes(LINE_TYPE.WALLLINE.SHED)
if (hasShed) {
// A형, B형 박공 지붕
if (
(gableOdd.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableEven.every((type) => gableType.includes(type))) ||
(gableEven.every((type) => type === LINE_TYPE.WALLLINE.EAVES) && gableOdd.every((type) => gableType.includes(type)))
) {
drawGabledRoof(this.id, this.canvas, textMode)
} else if (hasShed) {
const sheds = this.lines.filter((line) => line.attributes !== undefined && line.attributes.type === LINE_TYPE.WALLLINE.SHED)
const areLinesParallel = function (line1, line2) {
const angle1 = calculateAngle(line1.startPoint, line1.endPoint)
@ -396,15 +366,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
this.canvas = canvas
},
fillCellABType(
cell = {
width: 50,
height: 100,
padding: 5,
wallDirection: 'left',
referenceDirection: 'none',
startIndex: -1,
isCellCenter: false,
},
cell = { width: 50, height: 100, padding: 5, wallDirection: 'left', referenceDirection: 'none', startIndex: -1, isCellCenter: false },
) {
const points = this.points
@ -706,119 +668,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, {
return intersects % 2 === 1
},
inPolygonImproved(point) {
const vertices = this.points
let inside = false
const testX = Number(point.x.toFixed(this.toFixed))
const testY = Number(point.y.toFixed(this.toFixed))
for (let i = 0, j = vertices.length - 1; i < vertices.length; j = i++) {
const xi = Number(vertices[i].x.toFixed(this.toFixed))
const yi = Number(vertices[i].y.toFixed(this.toFixed))
const xj = Number(vertices[j].x.toFixed(this.toFixed))
const yj = Number(vertices[j].y.toFixed(this.toFixed))
// 점이 정점 위에 있는지 확인
if (Math.abs(xi - testX) < 0.01 && Math.abs(yi - testY) < 0.01) {
return true
}
// 점이 선분 위에 있는지 확인
if (this.isPointOnSegment(point, { x: xi, y: yi }, { x: xj, y: yj })) {
return true
}
// Ray casting 알고리즘 - 부동소수점 정밀도 개선
if (yi > testY !== yj > testY) {
const denominator = yj - yi
if (Math.abs(denominator) > 1e-10) {
// 0으로 나누기 방지
const intersection = ((xj - xi) * (testY - yi)) / denominator + xi
if (testX < intersection) {
inside = !inside
}
}
}
}
return inside
},
isPointOnSegment(point, segStart, segEnd) {
const tolerance = 0.1
const dxSegment = segEnd.x - segStart.x
const dySegment = segEnd.y - segStart.y
const dxPoint = point.x - segStart.x
const dyPoint = point.y - segStart.y
// 벡터의 외적을 계산하여 점이 선분 위에 있는지 확인
const crossProduct = Math.abs(dxPoint * dySegment - dyPoint * dxSegment)
if (crossProduct > tolerance) {
return false
}
// 점이 선분의 범위 내에 있는지 확인
const dotProduct = dxPoint * dxSegment + dyPoint * dySegment
const squaredLength = dxSegment * dxSegment + dySegment * dySegment
return dotProduct >= 0 && dotProduct <= squaredLength
},
setCoords: function () {
// 부모 클래스의 setCoords 호출
this.callSuper('setCoords')
// QPolygon의 경우 추가 처리 - 항상 강제로 재계산
if (this.canvas) {
// 모든 좌표 관련 캐시 초기화
delete this.oCoords
delete this.aCoords
delete this.__corner
// 다시 부모 setCoords 호출
this.callSuper('setCoords')
// 한 번 더 강제로 bounding rect 재계산
this._clearCache && this._clearCache()
}
},
containsPoint: function (point) {
// 먼저 좌표 업데이트
this.setCoords()
// 캔버스 줌과 viewport transform 고려한 좌표 변환
let localPoint = point
if (this.canvas) {
const vpt = this.canvas.viewportTransform
if (vpt) {
// viewport transform 역변환
const inverted = fabric.util.invertTransform(vpt)
localPoint = fabric.util.transformPoint(point, inverted)
}
}
// 오브젝트의 transform matrix를 고려한 좌표 변환
const matrix = this.calcTransformMatrix()
const invertedMatrix = fabric.util.invertTransform(matrix)
const transformedPoint = fabric.util.transformPoint(localPoint, invertedMatrix)
// pathOffset을 고려한 최종 좌표 계산
const pathOffset = this.get('pathOffset')
const finalPoint = {
x: Number((transformedPoint.x + pathOffset.x).toFixed(this.toFixed)),
y: Number((transformedPoint.y + pathOffset.y).toFixed(this.toFixed)),
}
if (this.name === POLYGON_TYPE.ROOF && this.isFixed) {
const isInside = this.inPolygonImproved(finalPoint)
if (!this.selectable) {
this.set('selectable', isInside)
}
const isInside = this.inPolygon(point)
this.set('selectable', isInside)
return isInside
} else {
return this.inPolygonImproved(finalPoint)
return this.callSuper('containsPoint', point)
}
},

View File

@ -2,7 +2,7 @@
import { useContext, useEffect, useRef } from 'react'
import { useRecoilValue, useResetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import QContextMenu from '@/components/common/context-menu/QContextMenu'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
@ -13,7 +13,7 @@ import { useContextMenu } from '@/hooks/useContextMenu'
import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitialize'
import { currentMenuState } from '@/store/canvasAtom'
import { totalDisplaySelector } from '@/store/settingAtom'
import { POLYGON_TYPE } from '@/common/common'
import { MENU, POLYGON_TYPE } from '@/common/common'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { QcastContext } from '@/app/QcastProvider'
import {
@ -30,14 +30,11 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useEvent } from '@/hooks/useEvent'
import { compasDegAtom } from '@/store/orientationAtom'
import { hotkeyStore } from '@/store/hotkeyAtom'
import { usePopup } from '@/hooks/usePopup'
export default function CanvasFrame() {
const canvasRef = useRef(null)
const { canvas } = useCanvas('canvas')
const { canvasLoadInit, gridInit } = useCanvasConfigInitialize()
const { closeAll } = usePopup()
const currentMenu = useRecoilValue(currentMenuState)
const { floorPlanState } = useContext(FloorPlanContext)
const { contextMenu, handleClick } = useContextMenu()
@ -66,34 +63,16 @@ export default function CanvasFrame() {
canvas?.loadFromJSON(JSON.parse(plan.canvasStatus), function () {
canvasLoadInit() //config
canvas?.renderAll() // .
if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE).length > 0) {
setSelectedMenu('module')
} else if (canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.WALL).length > 0) {
setSelectedMenu('outline')
} else {
setSelectedMenu('surface')
}
const roofs = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
roofs.forEach((roof) => {
const auxiliaryLines = canvas
.getObjects()
.filter((obj) => obj.name === 'auxiliaryLine' && roof.inPolygonImproved(obj.startPoint) && roof.inPolygonImproved(obj.endPoint))
auxiliaryLines.forEach((auxiliaryLine) => {
roof.innerLines.push(auxiliaryLine)
})
})
initEvent()
})
} else {
setSelectedMenu(null)
}
Object.keys(currentCanvasPlan).length > 0 && canvas && handleModuleSelectionTotal()
} else {
setSelectedMenu(null)
}
gridInit()
}
@ -113,8 +92,6 @@ export default function CanvasFrame() {
useEffect(() => {
setIsGlobalLoading(false)
// .
closeAll()
return () => {
canvas?.clear()
@ -133,38 +110,6 @@ export default function CanvasFrame() {
resetPcsCheckState()
}
/**
* 캔버스가 있을 경우 핫키 이벤트 처리
* hotkeyStore에 핫키 이벤트 리스너 추가
*
* const hotkeys = [
{ key: 'c', fn: () => asdf() },
{ key: 'v', fn: () => qwer() },
]
setHotkeyStore(hotkeys)
*/
const hotkeyState = useRecoilValue(hotkeyStore)
const hotkeyHandlerRef = useRef(null)
useEffect(() => {
hotkeyHandlerRef.current = (e) => {
hotkeyState.forEach((hotkey) => {
if (e.key === hotkey.key) {
hotkey.fn()
}
})
}
document.addEventListener('keyup', hotkeyHandlerRef.current)
return () => {
if (hotkeyHandlerRef.current) {
document.removeEventListener('keyup', hotkeyHandlerRef.current)
}
}
}, [hotkeyState])
/** 핫키 이벤트 처리 끝 */
return (
<div className="canvas-frame">
<canvas ref={canvasRef} id="canvas" style={{ position: 'relative' }}></canvas>

View File

@ -31,13 +31,12 @@ export default function CanvasLayout({ children }) {
return (
<div className="canvas-layout">
<div className={`canvas-page-list ${['outline', 'surface', 'module'].includes(selectedMenu) ? 'active' : ''}`}>
<div className="canvas-id">{objectNo}</div>
<div className="canvas-plane-wrap">
{plans.map((plan, index) => (
<button
key={`plan-${plan.id}`}
className={`canvas-page-box ${plan.isCurrent === true ? 'on' : ''}`}
onClick={() => (plan.isCurrent ? '' : handleCurrentPlan(plan.id))}
onClick={() => handleCurrentPlan(plan.id)}
>
<span>{`Plan ${plan.planNo}`}</span>
{plans.length > 1 && !pathname.includes('/estimate') && !pathname.includes('/simulator') && (

View File

@ -4,7 +4,7 @@ import { useContext, useEffect, useState } from 'react'
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
@ -25,7 +25,7 @@ import { useCommonUtils } from '@/hooks/common/useCommonUtils'
import useMenu from '@/hooks/common/useMenu'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { useAxios } from '@/hooks/useAxios'
import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState, currentCanvasPlanState } from '@/store/canvasAtom'
import { canvasSettingState, canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom'
import { sessionStore } from '@/store/commonAtom'
import { outerLinePointsState } from '@/store/outerLineAtom'
import { appMessageStore, globalLocaleStore } from '@/store/localeAtom'
@ -50,9 +50,7 @@ import JA from '@/locales/ja.json'
import { QcastContext } from '@/app/QcastProvider'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePolygon } from '@/hooks/usePolygon'
import { useTrestle } from '@/hooks/module/useTrestle'
export default function CanvasMenu(props) {
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const { selectedMenu, setSelectedMenu } = props
const pathname = usePathname()
const router = useRouter()
@ -69,7 +67,6 @@ export default function CanvasMenu(props) {
const globalLocale = useRecoilValue(globalLocaleStore)
const canvas = useRecoilValue(canvasState)
const { handleZoomClear, handleZoom } = useCanvasEvent()
const { setAllModuleSurfaceIsComplete, isAllComplete } = useTrestle()
const { handleMenu } = useMenu()
// const urlParams = useSearchParams()
const { handleEstimateSubmit, fetchSetting, estimateContextState, setEstimateContextState } = useEstimateController()
@ -99,7 +96,7 @@ export default function CanvasMenu(props) {
const [lockButtonStyle, setLockButtonStyle] = useState('') //
const setFloorPlanObjectNo = useSetRecoilState(floorPlanObjectState) //
const resetCommonUtils = useResetRecoilState(commonUtilsState)
//
const { objectNo, pid } = floorPlanState
@ -118,7 +115,7 @@ export default function CanvasMenu(props) {
const params = {
objectNo: objectNo,
planNo: selectedPlan?.planNo ? selectedPlan.planNo : pid,
planNo: selectedPlan.planNo,
schDownload: donwloadType,
schDrawingFlg: drawingFlg,
pwrGnrSimType: pwrGnrSimTypeRecoil.type,
@ -167,7 +164,6 @@ export default function CanvasMenu(props) {
}
const onClickNav = async (menu) => {
resetCommonUtils()
switch (menu.type) {
case 'drawing':
swalFire({
@ -198,7 +194,6 @@ export default function CanvasMenu(props) {
confirmFn: () => {
//
setAllModuleSurfaceIsComplete(false)
const moduleSurfacesArray = canvas
.getObjects()
.filter((obj) => [POLYGON_TYPE.MODULE_SETUP_SURFACE, POLYGON_TYPE.MODULE, POLYGON_TYPE.OBJECT_SURFACE].includes(obj.name))
@ -235,18 +230,11 @@ export default function CanvasMenu(props) {
router.push(`/floor-plan?pid=${pid}&objectNo=${objectNo}`)
setSelectedMenu('module')
}
await reloadCanvasStatus(objectNo, currentCanvasPlan?.planNo ?? pid)
await reloadCanvasStatus(objectNo, pid)
break
case 'estimate':
if (selectedMenu !== 'simulation') {
if (!isAllComplete()) {
swalFire({ text: getMessage('estimate.menu.move.valid1') })
return
}
}
setIsGlobalLoading(true)
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.estimateDate !== null) {
@ -254,7 +242,7 @@ export default function CanvasMenu(props) {
setCurrentMenu(menu.title)
setFloorPlanObjectNo({ floorPlanObjectNo: objectNo })
setIsGlobalLoading(false)
router.push(`/floor-plan/estimate/5?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
router.push(`/floor-plan/estimate/5?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
if (pathname === '/floor-plan/estimate/5') {
setIsGlobalLoading(false)
}
@ -267,13 +255,13 @@ export default function CanvasMenu(props) {
break
case 'simulation':
setIsGlobalLoading(true)
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan?.planNo ?? pid}/detail` }).then((res) => {
promiseGet({ url: `/api/estimate/${objectNo}/${selectedPlan.planNo}/detail` }).then((res) => {
if (res.status === 200) {
const estimateDetail = res.data
if (estimateDetail.estimateDate !== null && estimateDetail.docNo) {
setSelectedMenu(menu.type)
setCurrentMenu(menu.title)
router.push(`/floor-plan/simulator/6?pid=${selectedPlan?.planNo ?? pid}&objectNo=${objectNo}`)
router.push(`/floor-plan/simulator/6?pid=${selectedPlan.planNo}&objectNo=${objectNo}`)
if (pathname === '/floor-plan/simulator/6') {
setIsGlobalLoading(false)
}
@ -315,6 +303,7 @@ export default function CanvasMenu(props) {
const settingsModalOptions = useRecoilState(settingModalFirstOptionsState)
useEffect(() => {
console.log(selectedMenu)
if (selectedMenu === 'placement') {
onClickPlacementInitialMenu()
}
@ -559,26 +548,13 @@ export default function CanvasMenu(props) {
{
<div className={`vertical-horizontal ${verticalHorizontalMode ? 'on' : ''}`}>
<span>{getMessage('plan.mode.vertical.horizontal')}</span>
<button
title={`${getMessage('plan.mode.vertical.horizontal')} ${verticalHorizontalMode ? 'ON' : 'OFF'}`}
onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}
>
{verticalHorizontalMode ? 'ON' : 'OFF'}
</button>
<button onClick={() => setVerticalHorizontalMode(!verticalHorizontalMode)}>{verticalHorizontalMode ? 'ON' : 'OFF'}</button>
</div>
}
<div className="btn-from">
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')} title="文字作成"></button>
<button
className={`btn02 ${commonUtils.dimension ? 'active' : ''} `}
onClick={() => commonFunctions('dimension')}
title="寸法作成"
></button>
<button
className={`btn03 ${commonUtils.distance ? 'active' : ''} `}
onClick={() => commonFunctions('distance')}
title="定規"
></button>
<button className={`btn01 ${commonUtils.text ? 'active' : ''}`} onClick={() => commonFunctions('text')}></button>
<button className={`btn02 ${commonUtils.dimension ? 'active' : ''} `} onClick={() => commonFunctions('dimension')}></button>
<button className={`btn03 ${commonUtils.distance ? 'active' : ''} `} onClick={() => commonFunctions('distance')}></button>
</div>
{isObjectNotEmpty(selectedRoofMaterial) && addedRoofs.length > 0 && (
<div className="select-box">
@ -604,7 +580,6 @@ export default function CanvasMenu(props) {
sourceKey={'index'}
targetKey={'index'}
disabled={+basicSetting.roofSizeSet === 3}
tagTitle={'屋根材変更'}
/>
}
</div>
@ -613,10 +588,9 @@ export default function CanvasMenu(props) {
<button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
title="読込"
></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup} title="設定"></button>
<button className="btn04" onClick={handlePopup}></button>
</div>
<div className="size-control">
<button
@ -625,9 +599,7 @@ export default function CanvasMenu(props) {
handleZoom(false)
}}
></button>
<span onClick={handleZoomClear} title="拡大・縮小">
{canvasZoom}%
</span>
<span onClick={handleZoomClear}>{canvasZoom}%</span>
<button
className="control-btn plus"
onClick={() => {
@ -636,8 +608,8 @@ export default function CanvasMenu(props) {
></button>
</div>
<div className="btn-from">
<button className="btn08" onClick={handleSaveCanvas} title="保存"></button>
<button className="btn09" onClick={handleLeaveCanvas} title="物件検索画面へ移動"></button>
<button className="btn08" onClick={handleSaveCanvas}></button>
<button className="btn09" onClick={handleLeaveCanvas}></button>
</div>
</>
)}
@ -662,7 +634,7 @@ export default function CanvasMenu(props) {
onClick={() => setEstimatePopupOpen(true)}
>
<span className="ico ico01"></span>
<span className="name">{getMessage('plan.menu.estimate.docDownload')}</span>
<span className="name">{getMessage('plan.menu.estimate.docDown')}</span>
</button>
<button type="button" style={{ display: saveButtonStyle }} className="btn-frame gray ico-flx" onClick={handleEstimateSubmit}>
<span className="ico ico02"></span>

View File

@ -1,18 +1,15 @@
'use client'
import { useContext, useEffect } from 'react'
import { useEffect } from 'react'
import CanvasMenu from '@/components/floor-plan/CanvasMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { usePopup } from '@/hooks/usePopup'
import '@/styles/contents.scss'
import { notFound, useSearchParams } from 'next/navigation'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { useRecoilState, useResetRecoilState } from 'recoil'
import { correntObjectNoState } from '@/store/settingAtom'
import { currentMenuState } from '@/store/canvasAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { useAxios } from '@/hooks/useAxios'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
export default function FloorPlan({ children }) {
const [correntObjectNo, setCurrentObjectNo] = useRecoilState(correntObjectNoState)
@ -23,39 +20,12 @@ export default function FloorPlan({ children }) {
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const { fetchSettings } = useCanvasSetting()
const resetCurrentMenu = useResetRecoilState(currentMenuState)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { promiseGet } = useAxios(globalLocaleState)
const { setManagementState } = useContext(GlobalDataContext)
useEffect(() => {
getStuffDetailInfo()
return () => {
resetCurrentMenu()
}
}, [])
const getStuffDetailInfo = () => {
promiseGet({ url: `/api/object/${objectNo}/detail` }).then((res) => {
if (res.status === 200) {
const { data } = res
console.log(data)
let surfaceTypeValue
if (res.data.surfaceType === 'Ⅲ・Ⅳ') {
surfaceTypeValue = '3'
} else if (res.data.surfaceType === 'Ⅱ') {
surfaceTypeValue = '2'
}
// 0
if (res.data.installHeight === '0') {
res.data.installHeight = ''
}
setManagementState({ ...res.data, surfaceTypeValue: surfaceTypeValue })
}
})
}
/**
* URL 파라미터에서 objectNo 설정
*/

View File

@ -5,10 +5,9 @@ import { useEffect } from 'react'
import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { canvasState, currentMenuState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
import { useRecoilState, useRecoilValue } from 'recoil'
import { subMenusState } from '@/store/menuAtom'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { commonUtilsState } from '@/store/commonUtilsAtom'
export default function MenuDepth01() {
const canvas = useRecoilValue(canvasState)
@ -17,10 +16,8 @@ export default function MenuDepth01() {
const { selectedMenu, setSelectedMenu } = useCanvasMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
const subMenus = useRecoilValue(subMenusState)
const resetCommonUtils = useResetRecoilState(commonUtilsState)
const onClickMenu = ({ id, menu }) => {
resetCommonUtils()
if (menu === currentMenu) {
handleMenu(selectedMenu)
} else {

View File

@ -120,7 +120,7 @@ export default function ImgLoad() {
value={refImage ? (refImage?.name ?? '') : (currentCanvasPlan?.bgImageName ?? '')}
readOnly
/>
{currentCanvasPlan?.bgImageName && <button className="img-check" onClick={handleFileDelete}></button>}
{refImage && <button className="img-check" onClick={handleFileDelete}></button>}
</div>
</div>
</div>

View File

@ -8,12 +8,8 @@ import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
import OuterLineWall from '@/components/floor-plan/modal/lineTypes/OuterLineWall'
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
import { usePopup } from '@/hooks/usePopup'
import { useEffect } from 'react'
import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { closePopup } = usePopup()
@ -56,15 +52,6 @@ export default function AuxiliaryDrawing({ id, pos = { x: 50, y: 230 } }) {
cutAuxiliary,
} = useAuxiliaryDrawing(id)
useEffect(() => {
return () => {
const auxiliaryLines = canvas.getObjects().filter((line) => line.name === 'auxiliaryLine' && !line.isAuxiliaryFixed) // .
if (auxiliaryLines.length > 0) {
handleFix()
}
}
}, [])
const outerLineProps = {
length1,
setLength1,

View File

@ -40,15 +40,15 @@ export default function AuxiliaryEdit(props) {
if (currentObject) {
copy(
currentObject,
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
)
}
} else {
move(
currentObject,
arrow2 ? (arrow2 === '←' ? Number(+horizonSize / 10) * -1 : Number(+horizonSize / 10)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(+verticalSize / 10) * -1 : Number(+verticalSize / 10)) : 0,
arrow2 ? (arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize)) : 0,
arrow1 ? (arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize)) : 0,
)
}

View File

@ -1,88 +1,109 @@
import { POLYGON_TYPE, MODULE_SETUP_TYPE } from '@/common/common'
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import { useContext, useEffect, useRef, useState } from 'react'
import Module from '@/components/floor-plan/modal/basic/step/Module'
import PitchModule from '@/components/floor-plan/modal/basic/step/pitch/PitchModule'
import PitchPlacement from '@/components/floor-plan/modal/basic/step/pitch/PitchPlacement'
import Placement from '@/components/floor-plan/modal/basic/step/Placement'
import { useRecoilValue, useRecoilState } from 'recoil'
import { canvasSettingState, canvasState, checkedModuleState, isManualModuleSetupState } from '@/store/canvasAtom'
import { usePopup } from '@/hooks/usePopup'
import { Orientation } from '@/components/floor-plan/modal/basic/step/Orientation'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useEvent } from '@/hooks/useEvent'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { addedRoofsState, corridorDimensionSelector, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import Swal from 'sweetalert2'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleSelection } from '@/hooks/module/useModuleSelection'
import { useOrientation } from '@/hooks/module/useOrientation'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import {
canvasState,
checkedModuleState,
currentCanvasPlanState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { loginUserStore } from '@/store/commonAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState } from '@/store/selectedModuleOptions'
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { isObjectNotEmpty } from '@/util/common-utils'
import { useEffect, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
import Trestle from './step/Trestle'
import { currentCanvasPlanState } from '@/store/canvasAtom'
import { POLYGON_TYPE } from '@/common/common'
export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const [tabNum, setTabNum] = useState(1)
const canvasSetting = useRecoilValue(canvasSettingState)
const orientationRef = useRef(null)
const { initEvent } = useEvent()
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
const [isManualModuleLayoutSetup, setIsManualModuleLayoutSetup] = useRecoilState(isManualModuleLayoutSetupState)
const trestleRef = useRef(null)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const addedRoofs = useRecoilValue(addedRoofsState)
const loginUserState = useRecoilValue(loginUserStore)
const currentCanvasPlan = useRecoilValue(currentCanvasPlanState)
const canvas = useRecoilValue(canvasState)
const [basicSetting, setBasicSettings] = useRecoilState(basicSettingState)
const [isClosePopup, setIsClosePopup] = useState({ close: false, id: 0 })
const [checkedModules, setCheckedModules] = useRecoilState(checkedModuleState)
const [roofs, setRoofs] = useState(addedRoofs)
const [manualSetupMode, setManualSetupMode] = useRecoilState(toggleManualSetupModeState)
const [layoutSetup, setLayoutSetup] = useState([{}])
const {
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
} = useModuleSelection({ addedRoofs })
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const { trigger: orientationTrigger } = useCanvasPopupStatusController(1)
const { trigger: trestleTrigger } = useCanvasPopupStatusController(2)
const { trigger: placementTrigger } = useCanvasPopupStatusController(3)
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
// const { initEvent } = useContext(EventContext)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup, manualModuleLayoutSetup, restoreModuleInstArea } =
useModuleBasicSetting(tabNum)
const { manualModuleSetup, autoModuleSetup, manualFlatroofModuleSetup, autoFlatroofModuleSetup } = useModuleBasicSetting(tabNum)
const { updateObjectDate } = useMasterController()
useEffect(() => {
const moduleTabNum = basicSetting.roofSizeSet != 3 ? 3 : 2
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
Swal.fire({
title: getMessage('construction.length.difference'),
icon: 'warning',
})
return
}
//
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: moduleSelectionData.common.stdWindSpeed, //
verticalSnowCover: moduleSelectionData.common.stdSnowLd, //
surfaceType: moduleSelectionData.common.illuminationTpNm, //
installHeight: moduleSelectionData.common.instHt, //
userId: loginUserState.userId, //
})
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
}
setTabNum(tabNum + 1)
}
const placementRef = {
isChidori: useRef('false'),
setupLocation: useRef('eaves'),
isMaxSetup: useRef('false'),
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setIsManualModuleSetup(!isManualModuleSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
useEffect(() => {
let hasModules = canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.MODULE_SETUP_SURFACE)
@ -90,42 +111,23 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
if (hasModules) {
orientationRef.current.handleNextStep()
setTabNum(moduleTabNum)
setTabNum(3)
}
}, [])
useEffect(() => {
if (roofsStore && addedRoofs) {
setRoofs(
addedRoofs.map((roof, index) => {
return {
...roof,
...roofsStore[index]?.addRoof,
construction: roofsStore[index]?.construction,
trestle: roofsStore[index]?.trestle,
trestleDetail: roofsStore[index]?.trestleDetail,
}
}),
)
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: roofsStore.map((roof) => {
return {
roofIndex: roof.roofIndex,
addRoof: roof.addRoof,
construction: roof.construction,
trestle: roof.trestle,
trestleDetail: roof.trestleDetail,
}
}),
})
//
const handleClosePopup = (id) => {
if (tabNum == 3) {
if (isManualModuleSetup) {
setIsManualModuleSetup(false)
}
}
}, [roofsStore, addedRoofs])
setIsClosePopup({ close: true, id: id })
}
useEffect(() => {
if (basicSetting.roofSizeSet !== '3') {
manualModuleSetup()
manualModuleSetup(placementRef)
} else {
manualFlatroofModuleSetup(placementFlatRef)
}
@ -138,224 +140,55 @@ export default function BasicSetting({ id, pos = { x: 50, y: 230 } }) {
setIsManualModuleSetup(false)
}, [checkedModules])
useEffect(() => {
if (basicSetting.roofSizeSet !== '3') {
if (manualSetupMode.indexOf('manualSetup') > -1) {
manualModuleSetup()
} else if (manualSetupMode.indexOf('manualLayoutSetup') > -1) {
manualModuleLayoutSetup(layoutSetup)
} else if (manualSetupMode.indexOf('off') > -1) {
manualModuleSetup()
manualModuleLayoutSetup(layoutSetup)
}
} else {
manualFlatroofModuleSetup(placementFlatRef)
}
if (isClosePopup.close) {
closePopup(isClosePopup.id)
}
}, [manualSetupMode, isClosePopup])
useEffect(() => {
if (isManualModuleLayoutSetup) {
manualModuleLayoutSetup(layoutSetup)
}
}, [layoutSetup])
useEffect(() => {
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode(`off`)
}, [checkedModules])
const handleBtnNextStep = () => {
if (tabNum === 1) {
orientationRef.current.handleNextStep()
setAddedRoofs(roofs)
// setTabNum(tabNum + 1)
return
} else if (tabNum === 2) {
if (basicSetting.roofSizeSet !== '3') {
// if (addedRoofs.length !== moduleSelectionData.roofConstructions.length) {
// Swal.fire({
// title: getMessage('construction.length.difference'),
// icon: 'warning',
// })
// return
// }
trestleRef.current.isComplete().then((res) => {
if (!res) return
})
//
} else {
if (!isObjectNotEmpty(moduleSelectionData.module)) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
setTabNum(tabNum + 1)
}
}
}
const placementFlatRef = {
setupLocation: useRef('south'),
}
const handleManualModuleSetup = () => {
setManualSetupMode(`manualSetup_${!isManualModuleSetup}`)
setIsManualModuleSetup(!isManualModuleSetup)
}
const handleManualModuleLayoutSetup = () => {
setManualSetupMode(`manualLayoutSetup_${!isManualModuleLayoutSetup}`)
setIsManualModuleLayoutSetup(!isManualModuleLayoutSetup)
}
const updateObjectDataApi = async (params) => {
const res = await updateObjectDate(params)
}
//
const handleClosePopup = (id) => {
if (tabNum == 3) {
if (isManualModuleSetup) {
setIsManualModuleSetup(false)
}
if (isManualModuleLayoutSetup) {
setIsManualModuleLayoutSetup(false)
}
}
setIsClosePopup({ close: true, id: id })
}
const orientationProps = {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg, //
setCompasDeg,
selectedModules,
moduleSelectionData,
setModuleSelectionData,
roughnessCodes, //
windSpeedCodes, //
managementState,
setManagementState,
moduleList, //
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight, //
setInstallHeight,
standardWindSpeed, //
setStandardWindSpeed,
verticalSnowCover, //
setVerticalSnowCover,
currentCanvasPlan,
loginUserState,
handleChangeModule,
handleChangeSurfaceType,
handleChangeWindSpeed,
handleChangeInstallHeight,
handleChangeVerticalSnowCover,
orientationTrigger,
nextStep,
updateObjectDataApi,
}
const trestleProps = {
roofs,
setRoofs,
setRoofsStore,
tabNum,
setTabNum,
moduleSelectionData,
setModuleSelectionData,
trestleTrigger,
}
const placementProps = {}
return (
<WithDraggable isShow={true} pos={pos} className={basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' ? 'll' : 'lx-2'}>
<WithDraggable isShow={true} pos={pos} className="lx-2">
<WithDraggable.Header title={getMessage('plan.menu.module.circuit.setting.default')} onClose={() => handleClosePopup(id)} />
<WithDraggable.Body>
<div className="roof-module-tab">
<div className={`module-tab-bx act`}>{getMessage('modal.module.basic.setting.orientation.setting')}</div>
<span className={`tab-arr ${tabNum !== 1 ? 'act' : ''}`}></span>
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<>
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
<div className={`module-tab-bx ${tabNum === 2 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</>
)}
<div className={`module-tab-bx ${tabNum !== 1 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.setting')}</div>
<span className={`tab-arr ${tabNum === 3 ? 'act' : ''}`}></span>
<div className={`module-tab-bx ${tabNum === 3 ? 'act' : ''}`}>{getMessage('modal.module.basic.setting.module.placement')}</div>
</div>
{tabNum === 1 && <Orientation ref={orientationRef} {...orientationProps} />}
{tabNum === 1 && <Orientation ref={orientationRef} tabNum={tabNum} setTabNum={setTabNum} />}
{/*배치면 초기설정 - 입력방법: 복시도 입력 || 실측값 입력*/}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Trestle ref={trestleRef} {...trestleProps} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && (
<Placement setTabNum={setTabNum} layoutSetup={layoutSetup} setLayoutSetup={setLayoutSetup} />
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 2 && <Module setTabNum={setTabNum} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && tabNum === 3 && <Placement setTabNum={setTabNum} ref={placementRef} />}
{/*배치면 초기설정 - 입력방법: 육지붕*/}
{/* {basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && <PitchModule setTabNum={setTabNum} />} */}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && (
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 2 && <PitchModule setTabNum={setTabNum} />}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && tabNum === 3 && (
<PitchPlacement setTabNum={setTabNum} ref={placementFlatRef} />
)}
<div className="grid-btn-wrap">
{/* {tabNum === 1 && <button className="btn-frame modal mr5">{getMessage('modal.common.save')}</button>} */}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
{tabNum !== 1 && (
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
)}
{/*{tabNum !== 3 && <button className="btn-frame modal act mr5">{getMessage('modal.common.save')}</button>}*/}
{tabNum !== 3 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 3 && (
<>
{tabNum !== 1 && (
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
)}
{tabNum !== 3 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 3 && (
{basicSetting.roofSizeSet && basicSetting.roofSizeSet != '3' && (
<>
<button className={`btn-frame modal mr5 ${isManualModuleLayoutSetup ? 'act' : ''}`} onClick={handleManualModuleLayoutSetup}>
{getMessage('modal.module.basic.setting.row.batch')}
</button>
<button className="btn-frame modal mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.LAYOUT, layoutSetup)}>
{getMessage('modal.module.basic.setting.auto.row.batch')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>
<button className="btn-frame modal act mr5" onClick={() => autoModuleSetup(MODULE_SETUP_TYPE.AUTO)}>
<button className="btn-frame modal act" onClick={() => autoModuleSetup(placementRef)}>
{getMessage('modal.module.basic.setting.auto.placement')}
</button>
</>
)}
</>
)}
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
{tabNum === 1 && (
<button className="btn-frame modal" onClick={handleBtnNextStep}>
Next
</button>
)}
{tabNum === 2 && (
{basicSetting.roofSizeSet && basicSetting.roofSizeSet == '3' && (
<>
<button className="btn-frame modal mr5" onClick={() => setTabNum(tabNum - 1)}>
{getMessage('modal.module.basic.setting.prev')}
</button>
<button className={`btn-frame modal mr5 ${isManualModuleSetup ? 'act' : ''}`} onClick={handleManualModuleSetup}>
{getMessage('modal.module.basic.setting.passivity.placement')}
</button>

View File

@ -1,471 +1,115 @@
import { forwardRef, use, useContext, useEffect, useImperativeHandle, useState } from 'react'
import { forwardRef, useContext, useEffect, useImperativeHandle, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useOrientation } from '@/hooks/module/useOrientation'
import { getDegreeInOrientation } from '@/util/canvas-util'
import { numberCheck } from '@/util/common-utils'
import { addedRoofsState, basicSettingState } from '@/store/settingAtom'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import QSelectBox from '@/components/common/select/QSelectBox'
import { roofsState } from '@/store/roofAtom'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import Swal from 'sweetalert2'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
export const Orientation = forwardRef((props, ref) => {
export const Orientation = forwardRef(({ tabNum }, ref) => {
const { getMessage } = useMessage()
const { trigger: canvasPopupStatusTrigger } = useCanvasPopupStatusController(1)
const { nextStep, compasDeg, setCompasDeg } = useOrientation()
const [hasAnglePassivity, setHasAnglePassivity] = useState(false)
const basicSetting = useRecoilValue(basicSettingState)
const [addedRoofs, setAddedRoofs] = useRecoilState(addedRoofsState) //
const [roofsStore, setRoofsStore] = useRecoilState(roofsState)
const [roofTab, setRoofTab] = useState(0) //
const {
roofs,
setRoofs,
tabNum,
setTabNum,
compasDeg,
setCompasDeg,
selectedModules,
roughnessCodes,
windSpeedCodes,
managementState,
setManagementState,
moduleList,
moduleSelectionData,
setModuleSelectionData,
setSelectedModules,
selectedSurfaceType,
setSelectedSurfaceType,
installHeight,
setInstallHeight,
standardWindSpeed,
setStandardWindSpeed,
verticalSnowCover,
setVerticalSnowCover,
orientationTrigger,
nextStep,
currentCanvasPlan,
loginUserState,
updateObjectDataApi,
} = props
const [inputCompasDeg, setInputCompasDeg] = useState(compasDeg ?? 0)
const [inputInstallHeight, setInputInstallHeight] = useState('0')
const [inputMargin, setInputMargin] = useState('0')
const [inputVerticalSnowCover, setInputVerticalSnowCover] = useState('0')
const [inputRoughness, setInputRoughness] = useState(selectedSurfaceType)
const [inputStandardWindSpeed, setInputStandardWindSpeed] = useState(standardWindSpeed)
const { restoreModuleInstArea } = useModuleBasicSetting()
const moduleData = {
header: [
{ name: getMessage('module'), width: 150, prop: 'module', type: 'color-box' },
{
name: `${getMessage('height')} (mm)`,
prop: 'height',
},
{ name: `${getMessage('width')} (mm)`, prop: 'width' },
{ name: `${getMessage('output')} (W)`, prop: 'output' },
],
}
useEffect(() => {
if (basicSetting.roofSizeSet == '3') {
restoreModuleInstArea()
}
}, [])
useEffect(() => {
if (moduleSelectionData?.common) {
setInputMargin(moduleSelectionData?.common?.margin)
}
}, [moduleSelectionData])
useEffect(() => {
if (selectedModules) {
setSelectedModules(moduleList.find((module) => module.itemId === selectedModules.itemId))
}
}, [selectedModules])
useEffect(() => {
if (selectedSurfaceType) {
setInputRoughness(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
}
}, [selectedSurfaceType])
useEffect(() => {
if (standardWindSpeed) setInputStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
}, [standardWindSpeed])
useEffect(() => {
if (managementState?.installHeight && managementState?.installHeight) {
setSelectedSurfaceType(roughnessCodes.find((code) => code.clCode === managementState?.surfaceTypeValue))
setInputInstallHeight(managementState?.installHeight)
setStandardWindSpeed(windSpeedCodes.find((code) => code.clCode === managementState?.standardWindSpeedId))
setInputVerticalSnowCover(managementState?.verticalSnowCover)
}
}, [managementState])
useImperativeHandle(ref, () => ({
handleNextStep,
}))
const handleNextStep = () => {
if (isComplete()) {
const common = {
illuminationTp: inputRoughness.clCode,
illuminationTpNm: inputRoughness.clCodeNm,
instHt: inputInstallHeight,
stdWindSpeed: inputStandardWindSpeed?.clCode,
stdSnowLd: inputVerticalSnowCover,
saleStoreNorthFlg: managementState?.saleStoreNorthFlg,
moduleTpCd: selectedModules.itemTp,
moduleItemId: selectedModules.itemId,
margin: inputMargin,
}
setCompasDeg(inputCompasDeg)
setInstallHeight(inputInstallHeight)
setVerticalSnowCover(inputVerticalSnowCover)
setSelectedSurfaceType(inputRoughness)
setStandardWindSpeed(inputStandardWindSpeed)
nextStep(inputCompasDeg)
setManagementState({
...managementState,
installHeight: inputInstallHeight,
verticalSnowCover: inputVerticalSnowCover,
standardWindSpeedId: inputStandardWindSpeed?.clCode,
surfaceType: inputRoughness.clCodeNm,
surfaceTypeValue: inputRoughness.clCode,
})
setModuleSelectionData({
...moduleSelectionData,
module: {
...selectedModules,
},
common,
})
orientationTrigger({
compasDeg: inputCompasDeg,
common: common,
module: {
...selectedModules,
},
margin: inputMargin,
})
updateObjectDataApi({
objectNo: currentCanvasPlan.objectNo, //_no
standardWindSpeedId: inputStandardWindSpeed?.clCode, //
verticalSnowCover: inputVerticalSnowCover, //
surfaceType: inputRoughness.clCodeNm, //
installHeight: inputInstallHeight, //
userId: loginUserState.userId, //
})
setTabNum(2)
} else {
if (!selectedModules || !selectedModules.itemId) {
Swal.fire({
title: getMessage('module.not.found'),
icon: 'warning',
})
return
}
}
nextStep()
canvasPopupStatusTrigger(compasDeg)
}
useEffect(() => {
checkDegree(compasDeg)
}, [compasDeg])
const checkDegree = (e) => {
if (e === '-0' || e === '-') {
setInputCompasDeg('-')
setCompasDeg('-')
return
}
if (e === '0-') {
setInputCompasDeg('-0')
setCompasDeg('-0')
return
}
if (Number(e) >= -180 && Number(e) <= 180) {
if (numberCheck(Number(e))) {
setInputCompasDeg(Number(e))
setCompasDeg(Number(e))
}
} else {
setInputCompasDeg(compasDeg)
setCompasDeg(compasDeg)
}
}
const isComplete = () => {
if (!selectedModules || !selectedModules.itemId) return false
if (basicSetting && basicSetting.roofSizeSet !== '3') {
if (inputInstallHeight <= 0) {
return false
}
if (+inputVerticalSnowCover <= 0) {
return false
}
if (!inputStandardWindSpeed) return false
if (!inputRoughness) return false
}
return true
}
const handleChangeModule = (e) => {
resetRoofs()
setSelectedModules(e)
}
const handleChangeRoughness = (e) => {
resetRoofs()
setInputRoughness(e)
}
const handleChangeInstallHeight = (e) => {
resetRoofs()
setInputInstallHeight(e)
}
const handleChangeStandardWindSpeed = (e) => {
resetRoofs()
setInputStandardWindSpeed(e)
}
const handleChangeVerticalSnowCover = (e) => {
resetRoofs()
setInputVerticalSnowCover(e)
}
const resetRoofs = () => {
const newRoofs = addedRoofs.map((roof) => {
return {
...roof,
trestle: {
lengthBase: null,
trestleMkrCd: null,
constMthdCd: null,
constTp: null,
roofBaseCd: null,
roofPchBase: null,
},
addRoof: {
...roof.addRoof,
lengthBase: null,
eavesMargin: null,
kerabaMargin: null,
ridgeMargin: null,
},
construction: {
constTp: null,
cvrYn: 'N',
snowGdPossYn: 'N',
cvrChecked: false,
snowGdChecked: false,
},
}
})
// setRoofs(newRoofs)
// setAddedRoofs(newRoofs)
setRoofsStore(newRoofs)
}
useEffect(() => {
// selectedModules handleChangeModule
if (moduleList.length > 0 && (!selectedModules || !selectedModules.itemId)) {
handleChangeModule(moduleList[0]);
}
}, [moduleList]); //
return (
<>
<div className="properties-setting-wrap">
<div className="outline-wrap">
<div className="roof-module-inner">
<div className="compas-wrapper">
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
<div className="roof-module-compas">
<div className="compas-box">
<div className="compas-box-inner">
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${getDegreeInOrientation(inputCompasDeg) === -15 * index + 180 || (index === 0 && inputCompasDeg >= 172 && index === 0 && inputCompasDeg <= 180) || (inputCompasDeg === -180 && index === 0) ? 'act' : ''}`}
onClick={() => {
if (index === 0) {
setInputCompasDeg(180)
return
}
setInputCompasDeg(-15 * index + 180)
}}
>
{index === 0 && <i>180°</i>}
{index === 6 && <i>90°</i>}
</div>
))}
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${inputCompasDeg !== 180 && getDegreeInOrientation(inputCompasDeg) === -1 * 15 * index ? 'act' : ''}`}
onClick={() => setInputCompasDeg(15 * index * -1)}
>
{index === 0 && <i>0°</i>}
{index === 6 && <i>-90°</i>}
</div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${-1 * getDegreeInOrientation(inputCompasDeg)}deg)` }}></div>
</div>
<div className="guide">{getMessage('modal.module.basic.setting.orientation.setting.info')}</div>
<div className="roof-module-compas">
<div className="compas-box">
<div className="compas-box-inner">
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${getDegreeInOrientation(compasDeg) === -1 * (-15 * index + 180) || (index === 0 && compasDeg >= 172 && index === 0 && compasDeg <= 180) || (compasDeg === -180 && index === 0) ? 'act' : ''}`}
onClick={() => {
if (index === 0) {
setCompasDeg(180)
return
}
setCompasDeg(-1 * (-15 * index + 180))
}}
>
{index === 0 && <i>180°</i>}
{index === 6 && <i>-90°</i>}
</div>
</div>
</div>
<div className="center-wrap">
<div className="outline-form">
<div className="d-check-box pop mr10">
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}</label>
))}
{Array.from({ length: 180 / 15 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg !== 180 && getDegreeInOrientation(compasDeg) === 15 * index ? 'act' : ''}`}
onClick={() => setCompasDeg(15 * index)}
>
{index === 0 && <i>0°</i>}
{index === 6 && <i>90°</i>}
</div>
<div className="input-grid mr10" style={{ width: '60px' }}>
<input
type="text"
className="input-origin block"
value={inputCompasDeg}
readOnly={!hasAnglePassivity}
placeholder={0}
onChange={(e) => checkDegree(e.target.value)}
/>
</div>
<span className="thin">°</span>
<span className="thin"> -180 180 </span>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `rotate(${getDegreeInOrientation(compasDeg)}deg)` }}></div>
</div>
</div>
</div>
<div className="compas-table-wrap">
<div className="compas-table-box mb10">
<div className="outline-form mb10">
<span>{getMessage('modal.module.basic.setting.module.setting')}</span>
<div className="grid-select">
{moduleList && (
<QSelectBox
options={moduleList}
value={selectedModules}
targetKey={'itemId'}
sourceKey={'itemId'}
showKey={'itemNm'}
onChange={(e) => handleChangeModule(e)}
/>
)}
</div>
</div>
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((header) => {
return (
<th key={header.prop} style={{ width: header.width ? header.width + 'px' : '' }}>
{header.name}
</th>
)
})}
</tr>
</thead>
<tbody>
{Array.from({ length: 3 }).map((_, index) => {
return selectedModules && selectedModules?.itemList && selectedModules?.itemList?.length >= index + 1 ? (
<tr key={index}>
<td>
<div className="color-wrap">
<span
className="color-box"
style={{
backgroundColor: selectedModules.itemList[index].color,
}}
></span>
<span className="name">{selectedModules.itemList[index].itemNm}</span>
</div>
</td>
<td className="al-r">{Number(selectedModules.itemList[index].shortAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].longAxis).toFixed(0)}</td>
<td className="al-r">{Number(selectedModules.itemList[index].wpOut).toFixed(0)}</td>
</tr>
) : (
<tr key={index}>
<td>
<div className="color-wrap"></div>
</td>
<td className="al-r"></td>
<td className="al-r"></td>
<td className="al-r"></td>
</tr>
)
})}
</tbody>
</table>
</div>
{basicSetting && basicSetting.roofSizeSet == '3' && (
<div className="outline-form mt15">
<span>{getMessage('modal.module.basic.setting.module.placement.area')}</span>
<div className="input-grid mr10" style={{ width: '60px' }}>
<input type="number" className="input-origin block" value={inputMargin} onChange={(e) => setInputMargin(e.target.value)} />
</div>
<span className="thin">m</span>
</div>
)}
</div>
</div>
<div className="center-wrap">
<div className="d-check-box pop">
<input type="checkbox" id="ch99" checked={hasAnglePassivity} onChange={() => setHasAnglePassivity(!hasAnglePassivity)} />
<label htmlFor="ch99">{getMessage('modal.module.basic.setting.orientation.setting.angle.passivity')}-180 180</label>
</div>
<div className="outline-form">
<div className="input-grid mr10" style={{ width: '160px' }}>
<input
type="text"
className="input-origin block"
value={compasDeg}
readOnly={!hasAnglePassivity}
placeholder={0}
onChange={
(e) => checkDegree(e.target.value)
// setCompasDeg(
{basicSetting && basicSetting.roofSizeSet != '3' && (
<div className="compas-table-box">
<div className="compas-grid-table">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.surface.type')}</span>
<div className="grid-select">
{roughnessCodes.length > 0 && managementState && (
<QSelectBox
options={roughnessCodes}
value={inputRoughness}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeRoughness(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.fitting.height')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputInstallHeight}
onChange={(e) => handleChangeInstallHeight(e.target.value)}
/>
</div>
<span className="thin">m</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.wind.speed')}</span>
<div className="grid-select">
{windSpeedCodes.length > 0 && managementState && (
<QSelectBox
title={''}
options={windSpeedCodes}
value={inputStandardWindSpeed}
targetKey={'clCode'}
sourceKey={'clCode'}
showKey={'clCodeNm'}
onChange={(e) => handleChangeStandardWindSpeed(e)}
/>
)}
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.standard.snowfall.amount')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={inputVerticalSnowCover}
onChange={(e) => handleChangeVerticalSnowCover(e.target.value)}
/>
</div>
<span className="thin">cm</span>
</div>
</div>
</div>
)}
// e.target.value === '-' || (e.target.value !== '' && parseInt(e.target.value) <= 180 && parseInt(e.target.value) >= -180)
// ? e.target.value
// : 0,
// )
}
/>
</div>
<span className="thin">°</span>
</div>
</div>
</div>

View File

@ -1,45 +1,29 @@
import { forwardRef, useEffect, useState } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import {
checkedModuleState,
isManualModuleLayoutSetupState,
isManualModuleSetupState,
moduleRowColArrayState,
moduleSetupOptionState,
toggleManualSetupModeState,
} from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useResetRecoilState, useSetRecoilState } from 'recoil'
import { checkedModuleState, currentCanvasPlanState, isManualModuleSetupState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isObjectNotEmpty } from '@/util/common-utils'
import Image from 'next/image'
const Placement = forwardRef((props, refs) => {
const { getMessage } = useMessage()
const [useTab, setUseTab] = useState(true)
const [guideType, setGuideType] = useState('batch')
const [isChidori, setIsChidori] = useState(false)
const [isChidoriNotAble, setIsChidoriNotAble] = useState(false)
const [setupLocation, setSetupLocation] = useState('eaves')
const [isMaxSetup, setIsMaxSetup] = useState('false')
const [selectedItems, setSelectedItems] = useState({})
const [selectedModules, setSelectedModules] = useRecoilState(selectedModuleState)
const setCheckedModules = useSetRecoilState(checkedModuleState)
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const { makeModuleInitArea, roofOutlineColor } = useModuleBasicSetting(3)
const { makeModuleInitArea } = useModuleBasicSetting(3)
const [isMultiModule, setIsMultiModule] = useState(false)
//
const setIsManualModuleSetup = useSetRecoilState(isManualModuleSetupState)
const setIsManualModuleLayoutSetup = useSetRecoilState(isManualModuleLayoutSetupState)
const setManualSetupMode = useSetRecoilState(toggleManualSetupModeState)
const [moduleSetupOption, setModuleSetupOption] = useRecoilState(moduleSetupOptionState) //
const resetModuleSetupOption = useResetRecoilState(moduleSetupOptionState)
const [colspan, setColspan] = useState(1)
const moduleRowColArray = useRecoilValue(moduleRowColArrayState)
const [isManualModuleSetup, setIsManualModuleSetup] = useRecoilState(isManualModuleSetupState)
//
useEffect(() => {
@ -52,24 +36,11 @@ const Placement = forwardRef((props, refs) => {
makeModuleInitArea(moduleSelectionData)
}
if (moduleSelectionData.module.itemList.length > 1) {
setColspan(2)
}
return () => {
// refs.isChidori.current = 'false'
// refs.setupLocation.current = 'eaves'
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
resetModuleSetupOption()
}
}, [])
// useEffect(() => {
// console.log('moduleRowColArray', moduleRowColArray)
// }, [moduleRowColArray])
//
useEffect(() => {
if (isObjectNotEmpty(moduleSelectionData)) {
@ -83,10 +54,8 @@ const Placement = forwardRef((props, refs) => {
initCheckedModule = { ...initCheckedModule, [obj.itemId]: true }
}
})
setSelectedItems(initCheckedModule)
setSelectedModules(moduleSelectionData.module)
props.setLayoutSetup(moduleSelectionData.module.itemList.map((item) => ({ moduleId: item.itemId, col: 0, row: 0, checked: true })))
}
//
@ -111,79 +80,63 @@ const Placement = forwardRef((props, refs) => {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: getMessage('modal.module.basic.setting.module.placement.mix.asg.yn'), prop: 'mixAsgYn', width: 50 },
{ type: 'text', name: `段数`, prop: 'rows', width: 60 },
{ type: 'text', name: `列数`, prop: 'cols', width: 60 },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
],
rows: [],
}
const handleChangeChidori = (e) => {
const bool = e.target.value === 'true' ? true : false
setModuleSetupOption({ ...moduleSetupOption, isChidori: bool })
//
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
setIsChidori(bool)
refs.isChidori.current = e.target.value
}
const handleSetupLocation = (e) => {
setModuleSetupOption({ ...moduleSetupOption, setupLocation: e.target.value })
setSetupLocation(e.target.value)
refs.setupLocation.current = e.target.value
}
//
setIsManualModuleSetup(false)
setIsManualModuleLayoutSetup(false)
setManualSetupMode('off')
const handleMaxSetup = (e) => {
if (e.target.checked) {
setIsMaxSetup('true')
refs.isMaxSetup.current = 'true'
} else {
setIsMaxSetup('false')
refs.isMaxSetup.current = 'false'
}
}
//
const handleSelectedItem = (e, itemId) => {
const handleSelectedItem = (e) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
const newLayoutSetup = [...props.layoutSetup]
props.layoutSetup.forEach((item, index) => {
if (item.moduleId === itemId) {
newLayoutSetup[index] = { ...props.layoutSetup[index], checked: e.target.checked }
}
})
props.setLayoutSetup(newLayoutSetup)
}
const handleLayoutSetup = (e, itemId, index) => {
const newLayoutSetup = [...props.layoutSetup]
newLayoutSetup[index] = {
...newLayoutSetup[index],
moduleId: itemId,
[e.target.name]: Number(e.target.value),
}
props.setLayoutSetup(newLayoutSetup)
}
return (
<>
<div className="module-table-flex-wrap">
<div className="module-table-flex-wrap mb10">
<div className="module-table-box">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
) : (
data.name
)}
</th>
))}
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
) : (
data.name
)}
</th>
))}
</tr>
</thead>
<tbody>
{selectedModules?.itemList &&
selectedModules?.itemList?.map((item, index) => (
{selectedModules.itemList &&
selectedModules.itemList.map((item, index) => (
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
@ -192,7 +145,7 @@ const Placement = forwardRef((props, refs) => {
id={item.itemId}
name={item.itemId}
checked={selectedItems[item.itemId]}
onChange={(e) => handleSelectedItem(e, item.itemId)}
onChange={handleSelectedItem}
/>
<label htmlFor={item.itemId}></label>
</div>
@ -203,209 +156,93 @@ const Placement = forwardRef((props, refs) => {
<span className="name">{item.itemNm}</span>
</div>
</td>
<td className="al-c">
<div className="color-wrap">
<span className="name">{item.mixAsgYn}</span>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="row"
value={props.layoutSetup[index]?.row ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
<td className="al-r">
<div className="input-grid">
<input
type="text"
className="input-origin block"
name="col"
value={props.layoutSetup[index]?.col ?? 1}
defaultValue={0}
onChange={(e) => handleLayoutSetup(e, item.itemId, index)}
/>
</div>
</td>
<td className="al-r">{item.wpOut}</td>
</tr>
))}
<tr>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div className="module-table-box non-flex">
<div className="module-table-box">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
<th>{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</th>
<th>{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio02"
id="ra03"
checked={moduleSetupOption.isChidori}
disabled={isChidoriNotAble}
value={'true'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio02"
id="ra04"
checked={!moduleSetupOption.isChidori}
value={'false'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div>
</div>
</td>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio03"
id="ra05"
checked={moduleSetupOption.setupLocation === 'eaves'}
value={'eaves'}
onChange={handleSetupLocation}
/>
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra06"
checked={moduleSetupOption.setupLocation === 'ridge'}
value={'ridge'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra06">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
</div>
</div>
</td>
</tr>
</tbody>
</table>
<div className="self-table-tit">{getMessage('modal.module.basic.setting.module.placement.select.fitting.type')}</div>
<div className="module-self-table">
<div className="self-table-item">
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.waterfowl.arrangement')}</div>
<div className="self-item-td">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
checked={isChidori}
disabled={isChidoriNotAble}
value={'true'}
onChange={(e) => handleChangeChidori(e)}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.module.placement.do')}</label>
</div>
<div className="d-check-radio pop">
<input type="radio" name="radio02" id="ra02" checked={!isChidori} value={'false'} onChange={(e) => handleChangeChidori(e)} />
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.module.placement.do.not')}</label>
</div>
</div>
</div>
</div>
<div className="self-table-item">
<div className="self-item-th">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard')}</div>
<div className="self-item-td">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="radio03"
id="ra03"
checked={setupLocation === 'center'}
value={'center'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra03">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.center')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio04"
id="ra04"
checked={setupLocation === 'eaves'}
value={'eaves'}
onChange={handleSetupLocation}
/>
<label htmlFor="ra04">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.eaves')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio05"
id="ra05"
checked={setupLocation === 'ridge'}
value={'ridge'}
onChange={handleSetupLocation}
disabled={isMultiModule}
/>
<label htmlFor="ra05">{getMessage('modal.module.basic.setting.module.placement.arrangement.standard.ridge')}</label>
</div>
</div>
</div>
</div>
</div>
<div className="self-table-flx">
{/* <div className="d-check-box pop">
<input type="checkbox" id="ch04" checked={isMaxSetup === 'true'} value={'true'} onChange={handleMaxSetup} />
<label htmlFor="ch04">{getMessage('modal.module.basic.setting.module.placement.maximum')}</label>
</div> */}
</div>
</div>
</div>
</div>
<div className="hide-tab-wrap">
<div className="hide-check-guide">
{getMessage('modal.module.basic.setting.module.placement.info')}
<button className={`arr ${useTab ? 'act' : ''}`} onClick={() => setUseTab(!useTab)}></button>
</div>
<div className={`hide-tab-contents ${!useTab ? 'hide' : ''}`}>
<div className="roof-content-tab-wrap">
<button className={`btn-frame block modal mr5 ${guideType === 'batch' ? 'act' : ''} `} onClick={() => setGuideType('batch')}>
{getMessage('modal.module.basic.setting.module.placement.info.batch')}
</button>
<button className={`btn-frame block modal mr5 ${guideType === 'module' ? 'act' : ''}`} onClick={() => setGuideType('module')}>
{getMessage('modal.module.basic.setting.module.placement.info.module')}
</button>
</div>
{guideType === 'batch' && (
<div className={`roof-warning-wrap mt10`}>
<div className="guide">
{getMessage('modal.module.basic.setting.module.placement.info.batch.content1')}
<br />
{getMessage('modal.module.basic.setting.module.placement.info.batch.content2')}
</div>
<div className="roof-warning-img-wrap">
<div className="roof-warning-img">
<Image src={'/static/images/canvas/roof_warning_correct.png'} width={350} height={198} alt="" />
</div>
<div className="roof-warning-img">
<Image src={'/static/images/canvas/roof_warning_wrong.png'} width={350} height={198} alt="" />
</div>
</div>
</div>
)}
{guideType === 'module' && (
<div className={`module-table-box mt10 ${!useTab ? 'hide' : ''}`}>
<div className="module-table-inner">
<div className="roof-module-table">
<table className="">
<thead>
<tr>
<th rowSpan={2} style={{ width: '22%' }}></th>
{selectedModules &&
selectedModules.itemList?.map((item) => (
// <th colSpan={colspan}>
<th>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span>
</div>
</th>
))}
{colspan > 1 && <th rowSpan={2}>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>}
</tr>
<tr>
{selectedModules &&
selectedModules.itemList?.map((item) => (
<>
<th>{getMessage('modal.module.basic.setting.module.placement.max.row')}</th>
{/* {colspan > 1 && <th>{getMessage('modal.module.basic.setting.module.placement.max.rows.multiple')}</th>} */}
</>
))}
</tr>
</thead>
<tbody>
{moduleSelectionData.roofConstructions.map((item, index) => (
<tr>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: roofOutlineColor(item.addRoof?.index) }}></span>
<span className="name">{item.addRoof?.roofMatlNmJp}</span>
</div>
</td>
{moduleRowColArray[index]?.map((item, index2) => (
<>
<td className="al-c">{item.moduleMaxRows}</td>
{/* {colspan > 1 && <td className="al-c">{item.mixModuleMaxRows}</td>} */}
{colspan > 1 && index2 === moduleRowColArray[index].length - 1 && <td className="al-c">{item.maxRow}</td>}
</>
))}
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
)}
</div>
</div>
</>
)
})

View File

@ -1,790 +0,0 @@
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import QSelectBox from '@/components/common/select/QSelectBox'
import { useModuleBasicSetting } from '@/hooks/module/useModuleBasicSetting'
import { useModuleTrestle } from '@/hooks/module/useModuleTrestle'
import { useMessage } from '@/hooks/useMessage'
import { currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
import { roofsState } from '@/store/roofAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import Swal from 'sweetalert2'
const Trestle = forwardRef((props, ref) => {
const { tabNum, setTabNum, trestleTrigger, roofs, setRoofs, moduleSelectionData, setModuleSelectionData, setRoofsStore } = props
const { getMessage } = useMessage()
// const [selectedTrestle, setSelectedTrestle] = useState()
const currentAngleType = useRecoilValue(currentAngleTypeSelector)
const pitchText = useRecoilValue(pitchTextSelector)
const [selectedRoof, setSelectedRoof] = useState(null)
const {
trestleState,
trestleDetail,
dispatch,
raftBaseList,
trestleList,
constMthdList,
roofBaseList,
constructionList,
eavesMargin,
ridgeMargin,
kerabaMargin,
setEavesMargin,
setRidgeMargin,
setKerabaMargin,
lengthBase,
setLengthBase,
hajebichi,
setHajebichi,
cvrYn,
cvrChecked,
snowGdPossYn,
snowGdChecked,
setCvrYn,
setCvrChecked,
setSnowGdPossYn,
setSnowGdChecked,
} = useModuleTrestle({
selectedRoof,
})
const selectedModules = useRecoilValue(selectedModuleState) //
// const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const [selectedRaftBase, setSelectedRaftBase] = useState(null)
const [selectedTrestle, setSelectedTrestle] = useState(null)
const [selectedConstMthd, setSelectedConstMthd] = useState(null)
const [selectedConstruction, setSelectedConstruction] = useState(null)
const [selectedRoofBase, setSelectedRoofBase] = useState(null)
const { managementState } = useContext(GlobalDataContext)
const { restoreModuleInstArea } = useModuleBasicSetting()
const [flag, setFlag] = useState(false)
const tempModuleSelectionData = useRef(null)
useEffect(() => {
if (roofs && !selectedRoof) {
setSelectedRoof(roofs[0])
}
//
restoreModuleInstArea()
}, [roofs])
useEffect(() => {
if (flag && moduleSelectionData) {
if (JSON.stringify(tempModuleSelectionData.current) === JSON.stringify(moduleSelectionData)) {
setTabNum(tabNum + 1)
}
}
}, [flag, moduleSelectionData])
useEffect(() => {
if (selectedRoof) {
if (moduleSelectionData?.roofConstructions?.length >= selectedRoof.index + 1) {
const { construction, trestle, trestleDetail } = moduleSelectionData?.roofConstructions[selectedRoof.index]
dispatch({
type: 'SET_INITIALIZE',
roof: { common: moduleSelectionData.common, module: moduleSelectionData.module, construction, trestle, trestleDetail, ...selectedRoof },
})
} else {
dispatch({ type: 'SET_INITIALIZE', roof: { ...selectedRoof, common: moduleSelectionData.common, module: moduleSelectionData.module } })
}
}
}, [selectedRoof])
useEffect(() => {
if (raftBaseList.length > 0) {
setSelectedRaftBase(raftBaseList.find((raft) => raft.clCode === selectedRoof?.raft) ?? null)
} else {
setSelectedRaftBase(null)
}
}, [raftBaseList])
useEffect(() => {
if (trestleList.length > 0) {
setSelectedTrestle(trestleList.find((trestle) => trestle.trestleMkrCd === trestleState?.trestleMkrCd) ?? null)
} else {
setSelectedTrestle(null)
}
}, [trestleList])
useEffect(() => {
if (roofBaseList.length > 0) {
setSelectedRoofBase(roofBaseList.find((roofBase) => roofBase.roofBaseCd === trestleState?.roofBaseCd) ?? null)
} else {
setSelectedRoofBase(null)
}
}, [roofBaseList])
useEffect(() => {
if (constMthdList.length > 0) {
setSelectedConstMthd(constMthdList.find((constMthd) => constMthd.constMthdCd === trestleState?.constMthdCd) ?? null)
} else {
setSelectedConstMthd(null)
}
}, [constMthdList])
useEffect(() => {
if (constructionList.length > 0) {
setSelectedConstruction(constructionList.find((construction) => construction.constTp === trestleState?.construction?.constTp) ?? null)
if (constructionList.filter((construction) => construction.constPossYn === 'Y').length === 0) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error4', [selectedRoof?.nameJp]), // .
icon: 'warning',
})
}
} else {
setSelectedConstruction(null)
}
}, [constructionList])
const getConstructionState = (index) => {
if (constructionList && constructionList.length > 0) {
if (constructionList[index].constPossYn === 'Y') {
if (trestleState && trestleState.constTp === constructionList[index].constTp) {
return 'blue'
}
return 'white'
}
return 'no-click'
}
return 'no-click'
}
const onChangeLength = (e) => {
setLengthBase(e)
dispatch({
type: 'SET_LENGTH',
roof: {
length: e,
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
},
})
}
const onChangeRaftBase = (e) => {
setSelectedRaftBase(e)
dispatch({
type: 'SET_RAFT_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: e.clCode,
},
})
}
const onChangeHajebichi = (e) => {
setHajebichi(e)
// roofs selectedRoof.index
if (selectedRoof && selectedRoof.index !== undefined) {
const updatedRoofs = roofs.map((roof, index) => (index === selectedRoof.index ? { ...roof, hajebichi: Number(e) } : roof))
setRoofs(updatedRoofs)
}
dispatch({
type: 'SET_HAJEBICHI',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
hajebichi: e,
},
})
}
const onChangeTrestleMaker = (e) => {
setSelectedTrestle(e)
dispatch({
type: 'SET_TRESTLE_MAKER',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: e.trestleMkrCd,
},
})
}
const onChangeConstMthd = (e) => {
setSelectedConstMthd(e)
dispatch({
type: 'SET_CONST_MTHD',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: e.constMthdCd,
},
})
}
const onChangeRoofBase = (e) => {
setSelectedRoofBase(e)
dispatch({
type: 'SET_ROOF_BASE',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: e.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(hajebichi ?? 0),
},
})
}
const handleConstruction = (index) => {
if (constructionList[index]?.constPossYn === 'Y') {
dispatch({
type: 'SET_CONSTRUCTION',
roof: {
moduleTpCd: selectedModules.itemTp ?? '',
roofMatlCd: selectedRoof?.roofMatlCd ?? '',
raft: selectedRaftBase?.clCode,
trestleMkrCd: selectedTrestle.trestleMkrCd,
constMthdCd: selectedConstMthd.constMthdCd,
roofBaseCd: selectedRoofBase.roofBaseCd,
illuminationTp: managementState?.surfaceTypeValue ?? '',
instHt: managementState?.installHeight ?? '',
stdWindSpeed: managementState?.standardWindSpeedId ?? '',
stdSnowLd: managementState?.verticalSnowCover ?? '',
inclCd: selectedRoof?.pitch ?? 0,
roofPitch: Math.round(hajebichi ?? 0),
constTp: constructionList[index].constTp,
snowGdPossYn: constructionList[index].snowGdPossYn,
cvrYn: constructionList[index].cvrYn,
mixMatlNo: selectedModules.mixMatlNo,
// workingWidth: selectedRoof?.length?.toString() ?? '',
workingWidth: lengthBase,
},
})
setCvrYn(constructionList[index].cvrYn)
setSnowGdPossYn(constructionList[index].snowGdPossYn)
setCvrChecked(false)
setSnowGdChecked(false)
}
}
const handleChangeRoofMaterial = (index) => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof.index) {
return {
...selectedRoof,
hajebichi,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: selectedRoof.index,
raft: selectedRaftBase?.clCode,
trestle: {
hajebichi: hajebichi,
length: lengthBase,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn: cvrYn,
snowGdPossYn: snowGdPossYn,
cvrChecked: cvrChecked,
snowGdChecked: snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
setRoofs(newAddedRoofs)
setSelectedRoof(newAddedRoofs[index])
}
const isComplete = async () => {
const newAddedRoofs = roofs.map((roof, i) => {
if (i === selectedRoof?.index) {
return {
...selectedRoof,
length: lengthBase,
eavesMargin,
ridgeMargin,
kerabaMargin,
roofIndex: roof.index,
raft: selectedRaftBase?.clCode,
hajebichi: hajebichi,
trestle: {
length: lengthBase,
hajebichi: hajebichi,
...selectedRaftBase,
...selectedTrestle,
...selectedConstMthd,
...selectedRoofBase,
},
construction: {
...constructionList.find((data) => data.constTp === trestleState.constTp),
cvrYn,
snowGdPossYn,
cvrChecked,
snowGdChecked,
setupCover: cvrChecked ?? false,
setupSnowCover: snowGdChecked ?? false,
},
trestleDetail: trestleDetail,
}
}
return roof
})
let result = true
for (let i = 0; i < newAddedRoofs.length; i++) {
const roof = newAddedRoofs[i]
if (!roof.trestle?.trestleMkrCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error1', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.constMthdCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error2', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.trestle?.roofBaseCd) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error3', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (!roof.construction?.constTp) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error12', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.lenAuth === 'C') {
if (!roof.trestle?.length) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error5', [roof.nameJp]), // L .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.raftAuth)) {
if (!roof?.raft) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error6', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (['C', 'R'].includes(roof.roofPchAuth)) {
if (!roof?.roofPchBase) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error7', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
}
if (!roof?.eavesMargin || !roof?.ridgeMargin || !roof?.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error8', [roof.nameJp]), // .
icon: 'warning',
})
result = false
return false
}
if (roof.trestle.trestleMkrCd !== 'NO_DATA') {
//
if (roof.trestleDetail?.eaveIntvl > roof.eavesMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error9', [roof.trestleDetail?.eaveIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.ridgeIntvl > roof.ridgeMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error10', [roof.trestleDetail?.ridgeIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
if (roof.trestleDetail?.kerabaIntvl > roof.kerabaMargin) {
Swal.fire({
title: getMessage('modal.module.basic.settting.module.error11', [roof.trestleDetail?.kerabaIntvl, roof.nameJp]), // {0}mm .
icon: 'warning',
})
result = false
return false
}
}
}
if (result) {
const newRoofs = newAddedRoofs.map((roof) => {
const { addRoof, construction, trestle, trestleDetail, roofConstructions, ...rest } = roof
return rest
})
setModuleSelectionData({
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
})
setFlag(true)
tempModuleSelectionData.current = {
...moduleSelectionData,
roofConstructions: newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
trestle: roof.trestle,
addRoof: newRoofs[index],
construction: roof.construction,
trestleDetail: roof.trestleDetail,
})),
}
const updatePromises = [
// new Promise((resolve) => {
// resolve()
// }),
new Promise((resolve) => {
setRoofs(newRoofs)
resolve()
}),
new Promise((resolve) => {
const roofConstructions = newAddedRoofs.map((roof, index) => ({
roofIndex: newRoofs[index].index,
addRoof: newRoofs[index],
trestle: {
...roof.trestle,
raft: roof.raftBaseCd,
},
construction: {
// ...constructionList.find((construction) => newAddedRoofs[index].construction.constTp === construction.constTp),
...roof.construction,
roofIndex: roof.index,
selectedIndex: roof.index,
},
trestleDetail: roof.trestleDetail,
}))
trestleTrigger({
roofConstructions,
})
setRoofsStore(roofConstructions)
resolve()
}),
]
await Promise.all(updatePromises)
return true
}
return false
}
useImperativeHandle(ref, () => ({
isComplete,
}))
return (
<div className="roof-module-tab2-overflow">
<div className="module-table-box mb10">
<div className="module-box-tab">
{roofs &&
roofs.map((roof, index) => (
<button
key={index}
className={`module-btn ${selectedRoof?.index === index ? 'act' : ''}`}
onClick={() => (roof ? handleChangeRoofMaterial(index) : null)}
>
{roof !== undefined ? `${roof.nameJp} (${currentAngleType === 'slope' ? roof.pitch : roof.angle}${pitchText})` : '-'}
</button>
))}
</div>
<div className="module-table-inner">
<div className="module-table-flex-wrap tab2">
<div className="module-flex-item">
<div className="eaves-keraba-table">
{selectedRoof && selectedRoof.lenAuth === 'C' && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">L</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
value={lengthBase}
onChange={(e) => onChangeLength(e.target.value)}
disabled={selectedRoof.lenAuth === 'R'}
/>
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.raftAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.rafter.margin')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{raftBaseList.length > 0 && (
<QSelectBox
options={raftBaseList}
value={selectedRaftBase}
sourceKey={'clCode'}
targetKey={'clCode'}
showKey={'clCodeNm'}
disabled={selectedRoof.raftAuth === 'R'}
onChange={(e) => onChangeRaftBase(e)}
/>
)}
</div>
</div>
</div>
</>
)}
{selectedRoof && ['C', 'R'].includes(selectedRoof.roofPchAuth) && (
<>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.hajebichi')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
<input
type="text"
className="input-origin block"
disabled={selectedRoof.roofPchAuth === 'R'}
onChange={(e) => onChangeHajebichi(e.target.value)}
value={hajebichi}
/>
</div>
</div>
</div>
</>
)}
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.trestle.maker')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{trestleList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={trestleList}
value={selectedTrestle}
sourceKey={'trestleMkrCd'}
targetKey={'trestleMkrCd'}
showKey={'trestleMkrCdJp'}
onChange={(e) => onChangeTrestleMaker(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.construction.method')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{constMthdList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={constMthdList}
value={selectedConstMthd}
sourceKey={'constMthdCd'}
targetKey={'constMthdCd'}
showKey={'constMthdCdJp'}
onChange={(e) => onChangeConstMthd(e)}
/>
)}
</div>
</div>
</div>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">{getMessage('modal.module.basic.setting.module.under.roof')}</div>
<div className="eaves-keraba-td">
<div className="grid-select">
{roofBaseList && (
<QSelectBox
title={getMessage('selectbox.title')}
options={roofBaseList}
sourceKey={'roofBaseCd'}
targetKey={'roofBaseCd'}
showKey={'roofBaseCdJp'}
value={selectedRoofBase}
onChange={(e) => onChangeRoofBase(e)}
/>
)}
</div>
</div>
</div>
</div>
</div>
<div className="module-flex-item non-flex">
<div className="flex-item-btn-wrap">
<button className={`btn-frame roof ${getConstructionState(0)}`} onClick={() => handleConstruction(0)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}(I)
</button>
<button className={`btn-frame roof ${getConstructionState(3)}`} onClick={() => handleConstruction(3)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(1)}`} onClick={() => handleConstruction(1)}>
{getMessage('modal.module.basic.setting.module.standard.construction')}
</button>
<button className={`btn-frame roof ${getConstructionState(4)}`} onClick={() => handleConstruction(4)}>
{getMessage('modal.module.basic.setting.module.multiple.construction')}(II)
</button>
<button className={`btn-frame roof ${getConstructionState(2)}`} onClick={() => handleConstruction(2)}>
{getMessage('modal.module.basic.setting.module.enforce.construction')}
</button>
</div>
<div className="grid-check-form-flex">
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch01`}
disabled={!cvrYn || cvrYn === 'N'}
checked={cvrChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, cvrChecked: !trestleState.cvrChecked } })}
onChange={() => setCvrChecked(!cvrChecked)}
/>
<label htmlFor={`ch01`}>{getMessage('modal.module.basic.setting.module.eaves.bar.fitting')}</label>
</div>
<div className="d-check-box pop">
<input
type="checkbox"
id={`ch02`}
disabled={!snowGdPossYn || snowGdPossYn === 'N'}
checked={snowGdChecked || false}
// onChange={() => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, snowGdChecked: !trestleState.snowGdChecked } })}
onChange={() => setSnowGdChecked(!snowGdChecked)}
/>
<label htmlFor={`ch02`}>{getMessage('modal.module.basic.setting.module.blind.metal.fitting')}</label>
</div>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.area')}</div>
<div className="module-input-wrap">
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.eaves')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={eavesMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, eavesMargin: e.target.value } })}
onChange={(e) => setEavesMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form mr15">
<span>{getMessage('modal.module.basic.setting.module.placement.area.ridge')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={ridgeMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, ridgeMargin: e.target.value } })}
onChange={(e) => setRidgeMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form ">
<span>{getMessage('modal.module.basic.setting.module.placement.area.keraba')}</span>
<div className="input-grid mr10">
<input
type="number"
className="input-origin block"
value={kerabaMargin ?? 0}
// onChange={(e) => dispatch({ type: 'SET_TRESTLE_DETAIL', roof: { ...trestleState, kerabaMargin: e.target.value } })}
onChange={(e) => setKerabaMargin(+e.target.value)}
/>
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
<div className="module-input-area">
<div className="module-area-title">{getMessage('modal.module.basic.setting.module.placement.margin')}</div>
<div className="module-input-wrap">
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.horizontal')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlHor} readOnly />
</div>
<span className="thin">mm</span>
</div>
<div className="outline-form">
<span>{getMessage('modal.module.basic.setting.module.placement.margin.vertical')}</span>
<div className="input-grid mr10">
<input type="text" className="input-origin block" defaultValue={trestleDetail?.moduleIntvlVer} readOnly />
</div>
<span className="thin">mm</span>
</div>
</div>
</div>
</div>
</div>
<div className="module-bottom">
<div className="module-table-box ">
<div className="warning-guide">
<div className="warning">
{getMessage('modal.module.basic.setting.module.setting.info1')}
<br />
{getMessage('modal.module.basic.setting.module.setting.info2')}
</div>
</div>
</div>
</div>
</div>
)
})
export default Trestle

View File

@ -33,6 +33,14 @@ const PitchPlacement = forwardRef((props, refs) => {
setSelectedItems({ ...selectedItems, [e.target.name]: e.target.checked })
}
const moduleData = {
header: [
{ type: 'check', name: '', prop: 'check', width: 70 },
{ type: 'color-box', name: getMessage('module'), prop: 'module' },
{ type: 'text', name: `${getMessage('output')} (W)`, prop: 'output', width: 70 },
],
}
//
useEffect(() => {
const checkedModuleIds = Object.keys(selectedItems).filter((key) => selectedItems[key])
@ -97,91 +105,87 @@ const PitchPlacement = forwardRef((props, refs) => {
return (
<>
<div className="hexagonal-flex-wrap">
<div className="module-table-box ">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
<th style={{ width: '70px' }}>
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
{moduleData.header.map((data) => (
<th key={data.prop} style={{ width: data.width ? data.width : '' }}>
{data.type === 'check' ? (
<div className="d-check-box no-text pop">
<input type="checkbox" id="ch01" disabled />
<label htmlFor="ch01"></label>
</div>
) : (
data.name
)}
</th>
<th>{getMessage('module')}</th>
</tr>
</thead>
<tbody>
{selectedModules.itemList &&
selectedModules.itemList.map((item, index) => (
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
<input
type="checkbox"
id={item.itemId}
name={item.itemId}
checked={selectedItems[item.itemId]}
onChange={handleSelectedItem}
/>
<label htmlFor={item.itemId}></label>
</div>
</td>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
))}
</tr>
</thead>
<tbody>
{selectedModules.itemList &&
selectedModules.itemList.map((item, index) => (
<tr key={index}>
<td className="al-c">
<div className="d-check-box no-text pop">
<input
type="checkbox"
id={item.itemId}
name={item.itemId}
checked={selectedItems[item.itemId]}
onChange={handleSelectedItem}
/>
<label htmlFor={item.itemId}></label>
</div>
</td>
<td>
<div className="color-wrap">
<span className="color-box" style={{ backgroundColor: item.color }}></span>
<span className="name">{item.itemNm}</span>
</div>
</td>
<td className="al-r">{item.wpOut}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="module-table-box non-flex">
<div className="module-table-inner">
<div className="roof-module-table">
<table>
<thead>
<tr>
<th>{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div className="hexagonal-radio-wrap">
<div className="d-check-radio pop mb10">
<input
type="radio"
name="radio01"
id="ra01"
value={'south'}
defaultChecked={setupLocation === 'south'}
onClick={handleSetupLocation}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
value={'excreta'}
defaultChecked={setupLocation === 'excreta'}
onClick={handleSetupLocation}
/>
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<div className="module-table-box mb10">
<div className="module-table-inner">
<div className="hexagonal-wrap">
<div className="hexagonal-item">
<div className="bold-font">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting')}</div>
</div>
<div className="hexagonal-item">
<div className="pop-form-radio">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
value={'south'}
defaultChecked={setupLocation === 'south'}
onClick={handleSetupLocation}
/>
<label htmlFor="ra01">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.south')}</label>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
value={'excreta'}
defaultChecked={setupLocation === 'excreta'}
onClick={handleSetupLocation}
/>
<label htmlFor="ra02">{getMessage('modal.module.basic.setting.pitch.module.placement.standard.setting.select')}</label>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,27 +1,30 @@
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { useContext, useEffect, useRef, useState } from 'react'
import { useState, useEffect, useContext, useRef } from 'react'
import PowerConditionalSelect from '@/components/floor-plan/modal/circuitTrestle/step/PowerConditionalSelect'
import StepUp from '@/components/floor-plan/modal/circuitTrestle/step/StepUp'
import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import PassivityCircuitAllocation from './step/type/PassivityCircuitAllocation'
import { useMasterController } from '@/hooks/common/useMasterController'
import { useRecoilState, useRecoilValue } from 'recoil'
import { correntObjectNoState } from '@/store/settingAtom'
import { useRecoilValue } from 'recoil'
import { GlobalDataContext } from '@/app/GlobalDataProvider'
import { useRecoilState } from 'recoil'
import { makersState, modelsState, modelState, pcsCheckState, selectedMakerState, selectedModelsState, seriesState } from '@/store/circuitTrestleAtom'
import { POLYGON_TYPE } from '@/common/common'
import { useSwal } from '@/hooks/useSwal'
import { canvasState, canvasZoomState } from '@/store/canvasAtom'
import { canvasState } from '@/store/canvasAtom'
import { useTrestle } from '@/hooks/module/useTrestle'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { selectedModuleState } from '@/store/selectedModuleOptions'
import { v4 as uuidv4 } from 'uuid'
import { useEstimate } from '@/hooks/useEstimate'
import { useCircuitTrestle } from '@/hooks/useCirCuitTrestle'
import { useCanvasPopupStatusController } from '@/hooks/common/useCanvasPopupStatusController'
import { useImgLoader } from '@/hooks/floorPlan/useImgLoader'
import { usePlan } from '@/hooks/usePlan'
import { QcastContext } from '@/app/QcastProvider'
import { fabric } from 'fabric'
import { fontSelector } from '@/store/fontAtom'
const ALLOCATION_TYPE = {
AUTO: 'auto',
@ -34,16 +37,13 @@ export default function CircuitTrestleSetting({ id }) {
const { swalFire } = useSwal()
const { saveEstimate } = useEstimate()
const canvas = useRecoilValue(canvasState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const [tabNum, setTabNum] = useState(1)
const [allocationType, setAllocationType] = useState(ALLOCATION_TYPE.AUTO)
const [circuitAllocationType, setCircuitAllocationType] = useState(1)
const { managementState, setManagementState } = useContext(GlobalDataContext)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState)
const { getPcsAutoRecommendList, getPcsVoltageChk, getPcsVoltageStepUpList, getPcsManualConfChk } = useMasterController()
const flowText = useRecoilValue(fontSelector('flowText'))
const lengthText = useRecoilValue(fontSelector('lengthText'))
const circuitNumberText = useRecoilValue(fontSelector('circuitNumberText'))
// ()
const [selectedStepUpValues, setSelectedStepUpValues] = useState({})
@ -55,7 +55,7 @@ export default function CircuitTrestleSetting({ id }) {
const [seletedSubOption, setSeletedSubOption] = useState(null)
const { setModuleStatisticsData } = useCircuitTrestle()
const { handleCanvasToPng } = useImgLoader()
const moduleSelectionData = useRecoilValue(moduleSelectionDataState)
const passivityCircuitAllocationRef = useRef()
const { setIsGlobalLoading } = useContext(QcastContext)
@ -84,6 +84,7 @@ export default function CircuitTrestleSetting({ id }) {
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
useEffect(() => {
if (!managementState) {
setManagementState(managementStateLoaded)
}
// setCircuitData({
// makers,
@ -102,115 +103,6 @@ export default function CircuitTrestleSetting({ id }) {
}
}, [])
const capture = async (type) => {
beforeCapture(type)
await handleCanvasToPng(type)
afterCapture(type)
return new Promise((resolve) => {
setTimeout(() => {
resolve(true)
}, 1000)
})
}
//
const beforeCapture = (type) => {
setCanvasZoom(100)
canvas.set({ zoom: 1 })
// roof
const roofs = canvas.getObjects().filter((obj) => obj.name === 'roof')
if (roofs.length > 0) {
// roof x, y
const allPoints = []
roofs.forEach((roof) => {
if (roof.getCurrentPoints()) {
roof.getCurrentPoints().forEach((point) => {
allPoints.push({ x: point.x, y: point.y })
})
}
})
if (allPoints.length > 0) {
//
const minX = Math.min(...allPoints.map((p) => p.x))
const maxX = Math.max(...allPoints.map((p) => p.x))
const minY = Math.min(...allPoints.map((p) => p.y))
const maxY = Math.max(...allPoints.map((p) => p.y))
const centerX = (minX + maxX) / 2
const centerY = (minY + maxY) / 2
//
const canvasWidth = canvas.getWidth()
const canvasHeight = canvas.getHeight()
const offsetX = canvasWidth / 2 - centerX
const offsetY = canvasHeight / 2 - centerY
canvas.viewportTransform = [1, 0, 0, 1, offsetX, offsetY]
} else {
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
}
} else {
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
}
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber')
if (type === 2) {
modules.forEach((module) => {
module.set({ originColor: module.fill, fill: null, strokeWidth: 2 })
})
circuitNumberTexts.forEach((text) => {
text.set({ visible: false })
})
}
canvas.renderAll()
// roof polygon
const roofPolygons = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF)
let x, y
x = canvas.width / 2
y = canvas.height / 2
canvas.zoomToPoint(new fabric.Point(x, y), 0.4)
changeFontSize('lengthText', '28')
changeFontSize('circuitNumber', '28')
changeFontSize('flowText', '28')
canvas.renderAll()
}
//
const afterCapture = (type) => {
setCanvasZoom(100)
canvas.set({ zoom: 1 })
canvas.viewportTransform = [1, 0, 0, 1, 0, 0]
changeFontSize('lengthText', lengthText.fontSize.value)
changeFontSize('circuitNumber', circuitNumberText.fontSize.value)
changeFontSize('flowText', flowText.fontSize.value)
const modules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
const circuitNumberTexts = canvas.getObjects().filter((obj) => obj.name === 'circuitNumber')
if (type === 2) {
modules.forEach((module) => {
module.set({
fill: module.originColor,
strokeWidth: 0.3,
})
})
circuitNumberTexts.forEach((text) => {
text.set({ visible: true })
})
}
canvas.renderAll()
}
//
// PCS
@ -263,7 +155,6 @@ export default function CircuitTrestleSetting({ id }) {
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
if (res.resultCode === 'S') {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({
title: res.resultMsg,
@ -297,7 +188,6 @@ export default function CircuitTrestleSetting({ id }) {
}).then((res) => {
if (res?.result.resultCode === 'S' && res?.data) {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
} else {
swalFire({ text: getMessage('common.message.send.error') })
}
@ -397,8 +287,6 @@ export default function CircuitTrestleSetting({ id }) {
setSelectedModels(pcsItemList)
getPcsVoltageChk(pcsVoltageChkParams).then((res) => {
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
setAllModuleSurfaceIsComplete(false)
clearTrestle()
})
} else {
swalFire({
@ -421,15 +309,8 @@ export default function CircuitTrestleSetting({ id }) {
const target = pcsCheck.max ? moduleMaxQty : moduleStdQty
const placementModules = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.MODULE)
let moduleAmount = placementModules.reduce((acc, module) => {
if (moduleSelectionData.module.itemList.length === 1 || module.moduleInfo.itemId === moduleSelectionData.module.itemList[0].itemId) {
return acc + 1
} else {
return acc + 0.66
}
}, 0)
if (moduleAmount > target) {
if (placementModules.length > target) {
swalFire({
title: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.all.power.conditional.validation.error01'),
type: 'alert',
@ -438,7 +319,6 @@ export default function CircuitTrestleSetting({ id }) {
}
setAllocationType(ALLOCATION_TYPE.PASSIVITY)
clearTrestle()
}
}
@ -468,9 +348,13 @@ export default function CircuitTrestleSetting({ id }) {
.map((obj) => {
obj.pcses = getStepUpListData()
})
await capture(1)
setViewCircuitNumberTexts(false)
handleCanvasToPng(1)
// result=null
setViewCircuitNumberTexts(true)
//
//
@ -485,7 +369,7 @@ export default function CircuitTrestleSetting({ id }) {
const result = await getEstimateData()
if (result) {
await capture(2)
handleCanvasToPng(2)
//
await saveEstimate(result)
} else {
@ -495,16 +379,6 @@ export default function CircuitTrestleSetting({ id }) {
// removeNotAllocationModules()
}
const changeFontSize = (name, size) => {
const textObjs = canvas?.getObjects().filter((obj) => obj.name === name)
textObjs.forEach((obj) => {
obj.set({
fontSize: size,
})
})
canvas.renderAll()
}
//
const onClickPrev = () => {
// setAllocationType(ALLOCATION_TYPE.AUTO)
@ -605,7 +479,7 @@ export default function CircuitTrestleSetting({ id }) {
console.log(stepUpListData)
stepUpListData[0].pcsItemList.map((item, index) => {
return item.serQtyList
.filter((serQty) => serQty.selected && serQty.paralQty > 0)
.filter((serQty) => serQty.selected)
.forEach((serQty) => {
pcs.push({
pcsMkrCd: item.pcsMkrCd,
@ -781,7 +655,6 @@ export default function CircuitTrestleSetting({ id }) {
return
} else {
setTabNum(2)
setAllModuleSurfaceIsComplete(false)
}
})
}

View File

@ -5,7 +5,6 @@ import { useMasterController } from '@/hooks/common/useMasterController'
import { useMessage } from '@/hooks/useMessage'
import { useSwal } from '@/hooks/useSwal'
import { pcsCheckState } from '@/store/circuitTrestleAtom'
import { sessionStore } from '@/store/commonAtom'
import { globalLocaleStore } from '@/store/localeAtom'
import { moduleSelectionDataState, selectedModuleState } from '@/store/selectedModuleOptions'
import { isNullOrUndefined } from '@/util/common-utils'
@ -39,14 +38,13 @@ export default function PowerConditionalSelect(props) {
} = props
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const sessionState = useRecoilValue(sessionStore)
const { getMessage } = useMessage()
const [selectedRow, setSelectedRow] = useState(null)
const globalLocale = useRecoilValue(globalLocaleStore)
const { getPcsMakerList, getPcsModelList } = useMasterController()
const selectedModules = useRecoilValue(selectedModuleState)
const { swalFire } = useSwal()
// const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const { trigger: moduleSelectedDataTrigger } = useCanvasPopupStatusController(2)
const [moduleSelectionData, setModuleSelectionData] = useRecoilState(moduleSelectionDataState)
const modelHeader = [
{ name: getMessage('modal.circuit.trestle.setting.circuit.allocation.passivity.series'), width: '15%', prop: 'pcsSerNm', type: 'color-box' },
@ -74,9 +72,11 @@ export default function PowerConditionalSelect(props) {
]
useEffect(() => {
getPcsMakerList().then((res) => {
setMakers(res.data)
})
if (makers.length === 0) {
getPcsMakerList().then((res) => {
setMakers(res.data)
})
}
}, [])
const onCheckSeries = (data) => {
@ -110,7 +110,6 @@ export default function PowerConditionalSelect(props) {
selected: s.pcsSerCd === data.pcsSerCd ? !s.selected : false,
}
})
setSelectedModels([])
}
setSeries(copySeries)
handleSetmodels(copySeries.filter((s) => s.selected))
@ -132,7 +131,7 @@ export default function PowerConditionalSelect(props) {
mixMatlNo: item.mixMatlNo,
}
})
getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList, storeId: sessionState.storeId }).then((res) => {
getPcsModelList({ pcsMkrCd, pcsSerList, moduleItemList }).then((res) => {
if (res?.result.code === 200 && res?.data) {
setModels(
res.data.map((model) => {
@ -177,7 +176,7 @@ export default function PowerConditionalSelect(props) {
if (selectedMaker.pcsMkrMultiType === PCS_MKR_MULTI_TYPE.MULTI) {
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
} else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId && m.pcsSerCd === selectedRow.pcsSerCd)) {
} else if (!selectedModels.find((m) => m.itemId === selectedRow.itemId)) {
setSelectedModels([...selectedModels, { ...selectedRow, id: uuidv4() }])
}
setSelectedRow(null)

View File

@ -42,6 +42,7 @@ export default function StepUp(props) {
const [arrayLength, setArrayLength] = useState(3) //module-table-inner
const [pcsCheck, setPcsCheck] = useRecoilState(pcsCheckState)
const { getPcsVoltageStepUpList, getPcsAutoRecommendList, getPcsVoltageChk, getPcsConnOptionItemList } = useMasterController()
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const canvas = useRecoilValue(canvasState)
const selectedModules = useRecoilValue(selectedModuleState)
const [optCodes, setOptCodes] = useState([])
@ -109,7 +110,6 @@ export default function StepUp(props) {
/** 캔버스에 회로 정보 적용 */
// pcs setSubOpsions, setMainOptions
console.log('stepUpListData', stepUpListData)
let mChk = 0;
stepUpListData[0].pcsItemList.forEach((pcsItem, index) => {
const optionList = formatOptionCodes(pcsItem.optionList)
if (isMultiOptions()) {
@ -165,8 +165,6 @@ export default function StepUp(props) {
targetModule.pcsItemId = module.pcsItemId
targetModule.circuitNumber = module.circuit
canvas.add(moduleCircuitText)
} else {
mChk++;
}
})
})
@ -175,10 +173,6 @@ export default function StepUp(props) {
canvas.renderAll()
setModuleStatisticsData()
if (mChk > 0) {
swalFire({ text: getMessage('modal.circuit.trestle.setting.step.up.allocation.module.over.count') })
}
} else {
swalFire({ text: getMessage('common.message.send.error') })
}
@ -474,7 +468,7 @@ export default function StepUp(props) {
module.pcsItemId = null
})
/** 선택된 모듈 목록 추가 */
/** 선택된 모듈 목록 추가 */
selectedData.roofSurfaceList.forEach((roofSurface) => {
const targetSurface = canvas.getObjects().filter((obj) => obj.id === roofSurface.roofSurfaceId)[0]
const moduleIds = targetSurface.modules.map((module) => {
@ -523,7 +517,7 @@ export default function StepUp(props) {
canvas.renderAll()
setModuleStatisticsData()
}
}
/**
* 현재 선택된 값들을 가져오는 함수 추가
@ -579,7 +573,7 @@ export default function StepUp(props) {
value={seletedMainOption}
sourceKey="code"
targetKey="code"
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
showKey="name"
onChange={(e) => setSeletedMainOption(e)}
/>
)}
@ -592,7 +586,7 @@ export default function StepUp(props) {
value={seletedSubOption}
sourceKey="code"
targetKey="code"
showKey={`${globalLocale === 'ja' ? 'nameJp' : 'name'}`}
showKey="name"
onChange={(e) => setSeletedSubOption(e)}
/>
)}

View File

@ -25,7 +25,7 @@ export default function PassivityCircuitAllocation(props) {
const { swalFire } = useSwal()
const { getMessage } = useMessage()
const canvas = useRecoilValue(canvasState)
const { managementState } = useContext(GlobalDataContext)
const { managementState, setManagementState, managementStateLoaded } = useContext(GlobalDataContext)
const selectedModules = useRecoilValue(selectedModuleState)
const [selectedPcs, setSelectedPcs] = useState(selectedModels[0])
const { header, rows, footer } = useRecoilValue(moduleStatisticsState)
@ -38,6 +38,7 @@ export default function PassivityCircuitAllocation(props) {
useEffect(() => {
setModuleStatisticsData()
if (!managementState) {
setManagementState(managementStateLoaded)
}
canvas
.getObjects()
@ -86,26 +87,6 @@ export default function PassivityCircuitAllocation(props) {
.map((obj) => obj.circuitNumber),
),
]
const surfaceList = targetModules.map((module) => {
return canvas.getObjects().filter((obj) => obj.id === canvas.getObjects().filter((obj) => obj.id === module)[0].surfaceId)[0]
})
if (surfaceList.length > 1) {
let surfaceType = {}
surfaceList.forEach((surface) => {
surfaceType[`${surface.direction}-${surface.roofMaterial.pitch}`] = surface
})
if (Object.keys(surfaceType).length > 1) {
swalFire({
text: getMessage('module.circuit.fix.not.same.roof.error'),
type: 'alert',
icon: 'warning',
})
return
}
}
if (!circuitNumber || circuitNumber === 0) {
swalFire({
text: getMessage('module.circuit.minimun.error'),

View File

@ -18,7 +18,7 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra01" value="1" checked={type === '1'} onChange={(e) => onChange(e)} />
<label htmlFor="ra01">{getMessage('has.not.sleeve')}</label>
<label htmlFor="ra01">{getMessage('has.sleeve')}</label>
</div>
</div>
<div className="eaves-keraba-td">
@ -31,7 +31,7 @@ export default function WallMerge({ offsetRef, radioTypeRef }) {
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input type="radio" name="radio01" id="ra02" value="2" checked={type === '2'} onChange={(e) => onChange(e)} />
<label htmlFor="ra02">{getMessage('has.sleeve')}</label>
<label htmlFor="ra02">{getMessage('has.not.sleeve')}</label>
</div>
</div>
<div className="eaves-keraba-td">

View File

@ -7,7 +7,6 @@ import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
const FLOW_DIRECTION_TYPE = {
EIGHT_AZIMUTH: 'eightAzimuth',
@ -20,8 +19,6 @@ export default function FlowDirectionSetting(props) {
const canvas = useRecoilValue(canvasState)
const { getMessage } = useMessage()
const { changeSurfaceLineType } = useSurfaceShapeBatch({})
useEffect(() => {
return () => {
canvas?.discardActiveObject()
@ -32,40 +29,15 @@ export default function FlowDirectionSetting(props) {
const [flowDirection, setFlowDirection] = useState(target.direction)
const { closePopup } = usePopup()
useEffect(() => {
let newCompassDeg = 0
if ([-15, 0, 15].includes(compasDeg)) {
newCompassDeg = 0
} else if ([30, 45, 60].includes(compasDeg)) {
newCompassDeg = 45
} else if ([75, 90, 105].includes(compasDeg)) {
newCompassDeg = 90
} else if ([120, 135, 150].includes(compasDeg)) {
newCompassDeg = 135
} else if ([165, 180, -165].includes(compasDeg)) {
newCompassDeg = 180
} else if ([-120, -135, -150].includes(compasDeg)) {
newCompassDeg = -135
} else if ([-105, -90, -75].includes(compasDeg)) {
newCompassDeg = -90
} else if ([-60, -45, -30].includes(compasDeg)) {
newCompassDeg = -45
} else {
newCompassDeg = ''
}
const newOrientation = orientations.find((item) => item.value === newCompassDeg)
setSelectedOrientation(newOrientation)
}, [compasDeg])
const orientations = [
{ name: `${getMessage('commons.none')}`, value: '' },
{ name: `${getMessage('commons.south')}`, value: 0 },
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 45 },
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: -45 },
{ name: `${getMessage('commons.east')}`, value: 90 },
{ name: `${getMessage('commons.west')}`, value: -90 },
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 135 },
{ name: `${getMessage('commons.north')}${getMessage('commons.west')}`, value: -135 },
{ name: `${getMessage('commons.none')}`, value: 0 },
{ name: `${getMessage('commons.south')}`, value: 360 },
{ name: `${getMessage('commons.south')}${getMessage('commons.east')}`, value: 315 },
{ name: `${getMessage('commons.south')}${getMessage('commons.west')}`, value: 45 },
{ name: `${getMessage('commons.east')}`, value: 270 },
{ name: `${getMessage('commons.west')}`, value: 90 },
{ name: `${getMessage('commons.north')}${getMessage('commons.east')}`, value: 225 },
{ name: `${getMessage('commons.north')}${getMessage('commons.west')}`, value: 135 },
{ name: `${getMessage('commons.north')}`, value: 180 },
]
@ -81,7 +53,6 @@ export default function FlowDirectionSetting(props) {
})
drawDirectionArrow(roof)
canvas?.renderAll()
changeSurfaceLineType(roof)
closePopup(id)
}
@ -134,11 +105,6 @@ export default function FlowDirectionSetting(props) {
value={selectedOrientation}
options={orientations}
onChange={(e) => {
if (e.value === '') {
setCompasDeg(null)
setSelectedOrientation(e)
return
}
setType(FLOW_DIRECTION_TYPE.EIGHT_AZIMUTH)
setSelectedOrientation(e)
setCompasDeg(e.value)
@ -167,32 +133,31 @@ export default function FlowDirectionSetting(props) {
<div className="draw-flow-wrap">
<div className="compas-box">
<div className="compas-box-inner">
{Array.from({ length: 180 / 15 }).map((dot, index) => (
{Array.from({ length: 180 / 15 + 1 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg === -15 * index + 180 ? 'act' : ''}`}
className={`circle ${compasDeg === 15 * (12 + index) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? 'act' : ''}`}
onClick={() => {
if (index === 0) {
setCompasDeg(180)
return
}
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
setCompasDeg(-15 * index + 180)
setCompasDeg(15 * (12 + index))
}}
></div>
))}
{Array.from({ length: 180 / 15 }).map((dot, index) => (
{Array.from({ length: 180 / 15 - 1 }).map((dot, index) => (
<div
key={index}
className={`circle ${compasDeg === -1 * 15 * index ? 'act' : ''}`}
className={`circle ${compasDeg === 15 * (index + 1) && type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH ? 'act' : ''}`}
onClick={() => {
setType(FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH)
setCompasDeg(15 * index * -1)
setCompasDeg(15 * (index + 1))
}}
></div>
))}
<div className="compas">
<div className="compas-arr" style={{ transform: `${`rotate(${-1 * compasDeg}deg)`}` }}></div>
<div
className="compas-arr"
style={{ transform: `${type === FLOW_DIRECTION_TYPE.TWENTY_FOUR_AZIMUTH && `rotate(${compasDeg}deg)`}` }}
></div>
</div>
</div>
</div>

View File

@ -4,11 +4,9 @@ import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { gridColorState } from '@/store/gridAtom'
import { gridDisplaySelector } from '@/store/settingAtom'
import { currentObjectState } from '@/store/canvasAtom'
import { useGrid } from '@/hooks/common/useGrid'
const GRID_PADDING = 5
export default function GridCopy(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props
@ -17,39 +15,9 @@ export default function GridCopy(props) {
const [length, setLength] = useState('0')
const [arrow, setArrow] = useState(null)
const currentObject = useRecoilValue(currentObjectState)
const canvas = useRecoilValue(canvasState)
const gridColor = useRecoilValue(gridColorState)
const isGridDisplay = useRecoilValue(gridDisplaySelector)
const { copy } = useGrid()
const handleApply = () => {
copy(currentObject, ['↑', '←'].includes(arrow) ? (+length * -1) / 10 : +length / 10)
}
const copy = (object, length) => {
const lineStartX = object.direction === 'vertical' ? object.x1 + length : object.x1
const lineEndX = object.direction === 'vertical' ? object.x2 + length : object.x2
const lineStartY = object.direction === 'vertical' ? object.y1 : object.y1 + length
const lineEndY = object.direction === 'vertical' ? object.y2 : object.y1 + length
const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], {
stroke: gridColor,
strokeWidth: 1,
selectable: true,
lockMovementX: true,
lockMovementY: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
strokeDashArray: [5, 2],
opacity: 0.3,
padding: GRID_PADDING,
direction: object.direction,
visible: isGridDisplay,
name: object.name,
})
canvas.add(line)
canvas.setActiveObject(line)
canvas.renderAll()
copy(currentObject, ['↑', '←'].includes(arrow) ? +length * -1 : +length)
}
return (
<WithDraggable isShow={true} pos={pos} className="xm">

View File

@ -3,9 +3,12 @@ import { useMessage } from '@/hooks/useMessage'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilState, useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useCanvas } from '@/hooks/useCanvas'
import { canvasState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { useGrid } from '@/hooks/common/useGrid'
import { useSwal } from '@/hooks/useSwal'
import { set } from 'react-hook-form'
export default function GridMove(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
@ -14,6 +17,7 @@ export default function GridMove(props) {
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const { swalFire } = useSwal()
const { move } = useGrid()
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
const [isAll, setIsAll] = useState(false)
const [verticalSize, setVerticalSize] = useState('0')
@ -50,31 +54,21 @@ export default function GridMove(props) {
.forEach((grid) => {
move(
grid,
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
})
} else {
move(
currentObject,
arrow2 === '←' ? (Number(horizonSize) * -1) / 10 : Number(horizonSize) / 10,
arrow1 === '↑' ? (Number(verticalSize) * -1) / 10 : Number(verticalSize) / 10,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
}
canvas.renderAll()
handleClose()
}
const move = (object, x, y) => {
object.set({
...object,
x1: object.direction === 'vertical' ? object.x1 + x : object.x1,
x2: object.direction === 'vertical' ? object.x1 + x : object.x2,
y1: object.direction === 'vertical' ? object.y1 : object.y1 + y,
y2: object.direction === 'vertical' ? object.y2 : object.y1 + y,
})
}
const handleClose = () => {
closePopup(id)
}

View File

@ -4,18 +4,6 @@ import { onlyNumberInputChange } from '@/util/input-utils'
export default function RightAngle({ props }) {
const { getMessage } = useMessage()
const { length1, setLength1, length1Ref, length2, setLength2, length2Ref, arrow1, setArrow1, arrow2, setArrow2 } = props
const handleClickArrow = (arrow) => {
const arrowType = arrow === '↑' ? 'ArrowUp' : arrow === '↓' ? 'ArrowDown' : arrow === '←' ? 'ArrowLeft' : arrow === '→' ? 'ArrowRight' : ''
setArrow2(arrow)
const originLeng2Val = length2Ref.current.value
if (originLeng2Val === '') {
length2Ref.current.value = '0'
}
length2Ref.current.focus()
length2Ref.current.value = originLeng2Val
document.dispatchEvent(new KeyboardEvent('keydown', { key: arrowType }))
}
return (
<div className="outline-wrap">
<div className="outline-inner">
@ -101,25 +89,29 @@ export default function RightAngle({ props }) {
<button
className={`direction up ${arrow2 === '↑' ? 'act' : ''}`}
onClick={() => {
handleClickArrow('↑')
setArrow2('↑')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
}}
></button>
<button
className={`direction down ${arrow2 === '↓' ? 'act' : ''}`}
onClick={() => {
handleClickArrow('↓')
setArrow2('↓')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
}}
></button>
<button
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
onClick={() => {
handleClickArrow('←')
setArrow2('←')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
}}
></button>
<button
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
onClick={() => {
handleClickArrow('→')
setArrow2('→')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
}}
></button>
</div>

View File

@ -4,7 +4,10 @@ import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup'
import { useMessage } from '@/hooks/useMessage'
import { useEffect, useState } from 'react'
import { polygonToTurfPolygon } from '@/util/canvas-util'
import { deepCopyArray } from '@/util/common-utils'
import { canvasState } from '@/store/canvasAtom'
import * as turf from '@turf/turf'
import { POLYGON_TYPE } from '@/common/common'
import { useModule } from '@/hooks/module/useModule'
import { useSwal } from '@/hooks/useSwal'
@ -35,19 +38,6 @@ export default function PanelEdit(props) {
const isSetupModules = canvas.getObjects().filter((obj) => obj.name === 'module') // selectedObj
isSetupModules.forEach((obj) => obj.set({ lockMovementX: false, lockMovementY: false }))
}
//
return () => {
const modules = canvas.getObjects().filter((obj) => obj.name === 'module')
modules.forEach((obj) => {
obj.set({
stroke: 'black',
strokeWidth: 0.3,
})
})
canvas?.discardActiveObject() //
canvas.renderAll()
}
}, [])
//
@ -97,7 +87,7 @@ export default function PanelEdit(props) {
moduleMultiCopy('row', length, direction)
break
}
// closePopup(id)
closePopup(id)
}
return (

View File

@ -15,70 +15,73 @@ export default function FlowLine({ FLOW_LINE_REF }) {
const currentObject = useRecoilValue(currentObjectState)
const handleFocus = () => {
if (currentObject === null) {
FLOW_LINE_REF.POINTER_INPUT_REF.current.value = ''
FLOW_LINE_REF.FILLED_INPUT_REF.current.value = ''
FLOW_LINE_REF.FILLED_INPUT_REF.current.blur()
}
}
const handleInput = (e) => {
const regex = /^-?\d*$/
let value = e.target.value
if (!regex.test(value) && value !== '') return
if (value.startsWith('0') || value === '-0') return
setFilledInput(value.replace(/[^0-9-]/g, ''))
const value = e.target.value.replace(/^0+/, '')
setFilledInput(value.replace(/[^0-9]/g, ''))
}
return (
<>
<div className="outline-wrap">
<div className="guide">{getMessage('modal.movement.flow.line.info')}</div>
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.position')}</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />
</div>
</div>
<div className="moving-tab-content">
<div className="moving-tab-radio-wrap">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
ref={FLOW_LINE_REF.DOWN_LEFT_RADIO_REF}
onChange={() => {
setType(FLOW_LINE_TYPE.DOWN_LEFT)
}}
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.bottom.left')}</label>
<div className="eaves-keraba-table">
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
defaultChecked={true}
ref={FLOW_LINE_REF.DOWN_LEFT_RADIO_REF}
onChange={() => {
setType(FLOW_LINE_TYPE.DOWN_LEFT)
}}
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.bottom.left')}</label>
</div>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
ref={FLOW_LINE_REF.UP_RIGHT_RADIO_REF}
onChange={() => {
setType(FLOW_LINE_TYPE.UP_RIGHT)
}}
/>
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.top.right')}</label>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
{/*<input type="text" className="input-origin block" readOnly={true} ref={FLOW_LINE_REF.POINTER_INPUT_REF} />*/}
</div>
</div>
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.movement')}</span>
<div className="input-grid mr5">
<input
type="text"
className="input-origin block"
defaultValue={100}
ref={FLOW_LINE_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
onChange={handleInput}
/>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
ref={FLOW_LINE_REF.UP_RIGHT_RADIO_REF}
onChange={() => {
setType(FLOW_LINE_TYPE.UP_RIGHT)
}}
/>
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.top.right')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input
type="text"
className="input-origin block"
ref={FLOW_LINE_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
onChange={handleInput}
/>
</div>
<span className="thin">mm</span>
</div>
</div>
<span className="thin">mm</span>
</div>
</div>
</div>

View File

@ -15,7 +15,6 @@ export default function Updown({ UP_DOWN_REF }) {
const currentObject = useRecoilValue(currentObjectState)
const handleFocus = () => {
if (currentObject === null) {
UP_DOWN_REF.POINTER_INPUT_REF.current.value = ''
UP_DOWN_REF.FILLED_INPUT_REF.current.value = ''
UP_DOWN_REF.FILLED_INPUT_REF.current.blur()
}
@ -29,54 +28,61 @@ export default function Updown({ UP_DOWN_REF }) {
<>
<div className="outline-wrap">
<div className="guide">{getMessage('modal.movement.flow.line.updown.info')}</div>
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.position')}</span>
<div className="input-grid mr5">
<input type="text" className="input-origin block" defaultValue={100} readOnly={true} ref={UP_DOWN_REF.POINTER_INPUT_REF} />
</div>
</div>
<div className="moving-tab-content">
<div className="moving-tab-radio-wrap">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
ref={UP_DOWN_REF.UP_RADIO_REF}
defaultChecked={true}
onChange={() => {
setType(UP_DOWN_TYPE.UP)
}}
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.updown.up')}</label>
<div className="eaves-keraba-table">
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra01"
ref={UP_DOWN_REF.UP_RADIO_REF}
defaultChecked={true}
onChange={() => {
setType(UP_DOWN_TYPE.UP)
}}
/>
<label htmlFor="ra01">{getMessage('modal.movement.flow.line.updown.up')}</label>
</div>
</div>
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
ref={UP_DOWN_REF.DOWN_RADIO_REF}
onChange={() => {
setType(UP_DOWN_TYPE.DOWN)
}}
/>
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.updown.down')}</label>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
{/*<input type="text" className="input-origin block" readOnly={true} ref={UP_DOWN_REF.UP_INPUT_REF} />*/}
</div>
</div>
</div>
</div>
<div className="outline-form">
<span>{getMessage('modal.movement.flow.line.movement')}</span>
<div className="input-grid mr5">
<input
type="text"
className="input-origin block"
defaultValue={100}
ref={UP_DOWN_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
onChange={handleInput}
/>
<div className="eaves-keraba-item">
<div className="eaves-keraba-th">
<div className="d-check-radio pop">
<input
type="radio"
name="radio01"
id="ra02"
ref={UP_DOWN_REF.DOWN_RADIO_REF}
onChange={() => {
setType(UP_DOWN_TYPE.DOWN)
}}
/>
<label htmlFor="ra02">{getMessage('modal.movement.flow.line.updown.down')}</label>
</div>
</div>
<div className="eaves-keraba-td">
<div className="outline-form">
<div className="input-grid mr5" style={{ width: '100px' }}>
<input
type="text"
className="input-origin block"
ref={UP_DOWN_REF.FILLED_INPUT_REF}
value={filledInput}
onFocus={handleFocus}
onChange={handleInput}
/>
</div>
<span className="thin">mm</span>
</div>
</div>
<span className="thin">mm</span>
</div>
</div>
</div>

View File

@ -100,7 +100,7 @@ export default function ObjectSetting({ id, pos = { x: 50, y: 230 } }) {
]
return (
<WithDraggable isShow={true} pos={pos} className="lrr" isHidden={isHidden}>
<WithDraggable isShow={true} pos={pos} className="lrr" style={{ visibility: isHidden ? 'hidden' : 'visible' }}>
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.object')} onClose={() => closePopup(id)} />
<WithDraggable.Body>
<div className="modal-btn-wrap">

View File

@ -162,7 +162,6 @@ export default function WallLineSetting(props) {
<></>
)}
</div>
<div className="normal-font mt10">{getMessage('outerLine.property.info')}</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal mr5" onClick={handleRollback}>
{getMessage('modal.cover.outline.rollback')}

View File

@ -19,7 +19,6 @@ import { getChonByDegree, getDegreeByChon } from '@/util/canvas-util'
import { usePolygon } from '@/hooks/usePolygon'
import { canvasState } from '@/store/canvasAtom'
import { useRoofFn } from '@/hooks/common/useRoofFn'
import { usePlan } from '@/hooks/usePlan'
/**
* 지붕 레이아웃
@ -54,7 +53,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
rafter: useRef(null),
hajebichi: useRef(null),
}
const { saveCanvas } = usePlan()
/**
* 치수 입력방법(복시도입력/실측값입력/육지붕)
*/
@ -206,7 +205,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/**
* 배치면초기설정 저장 버튼 클릭
*/
const handleSaveBtn = async () => {
const handleSaveBtn = () => {
const roofInfo = {
...currentRoof,
planNo: basicSetting.planNo,
@ -227,7 +226,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
console.log('save Info', {
...basicSetting,
selectedRoofMaterial: {
...newAddedRoofs[0],
roofInfo,
},
})
@ -240,7 +239,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
* 선택된 지붕재 정보
*/
selectedRoofMaterial: {
...newAddedRoofs[0],
roofInfo,
},
})
@ -255,13 +254,14 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
/* 저장 후 화면 닫기 */
closePopup(id)
await saveCanvas(false)
}
return (
<WithDraggable isShow={true} pos={pos} className="ll">
<WithDraggable.Header title={getMessage('plan.menu.placement.surface.initial.setting')} />
<WithDraggable.Body>
<div className="left-bar modal-handle"></div>
<div className="right-bar modal-handle"></div>
<div className="placement-table">
<table>
<colgroup>
@ -271,11 +271,7 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
<tbody>
<tr>
<th>{getMessage('modal.placement.initial.setting.plan.drawing')}</th>
<td>
{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
{getMessage('modal.placement.initial.setting.plan.drawing.only.number')}
</td>
<td>{getMessage('modal.placement.initial.setting.plan.drawing.size.stuff')}</td>
</tr>
<tr>
<th>
@ -328,11 +324,11 @@ export default function PlacementShapeSetting({ id, pos = { x: 50, y: 180 }, pla
type="number"
className="input-origin block"
readOnly={currentRoof?.roofAngleSet !== item.value}
value={index === 0 ? (currentRoof?.pitch || '0') : (currentRoof?.angle || '0')}
value={index === 0 ? currentRoof?.pitch : currentRoof?.angle}
onChange={(e) =>
index === 0
? setCurrentRoof({ ...currentRoof, pitch: e.target.value * 1, angle: getDegreeByChon(e.target.value * 1) })
: setCurrentRoof({ ...currentRoof, pitch: getChonByDegree(e.target.value*1), angle: e.target.value * 1})
? setCurrentRoof({ ...currentRoof, pitch: e.target.value, angle: getDegreeByChon(e.target.value) })
: setCurrentRoof({ ...currentRoof, pitch: getChonByDegree(e.target.value), angle: e.target.value })
}
/>
</div>

View File

@ -27,7 +27,7 @@ export default function ActualSizeSetting(props) {
const handleFinish = () => {
swalFire({
text: getMessage("modal.roof.allocation.auxiliary.accept"),
text: '완료 하시겠습니까?',
type: 'confirm',
confirmFn: () => {
handleAlloc()
@ -37,7 +37,7 @@ export default function ActualSizeSetting(props) {
const handleClose = () => {
swalFire({
text: getMessage("modal.roof.allocation.auxiliary.accept"),
text: '완료 하시겠습니까?',
type: 'confirm',
confirmFn: () => {
handleAlloc()

View File

@ -186,7 +186,7 @@ export default function ContextRoofAllocationSetting(props) {
<input
type="text"
className="input-origin block"
value={roof.hajebichi === '' ? '0' : roof.hajebichi}
value={parseInt(roof.hajebichi)}
readOnly={roof.roofPchAuth === 'R'}
onChange={(e) => handleChangeInput(e, 'hajebichi', index)}
/>
@ -205,8 +205,8 @@ export default function ContextRoofAllocationSetting(props) {
// handleChangeInput(e, currentAngleType === 'slope' ? 'pitch' : 'angle', index)
handleChangePitch(e, index)
}}
value={currentAngleType === 'slope' ? (roof.pitch || '0') : (roof.angle || '0')}
defaultValue={currentAngleType === 'slope' ? (roof.pitch || '0') : (roof.angle || '0')}
value={currentAngleType === 'slope' ? roof.pitch : roof.angle}
defaultValue={currentAngleType === 'slope' ? roof.pitch : roof.angle}
/>
</div>
<span className="absol">{pitchText}</span>

View File

@ -105,16 +105,6 @@ export default function GridOption(props) {
initEvent()
}, [gridOptions])
useEffect(() => {
return () => {
setAdsorptionPointAddMode(false)
setTempGridMode(false)
setTimeout(() => {
initEvent()
}, 100)
}
}, [])
const dotLineGridProps = {
id: dotLineId,
setIsShow: setShowDotLineGridModal,

View File

@ -185,7 +185,7 @@ export default function SecondOption(props) {
const onClickOption = async (item) => {
let option4Data = settingModalSecondOptions?.option4
let adsorpPointData = adsorptionPointMode
let adsorpPointData = adsorptionPointMode.adsorptionPoint
// ( )
if (
@ -203,9 +203,11 @@ export default function SecondOption(props) {
//
setAdsorptionRange(item.range)
setAdsorptionPointMode(adsorpPointData)
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: adsorpPointData })
} else if (item === 'adsorpPoint') {
setAdsorptionPointMode(!adsorpPointData)
setAdsorptionPointMode({ ...adsorptionPointMode, adsorptionPoint: !adsorpPointData })
adsorpPointData = !adsorpPointData
}
setSettingsData({ ...settingsData, option4: [...option4Data], adsorptionPoint: adsorpPointData })
@ -255,7 +257,7 @@ export default function SecondOption(props) {
}}
>
<span>{getMessage('modal.canvas.setting.font.plan.absorption.point')}</span>
<i>{adsorptionPointMode ? 'ON' : 'OFF'}</i>
<i>{adsorptionPointMode.adsorptionPoint ? 'ON' : 'OFF'}</i>
</button>
</div>
</div>

View File

@ -6,22 +6,16 @@ import WithDraggable from '@/components/common/draggable/WithDraggable'
import SecondOption from '@/components/floor-plan/modal/setting01/SecondOption'
import { useMessage } from '@/hooks/useMessage'
import GridOption from '@/components/floor-plan/modal/setting01/GridOption'
import { adsorptionPointAddModeState, canGridOptionSeletor, tempGridModeState } from '@/store/canvasAtom'
import { useRecoilState, useRecoilValue } from 'recoil'
import { canGridOptionSeletor } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { usePopup } from '@/hooks/usePopup'
import { useCanvasSetting } from '@/hooks/option/useCanvasSetting'
import { useTempGrid } from '@/hooks/useTempGrid'
import { settingModalGridOptionsState } from '@/store/settingAtom'
import { useEvent } from '@/hooks/useEvent'
export default function SettingModal01(props) {
const { id } = props
const [buttonAct, setButtonAct] = useState(1)
const { getMessage } = useMessage()
const canGridOptionSeletorValue = useRecoilValue(canGridOptionSeletor)
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
const [tempGridMode, setTempGridMode] = useRecoilState(tempGridModeState)
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
const { closePopup } = usePopup()
const {
@ -77,22 +71,9 @@ export default function SettingModal01(props) {
setButtonAct(num)
}
const onClose = () => {
setTempGridMode(false)
setAdsorptionPointAddMode(false)
setGridOptions((prev) => {
const newSettingOptions = [...prev]
newSettingOptions[0].selected = false
newSettingOptions[2].selected = false
return [...newSettingOptions]
})
closePopup(id, true)
}
return (
<WithDraggable isShow={true} pos={{ x: 1275, y: 180 }} className="sm">
<WithDraggable.Header title={getMessage('modal.canvas.setting')} onClose={onClose} />
<WithDraggable.Header title={getMessage('modal.canvas.setting')} onClose={() => closePopup(id, true)} />
<WithDraggable.Body>
<div className="modal-btn-wrap">
<button className={`btn-frame modal ${buttonAct === 1 ? 'act' : ''}`} onClick={() => handleBtnClick(1)}>

Some files were not shown because too many files have changed in this diff Show More