Merge branch 'dev' into dev-yj

This commit is contained in:
yjnoh 2024-11-20 18:14:25 +09:00
commit da90a6d26a
24 changed files with 5407 additions and 4817 deletions

View File

@ -1,9 +1,53 @@
'ues client' 'ues client'
import { ErrorBoundary } from 'next/dist/client/components/error-boundary' // import { ErrorBoundary } from 'next/dist/client/components/error-boundary'
import ServerError from '../error' // import ServerError from '../error'
import { createContext, useEffect, useReducer, useState } from 'react'
export const FloorPlanProvider = ({ children }) => { const reducer = (prevState, nextState) => {
console.log('FloorPlanProvider') return { ...prevState, ...nextState }
return <ErrorBoundary fallback={<ServerError />}>{children}</ErrorBoundary>
} }
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: '', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
itemList: [],
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
}
export const FloorPlanContext = createContext({
floorPlanState: {},
setFloorPlanState: () => {},
estimateContextState: {},
setEstimateContextState: () => {},
})
const FloorPlanProvider = ({ children }) => {
const [floorPlanState, setFloorPlanState] = useState({
// 플랜 파일 업로드 모달 오픈 제어
refFileModalOpen: false,
// 플랜 회전 모드 제어
toggleRotate: false,
})
const [estimateContextState, setEstimateContextState] = useReducer(reducer, defaultEstimateData)
useEffect(() => {
console.log('🚀 ~ floorPlanState:', floorPlanState)
}, [floorPlanState])
return (
<FloorPlanContext.Provider value={{ floorPlanState, setFloorPlanState, estimateContextState, setEstimateContextState }}>
{children}
</FloorPlanContext.Provider>
)
}
export default FloorPlanProvider

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import FloorPlan from '@/components/floor-plan/FloorPlan' import FloorPlan from '@/components/floor-plan/FloorPlan'
import { FloorPlanProvider } from './FloorPlanProvider' import FloorPlanProvider from './FloorPlanProvider'
import CanvasLayout from '@/components/floor-plan/CanvasLayout' import CanvasLayout from '@/components/floor-plan/CanvasLayout'
export default function FloorPlanLayout({ children }) { export default function FloorPlanLayout({ children }) {

View File

@ -80,79 +80,32 @@ export default function Login() {
e.preventDefault() e.preventDefault()
const formData = new FormData(e.target) const formData = new FormData(e.target)
/////////////////////////////////////////////////////////// //
// const param = {
setSession({ loginId: formData.get('id'),
userId: 'NEW0166102', pwd: formData.get('password'),
saleStoreId: null, }
name: null, await promisePost({ url: '/api/login/v1.0/login', data: param })
mail: null, .then((res) => {
tel: null, if (res) {
storeId: 'TEMP02', if (res.data.result.resultCode === 'S') {
userNm: 'ㅇㅇ6610', setSession(res.data.data)
userNmKana: '신규사용자 16610', setSessionState(res.data.data)
category: '인상6610', // ID SAVE ,
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
custCd: '100000',
})
setSessionState({
userId: 'NEW0166102',
saleStoreId: null,
name: null,
mail: null,
tel: null,
storeId: 'TEMP02',
userNm: 'ㅇㅇ6610',
userNmKana: '신규사용자 16610',
category: '인상6610',
telNo: '336610',
fax: null,
email: 't10t@naver.com',
pwdInitYn: 'Y',
storeLvl: '1',
groupId: '60000',
custCd: '100000',
})
if (chkLoginId) { if (chkLoginId) {
Cookies.set('chkLoginId', formData.get('id'), { expires: 7 }) Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
} else { } else {
Cookies.remove('chkLoginId') Cookies.remove('chkLoginId')
} }
router.push('/') router.push('/')
// } else {
/////////////////////////////////////////////////////////// alert(res.data.result.resultMsg)
}
// - ** ** }
// const param = { })
// loginId: formData.get('id'), .catch((error) => {
// pwd: formData.get('password'), alert(error.response.data.message)
// } })
// await promisePost({ url: '/api/login/v1.0/login', data: param })
// .then((res) => {
// if (res) {
// if (res.data.result.resultCode === 'S') {
// setSession(res.data.data)
// setSessionState(res.data.data)
// // ID SAVE ,
// if (chkLoginId) {
// Cookies.set('chkLoginId', formData.get('id'), { expires: 7 })
// } else {
// Cookies.remove('chkLoginId')
// }
// router.push('/')
// } else {
// alert(res.data.result.resultMsg)
// }
// }
// })
// .catch((error) => {
// alert(error.response.data.message)
// })
} }
// //

View File

@ -58,7 +58,7 @@ export default function Estimate({ params }) {
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
// //
const { state, setState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid) const { estimateContextState, setEstimateContextState, addItem, handleEstimateFileDownload } = useEstimateController(params.pid)
// List // List
const [specialNoteList, setSpecialNoteList] = useState([]) const [specialNoteList, setSpecialNoteList] = useState([])
@ -105,9 +105,9 @@ export default function Estimate({ params }) {
let url = `/api/estimate/special-note-list` let url = `/api/estimate/special-note-list`
get({ url: url }).then((res) => { get({ url: url }).then((res) => {
if (isNotEmptyArray(res)) { if (isNotEmptyArray(res)) {
if (state?.estimateOption) { if (estimateContextState?.estimateOption) {
res.map((row) => { res.map((row) => {
let estimateOption = state?.estimateOption?.split('、') let estimateOption = estimateContextState?.estimateOption?.split('、')
row.text = false row.text = false
estimateOption.map((row2) => { estimateOption.map((row2) => {
if (row2 === row.code) { if (row2 === row.code) {
@ -119,16 +119,21 @@ export default function Estimate({ params }) {
} }
} }
}) })
}, [state?.estimateOption]) }, [estimateContextState?.estimateOption])
// set // set
useEffect(() => { useEffect(() => {
let estimateDate = dayjs(startDate).format('YYYY-MM-DD') let estimateDate = dayjs(startDate).format('YYYY-MM-DD')
setState({ estimateDate: estimateDate }) setEstimateContextState({ estimateDate: estimateDate })
}, [startDate]) }, [startDate])
//API
useEffect(() => { useEffect(() => {
// setState setStartDate(estimateContextState?.estimateDate)
}, [estimateContextState?.estimateDate])
useEffect(() => {
// setEstimateContextState
if (isNotEmptyArray(specialNoteList)) { if (isNotEmptyArray(specialNoteList)) {
const liveCheckedData = specialNoteList.filter((row) => row.text === true) const liveCheckedData = specialNoteList.filter((row) => row.text === true)
@ -138,7 +143,7 @@ export default function Estimate({ params }) {
} }
const newData = data.join('、') const newData = data.join('、')
setState({ estimateOption: newData }) setEstimateContextState({ estimateOption: newData })
} }
}, [specialNoteList]) }, [specialNoteList])
@ -148,23 +153,23 @@ export default function Estimate({ params }) {
event.stopPropagation() event.stopPropagation()
} }
// state // estimateContextState
useEffect(() => { useEffect(() => {
if (isNotEmptyArray(files)) { if (isNotEmptyArray(files)) {
files.map((row) => { files.map((row) => {
setState({ fileList: row.data }) setEstimateContextState({ fileList: row.data })
}) })
} else { } else {
setState({ fileList: [] }) setEstimateContextState({ fileList: [] })
} }
}, [files]) }, [files])
// set // set
useEffect(() => { useEffect(() => {
if (isNotEmptyArray(state.fileList)) { if (isNotEmptyArray(estimateContextState.fileList)) {
setOriginFiles(state.fileList) setOriginFiles(estimateContextState.fileList)
} }
}, [state?.fileList]) }, [estimateContextState?.fileList])
// //
const deleteOriginFile = async (objectNo, no) => { const deleteOriginFile = async (objectNo, no) => {
@ -176,7 +181,7 @@ export default function Estimate({ params }) {
await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => { await promisePost({ url: 'api/file/fileDelete', data: delParams }).then((res) => {
if (res.status === 204) { if (res.status === 204) {
setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no)) setOriginFiles(originFiles.filter((file) => file.objectNo === objectNo && file.no !== no))
setState({ setEstimateContextState({
fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no), fileList: originFiles.filter((file) => file.objectNo === objectNo && file.no !== no),
}) })
} }
@ -185,11 +190,11 @@ export default function Estimate({ params }) {
// option && // option &&
useEffect(() => { useEffect(() => {
if (state.estimateType !== '') { if (estimateContextState.estimateType !== '') {
const param = { const param = {
saleStoreId: session.storeId, saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: session.custCd,
docTpCd: state?.estimateType, docTpCd: estimateContextState?.estimateType,
} }
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
@ -198,14 +203,16 @@ export default function Estimate({ params }) {
setStorePriceList(res.data) setStorePriceList(res.data)
} }
}) })
setItemChangeYn(true)
} }
}, [state?.estimateType]) }, [estimateContextState?.estimateType])
useEffect(() => { useEffect(() => {
if (state?.priceCd) { if (estimateContextState?.priceCd) {
setShowPriceCd(state.priceCd) setShowPriceCd(estimateContextState.priceCd)
} }
}, [state?.priceCd]) }, [estimateContextState?.priceCd])
// option // option
const onChangeStorePriceList = (priceCd) => { const onChangeStorePriceList = (priceCd) => {
@ -215,13 +222,9 @@ export default function Estimate({ params }) {
docTpCd: priceCd, docTpCd: priceCd,
} }
// tempPriceCd ? // priceCd setEstimateContextState
// priceCd setState
// showPriceCd // showPriceCd
setShowPriceCd(priceCd) setShowPriceCd(priceCd)
//setState({
// tempPriceCd: priceCd,
//})
const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}` const apiUrl = `/api/estimate/price/store-price-list?${queryStringFormatter(param)}`
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
@ -232,14 +235,14 @@ export default function Estimate({ params }) {
} }
//Pricing //Pricing
const handlePricing = async (priceCd) => { const handlePricing = async (showPriceCd) => {
const param = { const param = {
saleStoreId: session.storeId, saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd, sapSalesStoreCd: session.custCd,
docTpCd: state.estimateType, docTpCd: estimateContextState.estimateType,
priceCd: priceCd, priceCd: showPriceCd,
//itemIdList: state.itemList, // delFlg 0.. //itemIdList: estimateContextState.itemList, // delFlg 0..
itemIdList: state.itemList.filter((item) => item.delFlg === '0'), itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0'),
} }
if (param.itemIdList.length > 0) { if (param.itemIdList.length > 0) {
@ -266,7 +269,7 @@ export default function Estimate({ params }) {
//itemId //itemId
if (data.result.code === 200) { if (data.result.code === 200) {
if (isNotEmptyArray(data.data2)) { if (isNotEmptyArray(data.data2)) {
state.itemList.map((item) => { estimateContextState.itemList.map((item) => {
let checkYn = false let checkYn = false
data.data2.map((item2) => { data.data2.map((item2) => {
if (item2.itemId === item.itemId) { if (item2.itemId === item.itemId) {
@ -280,8 +283,8 @@ export default function Estimate({ params }) {
} }
}) })
setState({ setEstimateContextState({
priceCd: priceCd, priceCd: showPriceCd,
itemList: updateList, itemList: updateList,
}) })
@ -305,11 +308,31 @@ export default function Estimate({ params }) {
setSelection(newSelection) setSelection(newSelection)
} }
//PKG input
const onChangePkgAsp = (value) => {
if (estimateContextState.estimateType === 'YJSS') {
let pkgAsp = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
if (isNaN(pkgAsp)) {
pkgAsp = 0
} else {
pkgAsp = pkgAsp.toLocaleString()
}
// PKG
let totVolKw = estimateContextState.totVolKw * 1000
let pkgTotPrice = pkgAsp * totVolKw
setEstimateContextState({
pkgAsp: pkgAsp,
pkgTotPrice: pkgTotPrice.toFixed(3),
})
}
}
// //
const onChangeAmount = (value, dispOrder, index) => { const onChangeAmount = (value, dispOrder, index) => {
//itemChangeFlg = 1, partAdd = 0 //itemChangeFlg = 1, partAdd = 0
let amount = value let amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
amount = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
if (isNaN(amount)) { if (isNaN(amount)) {
amount = 0 amount = 0
} else { } else {
@ -321,9 +344,9 @@ export default function Estimate({ params }) {
updates.amount = amount updates.amount = amount
updates.itemChangeFlg = '1' updates.itemChangeFlg = '1'
updates.partAdd = '0' updates.partAdd = '0'
updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * state.itemList[index].salePrice.replaceAll(',', '')).toLocaleString() updates.saleTotPrice = (Number(amount.replaceAll(',', '')) * estimateContextState.itemList[index].salePrice.replaceAll(',', '')).toLocaleString()
updateList = state.itemList.map((item) => { updateList = estimateContextState.itemList.map((item) => {
if (item.dispOrder === dispOrder) { if (item.dispOrder === dispOrder) {
return { ...item, ...updates } return { ...item, ...updates }
} else { } else {
@ -331,7 +354,7 @@ export default function Estimate({ params }) {
} }
}) })
setState({ setEstimateContextState({
itemList: updateList, itemList: updateList,
}) })
@ -341,8 +364,7 @@ export default function Estimate({ params }) {
// //
const onChangeSalePrice = (value, dispOrder, index) => { const onChangeSalePrice = (value, dispOrder, index) => {
//itemChangeFlg, partAdd //itemChangeFlg, partAdd
let salePrice let salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
salePrice = Number(value.replace(/[^0-9]/g, '').replaceAll(',', ''))
if (isNaN(salePrice)) { if (isNaN(salePrice)) {
salePrice = 0 salePrice = 0
} else { } else {
@ -351,9 +373,9 @@ export default function Estimate({ params }) {
let updateList = [] let updateList = []
let updates = {} let updates = {}
updates.salePrice = salePrice updates.salePrice = salePrice
updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * state.itemList[index].amount.replaceAll(',', '')).toLocaleString() updates.saleTotPrice = (Number(salePrice.replaceAll(',', '')) * estimateContextState.itemList[index].amount.replaceAll(',', '')).toLocaleString()
updateList = state.itemList.map((item) => { updateList = estimateContextState.itemList.map((item) => {
if (item.dispOrder === dispOrder) { if (item.dispOrder === dispOrder) {
return { ...item, ...updates } return { ...item, ...updates }
} else { } else {
@ -361,7 +383,7 @@ export default function Estimate({ params }) {
} }
}) })
setState({ setEstimateContextState({
itemList: updateList, itemList: updateList,
}) })
@ -377,7 +399,7 @@ export default function Estimate({ params }) {
let updateList = [] let updateList = []
let updates = {} let updates = {}
get({ url: apiUrl }).then((res) => { get({ url: apiUrl }).then((res) => {
console.log('아이템상세정보:::::::', res) // console.log(':::::::', res)
updates.objectNo = objectNo updates.objectNo = objectNo
updates.planNo = planNo updates.planNo = planNo
updates.itemId = res.itemId updates.itemId = res.itemId
@ -397,11 +419,12 @@ export default function Estimate({ params }) {
updates.specialNoteCd = res.spnAttrCds updates.specialNoteCd = res.spnAttrCds
updates.itemGroup = res.itemGroup updates.itemGroup = res.itemGroup
updates.delFlg = '0' // 0 updates.delFlg = '0' // 0
updates.saleTotPrice = res.salePrice * state.itemList[index].amount // updates.saleTotPrice = res.salePrice * estimateContextState.itemList[index].amount
updates.saleTotPrice = '0' // 0
//104671 //104671
let bomList = res.itemBomList let bomList = res.itemBomList
updateList = state.itemList.map((item) => { updateList = estimateContextState.itemList.map((item) => {
if (item.dispOrder === dispOrder) { if (item.dispOrder === dispOrder) {
return { ...item, ...updates } return { ...item, ...updates }
} else if (item.paDispOrder === dispOrder) { } else if (item.paDispOrder === dispOrder) {
@ -413,18 +436,18 @@ export default function Estimate({ params }) {
//paDispOrder //paDispOrder
if (bomList) { if (bomList) {
bomList.map((bomItem, index) => { bomList.map((bomItem, index) => {
let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder)) let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
bomItem.dispOrder = index + 1 + newItemDispOrder bomItem.dispOrder = index + 1 + newItemDispOrder
bomItem.delFlg = '0' bomItem.delFlg = '0'
bomItem.objectNo = objectNo bomItem.objectNo = objectNo
bomItem.planNo = planNo bomItem.planNo = planNo
}) })
setState({ setEstimateContextState({
itemList: [...updateList, ...bomList], itemList: [...updateList, ...bomList],
}) })
} else { } else {
setState({ setEstimateContextState({
itemList: updateList, itemList: updateList,
}) })
} }
@ -437,7 +460,7 @@ export default function Estimate({ params }) {
const removeItem = () => { const removeItem = () => {
const array = [...selection] const array = [...selection]
let delList = [] let delList = []
state.itemList.filter((row) => { estimateContextState.itemList.filter((row) => {
array.map((row2) => { array.map((row2) => {
if (row2 === row.dispOrder) { if (row2 === row.dispOrder) {
delList.push({ ...row }) delList.push({ ...row })
@ -448,7 +471,7 @@ export default function Estimate({ params }) {
}) })
}) })
const updateList = state.itemList.map((item) => { const updateList = estimateContextState.itemList.map((item) => {
const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder) const isDeleted = delList.some((row) => item.delFlg === '1' || item.dispOrder === row.dispOrder)
return { return {
...item, ...item,
@ -467,7 +490,7 @@ export default function Estimate({ params }) {
return alert(getMessage('estimate.detail.save.requiredItem')) return alert(getMessage('estimate.detail.save.requiredItem'))
} }
setState({ setEstimateContextState({
itemList: updateList, itemList: updateList,
}) })
@ -477,31 +500,94 @@ export default function Estimate({ params }) {
useEffect(() => { useEffect(() => {
if (itemChangeYn) { if (itemChangeYn) {
// console.log(' ::::::::::', state.itemList)
// console.log(' ::::::', itemList)
//delFlg 0 ..
//(PCS) : totAmount
//( Kw) : totVolKw
// : supplyPrice
//(10%) : vatPrice
// :totPrice
let totAmount = 0 let totAmount = 0
let amount = 0 let totVolKw = 0
state.itemList.map((item) => { let supplyPrice = 0
let vatPrice = 0
let totPrice = 0
let addPkgPrice = 0
if (estimateContextState.estimateType === 'YJOD') {
estimateContextState.itemList.map((item) => {
if (item.delFlg === '0') { if (item.delFlg === '0') {
amount = item.amount.replace(/[^0-9]/g, '').replaceAll(',', '') const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', ''))
totAmount += Number(amount) const price = Number(item.saleTotPrice.replaceAll(',', ''))
if (item.moduleFlg === '1') {
//(Kw) 1
const volKw = (item.pnowW * amount) / 1000
totVolKw += volKw
}
// const price
totAmount += amount
supplyPrice += price
} }
}) })
setState({ vatPrice = supplyPrice * 0.1
totPrice = supplyPrice + vatPrice
setEstimateContextState({
totAmount: totAmount, totAmount: totAmount,
totVolKw: totVolKw.toFixed(3),
supplyPrice: supplyPrice.toFixed(3),
vatPrice: vatPrice.toFixed(3),
totPrice: totPrice.toFixed(3),
}) })
} else {
//YJSS
estimateContextState.itemList.map((item) => {
if (item.delFlg === '0') {
const amount = Number(item.amount.replace(/[^0-9]/g, '').replaceAll(',', ''))
const price = Number(item.saleTotPrice.replaceAll(',', ''))
const salePrice = Number(item.salePrice.replaceAll(',', ''))
if (item.moduleFlg === '1') {
//(Kw) 1
const volKw = (item.pnowW * amount) / 1000
totVolKw += volKw
}
if (item.pkgMaterialFlg === '1') {
const pkgPrice = amount * salePrice
//YJSS PKG *
addPkgPrice += pkgPrice
}
// const price
totAmount += amount
supplyPrice += price
}
})
vatPrice = supplyPrice * 0.1
totPrice = supplyPrice + vatPrice
setEstimateContextState({
totAmount: totAmount,
totVolKw: totVolKw.toFixed(3),
supplyPrice: supplyPrice.toFixed(3),
vatPrice: vatPrice.toFixed(3),
totPrice: totPrice.toFixed(3),
})
}
setItemChangeYn(false) setItemChangeYn(false)
} }
}, [itemChangeYn]) }, [itemChangeYn, estimateContextState.itemList])
//
const handleBlurObjectName = (e) => {
setEstimateContextState({ objectName: e.target.value })
}
//
const handleBlurCharger = (e) => {
setEstimateContextState({ charger: e.target.value })
}
//
const handleBlurRemarks = (e) => {
setEstimateContextState({ remarks: e.target.value })
}
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
@ -518,17 +604,21 @@ export default function Estimate({ params }) {
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div> <div className="estimate-tit">{getMessage('estimate.detail.docNo')}</div>
<div className="estimate-name">{state.docNo}</div> <div className="estimate-name">{estimateContextState.docNo}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div> <div className="estimate-tit">{getMessage('estimate.detail.drawingEstimateCreateDate')}</div>
<div className="estimate-name"> <div className="estimate-name">
{state?.drawingEstimateCreateDate ? `${dayjs(state.drawingEstimateCreateDate).format('YYYY.MM.DD')}` : ''} {estimateContextState?.drawingEstimateCreateDate
? `${dayjs(estimateContextState.drawingEstimateCreateDate).format('YYYY.MM.DD')}`
: ''}
</div> </div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div> <div className="estimate-tit">{getMessage('estimate.detail.lastEditDatetime')}</div>
<div className="estimate-name">{state?.lastEditDatetime ? `${dayjs(state.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}</div> <div className="estimate-name">
{estimateContextState?.lastEditDatetime ? `${dayjs(estimateContextState.lastEditDatetime).format('YYYY.MM.DD HH:mm')}` : ''}
</div>
</div> </div>
</div> </div>
</div> </div>
@ -554,7 +644,7 @@ export default function Estimate({ params }) {
<tr> <tr>
{/* 1차 판매점명 */} {/* 1차 판매점명 */}
<th>{getMessage('estimate.detail.saleStoreId')}</th> <th>{getMessage('estimate.detail.saleStoreId')}</th>
<td>{state?.firstSaleStoreName}</td> <td>{estimateContextState?.firstSaleStoreName}</td>
{/* 견적일 */} {/* 견적일 */}
<th> <th>
{getMessage('estimate.detail.estimateDate')} <span className="important">*</span> {getMessage('estimate.detail.estimateDate')} <span className="important">*</span>
@ -568,22 +658,14 @@ export default function Estimate({ params }) {
<tr> <tr>
{/* 2차 판매점명 */} {/* 2차 판매점명 */}
<th>{getMessage('estimate.detail.otherSaleStoreId')}</th> <th>{getMessage('estimate.detail.otherSaleStoreId')}</th>
<td>{state?.agencySaleStoreName}</td> <td>{estimateContextState?.agencySaleStoreName}</td>
{/* 담당자 */} {/* 담당자 */}
<th> <th>
{getMessage('estimate.detail.receiveUser')} <span className="important">*</span> {getMessage('estimate.detail.receiveUser')} <span className="important">*</span>
</th> </th>
<td> <td>
<div className="input-wrap" style={{ width: '350px' }}> <div className="input-wrap" style={{ width: '350px' }}>
<input <input type="text" className="input-light" defaultValue={estimateContextState?.charger} onBlur={handleBlurCharger} />
type="text"
className="input-light"
defaultValue={state?.charger}
onChange={(e) => {
// charger
setState({ charger: e.target.value })
}}
/>
</div> </div>
</td> </td>
</tr> </tr>
@ -595,15 +677,7 @@ export default function Estimate({ params }) {
<td colSpan={3}> <td colSpan={3}>
<div className="form-flex-wrap"> <div className="form-flex-wrap">
<div className="input-wrap mr5" style={{ width: '610px' }}> <div className="input-wrap mr5" style={{ width: '610px' }}>
<input <input type="text" className="input-light" defaultValue={estimateContextState?.objectName} onBlur={handleBlurObjectName} />
type="text"
className="input-light"
defaultValue={state?.objectName}
onChange={(e) => {
// objectName
setState({ objectName: e.target.value })
}}
/>
</div> </div>
<div className="select-wrap" style={{ width: '200px' }}> <div className="select-wrap" style={{ width: '200px' }}>
<Select <Select
@ -615,17 +689,17 @@ export default function Estimate({ params }) {
options={honorificCodeList} options={honorificCodeList}
onChange={(e) => { onChange={(e) => {
if (isObjectNotEmpty(e)) { if (isObjectNotEmpty(e)) {
setState({ objectNameOmit: e.clCodeNm }) setEstimateContextState({ objectNameOmit: e.clCodeNm })
} else { } else {
setState({ objectNameOmit: '' }) setEstimateContextState({ objectNameOmit: '' })
} }
}} }}
getOptionLabel={(x) => x.clCodeNm} getOptionLabel={(x) => x.clCodeNm}
getOptionValue={(x) => x.clCode} getOptionValue={(x) => x.clCode}
isClearable={true} isClearable={false}
isSearchable={false} isSearchable={false}
value={honorificCodeList.filter(function (option) { value={honorificCodeList.filter(function (option) {
return option.clCodeNm === state.objectNameOmit return option.clCodeNm === estimateContextState.objectNameOmit
})} })}
/> />
</div> </div>
@ -635,7 +709,7 @@ export default function Estimate({ params }) {
<tr> <tr>
{/* 물건정보에서 입력한 메모 */} {/* 물건정보에서 입력한 메모 */}
<th>{getMessage('estimate.detail.objectRemarks')}</th> <th>{getMessage('estimate.detail.objectRemarks')}</th>
<td colSpan={3}>{state?.objectRemarks}</td> <td colSpan={3}>{estimateContextState?.objectRemarks}</td>
</tr> </tr>
<tr> <tr>
{/* 주문분류 */} {/* 주문분류 */}
@ -650,13 +724,13 @@ export default function Estimate({ params }) {
name="estimateType" name="estimateType"
id="YJSS" id="YJSS"
value={'YJSS'} value={'YJSS'}
checked={state?.estimateType === 'YJSS' ? true : false} checked={estimateContextState?.estimateType === 'YJSS' ? true : false}
onChange={(e) => { onChange={(e) => {
// //
setState({ estimateType: e.target.value }) setEstimateContextState({ estimateType: e.target.value })
}} }}
/> />
<label htmlFor="YJSS">住宅PKG</label> <label htmlFor="YJSS">{getMessage('estimate.detail.estimateType.yjss')}</label>
</div> </div>
<div className="d-check-radio light"> <div className="d-check-radio light">
<input <input
@ -664,12 +738,12 @@ export default function Estimate({ params }) {
name="estimateType" name="estimateType"
id="YJOD" id="YJOD"
value={'YJOD'} value={'YJOD'}
checked={state?.estimateType === 'YJOD' ? true : false} checked={estimateContextState?.estimateType === 'YJOD' ? true : false}
onChange={(e) => { onChange={(e) => {
setState({ estimateType: e.target.value }) setEstimateContextState({ estimateType: e.target.value })
}} }}
/> />
<label htmlFor="YJOD">積上げ( YJOD )</label> <label htmlFor="YJOD">{getMessage('estimate.detail.estimateType.yjod')}</label>
</div> </div>
</div> </div>
</td> </td>
@ -678,20 +752,20 @@ export default function Estimate({ params }) {
{/* 지붕재・사양시공 최대4개*/} {/* 지붕재・사양시공 최대4개*/}
<th>{getMessage('estimate.detail.roofCns')}</th> <th>{getMessage('estimate.detail.roofCns')}</th>
<td colSpan={3}> <td colSpan={3}>
{state?.roofMaterialIdMulti?.split('、').map((row, index) => { {estimateContextState?.roofMaterialIdMulti?.split('、').map((row, index) => {
// //
let roofList = row let roofList = row
let roofListLength = state?.roofMaterialIdMulti?.split('、').length let roofListLength = estimateContextState?.roofMaterialIdMulti?.split('、').length
let style = 'mb5' let style = 'mb5'
if (roofListLength == index + 1) { if (roofListLength == index + 1) {
style = '' style = ''
} }
// //
let constructSpecificationMulti = state?.constructSpecificationMulti?.split('、') let constructSpecificationMulti = estimateContextState?.constructSpecificationMulti?.split('、')
return ( return (
<> <>
<div className={`form-flex-wrap ${style}`} key={uuidv4()}> <div className={`form-flex-wrap ${style}`} key={fixedKey}>
<div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}> <div className="input-wrap mr5" style={{ width: '610px' }} key={`roof${index}`}>
<input type="text" className="input-light" value={roofList} readOnly /> <input type="text" className="input-light" value={roofList} readOnly />
</div> </div>
@ -709,15 +783,7 @@ export default function Estimate({ params }) {
<th>{getMessage('estimate.detail.remarks')}</th> <th>{getMessage('estimate.detail.remarks')}</th>
<td colSpan={3}> <td colSpan={3}>
<div className="input-wrap"> <div className="input-wrap">
<input <input type="text" className="input-light" defaultValue={estimateContextState?.remarks} onBlur={handleBlurRemarks} />
type="text"
className="input-light"
defaultValue={state?.remarks}
onChange={(e) => {
//
setState({ remarks: e.target.value })
}}
/>
</div> </div>
</td> </td>
</tr> </tr>
@ -732,9 +798,9 @@ export default function Estimate({ params }) {
<input <input
type="checkbox" type="checkbox"
id="next" id="next"
checked={state?.fileFlg === '0' ? false : true} checked={estimateContextState?.fileFlg === '0' ? false : true}
onChange={(e) => { onChange={(e) => {
setState({ setEstimateContextState({
fileFlg: e.target.checked ? '1' : '0', fileFlg: e.target.checked ? '1' : '0',
}) })
}} }}
@ -876,28 +942,28 @@ export default function Estimate({ params }) {
<div className="estimate-list-wrap one"> <div className="estimate-list-wrap one">
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totAmount')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totAmount)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.totAmount)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totVolKw')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.totVolKw)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.supplyPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.supplyPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.supplyPrice)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.vatPrice')}</div>
<div className="estimate-name blue">{convertNumberToPriceDecimal(state?.vatPrice)}</div> <div className="estimate-name blue">{convertNumberToPriceDecimal(estimateContextState?.vatPrice)}</div>
</div> </div>
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div> <div className="estimate-tit">{getMessage('estimate.detail.sepcialEstimateProductInfo.totPrice')}</div>
<div className="estimate-name red">{convertNumberToPriceDecimal(state?.totPrice)}</div> <div className="estimate-name red">{convertNumberToPriceDecimal(estimateContextState?.totPrice)}</div>
</div> </div>
</div> </div>
</div> </div>
{/* YJOD면 아래영역 숨김 */} {/* YJOD면 아래영역 숨김 */}
<div className="common-table bt-able" style={{ display: state?.estimateType === 'YJSS' ? '' : 'none' }}> <div className="common-table bt-able" style={{ display: estimateContextState?.estimateType === 'YJSS' ? '' : 'none' }}>
<table> <table>
<colgroup> <colgroup>
<col style={{ width: '160px' }} /> <col style={{ width: '160px' }} />
@ -915,13 +981,20 @@ export default function Estimate({ params }) {
</th> </th>
<td> <td>
<div className="input-wrap"> <div className="input-wrap">
<input type="text" className="input-light" /> <input
type="text"
className="input-light"
value={estimateContextState?.pkgAsp}
onChange={(e) => {
onChangePkgAsp(e.target.value)
}}
/>
</div> </div>
</td> </td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgWeight')}</th>
<td>{getMessage('estimate.detail.sepcialEstimateProductInfo.calcFormula1')}</td> <td>{convertNumberToPriceDecimal(estimateContextState?.totVolKw)}</td>
<th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th> <th>{getMessage('estimate.detail.sepcialEstimateProductInfo.pkgPrice')}</th>
<td>{getMessage('estimate.detail.sepcialEstimateProductInfo.calcFormula2')}</td> <td>{convertNumberToPriceDecimal(estimateContextState?.pkgTotPrice)}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
@ -953,7 +1026,7 @@ export default function Estimate({ params }) {
type="button" type="button"
className="btn-origin grey ml5" className="btn-origin grey ml5"
onClick={() => { onClick={() => {
handlePricing(state?.priceCd) handlePricing(showPriceCd)
}} }}
> >
{getMessage('estimate.detail.showPrice.pricingBtn')} {getMessage('estimate.detail.showPrice.pricingBtn')}
@ -1030,20 +1103,19 @@ export default function Estimate({ params }) {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{state?.itemList.length > 0 && {estimateContextState?.itemList.length > 0 &&
state.itemList.map((item, index) => { estimateContextState.itemList.map((item, index) => {
if (item.delFlg === '0') { if (item.delFlg === '0') {
return ( return (
<> <tr key={item?.dispOrder || index}>
<tr key={fixedKey}>
<td className="al-c"> <td className="al-c">
<div className="d-check-box light no-text"> <div className="d-check-box light no-text">
<input <input
type="checkbox" type="checkbox"
id={item?.dispOrder} id={item?.dispOrder}
disabled={item?.paDispOrder ? true : false} disabled={!!item?.paDispOrder}
onChange={() => onChangeSelect(item.dispOrder)} onChange={() => onChangeSelect(item.dispOrder)}
checked={selection.has(item.dispOrder) ? true : false} checked={!!selection.has(item.dispOrder)}
/> />
<label htmlFor={item?.dispOrder}></label> <label htmlFor={item?.dispOrder}></label>
</div> </div>
@ -1067,7 +1139,7 @@ export default function Estimate({ params }) {
getOptionLabel={(x) => x.itemName} getOptionLabel={(x) => x.itemName}
getOptionValue={(x) => x.itemId} getOptionValue={(x) => x.itemId}
isClearable={false} isClearable={false}
isDisabled={item?.paDispOrder ? true : false} isDisabled={!!item?.paDispOrder}
value={displayItemList.filter(function (option) { value={displayItemList.filter(function (option) {
return option.itemId === item.itemId return option.itemId === item.itemId
})} })}
@ -1104,7 +1176,7 @@ export default function Estimate({ params }) {
type="text" type="text"
className="input-light al-r" className="input-light al-r"
value={item?.amount} value={item?.amount}
disabled={item.itemId === '' ? true : item?.paDispOrder ? true : false} disabled={item.itemId === '' || !!item?.paDispOrder}
onChange={(e) => { onChange={(e) => {
onChangeAmount(e.target.value, item.dispOrder, index) onChangeAmount(e.target.value, item.dispOrder, index)
}} }}
@ -1118,19 +1190,13 @@ export default function Estimate({ params }) {
<input <input
type="text" type="text"
className="input-light al-r" className="input-light al-r"
value={item?.salePrice} value={convertNumberToPriceDecimal(item?.salePrice.replaceAll(',', ''))}
disabled={ disabled={
state?.estimateType === 'YJSS' estimateContextState?.estimateType === 'YJSS'
? item?.paDispOrder ? item?.paDispOrder
? true ? true
: item.pkgMaterialFlg === '1' : item.pkgMaterialFlg !== '1'
? false : item.itemId === '' || !!item?.paDispOrder
: true
: item.itemId === ''
? true
: item?.paDispOrder
? true
: false
} }
onChange={(e) => { onChange={(e) => {
onChangeSalePrice(e.target.value, item.dispOrder, index) onChangeSalePrice(e.target.value, item.dispOrder, index)
@ -1142,9 +1208,8 @@ export default function Estimate({ params }) {
</div> */} </div> */}
</div> </div>
</td> </td>
<td className="al-r">{item?.saleTotPrice}</td> <td className="al-r">{convertNumberToPriceDecimal(item?.saleTotPrice.replaceAll(',', ''))}</td>
</tr> </tr>
</>
) )
} else { } else {
return null return null

View File

@ -0,0 +1,313 @@
'use client'
import { useEffect, useState, useContext, useRef } from 'react'
import { useMessage } from '@/hooks/useMessage'
import { useAxios } from '@/hooks/useAxios'
import Select, { components } from 'react-select'
import { SessionContext } from '@/app/SessionProvider'
import { isEmptyArray, isObjectNotEmpty } from '@/util/common-utils'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
export default function EstimateCopyPop({ planNo, setEstimateCopyPopupOpen }) {
const { getMessage } = useMessage()
const { get } = useAxios()
const { handleEstimateCopy, state } = useEstimateController(planNo)
const { session } = useContext(SessionContext)
const [saleStoreList, setSaleStoreList] = useState([]) //
const [favoriteStoreList, setFavoriteStoreList] = useState([]) //
const [showSaleStoreList, setShowSaleStoreList] = useState([]) //
const [otherSaleStoreList, setOtherSaleStoreList] = useState([])
const [originOtherSaleStoreList, setOriginOtherSaleStoreList] = useState([])
const [saleStoreId, setSaleStoreId] = useState('') // 1
const [otherSaleStoreId, setOtherSaleStoreId] = useState('') // 1
const [sendPlanNo, setSendPlanNo] = useState('1')
const [copyReceiveUser, setCopyReceiveUser] = useState('')
const ref = useRef() //2
useEffect(() => {
let url
let firstList
let favList
let otherList
if (session.storeId === 'T01') {
url = `/api/object/saleStore/${session?.storeId}/firstList?userId=${session?.userId}`
} else {
if (session.storeLvl === '1') {
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
} else {
//T01 or 1
// url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=1&userId=${session?.userId}`
}
}
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
//
row.value = row.saleStoreId
row.label = row.saleStoreName
})
if (session.storeId === 'T01') {
firstList = res
//T01
firstList.sort((a, b) => (a.saleStoreId !== 'T01') - (b.saleStoreId !== 'T01') || a.saleStoreId - b.saleStoreId)
favList = firstList.filter((row) => row.saleStoreId === 'T01' || row.priority !== 'B')
setSaleStoreList(firstList)
setFavoriteStoreList(favList)
setShowSaleStoreList(favList)
setSaleStoreId(session?.storeId)
// T01 T01 2 (onSelectionChange..) 2
url = `/api/object/saleStore/${session?.storeId}/list?firstFlg=0&userId=${session?.userId}`
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
row.value == row.saleStoreId
row.label = row.saleStoreName
})
otherList = res
setOtherSaleStoreList(otherList)
setOriginOtherSaleStoreList(otherList)
} else {
setOtherSaleStoreList([])
}
})
} else {
if (session.storeLvl === '1') {
firstList = res
favList = res.filter((row) => row.priority !== 'B')
otherList = res.filter((row) => row.firstAgentYn === 'N')
setSaleStoreList(firstList)
setFavoriteStoreList(firstList)
setShowSaleStoreList(firstList)
setSaleStoreId(firstList[0].saleStoreId)
setOtherSaleStoreList(otherList)
} else {
//T01 or 1
}
}
}
})
}, [])
useEffect(() => {
if (planNo) {
setSendPlanNo(planNo)
}
}, [planNo])
useEffect(() => {
if (state?.charger) {
setCopyReceiveUser(state.charger)
}
}, [state.charger])
//T01 1
const onInputChange = (key) => {
if (key !== '') {
setShowSaleStoreList(saleStoreList)
} else {
setShowSaleStoreList(favoriteStoreList)
}
}
// 1
const onSelectionChange = (key) => {
if (isObjectNotEmpty(key)) {
if (key.saleStoreId === saleStoreId) {
return
}
}
if (isObjectNotEmpty(key)) {
setSaleStoreId(key.saleStoreId)
const url = `/api/object/saleStore/${key.saleStoreId}/list?firstFlg=0&userId=${session?.userId}`
let otherList
get({ url: url }).then((res) => {
if (!isEmptyArray(res)) {
res.map((row) => {
row.value = row.saleStoreId
row.label = row.saleStoreName
})
otherList = res
setOtherSaleStoreList(otherList)
setOtherSaleStoreId('')
} else {
setOtherSaleStoreId('')
setOtherSaleStoreList([])
}
})
} else {
setSaleStoreId('')
//otherSaleStoreId onSelectionChange2
setOtherSaleStoreList(originOtherSaleStoreList)
handleClear()
}
}
// 2
const onSelectionChange2 = (key) => {
if (isObjectNotEmpty(key)) {
if (key.saleStoreId === otherSaleStoreId) {
return
}
}
if (isObjectNotEmpty(key)) {
setOtherSaleStoreId(key.saleStoreId)
} else {
setOtherSaleStoreId('')
}
}
//2
const handleClear = () => {
if (ref.current) {
ref.current.clearValue()
}
}
return (
<div className="modal-popup">
<div className="modal-dialog">
<div className="modal-content">
<div className="modal-header">
<h1 className="title">{getMessage('estimate.detail.estimateCopyPopup.title')}</h1>
<button
type="button"
className="modal-close"
onClick={() => {
setEstimateCopyPopupOpen(false)
}}
>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
</div>
<div className="modal-body">
<div className="modal-body-inner">
<div className="explane">{getMessage('estimate.detail.estimateCopyPopup.explane')}</div>
<div className="estimate-copy-info-wrap">
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">
{getMessage('estimate.detail.estimateCopyPopup.label.saleStoreId')} <span className="red">*</span>
</div>
{session.storeId === 'T01' && (
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={showSaleStoreList}
onInputChange={onInputChange}
onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={true}
isDisabled={false}
value={saleStoreList.filter(function (option) {
return option.saleStoreId === saleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{saleStoreId}</div>
</div>
)}
{session.storeId !== 'T01' && session.storeLvl === '1' && (
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select1"
instanceId="long-value-select1"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
options={showSaleStoreList[0]}
onChange={onSelectionChange}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={false}
isDisabled={session?.storeLvl !== '1' ? true : session?.storeId !== 'T01' ? true : false}
value={showSaleStoreList.filter(function (option) {
return option.saleStoreId === saleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{saleStoreId}</div>
</div>
)}
</div>
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">{getMessage('estimate.detail.estimateCopyPopup.label.otherSaleStoreId')}</div>
<div className="estimate-copy-info-box">
<div className="estimate-copy-sel">
<Select
id="long-value-select2"
instanceId="long-value-select2"
className="react-select-custom"
classNamePrefix="custom"
placeholder="Select"
ref={ref}
options={otherSaleStoreList}
onChange={onSelectionChange2}
getOptionLabel={(x) => x.saleStoreName}
getOptionValue={(x) => x.saleStoreId}
isClearable={true}
isDisabled={otherSaleStoreList.length > 0 ? false : true}
value={otherSaleStoreList.filter(function (option) {
return option.saleStoreId === otherSaleStoreId
})}
/>
</div>
<div className="estimate-copy-id">{otherSaleStoreId}</div>
</div>
</div>
<div className="estimate-copy-info-item">
<div className="estimate-copy-info-tit">
{getMessage('estimate.detail.estimateCopyPopup.label.receiveUser')} <span className="red">*</span>
</div>
<div className="input-wrap">
<input
type="text"
className="input-light"
required
defaultValue={state?.charger}
onChange={(e) => {
setCopyReceiveUser(e.target.value)
}}
/>
</div>
</div>
</div>
</div>
<div className="footer-btn-wrap">
<button type="button" className="btn-origin grey mr5" onClick={() => setEstimateCopyPopupOpen(false)}>
{getMessage('estimate.detail.estimateCopyPopup.close')}
</button>
<button
type="button"
className="btn-origin navy"
onClick={() => {
handleEstimateCopy(sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId)
}}
>
{getMessage('estimate.detail.estimateCopyPopup.copyBtn')}
</button>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -14,6 +14,7 @@ import { useCanvasConfigInitialize } from '@/hooks/common/useCanvasConfigInitial
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics' import PanelBatchStatistics from '@/components/floor-plan/modal/panelBatch/PanelBatchStatistics'
import { totalDisplaySelector } from '@/store/settingAtom' import { totalDisplaySelector } from '@/store/settingAtom'
import ImgLoad from '@/components/floor-plan/modal/ImgLoad'
export default function CanvasFrame() { export default function CanvasFrame() {
const canvasRef = useRef(null) const canvasRef = useRef(null)
@ -76,6 +77,8 @@ export default function CanvasFrame() {
MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION, MENU.MODULE_CIRCUIT_SETTING.PLAN_ORIENTATION,
].includes(currentMenu) && ].includes(currentMenu) &&
totalDisplay && <PanelBatchStatistics />} totalDisplay && <PanelBatchStatistics />}
{/* 이미지 로드 팝업 */}
<ImgLoad />
</div> </div>
) )
} }

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import { useEffect, useState } from 'react' import { useContext, useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
@ -35,6 +35,8 @@ import { MENU } from '@/common/common'
import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController'
import { estimateState } from '@/store/floorPlanObjectAtom' import { estimateState } from '@/store/floorPlanObjectAtom'
import DocDownOptionPop from '../estimate/popup/DocDownOptionPop' import DocDownOptionPop from '../estimate/popup/DocDownOptionPop'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import EstimateCopyPop from '../estimate/popup/EstimateCopyPop'
export default function CanvasMenu(props) { export default function CanvasMenu(props) {
const { menuNumber, setMenuNumber } = props const { menuNumber, setMenuNumber } = props
@ -59,6 +61,7 @@ export default function CanvasMenu(props) {
const { handleEstimateSubmit } = useEstimateController() const { handleEstimateSubmit } = useEstimateController()
const estimateRecoilState = useRecoilValue(estimateState) const estimateRecoilState = useRecoilValue(estimateState)
const [estimatePopupOpen, setEstimatePopupOpen] = useState(false) const [estimatePopupOpen, setEstimatePopupOpen] = useState(false)
const [estimateCopyPopupOpen, setEstimateCopyPopupOpen] = useState(false)
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { currentCanvasPlan, saveCanvas } = usePlan() const { currentCanvasPlan, saveCanvas } = usePlan()
@ -67,6 +70,7 @@ export default function CanvasMenu(props) {
const commonUtils = useRecoilValue(commonUtilsState) const commonUtils = useRecoilValue(commonUtilsState)
const { commonFunctions } = useCommonUtils() const { commonFunctions } = useCommonUtils()
const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }]
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const onClickNav = (menu) => { const onClickNav = (menu) => {
setMenuNumber(menu.index) setMenuNumber(menu.index)
@ -161,20 +165,6 @@ export default function CanvasMenu(props) {
}) })
} }
/**
* 견적서 복사버튼
* (견적서 번호(estimateRecoilState.docNo) 생성된 이후 버튼 활성화 )
* T01관리자 계정 1차판매점에게만 제공
*/
const handleEstimateCopy = () => {
// console.log('estimateRecoilState::', estimateRecoilState)
//objectNo, planNo
console.log('복사')
console.log('물건정보+도면+견적서를 모두 복사')
console.log('견적서 가격은 정가를 표시')
}
useEffect(() => { useEffect(() => {
if (globalLocale === 'ko') { if (globalLocale === 'ko') {
setAppMessageState(KO) setAppMessageState(KO)
@ -240,7 +230,10 @@ export default function CanvasMenu(props) {
<QSelectBox title={'瓦53A'} option={SelectOption} /> <QSelectBox title={'瓦53A'} option={SelectOption} />
</div> </div>
<div className="btn-from"> <div className="btn-from">
<button className="btn10"></button> <button
className={`btn10 ${floorPlanState.refFileModalOpen && 'active'}`}
onClick={() => setFloorPlanState({ ...floorPlanState, refFileModalOpen: true })}
></button>
{/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/} {/*<button className="btn04" onClick={() => setShowCanvasSettingModal(true)}></button>*/}
<button className="btn04" onClick={handlePopup}></button> <button className="btn04" onClick={handlePopup}></button>
<button className="btn05"></button> <button className="btn05"></button>
@ -279,7 +272,6 @@ export default function CanvasMenu(props) {
<span className="ico ico02"></span> <span className="ico ico02"></span>
<span>{getMessage('plan.menu.estimate.save')}</span> <span>{getMessage('plan.menu.estimate.save')}</span>
</button> </button>
{/* {estimateRecoilState?.docNo != null && ( */}
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
onClick={() => { onClick={() => {
@ -289,17 +281,18 @@ export default function CanvasMenu(props) {
<span className="ico ico03"></span> <span className="ico ico03"></span>
<span>{getMessage('plan.menu.estimate.reset')}</span> <span>{getMessage('plan.menu.estimate.reset')}</span>
</button> </button>
{/* )} */}
{estimateRecoilState?.docNo !== null && (sessionState.storeId === 'T01' || sessionState.storeLvl === '1') && (
<button <button
className="btn-frame gray ico-flx" className="btn-frame gray ico-flx"
onClick={() => { onClick={() => {
handleEstimateCopy() setEstimateCopyPopupOpen(true)
}} }}
> >
<span className="ico ico04"></span> <span className="ico ico04"></span>
<span>{getMessage('plan.menu.estimate.copy')}</span> <span>{getMessage('plan.menu.estimate.copy')}</span>
</button> </button>
)}
</div> </div>
</> </>
)} )}
@ -324,6 +317,8 @@ export default function CanvasMenu(props) {
</div> </div>
{/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */} {/* 견적서(menuNumber=== 5) 상세화면인경우 문서다운로드 팝업 */}
{estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />} {estimatePopupOpen && <DocDownOptionPop planNo={estimateRecoilState?.planNo} setEstimatePopupOpen={setEstimatePopupOpen} />}
{/* 견적서(menuNumber ===5)복사 팝업 */}
{estimateCopyPopupOpen && <EstimateCopyPop planNo={estimateRecoilState?.planNo} setEstimateCopyPopupOpen={setEstimateCopyPopupOpen} />}
</div> </div>
) )
} }

View File

@ -1,3 +1,6 @@
import { useContext, useEffect } from 'react'
import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRefFiles } from '@/hooks/common/useRefFiles' import { useRefFiles } from '@/hooks/common/useRefFiles'
import { usePlan } from '@/hooks/usePlan' import { usePlan } from '@/hooks/usePlan'
@ -20,9 +23,24 @@ export default function ImgLoad() {
} = useRefFiles() } = useRefFiles()
const { currentCanvasPlan } = usePlan() const { currentCanvasPlan } = usePlan()
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { floorPlanState, setFloorPlanState } = useContext(FloorPlanContext)
const handleModal = () => {
setFloorPlanState({ ...floorPlanState, refFileModalOpen: false })
}
useEffect(() => {
console.log('🚀 ~ ImgLoad ~ floorPlanState.refFileModalOpen:', floorPlanState.refFileModalOpen)
console.log('🚀 ~ ImgLoad ~ currentCanvasPlan:', currentCanvasPlan)
}, [floorPlanState.refFileModalOpen])
useEffect(() => {
const refFileMethod = currentCanvasPlan?.mapPositionAddress === null ? '1' : '2'
setRefFileMethod(refFileMethod)
}, [currentCanvasPlan])
return ( return (
<WithDraggable isShow={true}> <WithDraggable isShow={floorPlanState.refFileModalOpen} pos={{ x: 1000, y: 200 }} handle=".modal">
<div className={`modal-pop-wrap r`}> <div className={`modal-pop-wrap r`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('common.input.file')}</h1> <h1 className="title">{getMessage('common.input.file')}</h1>
@ -32,7 +50,11 @@ export default function ImgLoad() {
<div className="img-flex-box"> <div className="img-flex-box">
<span className="normal-font mr10">サイズ調整と回転</span> <span className="normal-font mr10">サイズ調整と回転</span>
<label className="toggle-btn"> <label className="toggle-btn">
<input type="checkbox" /> <input
type="checkbox"
checked={floorPlanState.toggleRotate}
onChange={(e) => setFloorPlanState({ ...floorPlanState, toggleRotate: e.target.checked })}
/>
<span className="slider"></span> <span className="slider"></span>
</label> </label>
</div> </div>
@ -48,13 +70,18 @@ export default function ImgLoad() {
<span className="img-edit"></span> <span className="img-edit"></span>
ファイルの追加 ファイルの追加
</label> </label>
<input type="file" id="img_file" style={{ display: 'none' }} onChange={(e) => handleRefFile(e.target.files[0])} /> <input
type="file"
id="img_file"
style={{ display: 'none' }}
onChange={refFileMethod === '1' ? (e) => handleRefFile(e.target.files[0]) : () => {}}
/>
</div> </div>
<div className="img-name-wrap"> <div className="img-name-wrap">
{/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly /> {/* <input type="text" className="input-origin al-l" defaultValue={'IMG_Name.PNG'} readOnly />
<button className="img-check"></button> */} <button className="img-check"></button> */}
{currentCanvasPlan?.bgImageName === null ? ( {currentCanvasPlan?.bgImageName === null ? (
<input type="text" className="input-origin al-l" defaultValue={''} value={refImage ? refImage.name : ''} readOnly /> <input type="text" className="input-origin al-l" value={refImage ? refImage.name : ''} readOnly />
) : ( ) : (
<input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly /> <input type="text" className="input-origin al-l" value={currentCanvasPlan?.bgImageName} readOnly />
)} )}
@ -64,13 +91,13 @@ export default function ImgLoad() {
</div> </div>
<div className="img-load-item"> <div className="img-load-item">
<div className="d-check-radio pop"> <div className="d-check-radio pop">
<input type="radio" name="radio03" id="ra06" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} /> <input type="radio" name="radio03" id="ra07" value={'2'} onChange={(e) => handleRefFileMethod(e)} checked={refFileMethod === '2'} />
<label htmlFor="ra06">アドレスを読み込む</label> <label htmlFor="ra07">アドレスを読み込む</label>
</div> </div>
<div className="img-flex-box for-address"> <div className="img-flex-box for-address">
<input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} /> <input type="text" className="input-origin al-l mr10" placeholder={'住所入力'} value={currentCanvasPlan?.mapPositionAddress} />
<div className="img-edit-wrap"> <div className="img-edit-wrap">
<button className="img-edit-btn" onClick={handleMapImageDown}> <button className="img-edit-btn" onClick={refFileMethod === '2' ? handleMapImageDown : () => {}}>
完了 完了
</button> </button>
</div> </div>
@ -80,7 +107,9 @@ export default function ImgLoad() {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">完了</button> <button className="btn-frame modal act" onClick={handleModal}>
完了
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,84 +0,0 @@
import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom'
import { useState } from 'react'
export default function AuxiliaryCopy(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props
const { getMessage } = useMessage()
const { closePopup } = usePopup()
const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null)
return (
<WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}>
<div className="modal-head">
<h1 className="title">{getMessage('modal.auxiliary.copy')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}>
닫기
</button>
</div>
<div className="modal-body">
<div className="grid-option-tit">{getMessage('modal.auxiliary.copy.info')}</div>
<div className="grid-option-wrap">
<div className="grid-option-box">
<div className="move-form">
<p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
<div className="direction-move-wrap">
<button
className={`direction up ${arrow1 === '↑' ? 'act' : ''}`}
onClick={() => {
setArrow1('↑')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }))
}}
></button>
<button
className={`direction down ${arrow1 === '↓' ? 'act' : ''}`}
onClick={() => {
setArrow1('↓')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowDown' }))
}}
></button>
</div>
</div>
<div className="input-move-wrap">
<div className="input-move">
<input type="text" className="input-origin" defaultValue={910} />
</div>
<span>mm</span>
<div className="direction-move-wrap">
<button
className={`direction left ${arrow2 === '←' ? 'act' : ''}`}
onClick={() => {
setArrow2('←')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowLeft' }))
}}
></button>
<button
className={`direction right ${arrow2 === '→' ? 'act' : ''}`}
onClick={() => {
setArrow2('→')
document.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowRight' }))
}}
></button>
</div>
</div>
</div>
</div>
</div>
<div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button>
</div>
</div>
</div>
</WithDraggable>
)
}

View File

@ -1,37 +1,62 @@
'use client'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import WithDraggable from '@/components/common/draggable/WithDraggable' import WithDraggable from '@/components/common/draggable/WithDraggable'
import { usePopup } from '@/hooks/usePopup'
import { useRecoilValue } from 'recoil' import { useRecoilValue } from 'recoil'
import { contextPopupPositionState } from '@/store/popupAtom' import { contextPopupPositionState } from '@/store/popupAtom'
import { usePopup } from '@/hooks/usePopup'
import { useState } from 'react' import { useState } from 'react'
import { currentObjectState } from '@/store/canvasAtom'
import { useLine } from '@/hooks/useLine'
import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing'
export default function AuxiliaryMove(props) { export default function AuxiliaryEdit(props) {
const contextPopupPosition = useRecoilValue(contextPopupPositionState) const contextPopupPosition = useRecoilValue(contextPopupPositionState)
const { id, pos = contextPopupPosition } = props const { id, pos = contextPopupPosition, type } = props
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { closePopup } = usePopup() const { closePopup } = usePopup()
const { move, copy } = useAuxiliaryDrawing()
const [verticalSize, setVerticalSize] = useState('0')
const [horizonSize, setHorizonSize] = useState('0')
const [arrow1, setArrow1] = useState(null) const [arrow1, setArrow1] = useState(null)
const [arrow2, setArrow2] = useState(null) const [arrow2, setArrow2] = useState(null)
const { addLine, removeLine } = useLine()
const currentObject = useRecoilValue(currentObjectState)
const handleSave = () => {
if (type === 'copy') {
if (currentObject) {
copy(
currentObject,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
}
} else {
move(
currentObject,
arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize),
arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize),
)
}
closePopup(id)
}
return ( return (
<WithDraggable isShow={true} pos={pos}> <WithDraggable isShow={true} pos={pos}>
<div className={`modal-pop-wrap xm mount`}> <div className={`modal-pop-wrap xm mount`}>
<div className="modal-head"> <div className="modal-head">
<h1 className="title">{getMessage('modal.auxiliary.move')}</h1> <h1 className="title">{getMessage(type === 'copy' ? 'modal.auxiliary.copy' : 'modal.auxiliary.move')} </h1>
<button className="modal-close" onClick={() => closePopup(id)}> <button className="modal-close" onClick={() => closePopup(id)}>
닫기 닫기
</button> </button>
</div> </div>
<div className="modal-body"> <div className="modal-body">
<div className="grid-option-tit">{getMessage('modal.auxiliary.move.info')}</div> <div className="grid-option-tit">{getMessage(type === 'copy' ? 'modal.auxiliary.copy.info' : 'modal.auxiliary.move.info')}</div>
<div className="grid-option-wrap"> <div className="grid-option-wrap">
<div className="grid-option-box"> <div className="grid-option-box">
<div className="move-form"> <div className="move-form">
<p className="mb5">{getMessage('length')}</p> <p className="mb5">{getMessage('length')}</p>
<div className="input-move-wrap mb5"> <div className="input-move-wrap mb5">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={910} /> <input type="text" className="input-origin" value={verticalSize} onChange={(e) => setVerticalSize(e.target.value)} />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -53,7 +78,7 @@ export default function AuxiliaryMove(props) {
</div> </div>
<div className="input-move-wrap"> <div className="input-move-wrap">
<div className="input-move"> <div className="input-move">
<input type="text" className="input-origin" defaultValue={910} /> <input type="text" className="input-origin" value={horizonSize} onChange={(e) => setHorizonSize(e.target.value)} />
</div> </div>
<span>mm</span> <span>mm</span>
<div className="direction-move-wrap"> <div className="direction-move-wrap">
@ -77,7 +102,9 @@ export default function AuxiliaryMove(props) {
</div> </div>
</div> </div>
<div className="grid-btn-wrap"> <div className="grid-btn-wrap">
<button className="btn-frame modal act">{getMessage('modal.common.save')}</button> <button className="btn-frame modal act" onClick={handleSave}>
{getMessage('modal.common.save')}
</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -116,8 +116,6 @@ export default function StuffSearchCondition() {
schAddress: address, schAddress: address,
schObjectName: objectName, schObjectName: objectName,
schDispCompanyName: dispCompanyName, schDispCompanyName: dispCompanyName,
// schSelSaleStoreId: stuffSearch?.schSelSaleStoreId,
// schOtherSelSaleStoreId: stuffSearch?.schOtherSelSaleStoreId,
schSelSaleStoreId: schSelSaleStoreId, schSelSaleStoreId: schSelSaleStoreId,
schOtherSelSaleStoreId: otherSaleStoreId, schOtherSelSaleStoreId: otherSaleStoreId,
schReceiveUser: receiveUser, schReceiveUser: receiveUser,
@ -315,8 +313,10 @@ export default function StuffSearchCondition() {
} else { } else {
//X //X
// //
// setSchSelSaleStoreId('') // .. setSchSelSaleStoreId('')
setSchSelSaleStoreId(null) setOtherSaleStoreId('')
stuffSearch.schSelSaleStoreId = ''
stuffSearch.schOtherSelSaleStoreId = ''
//2 //2
setOtherSaleStoreList([]) setOtherSaleStoreList([])

View File

@ -28,9 +28,7 @@ export default function StuffSubHeader({ type }) {
const param = { const param = {
pid: '1', pid: '1',
} }
//
const url = `/floor-plan?${queryStringFormatter(param)}` const url = `/floor-plan?${queryStringFormatter(param)}`
console.log(url)
router.push(url) router.push(url)
} }
@ -107,7 +105,7 @@ export default function StuffSubHeader({ type }) {
<span>{getMessage('header.menus.management')}</span> <span>{getMessage('header.menus.management')}</span>
</li> </li>
<li className="location-item"> <li className="location-item">
<span>{getMessage('header.menus.management.newStuff')}</span> <span>{getMessage('header.menus.management.detail')}</span>
</li> </li>
</ul> </ul>
</> </>

View File

@ -41,6 +41,7 @@ export default function Simulator() {
// //
const [chartData, setChartData] = useState([]) const [chartData, setChartData] = useState([])
const data = { const data = {
labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'], labels: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
datasets: [ datasets: [
@ -118,13 +119,30 @@ export default function Simulator() {
// //
const [pcsInfoList, setPcsInfoList] = useState([]) const [pcsInfoList, setPcsInfoList] = useState([])
// list
const [hatsudenryouAll, setHatsudenryouAll] = useState([])
const [hatsudenryouAllSnow, setHatsudenryouAllSnow] = useState([])
const [hatsudenryouPeakcutAll, setHatsudenryouPeakcutAll] = useState([])
const [hatsudenryouPeakcutAllSnow, setHatsudenryouPeakcutAllSnow] = useState([])
const fetchObjectDetail = async (objectNo) => { const fetchObjectDetail = async (objectNo) => {
const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}` const apiUrl = `/api/pwrGnrSimulation/calculations?objectNo=${objectNo}&planNo=${plan?.id}`
const resultData = await get({ url: apiUrl }) const resultData = await get({ url: apiUrl })
if (resultData) { if (resultData) {
setObjectDetail(resultData) setObjectDetail(resultData)
if (resultData.frcPwrGnrList) { if (resultData.hatsudenryouAll) {
setChartData(resultData.frcPwrGnrList) setHatsudenryouAll(resultData.hatsudenryouAll)
}
if (resultData.hatsudenryouAllSnow) {
setHatsudenryouAllSnow(resultData.hatsudenryouAllSnow)
}
if (resultData.hatsudenryouPeakcutAll) {
setHatsudenryouPeakcutAll(resultData.hatsudenryouPeakcutAll)
}
if (resultData.hatsudenryouPeakcutAllSnow) {
setHatsudenryouPeakcutAllSnow(resultData.hatsudenryouPeakcutAllSnow)
setChartData(resultData.hatsudenryouPeakcutAllSnow)
} }
if (resultData.pcsList) { if (resultData.pcsList) {
setPcsInfoList(resultData.pcsList) setPcsInfoList(resultData.pcsList)
@ -148,6 +166,26 @@ export default function Simulator() {
}) })
} }
// , list type
const [pwrGnrSimType, setPwrGnrSimType] = useState('D')
const handleChartChangeData = (type) => {
setPwrGnrSimType(type)
switch (type) {
case 'A':
setChartData(hatsudenryouAll)
break
case 'B':
setChartData(hatsudenryouAllSnow)
break
case 'C':
setChartData(hatsudenryouPeakcutAll)
break
case 'D':
setChartData(hatsudenryouPeakcutAllSnow)
break
}
}
return ( return (
<div className="sub-content estimate"> <div className="sub-content estimate">
<div className="sub-content-inner"> <div className="sub-content-inner">
@ -176,7 +214,7 @@ export default function Simulator() {
{/* 연간예측발전량 */} {/* 연간예측발전량 */}
<div className="estimate-box"> <div className="estimate-box">
<div className="estimate-tit">{getMessage('simulator.title.sub4')}</div> <div className="estimate-tit">{getMessage('simulator.title.sub4')}</div>
<div className="estimate-name">{objectDetail.anlFrcsGnrt ? convertNumberToPriceDecimal(objectDetail.anlFrcsGnrt) : ''}</div> <div className="estimate-name">{chartData[chartData.length - 1]}</div>
</div> </div>
</div> </div>
<div className="estimate-list-wrap"> <div className="estimate-list-wrap">
@ -208,6 +246,22 @@ export default function Simulator() {
<div className="chart-inner"> <div className="chart-inner">
<div className="sub-table-box"> <div className="sub-table-box">
<div className="chart-box"> <div className="chart-box">
{/* chart */}
<div className="select-wrap">
<select
style={{ width: '30%' }}
className="select-light"
defaultValue={`D`}
onChange={(e) => {
handleChartChangeData(e.target.value)
}}
>
<option value={`A`}>積雪考慮なし(ピークカットなし発電量)</option>
<option value={`B`}>積雪考慮なし(ピークカットあり発電量)</option>
<option value={`C`}>積雪考慮あり(ピークカットなし発電量)</option>
<option value={`D`}>積雪考慮あり(ピークカットあり発電量)</option>
</select>
</div>
<Bar ref={chartRef} data={data} options={options} /> <Bar ref={chartRef} data={data} options={options} />
</div> </div>
<div className="table-box-title-wrap"> <div className="table-box-title-wrap">
@ -239,7 +293,7 @@ export default function Simulator() {
{chartData.length > 0 ? ( {chartData.length > 0 ? (
<tr> <tr>
{chartData.map((data) => ( {chartData.map((data) => (
<td key={data}>{convertNumberToPriceDecimal(data)}</td> <td key={data}>{data}</td>
))} ))}
</tr> </tr>
) : ( ) : (

View File

@ -1,8 +1,30 @@
import { menuNumberState } from '@/store/menuAtom' import { menuNumberState } from '@/store/menuAtom'
import { useRecoilState } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { useEffect } from 'react'
import { canvasState } from '@/store/canvasAtom'
import { usePolygon } from '@/hooks/usePolygon'
import { POLYGON_TYPE } from '@/common/common'
export const useCanvasMenu = () => { export const useCanvasMenu = () => {
const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState) const [menuNumber, setMenuNumber] = useRecoilState(menuNumberState)
const canvas = useRecoilValue(canvasState)
const { drawDirectionArrow } = usePolygon()
useEffect(() => {
/*
* 모듈,회로 구성을 벗어나면 방향 표시 초기화 필요
* */
if (!canvas) return
if (![4, 5].includes(menuNumber)) {
canvas
.getObjects()
.filter((obj) => obj.name === POLYGON_TYPE.ROOF)
.forEach((obj) => {
obj.set('moduleCompass', null)
drawDirectionArrow(obj)
})
}
}, [menuNumber])
return { return {
menuNumber, menuNumber,

View File

@ -6,27 +6,11 @@ import { estimateState, floorPlanObjectState } from '@/store/floorPlanObjectAtom
import { isObjectNotEmpty } from '@/util/common-utils' import { isObjectNotEmpty } from '@/util/common-utils'
import { SessionContext } from '@/app/SessionProvider' import { SessionContext } from '@/app/SessionProvider'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import { useRouter } from 'next/navigation'
const reducer = (prevState, nextState) => { import { FloorPlanContext } from '@/app/floor-plan/FloorPlanProvider'
return { ...prevState, ...nextState }
}
// Constants // Constants
const ESTIMATE_API_ENDPOINT = '/api/estimates' // API 엔드포인트 정의 const ESTIMATE_API_ENDPOINT = '/api/estimate' // API 엔드포인트 정의
const defaultEstimateData = {
estimateDate: new Date(), //견적일
charger: '', //담당자
objectName: '', //안건명
objectNameOmit: '', //경칭코드
estimateType: '', //주문분류
remarks: '', //비고
estimateOption: '', //견적특이사항
itemList: [],
fileList: [],
fileFlg: '0', //후일 자료 제출 (체크 1 노체크 0)
priceCd: '',
}
// Helper functions // Helper functions
const updateItemInList = (itemList, dispOrder, updates) => { const updateItemInList = (itemList, dispOrder, updates) => {
@ -34,6 +18,7 @@ const updateItemInList = (itemList, dispOrder, updates) => {
} }
export const useEstimateController = (planNo) => { export const useEstimateController = (planNo) => {
const router = useRouter()
const { session } = useContext(SessionContext) const { session } = useContext(SessionContext)
const globalLocaleState = useRecoilValue(globalLocaleStore) const globalLocaleState = useRecoilValue(globalLocaleStore)
const objectRecoil = useRecoilValue(floorPlanObjectState) const objectRecoil = useRecoilValue(floorPlanObjectState)
@ -41,31 +26,32 @@ export const useEstimateController = (planNo) => {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const { get, post, promisePost } = useAxios(globalLocaleState) const { promiseGet, post, promisePost } = useAxios(globalLocaleState)
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
const [state, setState] = useReducer(reducer, defaultEstimateData) const { estimateContextState, setEstimateContextState } = useContext(FloorPlanContext)
useEffect(() => { useEffect(() => {
if (!isLoading) { if (planNo && !isLoading) {
if (objectRecoil.floorPlanObjectNo && planNo) { if (objectRecoil.floorPlanObjectNo && planNo) {
fetchSetting() fetchSetting(objectRecoil.floorPlanObjectNo, planNo)
} }
} }
}, []) }, [])
// 상세 조회 // 상세 조회
const fetchSetting = async () => { const fetchSetting = async (objectNo, planNo) => {
try { try {
await get({ url: `/api/estimate/${objectRecoil.floorPlanObjectNo}/${planNo}/detail` }).then((res) => { await promiseGet({ url: `/api/estimate/${objectNo}/${planNo}/detail` }).then((res) => {
if (isObjectNotEmpty(res)) { if (res.status === 200) {
if (res.itemList.length > 0) { if (isObjectNotEmpty(res.data)) {
res.itemList.map((item) => { if (res.data.itemList.length > 0) {
res.data.itemList.map((item) => {
item.delFlg = '0' item.delFlg = '0'
}) })
} }
setEstimateContextState(res.data)
setState(res) }
} }
}) })
setIsLoading(true) setIsLoading(true)
@ -76,18 +62,17 @@ export const useEstimateController = (planNo) => {
} }
const updateItem = (dispOrder, updates) => { const updateItem = (dispOrder, updates) => {
setState({ setEstimateContextState({
itemList: updateItemInList(state.itemList, dispOrder, updates), itemList: updateItemInList(estimateContextState.itemList, dispOrder, updates),
}) })
} }
const addItem = () => { const addItem = () => {
// const newItemDispOrder = (Math.max(...state.itemList.map((item) => item.dispOrder)) / 100 + 1) * 100 let newItemDispOrder = Math.max(...estimateContextState.itemList.map((item) => item.dispOrder))
let newItemDispOrder = Math.max(...state.itemList.map((item) => item.dispOrder))
newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100 newItemDispOrder = (Math.floor(newItemDispOrder / 100) + 1) * 100
setState({ setEstimateContextState({
itemList: [ itemList: [
...state.itemList, ...estimateContextState.itemList,
{ {
objectNo: objectRecoil.floorPlanObjectNo, objectNo: objectRecoil.floorPlanObjectNo,
planNo: planNo, planNo: planNo,
@ -109,8 +94,8 @@ export const useEstimateController = (planNo) => {
} }
useEffect(() => { useEffect(() => {
setEstimateData({ ...state, userId: session.userId, sapSalesStoreCd: session.custCd }) setEstimateData({ ...estimateContextState, userId: session.userId, sapSalesStoreCd: session.custCd })
}, [state]) }, [estimateContextState])
// 첨부파일 다운로드 // 첨부파일 다운로드
const handleEstimateFileDownload = async (originFile) => { const handleEstimateFileDownload = async (originFile) => {
@ -141,24 +126,41 @@ export const useEstimateController = (planNo) => {
const handleEstimateSubmit = async () => { const handleEstimateSubmit = async () => {
//0. 필수체크 //0. 필수체크
let flag = true let flag = true
if (estimateData.charger.trim().length === 0) {
flag = false
return alert(getMessage('estimate.detail.save.requiredCharger'))
}
if (estimateData.objectName.trim().length === 0) {
flag = false
return alert(getMessage('estimate.detail.save.requiredObjectName'))
}
if (isNaN(Date.parse(estimateData.estimateDate))) {
flag = false
return alert(getMessage('estimate.detail.save.requiredEstimateDate'))
}
// console.log('첨부파일:::::', estimateData.fileList) // console.log('첨부파일:::::', estimateData.fileList)
//첨부파일을 첨부안했는데 //첨부파일을 첨부안했는데
//아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼 //아이템 fileUploadFlg가1(첨부파일 필수)이 1개라도 있는데 후일 자료 제출(fileFlg) 체크안했으면(0) alert 저장안돼
let fileFlg = true
if (estimateData.fileList.length < 1) { if (estimateData.fileList.length < 1) {
if (estimateData.itemList.length > 1) { if (estimateData.itemList.length > 1) {
estimateData.itemList.map((row) => { estimateData.itemList.map((row) => {
if (row.fileUploadFlg === '1') { if (row.fileUploadFlg === '1') {
if (fileFlg) {
if (estimateData.fileFlg === '0') { if (estimateData.fileFlg === '0') {
alert(getMessage('estimate.detail.save.requiredMsg')) fileFlg = false
flag = false return alert(getMessage('estimate.detail.save.requiredFileUpload'))
}
} }
} }
}) })
} }
} }
if (flag) { if (flag && fileFlg) {
//1. 첨부파일 저장시작 //1. 첨부파일 저장시작
const formData = new FormData() const formData = new FormData()
formData.append('file', estimateData.fileList) formData.append('file', estimateData.fileList)
@ -184,24 +186,62 @@ export const useEstimateController = (planNo) => {
} }
console.log('최종 정보::;', estimateData) console.log('최종 정보::;', estimateData)
console.log('최종 남은 아이템정보:::', estimateData.itemList)
//2. 상세데이터 저장 //2. 상세데이터 저장
// return // return
await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => { await promisePost({ url: `${ESTIMATE_API_ENDPOINT}/save-estimate`, data: estimateData }).then((res) => {
if (res) { if (res.status === 201) {
alert(getMessage('estimate.detail.save.alertMsg')) alert(getMessage('estimate.detail.save.alertMsg'))
//어디로 보낼지
fetchSetting(objectRecoil.floorPlanObjectNo, estimateData.planNo)
} }
}) })
} }
} }
/**
* 견적서 복사버튼
* (견적서 번호(estimateData.docNo) 생성된 이후 버튼 활성화 )
* T01관리자 계정 1차판매점에게만 제공
*/
const handleEstimateCopy = async (sendPlanNo, copyReceiveUser, saleStoreId, otherSaleStoreId) => {
if (saleStoreId === '') {
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredStoreId'))
}
if (copyReceiveUser.trim().length === 0) {
return alert(getMessage('estimate.detail.productFeaturesPopup.requiredReceiveUser'))
}
const params = {
saleStoreId: session.storeId,
sapSalesStoreCd: session.custCd,
objectNo: objectRecoil.floorPlanObjectNo,
planNo: sendPlanNo,
copySaleStoreId: otherSaleStoreId ? otherSaleStoreId : saleStoreId,
copyReceiveUser: copyReceiveUser,
userId: session.userId,
}
// return
await promisePost({ url: '/api/estimate/save-estimate-copy', data: params }).then((res) => {
if (res.status === 201) {
if (isObjectNotEmpty(res.data)) {
let newObjectNo = res.data.objectNo
alert(getMessage('estimate.detail.estimateCopyPopup.copy.alertMessage'))
router.push(`/management/stuff/detail?objectNo=${newObjectNo.toString()}`, { scroll: false })
}
}
})
}
return { return {
state, estimateContextState,
setState, setEstimateContextState,
updateItem, updateItem,
addItem, addItem,
handleEstimateSubmit, handleEstimateSubmit,
fetchSetting, fetchSetting,
handleEstimateFileDownload, handleEstimateFileDownload,
handleEstimateCopy,
} }
} }

View File

@ -47,6 +47,8 @@ export function useModuleBasicSetting() {
flowDirection: roof.direction, flowDirection: roof.direction,
}) })
setupSurface.setViewLengthText(false)
canvas.add(setupSurface) canvas.add(setupSurface)
//지붕면 선택 금지 //지붕면 선택 금지
roof.set({ roof.set({
@ -567,6 +569,7 @@ export function useModuleBasicSetting() {
lineCol: col, lineCol: col,
lineRow: row, lineRow: row,
}) })
tempModule.setViewLengthText(false)
canvas?.add(tempModule) canvas?.add(tempModule)
moduleSetupArray.push(tempModule) moduleSetupArray.push(tempModule)
} }
@ -596,6 +599,106 @@ export function useModuleBasicSetting() {
}) })
setModuleIsSetup(moduleSetupArray) setModuleIsSetup(moduleSetupArray)
console.log(calculateForApi(moduleSetupArray))
}
const calculateForApi = (moduleSetupArray) => {
const centerPoints = []
moduleSetupArray.forEach((module, index) => {
module.tempIndex = index
const { x, y } = module.getCenterPoint()
const { width, height } = module
centerPoints.push({ x, y, width, height })
const circle = new fabric.Circle({
radius: 5,
fill: 'red',
name: 'redCircle',
left: x - 5,
top: y - 5,
index: index,
selectable: false,
})
canvas.add(circle)
})
//완전 노출 하면
let exposedBottom = 0
// 반 노출 하면
let exposedHalfBottom = 0
// 완전 노출 상면
let exposedTop = 0
//반 노출 상면
let exposedHalfTop = 0
// 완전 접면
let touchDimension = 0
//반접면
let halfTouchDimension = 0
// 노출하면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
// centerPoints중에 현재 centerPoint와 x값이 같고, y값이 y-height값과 같은 centerPoint가 있는지 확인
const bottomCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y + height)) < 2)
if (bottomCell.length === 1) {
touchDimension++
return
}
// 바로 아래에 셀이 없는 경우 물떼세 배치가 왼쪽 되어있는 셀을 찾는다.
const leftBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - (x - width / 2)) < 2 && Math.abs(centerPoint.y - (y + height)) < 2,
).length
const rightBottomCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - (x + width / 2)) < 2 && Math.abs(centerPoint.y - (y + height)) < 2,
).length
if (leftBottomCnt + rightBottomCnt === 2) {
touchDimension++
return
}
if (leftBottomCnt + rightBottomCnt === 1) {
halfTouchDimension++
exposedHalfBottom++
return
}
if (leftBottomCnt + rightBottomCnt === 0) {
exposedBottom++
return
}
})
// 노출상면 체크
centerPoints.forEach((centerPoint, index) => {
const { x, y, width, height } = centerPoint
const topCell = centerPoints.filter((centerPoint) => centerPoint.x === x && Math.abs(centerPoint.y - (y - height)) < 2)
if (topCell.length === 1) {
return
}
const leftTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - (x - width) < 2) && Math.abs(centerPoint.y - (y - height)) < 2,
).length
const rightTopCnt = centerPoints.filter(
(centerPoint) => Math.abs(centerPoint.x - (x + width / 2) < 2) && Math.abs(centerPoint.y - (y - height)) < 2,
).length
if (leftTopCnt + rightTopCnt === 1) {
exposedHalfTop++
return
}
if (leftTopCnt + rightTopCnt === 0) {
exposedTop++
return
}
})
return {
exposedBottom,
exposedHalfBottom,
exposedTop,
exposedHalfTop,
touchDimension,
halfTouchDimension,
}
} }
const coordToTurfPolygon = (points) => { const coordToTurfPolygon = (points) => {

View File

@ -15,14 +15,12 @@ import {
outerLineLength2State, outerLineLength2State,
outerLineTypeState, outerLineTypeState,
} from '@/store/outerLineAtom' } from '@/store/outerLineAtom'
import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util' import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util'
import { fabric } from 'fabric' import { fabric } from 'fabric'
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
import { booleanPointInPolygon } from '@turf/turf'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils'
import { QPolygon } from '@/components/fabric/QPolygon'
import { POLYGON_TYPE } from '@/common/common' import { POLYGON_TYPE } from '@/common/common'
// 보조선 작성 // 보조선 작성
@ -122,6 +120,38 @@ export function useAuxiliaryDrawing(id) {
setOuterLineDiagonalLength(0) setOuterLineDiagonalLength(0)
} }
const move = (object, x, y) => {
const line = copy(object, x, y)
canvas.remove(object)
canvas.setActiveObject(line)
}
const copy = (object, x, y) => {
return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
}
const addBisectorLine = (target) => {
const slope = (target.y2 - target.y1) / (target.x2 - target.x1)
const bisectorSlope = -1 / slope
const length = target.length
const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope)
const dy = bisectorSlope * dx
const endX = (target.x1 + target.x2) / 2
const endY = (target.y1 + target.y2) / 2
addLine([dx, dy, endX, endY], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
}
const keydown = { const keydown = {
outerLine: (e) => { outerLine: (e) => {
if (mousePointerArr.current.length === 0) { if (mousePointerArr.current.length === 0) {
@ -130,7 +160,7 @@ export function useAuxiliaryDrawing(id) {
// 포커스가 length1에 있지 않으면 length1에 포커스를 줌 // 포커스가 length1에 있지 않으면 length1에 포커스를 줌
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current) { if (activeElem !== length1Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
const key = e.key const key = e.key
@ -180,7 +210,7 @@ export function useAuxiliaryDrawing(id) {
const activeElem = document.activeElement const activeElem = document.activeElement
if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) { if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) {
length1Ref.current.focus() length1Ref?.current?.focus()
} }
switch (key) { switch (key) {
@ -871,5 +901,8 @@ export function useAuxiliaryDrawing(id) {
handleRollback, handleRollback,
buttonAct, buttonAct,
setButtonAct, setButtonAct,
move,
copy,
addBisectorLine,
} }
} }

View File

@ -1,5 +1,5 @@
import { useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { canvasState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util' import { setSurfaceShapePattern } from '@/util/canvas-util'
import { useSwal } from '@/hooks/useSwal' import { useSwal } from '@/hooks/useSwal'
@ -10,6 +10,9 @@ import { POLYGON_TYPE } from '@/common/common'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting' import ActualSizeSetting from '@/components/floor-plan/modal/roofAllocation/ActualSizeSetting'
import { useMessage } from '@/hooks/useMessage' import { useMessage } from '@/hooks/useMessage'
import useMenu from '@/hooks/common/useMenu'
import { useCanvasMenu } from '@/hooks/common/useCanvasMenu'
import { menuTypeState } from '@/store/menuAtom'
// 지붕면 할당 // 지붕면 할당
export function useRoofAllocationSetting(id) { export function useRoofAllocationSetting(id) {
@ -21,6 +24,8 @@ export function useRoofAllocationSetting(id) {
const { getMessage } = useMessage() const { getMessage } = useMessage()
const currentObject = useRecoilValue(currentObjectState) const currentObject = useRecoilValue(currentObjectState)
const { swalFire } = useSwal() const { swalFire } = useSwal()
const { setMenuNumber } = useCanvasMenu()
const setMenuType = useSetRecoilState(menuTypeState)
const roofMaterials = [ const roofMaterials = [
{ {
id: 'A', id: 'A',
@ -132,6 +137,9 @@ export function useRoofAllocationSetting(id) {
setValues(values.filter((value) => value.id !== id)) setValues(values.filter((value) => value.id !== id))
} }
const { handleMenu } = useMenu()
const [currentMenu, setCurrentMenu] = useRecoilState(currentMenuState)
// 선택한 지붕재로 할당 // 선택한 지붕재로 할당
const handleSave = () => { const handleSave = () => {
// 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정 // 모두 actualSize 있으면 바로 적용 없으면 actualSize 설정
@ -213,6 +221,8 @@ export function useRoofAllocationSetting(id) {
}) })
setEditingLines([]) setEditingLines([])
closeAll() closeAll()
setMenuNumber(3)
setMenuType('surface')
} }
const setLineSize = (id, size) => { const setLineSize = (id, size) => {

View File

@ -1,8 +1,7 @@
import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'
import { currentMenuState, currentObjectState } from '@/store/canvasAtom' import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { MENU } from '@/common/common' import { MENU } from '@/common/common'
import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove'
import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize'
import { usePopup } from '@/hooks/usePopup' import { usePopup } from '@/hooks/usePopup'
import { v4 as uuidv4 } from 'uuid' import { v4 as uuidv4 } from 'uuid'
@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy'
import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal' import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal'
import { gridColorState } from '@/store/gridAtom' import { gridColorState } from '@/store/gridAtom'
import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom' import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom'
import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy' import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit'
import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting' import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting'
import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting' import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting'
import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset' import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset'
@ -34,8 +33,10 @@ import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumbe
import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useObjectBatch } from '@/hooks/object/useObjectBatch'
import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch'
import { fontSelector, globalFontAtom } from '@/store/fontAtom' import { fontSelector, globalFontAtom } from '@/store/fontAtom'
import { useLine } from '@/hooks/useLine'
export function useContextMenu() { export function useContextMenu() {
const canvas = useRecoilValue(canvasState)
const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴
const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴 const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴
const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu
@ -53,6 +54,7 @@ export function useContextMenu() {
const { moveObjectBatch } = useObjectBatch({}) const { moveObjectBatch } = useObjectBatch({})
const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() const { moveSurfaceShapeBatch } = useSurfaceShapeBatch()
const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom)
const { addLine, removeLine } = useLine()
const commonTextFont = useRecoilValue(fontSelector('commonText')) const commonTextFont = useRecoilValue(fontSelector('commonText'))
const currentMenuSetting = () => { const currentMenuSetting = () => {
@ -130,22 +132,57 @@ export function useContextMenu() {
id: 'auxiliaryMove', id: 'auxiliaryMove',
name: `${getMessage('contextmenu.auxiliary.move')}(M)`, name: `${getMessage('contextmenu.auxiliary.move')}(M)`,
shortcut: ['m', 'M'], shortcut: ['m', 'M'],
component: <AuxiliaryMove id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'move'} />,
}, },
{ {
id: 'auxiliaryCopy', id: 'auxiliaryCopy',
name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, name: `${getMessage('contextmenu.auxiliary.copy')}(C)`,
shortcut: ['c', 'C'], shortcut: ['c', 'C'],
component: <AuxiliaryCopy id={popupId} />, component: <AuxiliaryEdit id={popupId} type={'copy'} />,
}, },
{ {
id: 'auxiliaryRemove', id: 'auxiliaryRemove',
shortcut: ['d', 'D'], shortcut: ['d', 'D'],
name: `${getMessage('contextmenu.auxiliary.remove')}(D)`, name: `${getMessage('contextmenu.auxiliary.remove')}(D)`,
fn: () => {
canvas.remove(currentObject)
canvas.discardActiveObject()
},
}, },
{ {
id: 'auxiliaryVerticalBisector', id: 'auxiliaryVerticalBisector',
name: getMessage('contextmenu.auxiliary.vertical.bisector'), name: getMessage('contextmenu.auxiliary.vertical.bisector'),
fn: () => {
const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1)
const length = currentObject.length
let startX, startY, endX, endY
if (slope === 0) {
startX = endX = (currentObject.x1 + currentObject.x2) / 2
startY = currentObject.y2 - length / 2
endY = currentObject.y2 + length / 2
} else if (slope === Infinity) {
startX = currentObject.x1 - length / 2
startY = endY = (currentObject.y1 + currentObject.y2) / 2
endX = currentObject.x1 + length / 2
} else {
const bisectorSlope = -1 / slope
const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope)
const dy = bisectorSlope * dx
startX = (currentObject.x1 + currentObject.x2) / 2 + dx
startY = (currentObject.y1 + currentObject.y2) / 2 + dy
endX = (currentObject.x1 + currentObject.x2) / 2 - dx
endY = (currentObject.y1 + currentObject.y2) / 2 - dy
}
addLine([startX, startY, endX, endY], {
stroke: 'red',
strokeWidth: 1,
selectable: true,
name: 'auxiliaryLine',
})
},
}, },
{ {
id: 'auxiliaryCut', id: 'auxiliaryCut',
@ -472,6 +509,7 @@ export function useContextMenu() {
{ {
id: 'removeAll', id: 'removeAll',
name: getMessage('contextmenu.remove.all'), name: getMessage('contextmenu.remove.all'),
fn: () => {},
}, },
], ],
]) ])

View File

@ -4,6 +4,7 @@
"header.menus.home": "ホームへv", "header.menus.home": "ホームへv",
"header.menus.management": "物品及び図面管理", "header.menus.management": "物品及び図面管理",
"header.menus.management.newStuff": "新規 物件 登録", "header.menus.management.newStuff": "新規 物件 登録",
"header.menus.management.detail": "物件詳細",
"header.menus.management.stuffList": "物件の状況", "header.menus.management.stuffList": "物件の状況",
"header.menus.community": "コミュニティ", "header.menus.community": "コミュニティ",
"header.menus.community.notice": "お知らせ", "header.menus.community.notice": "お知らせ",
@ -160,7 +161,7 @@
"plan.menu.estimate.docDown": "文書のダウンロード", "plan.menu.estimate.docDown": "文書のダウンロード",
"plan.menu.estimate.save": "保存", "plan.menu.estimate.save": "保存",
"plan.menu.estimate.reset": "初期化", "plan.menu.estimate.reset": "初期化",
"plan.menu.estimate.copy": "コピー", "plan.menu.estimate.copy": "見積書のコピー",
"plan.menu.simulation": "発展シミュレーション", "plan.menu.simulation": "発展シミュレーション",
"plan.menu.simulation.excel": "Excel", "plan.menu.simulation.excel": "Excel",
"plan.menu.simulation.pdf": "PDF", "plan.menu.simulation.pdf": "PDF",
@ -822,6 +823,8 @@
"estimate.detail.objectName": "案件名", "estimate.detail.objectName": "案件名",
"estimate.detail.objectRemarks": "メモ", "estimate.detail.objectRemarks": "メモ",
"estimate.detail.estimateType": "注文分類", "estimate.detail.estimateType": "注文分類",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.roofCns": "屋根材・仕様施工", "estimate.detail.roofCns": "屋根材・仕様施工",
"estimate.detail.remarks": "備考", "estimate.detail.remarks": "備考",
"estimate.detail.fileFlg": "後日資料提出", "estimate.detail.fileFlg": "後日資料提出",
@ -838,8 +841,6 @@
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "住宅PKG単価 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG容量 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額", "estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG金額",
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(モジュール容量 × 数量)÷1000",
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG単価 (W)×PKG容量(W)",
"estimate.detail.header.showPrice": "価格表示", "estimate.detail.header.showPrice": "価格表示",
"estimate.detail.header.unitPrice": "定価", "estimate.detail.header.unitPrice": "定価",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
@ -875,11 +876,24 @@
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "含まない",
"estimate.detail.docPopup.close": "閉じる", "estimate.detail.docPopup.close": "閉じる",
"estimate.detail.docPopup.docDownload": "文書のダウンロード", "estimate.detail.docPopup.docDownload": "文書のダウンロード",
"estimate.detail.estimateCopyPopup.title": "見積もり",
"estimate.detail.estimateCopyPopup.explane": "見積書をコピーする販売店を設定します。見積もりは定価にコピーされます.",
"estimate.detail.estimateCopyPopup.label.saleStoreId": "一次販売店名 / ID",
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "二次販売店名 / ID",
"estimate.detail.estimateCopyPopup.label.receiveUser": "担当者",
"estimate.detail.estimateCopyPopup.close": "閉じる",
"estimate.detail.estimateCopyPopup.copyBtn": "見積もり",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "見積書がコピーされました. コピーした商品情報に移動します.",
"estimate.detail.productFeaturesPopup.title": "製品特異事項", "estimate.detail.productFeaturesPopup.title": "製品特異事項",
"estimate.detail.productFeaturesPopup.close": "閉じる", "estimate.detail.productFeaturesPopup.close": "閉じる",
"estimate.detail.productFeaturesPopup.requiredStoreId": "一次販売店は必須です.",
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "担当者は必須です.",
"estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.", "estimate.detail.save.alertMsg": "保存されている見積書で製品を変更した場合、図面や回路には反映されません.",
"estimate.detail.save.requiredMsg": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.", "estimate.detail.save.requiredFileUpload": "ファイル添付が必須のアイテムがあります。ファイルを添付するか、後日添付をチェックしてください.",
"estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.", "estimate.detail.save.requiredItem": "製品は1つ以上登録する必要があります.",
"estimate.detail.save.requiredCharger": "担当者は必須です.",
"estimate.detail.save.requiredObjectName": "案件名は必須です.",
"estimate.detail.save.requiredEstimateDate": "見積日は必須です.",
"estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?", "estimate.detail.reset.confirmMsg": "保存した見積書情報が初期化され、図面情報が反映されます。本当に初期化しますか?",
"simulator.title.sub1": "物件番号", "simulator.title.sub1": "物件番号",
"simulator.title.sub2": "作成日", "simulator.title.sub2": "作成日",

View File

@ -4,6 +4,7 @@
"header.menus.home": "Home", "header.menus.home": "Home",
"header.menus.management": "물건 및 도면 관리", "header.menus.management": "물건 및 도면 관리",
"header.menus.management.newStuff": "신규 물건 등록", "header.menus.management.newStuff": "신규 물건 등록",
"header.menus.management.detail": "물건 상세",
"header.menus.management.stuffList": "물건 현황", "header.menus.management.stuffList": "물건 현황",
"header.menus.community": "커뮤니티", "header.menus.community": "커뮤니티",
"header.menus.community.notice": "공지", "header.menus.community.notice": "공지",
@ -164,7 +165,7 @@
"plan.menu.estimate.docDown": "문서 다운로드", "plan.menu.estimate.docDown": "문서 다운로드",
"plan.menu.estimate.save": "저장", "plan.menu.estimate.save": "저장",
"plan.menu.estimate.reset": "초기화", "plan.menu.estimate.reset": "초기화",
"plan.menu.estimate.copy": "복사", "plan.menu.estimate.copy": "견적서 복사",
"plan.menu.simulation": "발전 시뮬레이션", "plan.menu.simulation": "발전 시뮬레이션",
"plan.menu.simulation.excel": "Excel", "plan.menu.simulation.excel": "Excel",
"plan.menu.simulation.pdf": "PDF", "plan.menu.simulation.pdf": "PDF",
@ -832,6 +833,8 @@
"estimate.detail.objectName": "안건명", "estimate.detail.objectName": "안건명",
"estimate.detail.objectRemarks": "메모", "estimate.detail.objectRemarks": "메모",
"estimate.detail.estimateType": "주문분류", "estimate.detail.estimateType": "주문분류",
"estimate.detail.estimateType.yjss": "住宅PKG",
"estimate.detail.estimateType.yjod": "積上げ( YJOD )",
"estimate.detail.roofCns": "지붕재・사양시공", "estimate.detail.roofCns": "지붕재・사양시공",
"estimate.detail.remarks": "비고", "estimate.detail.remarks": "비고",
"estimate.detail.fileFlg": "후일자료제출", "estimate.detail.fileFlg": "후일자료제출",
@ -848,8 +851,6 @@
"estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)", "estimate.detail.sepcialEstimateProductInfo.pkgUnitPrice": "주택PKG단가 (W)",
"estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)", "estimate.detail.sepcialEstimateProductInfo.pkgWeight": "PKG 용량 (Kw)",
"estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액", "estimate.detail.sepcialEstimateProductInfo.pkgPrice": "PKG 금액",
"estimate.detail.sepcialEstimateProductInfo.calcFormula1": "(모듈용량 * 수량)÷100",
"estimate.detail.sepcialEstimateProductInfo.calcFormula2": "PKG단가(W) * PKG용량(W)",
"estimate.detail.header.showPrice": "가격표시", "estimate.detail.header.showPrice": "가격표시",
"estimate.detail.header.unitPrice": "정가", "estimate.detail.header.unitPrice": "정가",
"estimate.detail.showPrice.pricingBtn": "Pricing", "estimate.detail.showPrice.pricingBtn": "Pricing",
@ -885,11 +886,24 @@
"estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "미포함", "estimate.detail.docPopup.schDrawingFlg.schDrawingFlg0": "미포함",
"estimate.detail.docPopup.close": "닫기", "estimate.detail.docPopup.close": "닫기",
"estimate.detail.docPopup.docDownload": "문서 다운로드", "estimate.detail.docPopup.docDownload": "문서 다운로드",
"estimate.detail.estimateCopyPopup.title": "견적복사",
"estimate.detail.estimateCopyPopup.explane": "견적서를 복사할 판매점을 설정하십시오. 견적서는 정가로 복사됩니다.",
"estimate.detail.estimateCopyPopup.label.saleStoreId": "1차 판매점명 / ID",
"estimate.detail.estimateCopyPopup.label.otherSaleStoreId": "2차 판매점명 / ID",
"estimate.detail.estimateCopyPopup.label.receiveUser": "담당자",
"estimate.detail.estimateCopyPopup.close": "닫기",
"estimate.detail.estimateCopyPopup.copyBtn": "견적복사",
"estimate.detail.estimateCopyPopup.copy.alertMessage": "견적서가 복사되었습니다. 복사된 물건정보로 이동합니다.",
"estimate.detail.productFeaturesPopup.title": "제품특이사항", "estimate.detail.productFeaturesPopup.title": "제품특이사항",
"estimate.detail.productFeaturesPopup.close": "닫기", "estimate.detail.productFeaturesPopup.close": "닫기",
"estimate.detail.productFeaturesPopup.requiredStoreId": "1차 판매점은 필수값 입니다.",
"estimate.detail.productFeaturesPopup.requiredReceiveUser": "담당자는 필수값 입니다.",
"estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.", "estimate.detail.save.alertMsg": "저장되었습니다. 견적서에서 제품을 변경할 경우, 도면 및 회로에 반영되지 않습니다.",
"estimate.detail.save.requiredMsg": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.", "estimate.detail.save.requiredFileUpload": "파일첨부가 필수인 아이템이 있습니다. 파일을 첨부하거나 후일첨부를 체크해주십시오.",
"estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.", "estimate.detail.save.requiredItem": "제품은 1개이상 등록해야 됩니다.",
"estimate.detail.save.requiredCharger": "담당자는 필수값 입니다.",
"estimate.detail.save.requiredObjectName": "안건명은 필수값 입니다.",
"estimate.detail.save.requiredEstimateDate": "견적일은 필수값 입니다.",
"estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?", "estimate.detail.reset.confirmMsg": "저장된 견적서 정보가 초기화되고, 도면정보가 반영됩니다. 정말로 초기화 하시겠습니까?",
"simulator.title.sub1": "물건번호", "simulator.title.sub1": "물건번호",
"simulator.title.sub2": "작성일", "simulator.title.sub2": "작성일",

File diff suppressed because it is too large Load Diff

View File

@ -3,98 +3,27 @@
-moz-text-size-adjust:none; -moz-text-size-adjust:none;
-ms-text-size-adjust:none; -ms-text-size-adjust:none;
text-size-adjust: none; text-size-adjust: none;
box-sizing: content-box; box-sizing: content-box
} }
*, *, ::after, ::before {
::after,
::before {
box-sizing: border-box; box-sizing: border-box;
} }
html, html, body{
body {
font-size: 16px; font-size: 16px;
} }
html, html, body, div, span, applet, object, iframe,
body, h1, h2, h3, h4, h5, h6, p, blockquote, pre,
div, a, abbr, acronym, address, big, cite, code,
span, del, dfn, em, img, ins, kbd, q, s, samp,
applet, small, strike, strong, sub, sup, tt, var,
object, b, u, i, center,
iframe, dl, dt, dd, ol, ul, li,
h1, fieldset, form, label, legend,
h2, table, caption, tbody, tfoot, thead, tr, th, td,
h3, article, aside, canvas, details, embed,
h4, figure, figcaption, footer, header, hgroup,
h5, menu, nav, output, ruby, section, summary,
h6, time, mark, audio, video {
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0; margin: 0;
padding: 0; padding: 0;
border: 0; border: 0;
@ -106,38 +35,23 @@ video {
font-smooth: never; font-smooth: never;
} }
/* HTML5 display-role reset for older browsers */ /* HTML5 display-role reset for older browsers */
article, article, aside, details, figcaption, figure,
aside, footer, header, hgroup, menu, nav, section {
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block; display: block;
} }
body { body {
line-height: 1.4; line-height: 1.4;
} }
body:first-of-type caption { body:first-of-type caption { display:none;}
display: none;
}
ol, ol, ul {
ul {
list-style: none; list-style: none;
} }
blockquote, blockquote, q {
q {
quotes: none; quotes: none;
} }
blockquote:before, blockquote:before, blockquote:after,
blockquote:after, q:before, q:after {
q:before,
q:after {
content: ''; content: '';
content: none; content: none;
} }
@ -147,9 +61,7 @@ table {
border-spacing:0; border-spacing:0;
border:0 none; border:0 none;
} }
caption, caption, th, td {
th,
td {
text-align:left; text-align:left;
font-weight: normal; font-weight: normal;
border:0; border:0;
@ -159,38 +71,26 @@ a {
cursor:pointer; cursor:pointer;
color:#000; color:#000;
} }
a, a, a:hover, a:active {
a:hover,
a:active {
text-decoration:none; text-decoration:none;
-webkit-tap-highlight-color: transparent; -webkit-tap-highlight-color: transparent;
} }
/*form_style*/ /*form_style*/
input, input, select, textarea, button, a, label {
select,
textarea,
button,
a,
label {
-webkit-tap-highlight-color:rgba(0,0,0,0); -webkit-tap-highlight-color:rgba(0,0,0,0);
} }
button, button,input[type=text], input[type=button] {
input[type='text'],
input[type='button'] {
-webkit-appearance: none; -webkit-appearance: none;
-webkit-border-radius: 0; -webkit-border-radius: 0;
-webkit-appearance:none; -webkit-appearance:none;
appearance: none; appearance: none;
border-radius: 0; border-radius: 0
} }
input[type='checkbox'], input[type=checkbox], input[type=radio] {
input[type='radio'] {
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
} }
input, input, select, button {
select,
button {
border:0 none; border:0 none;
outline:none; outline:none;
margin:0; margin:0;
@ -233,53 +133,24 @@ button {
} }
// margin // margin
.mt5 { .mt5{margin-top: 5px !important;}
margin-top: 5px !important; .mt10{margin-top: 10px !important;}
} .mt15{margin-top: 15px !important;}
.mt10 { .mb5{margin-bottom: 5px !important;}
margin-top: 10px !important; .mb10{margin-bottom: 10px !important;}
} .mb15{margin-bottom: 15px !important;}
.mt15 { .mr5{margin-right: 5px !important;}
margin-top: 15px !important; .mr10{margin-right: 10px !important;}
} .mr15{margin-right: 15px !important;}
.mb5 { .ml5{margin-left: 5px !important;}
margin-bottom: 5px !important; .ml10{margin-left: 10px !important;}
} .ml15{margin-left: 15px !important;}
.mb10 {
margin-bottom: 10px !important;
}
.mb15 {
margin-bottom: 15px !important;
}
.mr5 {
margin-right: 5px !important;
}
.mr10 {
margin-right: 10px !important;
}
.mr15 {
margin-right: 15px !important;
}
.ml5 {
margin-left: 5px !important;
}
.ml10 {
margin-left: 10px !important;
}
.ml15 {
margin-left: 15px !important;
}
// align // align
.al-l { .al-l{text-align: left !important;}
text-align: left !important; .al-r{text-align: right !important;}
} .al-c{text-align: center !important;}
.al-r {
text-align: right !important;
}
.al-c {
text-align: center !important;
}
// button // button
.btn-frame{ .btn-frame{
@ -294,7 +165,7 @@ button {
border: 1px solid #000; border: 1px solid #000;
text-align: center; text-align: center;
font-family: 'Pretendard', sans-serif; font-family: 'Pretendard', sans-serif;
transition: all 0.17s ease-in-out; transition: all .17s ease-in-out;
cursor: pointer; cursor: pointer;
&.block{ &.block{
width: 100%; width: 100%;
@ -307,15 +178,15 @@ button {
} }
&.deepgray{ &.deepgray{
background-color: #2c2c2c; background-color: #2C2C2C;
border: 1px solid #484848; border: 1px solid #484848;
} }
&.gray{ &.gray{
background-color: #3c3c3c; background-color: #3C3C3C;
border: 1px solid #545454; border: 1px solid #545454;
} }
&.dark{ &.dark{
background-color: #1c1c1c; background-color: #1C1C1C;
border: 1px solid #484848; border: 1px solid #484848;
} }
&.modal{ &.modal{
@ -324,8 +195,8 @@ button {
border: 1px solid #484848; border: 1px solid #484848;
color: #aaa; color: #aaa;
&:hover{ &:hover{
background-color: #1083e3; background-color: #1083E3;
border: 1px solid #1083e3; border: 1px solid #1083E3;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
} }
@ -335,13 +206,13 @@ button {
padding: 0 10px; padding: 0 10px;
line-height: 28px; line-height: 28px;
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
background-color: #2d2d2d; background-color: #2D2D2D;
border: 1px solid #393939; border: 1px solid #393939;
color: #aaa; color: #aaa;
&.act, &.act,
&:hover{ &:hover{
background-color: #414e6c; background-color: #414E6C;
border: 1px solid #414e6c; border: 1px solid #414E6C;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
} }
@ -355,11 +226,11 @@ button {
border: 1px solid #484848; border: 1px solid #484848;
color: #fff; color: #fff;
&.blue{ &.blue{
background-color: #414e6c; background-color: #414E6C;
border: 1px solid #414e6c; border: 1px solid #414E6C;
&:hover{ &:hover{
background-color: #414e6c; background-color: #414E6C;
border: 1px solid #414e6c; border: 1px solid #414E6C;
} }
} }
&.white{ &.white{
@ -386,7 +257,7 @@ button {
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
background-color: transparent; background-color: transparent;
border: 1px solid #676767; border: 1px solid #676767;
color: #aaaaaa; color: #AAAAAA;
&:hover{ &:hover{
background-color: #272727; background-color: #272727;
border-color: #676767; border-color: #676767;
@ -395,8 +266,8 @@ button {
} }
&:hover, &:hover,
&.act{ &.act{
background-color: #1083e3; background-color: #1083E3;
border: 1px solid #1083e3; border: 1px solid #1083E3;
color: #fff; color: #fff;
font-weight: 500; font-weight: 500;
} }
@ -426,7 +297,7 @@ button {
color: #fff; color: #fff;
font-size: 13px; font-size: 13px;
font-weight: 400; font-weight: 400;
transition: all 0.15s ease-in-out; transition: all .15s ease-in-out;
&.navy{ &.navy{
background-color: #304961; background-color: #304961;
&:hover{ &:hover{
@ -434,21 +305,21 @@ button {
} }
} }
&.grey{ &.grey{
background-color: #94a0ad; background-color: #94A0AD;
&:hover{ &:hover{
background-color: #607f9a; background-color: #607F9A;
} }
} }
&.green{ &.green{
background-color: #a6bba8; background-color: #A6BBA8;
&:hover{ &:hover{
background-color: #98af9b; background-color: #98af9b;
} }
} }
&.white{ &.white{
border: 1px solid #94a0ad; border: 1px solid #94A0AD;
background-color: #fff; background-color: #fff;
color: #94a0ad; color: #94A0AD;
&:hover{ &:hover{
background-color: #fff; background-color: #fff;
} }
@ -464,7 +335,7 @@ button {
line-height: 30px; line-height: 30px;
padding: 0 25px 0 10px; padding: 0 25px 0 10px;
background-color: #373737; background-color: #373737;
border: 1px solid #3f3f3f; border: 1px solid #3F3F3F;
border-radius: 2px; border-radius: 2px;
border-top-left-radius: 2px; border-top-left-radius: 2px;
color: #fff; color: #fff;
@ -489,7 +360,7 @@ button {
max-height: 100px; max-height: 100px;
overflow-y: auto; overflow-y: auto;
background-color: #373737; background-color: #373737;
border: 1px solid #3f3f3f; border: 1px solid #3F3F3F;
border-radius: 2px; border-radius: 2px;
transition: all 0.17s ease-in-out; transition: all 0.17s ease-in-out;
visibility: hidden; visibility: hidden;
@ -499,19 +370,20 @@ button {
align-items: center; align-items: center;
padding: 8px 20px; padding: 8px 20px;
line-height: 1.4; line-height: 1.4;
transition: all 0.17s ease-in-out; transition: all .17s ease-in-out;
button{ button{
font-size: 12px; font-size: 12px;
color: #fff; color: #fff;
line-height: 1.4; line-height: 1.4;
} }
&:hover{ &:hover{
background-color: #2c2c2c; background-color: #2C2C2C;
} }
} }
&::-webkit-scrollbar { &::-webkit-scrollbar {
width: 2px; width: 2px;
background-color: transparent; background-color: transparent;
} }
&::-webkit-scrollbar-thumb { &::-webkit-scrollbar-thumb {
background-color: #5a5a5a; background-color: #5a5a5a;
@ -531,7 +403,7 @@ button {
height: 6px; height: 6px;
background: url(/static/images/common/select-arr.svg) no-repeat center; background: url(/static/images/common/select-arr.svg) no-repeat center;
background-size: cover; background-size: cover;
transition: all 0.17s ease-in-out; transition: all .17s ease-in-out;
} }
&.active{ &.active{
.select-item-wrap{ .select-item-wrap{
@ -549,17 +421,17 @@ button {
display: block; display: block;
width: 100%; width: 100%;
height: 30px; height: 30px;
background: #fff url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat; background: #FFF url(../../public/static/images/common/select_light_arr.svg) calc(100% - 11px) center no-repeat;
background-size: 10px 6px; background-size: 10px 6px;
border: 1px solid #eee; border: 1px solid #eee;
padding: 0 30px 0 10px; padding: 0 30px 0 10px;
font-size: 13px; font-size: 13px;
color: #45576f; color: #45576F;
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
cursor: pointer; cursor: pointer;
&:disabled{ &:disabled{
opacity: 1; opacity: 1;
background-color: #fafafa; background-color: #FAFAFA;
color: #999; color: #999;
cursor: default; cursor: default;
} }
@ -575,6 +447,7 @@ button {
} }
} }
// input // input
.form-input{ .form-input{
label{ label{
@ -585,9 +458,9 @@ button {
margin-bottom: 10px; margin-bottom: 10px;
} }
} }
input[type='password'], input[type=password],
input[type='number'], input[type=number],
input[type='text'] { input[type=text]{
&.input-origin{ &.input-origin{
display: inline-block; display: inline-block;
height: 30px; height: 30px;
@ -602,9 +475,9 @@ input[type='text'] {
padding: 0 10px; padding: 0 10px;
letter-spacing: 0px; letter-spacing: 0px;
text-align: right; text-align: right;
transition: border 0.15s ease-in-out; transition: border .15s ease-in-out;
&:focus{ &:focus{
border: 1px solid #1083e3; border: 1px solid #1083E3;
} }
&::placeholder{ &::placeholder{
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
@ -616,7 +489,7 @@ input[type='text'] {
width: 100%; width: 100%;
} }
&:read-only{ &:read-only{
color: #aaa; color: #AAA;
&:focus{ &:focus{
border: 1px solid #323234; border: 1px solid #323234;
} }
@ -637,15 +510,15 @@ input[type='text'] {
background-color: #fff; background-color: #fff;
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
font-size: 13px; font-size: 13px;
color: #45576f; color: #45576F;
font-weight: normal; font-weight: normal;
transition: border-color 0.17s ease-in-out; transition: border-color .17s ease-in-out;
text-align: left; text-align: left;
&:focus{ &:focus{
border-color: #94a0ad; border-color: #94A0AD;
} }
&:read-only{ &:read-only{
background-color: #fafafa; background-color: #FAFAFA;
color: #999999; color: #999999;
&:focus{ &:focus{
border-color: #eee; border-color: #eee;
@ -654,14 +527,16 @@ input[type='text'] {
} }
} }
// check-btn // check-btn
.check-btn{ .check-btn{
display: flex; display: flex;
align-items: center; align-items: center;
height: 30px; height: 30px;
background-color: #3a3a3a; background-color: #3A3A3A;
border-radius: 3px; border-radius: 3px;
transition: all 0.17s ease-in-out; transition: all .17s ease-in-out;
.check-area{ .check-area{
flex: none; flex: none;
width: 30px; width: 30px;
@ -698,10 +573,10 @@ input[type='text'] {
display: block; display: block;
height: 30px; height: 30px;
border-radius: 3px; border-radius: 3px;
background-color: #3a3a3a; background-color: #3A3A3A;
padding: 0 11px; padding: 0 11px;
text-align: left; text-align: left;
transition: all 0.17s ease-in-out; transition: all .17s ease-in-out;
span{ span{
position: relative; position: relative;
font-size: 12px; font-size: 12px;
@ -735,15 +610,15 @@ input[type='text'] {
background-color: #272727; background-color: #272727;
border: 1px solid #484848; border: 1px solid #484848;
span{ span{
color: #fff; color: #Fff;
&:after{ &:after{
background: url(../../public/static/images/canvas/arr_btn_ico_white.svg)no-repeat center; background: url(../../public/static/images/canvas/arr_btn_ico_white.svg)no-repeat center;
} }
} }
&:hover, &:hover,
&.act{ &.act{
background-color: #1083e3; background-color: #1083E3;
border: 1px solid #1083e3; border: 1px solid #1083E3;
} }
} }
} }
@ -753,8 +628,8 @@ input[type='text'] {
.d-check-box{ .d-check-box{
line-height: 1.1; line-height: 1.1;
cursor: pointer; cursor: pointer;
input[type='checkbox'], input[type=checkbox],
input[type='radio'] { input[type=radio]{
position: static; position: static;
margin-left: 0; margin-left: 0;
cursor: pointer; cursor: pointer;
@ -777,7 +652,7 @@ input[type='text'] {
} }
&.light{ &.light{
label{ label{
color: #45576f; color: #45576F;
} }
} }
&.no-text{ &.no-text{
@ -791,7 +666,7 @@ input[type='text'] {
label{ label{
&::before{ &::before{
cursor: pointer; cursor: pointer;
content: ''; content: "";
display: inline-block; display: inline-block;
position: absolute; position: absolute;
width: 17px; width: 17px;
@ -805,13 +680,11 @@ input[type='text'] {
text-align:center; text-align:center;
font-size:13px; font-size:13px;
line-height:1.4; line-height:1.4;
transition: transition: border 0.15s ease-in-out, color 0.15s ease-in-out;
border 0.15s ease-in-out,
color 0.15s ease-in-out;
} }
&::after{ &::after{
cursor: pointer; cursor: pointer;
content: ''; content: "";
display: inline-block; display: inline-block;
position: absolute; position: absolute;
width: 9px; width: 9px;
@ -827,22 +700,20 @@ input[type='text'] {
line-height:1.4; line-height:1.4;
opacity: 0; opacity: 0;
visibility: hidden; visibility: hidden;
transition: transition: opacity 0.15s ease-in-out, color 0.15s ease-in-out;
opacity 0.15s ease-in-out,
color 0.15s ease-in-out;
} }
} }
&.light{ &.light{
label{ label{
&:before{ &:before{
border-color: #d6d6d7; border-color: #D6D6D7;
} }
&:after{ &:after{
background-color: #697c8f; background-color: #697C8F;
} }
} }
} }
input[type='radio']:checked + label::after { input[type=radio]:checked + label::after{
opacity: 1; opacity: 1;
visibility: visible; visibility: visible;
} }
@ -866,12 +737,10 @@ input[type='text'] {
// check-box // check-box
.d-check-box{ .d-check-box{
label{ label{
&.red { &.red{color: #FFCACA;}
color: #ffcaca;
}
&::before{ &::before{
cursor: pointer; cursor: pointer;
content: ''; content: "";
display: inline-block; display: inline-block;
position: absolute; position: absolute;
width: 17px; width: 17px;
@ -879,48 +748,50 @@ input[type='text'] {
top: 2px; top: 2px;
left: 0; left: 0;
margin-left: -12px; margin-left: -12px;
border: 1px solid #d6d6d7; border: 1px solid #D6D6D7;
background-color: #fff; background-color: #fff;
transition: transition: border 0.15s ease-in-out, color 0.15s ease-in-out;
border 0.15s ease-in-out,
color 0.15s ease-in-out;
} }
&:after{ &:after{
cursor: pointer; cursor: pointer;
content: ''; content: "";
display: inline-block; display: inline-block;
position: absolute; position: absolute;
width: 16px; width: 16px;
height: 16px; height: 16px;
top:0; top:0;
left: 0; left: 0;
margin-left: -0.8rem; margin-left: -.8rem;
transition: transition: border 0.05s ease-in-out, color 0.05s ease-in-out;
border 0.05s ease-in-out,
color 0.05s ease-in-out;
} }
} }
input[type='checkbox']:checked + label::after { input[type=checkbox]:checked + label::after{
content: ''; content: "";
display: inline-block; display: inline-block;
position: absolute; position: absolute;
top: 1px; top: 1px;
left: -1px; left: -1px;
width: 5px; width: 5px;
height: 8px; height: 8px;
border: 2px solid #697c8f; border: 2px solid #697C8F;
border-left: none; border-left: none;
border-top: none; border-top: none;
transform: translate(7.75px,4.5px) rotate(45deg); transform: translate(7.75px,4.5px) rotate(45deg);
-ms-transform: translate(7.75px,4.5px) rotate(45deg); -ms-transform: translate(7.75px,4.5px) rotate(45deg);
} }
input[type=checkbox]:disabled + label::before{
background-color: #FAFAFA;
}
input[type=checkbox]:disabled + label::after{
border-color: #999;
}
&.pop{ &.pop{
label{ label{
&:before{ &:before{
background-color: transparent; background-color: transparent;
} }
} }
input[type='checkbox']:checked + label::after { input[type=checkbox]:checked + label::after{
border-color: #fff; border-color: #fff;
} }
&.no-text{ &.no-text{
@ -930,25 +801,23 @@ input[type='text'] {
} }
} }
&.sel{ &.sel{
input[type='checkbox']:checked + label { input[type=checkbox]:checked + label{
color: #d7c863; color: #D7C863;
} }
input[type='checkbox']:checked + label::before, input[type=checkbox]:checked + label::before,
input[type='checkbox']:checked + label::after { input[type=checkbox]:checked + label::after{
border-color: #d7c863; border-color: #D7C863;
} }
} }
} }
// date-picker // date-picker
.date-picker{ .date-picker{
svg { svg{display: none;}
display: none;
}
.react-datepicker-wrapper{ .react-datepicker-wrapper{
width: 100%; width: 100%;
} }
input[type='text'] { input[type=text]{
display: block; display: block;
width: 100%; width: 100%;
height: 30px; height: 30px;
@ -956,7 +825,7 @@ input[type='text'] {
border-radius: 2px; border-radius: 2px;
border: 1px solid #eee; border: 1px solid #eee;
font-size: 13px; font-size: 13px;
color: #45576f; color: #45576F;
font-weight: normal; font-weight: normal;
font-family: 'Noto Sans JP', sans-serif; font-family: 'Noto Sans JP', sans-serif;
background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat; background: #fff url(../../public/static/images/common/datepicker.svg) calc(100% - 11px) center no-repeat;
@ -972,17 +841,18 @@ input[type='text'] {
height: 30px; height: 30px;
min-height: unset; min-height: unset;
border-radius: 2px; border-radius: 2px;
border-color: #eee; border-color: #EEE;
background-color: #fff; background-color: #fff;
&:hover{ &:hover{
border-color: #eee; border-color: #EEE;
} }
} }
.custom__control--is-focused{ .custom__control--is-focused{
border-color: #eee; border-color: #EEE;
box-shadow: unset; box-shadow: unset;
&:hover{ &:hover{
border-color: #eee; border-color: #EEE;
} }
} }
.custom__value-container { .custom__value-container {
@ -995,7 +865,7 @@ input[type='text'] {
} }
.custom__single-value{ .custom__single-value{
font-size: 13px; font-size: 13px;
color: #45576f; color: #45576F;
font-weight: 400; font-weight: 400;
} }
.custom__placeholder{ .custom__placeholder{
@ -1032,7 +902,7 @@ input[type='text'] {
.custom__option{ .custom__option{
font-size: 13px; font-size: 13px;
padding: 7px 10px; padding: 7px 10px;
color: #45576f; color: #45576F;
} }
.custom__option--is-selected{ .custom__option--is-selected{
color: #fff; color: #fff;
@ -1040,7 +910,7 @@ input[type='text'] {
// disable // disable
&.custom--is-disabled{ &.custom--is-disabled{
.custom__control{ .custom__control{
background-color: #fafafa; background-color: #FAFAFA ;
} }
.custom__single-value{ .custom__single-value{
color: #999999; color: #999999;
@ -1066,7 +936,7 @@ input[type='text'] {
right: 0; right: 0;
bottom: 0; bottom: 0;
background-color: #454545; background-color: #454545;
transition: 0.4s; transition: .4s;
border-radius: 10px; border-radius: 10px;
text-align: center; text-align: center;
line-height: 20px; line-height: 20px;
@ -1083,18 +953,18 @@ input[type='text'] {
} }
&:before { &:before {
position: absolute; position: absolute;
content: ''; content: "";
height: 16px; height: 16px;
width: 16px; width: 16px;
left: 2px; left: 2px;
bottom: 2px; bottom: 2px;
background-color: white; background-color: white;
transition: 0.4s; transition: .4s;
border-radius: 50%; border-radius: 50%;
} }
} }
input:checked + .slider { input:checked + .slider {
background-color: #2563eb; background-color: #2563EB;
&:after { &:after {
content: 'ON'; content: 'ON';
left: 10px; left: 10px;

3773
yarn.lock

File diff suppressed because it is too large Load Diff