diff --git a/ecosystem.config.js b/ecosystem.config.js new file mode 100644 index 00000000..295b7871 --- /dev/null +++ b/ecosystem.config.js @@ -0,0 +1,10 @@ +module.exports = { + apps: [ + { + name: 'qcast-front-production', + script: 'npm run start:dev', + instance: 2, + exec_mode: 'cluster', + }, + ], +} diff --git a/package.json b/package.json index e6aabebd..d9c1af06 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "scripts": { "dev": "next dev", "build": "next build", - "start": "next start -p 3000", - "start:dev": "next start -p 3001", + "start:cluster1": "next start -p 5000", + "start:cluster2": "next start -p 5001", + "start:dev": "next start -p 5010", "lint": "next lint", "serve": "node server.js" }, diff --git a/src/common/common.js b/src/common/common.js index d82d43f0..abda5acd 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -203,6 +203,7 @@ export const SAVE_KEY = [ 'fontWeight', 'dormerAttributes', 'toFixed', + 'isSortedPoints', ] export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype, fabric.Group.prototype] diff --git a/src/components/auth/AutoLogin.jsx b/src/components/auth/AutoLogin.jsx index d400f2c4..b3dcbb6e 100644 --- a/src/components/auth/AutoLogin.jsx +++ b/src/components/auth/AutoLogin.jsx @@ -2,26 +2,111 @@ import { useState } from 'react' import { useMessage } from '@/hooks/useMessage' +import { setSession, login } from '@/lib/authActions' +import { sessionStore } from '@/store/commonAtom' +import { useRecoilState } from 'recoil' +import { useAxios } from '@/hooks/useAxios' +import { globalLocaleStore } from '@/store/localeAtom' +import { useRouter } from 'next/navigation' + import GlobalSpinner from '@/components/common/spinner/GlobalSpinner' -export default function AutoLoginPage() { - const [isLoading, setIsLoading] = useState(true) +export default function AutoLoginPage({ autoLoginParam }) { + const router = useRouter() + + const [isLoading, setIsLoading] = useState(autoLoginParam === 'Y' ? false : true) + const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore) + + const { promisePost } = useAxios(globalLocaleState) const { getMessage } = useMessage() + const [userId, setUserId] = useState('') + const [sessionState, setSessionState] = useRecoilState(sessionStore) + + const [idFocus, setIdFocus] = useState(false) + + const loginProcess = async () => { + setIsLoading(true) + await promisePost({ url: '/api/login/v1.0/user', data: { loginId: userId } }).then((response) => { + setIsLoading(false) + if (response.data) { + const res = response.data + const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' } + setSession(result) + setSessionState(result) + login() + } else { + alert(getMessage('login.fail')) + router.push('/login?autoLoginParam1=Y') + } + }) + } + return ( <> {isLoading && } -
-
- {getMessage('site.name')} - {getMessage('site.sub_name')} -
-
-
- {getMessage('login.auto.page.text')} + {autoLoginParam !== 'Y' ? ( + <> +
+
+ {getMessage('site.name')} + {getMessage('site.sub_name')} +
+
+
+ {getMessage('login.auto.page.text')} +
+
-
-
+ + ) : ( + <> +
+
{ + e.preventDefault() + loginProcess() + }} + className="space-y-6" + > +
+ {getMessage('site.name')} + {getMessage('site.sub_name')} +
+
+
+ { + setUserId(e.target.value) + }} + onFocus={() => setIdFocus(true)} + onBlur={() => setIdFocus(false)} + /> + +
+
+ +
+
+
+
+ + )} ) } diff --git a/src/components/auth/Login.jsx b/src/components/auth/Login.jsx index 85e342d4..7a6ba957 100644 --- a/src/components/auth/Login.jsx +++ b/src/components/auth/Login.jsx @@ -25,7 +25,9 @@ export default function Login() { useEffect(() => { if (autoLoginParam) { - autoLoginProcess(autoLoginParam) + if (autoLoginParam !== 'Y') { + autoLoginProcess(autoLoginParam) + } } // console.log('๐Ÿš€ ~ checkSession ~ checkSession():', checkSession()) @@ -334,7 +336,7 @@ export default function Login() {
)} - {autoLoginParam && } + {autoLoginParam && }
COPYRIGHTยฉ2024 Hanwha Japan All Rights Reserved.
diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index cd8b9e2b..eec45f41 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -6,29 +6,19 @@ import { contextMenuListState, contextMenuState } from '@/store/contextMenu' import { useTempGrid } from '@/hooks/useTempGrid' import { useContextMenu } from '@/hooks/useContextMenu' import { useEvent } from '@/hooks/useEvent' -import { canvasState } from '@/store/canvasAtom' +import { canvasState, currentObjectState } from '@/store/canvasAtom' export default function QContextMenu(props) { const canvas = useRecoilValue(canvasState) const { contextRef, canvasProps } = props const [contextMenu, setContextMenu] = useRecoilState(contextMenuState) const contextMenuList = useRecoilValue(contextMenuListState) - const activeObject = canvasProps?.getActiveObject() //์•กํ‹ฐ๋ธŒ๋œ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ ธ์˜ด + const currentObject = useRecoilValue(currentObjectState) const { tempGridMode, setTempGridMode } = useTempGrid() const { handleKeyup } = useContextMenu() const { addDocumentEventListener, removeDocumentEvent } = useEvent() // const { addDocumentEventListener, removeDocumentEvent } = useContext(EventContext) - let contextType = '' - - if (activeObject) { - if (activeObject.initOptions && activeObject.initOptions.name) { - //์ด๊ฑด ๋ฐ”๋€” ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ์Œ - if (activeObject.initOptions?.name?.indexOf('guide') > -1) { - contextType = 'surface' //๋ฉดํ˜•์ƒ - } - } - } const getYPosition = (e) => { const contextLength = contextMenuList.reduce((acc, cur, index) => { return acc + cur.length @@ -36,11 +26,13 @@ export default function QContextMenu(props) { return e?.clientY - (contextLength * 25 + contextMenuList.length * 2 * 17) } - useEffect(() => { - if (!contextRef.current) return + const handleContextMenu = (e) => { + // e.preventDefault() //๊ธฐ์กด contextmenu ๋ง‰๊ณ  + + if (currentObject) { + const isArray = currentObject.hasOwnProperty('arrayData') + if (isArray && currentObject.arrayData.length === 0) return - const handleContextMenu = (e) => { - e.preventDefault() //๊ธฐ์กด contextmenu ๋ง‰๊ณ  if (tempGridMode) return const position = { x: window.innerWidth / 2 < e.pageX ? e.pageX - 240 : e.pageX, @@ -48,21 +40,24 @@ export default function QContextMenu(props) { } setContextMenu({ visible: true, ...position, currentMousePos: canvasProps.getPointer(e) }) addDocumentEventListener('keyup', document, handleKeyup) - canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //ํ•œ๋ฒˆ ๋…ธ์ถœ ํ›„ ์ด๋ฒคํŠธ ์‚ญ์ œ } + } - const handleClick = (e) => { - // e.preventDefault() + const handleClick = (e) => { + // e.preventDefault() + setContextMenu({ ...contextMenu, visible: false }) + } + + const handleOutsideClick = (e) => { + // e.preventDefault() + if (contextMenu.visible) { setContextMenu({ ...contextMenu, visible: false }) + removeDocumentEvent('keyup') } + } - const handleOutsideClick = (e) => { - // e.preventDefault() - if (contextMenu.visible) { - setContextMenu({ ...contextMenu, visible: false }) - removeDocumentEvent('keyup') - } - } + useEffect(() => { + if (!contextRef.current) return canvasProps?.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) document.addEventListener('click', handleClick) @@ -72,43 +67,9 @@ export default function QContextMenu(props) { removeDocumentEvent('keyup') document.removeEventListener('click', handleClick) document.removeEventListener('click', handleOutsideClick) + canvasProps?.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //ํ•œ๋ฒˆ ๋…ธ์ถœ ํ›„ ์ด๋ฒคํŠธ ์‚ญ์ œ } - }, [contextRef, contextMenuList]) - - const handleObjectMove = () => { - activeObject.set({ - lockMovementX: false, // X ์ถ• ์ด๋™ ์ž ๊ธˆ - lockMovementY: false, // Y ์ถ• ์ด๋™ ์ž ๊ธˆ - }) - - canvasProps?.on('object:modified', function (e) { - activeObject.set({ - lockMovementX: true, // X ์ถ• ์ด๋™ ์ž ๊ธˆ - lockMovementY: true, // Y ์ถ• ์ด๋™ ์ž ๊ธˆ - }) - }) - } - - const handleObjectDelete = () => { - if (confirm('์‚ญ์ œํ•˜์‹ค๊ฑฐ?')) { - canvasProps.remove(activeObject) - } - } - - const handleObjectCopy = () => { - activeObject.clone((cloned) => { - cloned.set({ - left: activeObject.left + activeObject.width + 20, - initOptions: { ...activeObject.initOptions }, - lockMovementX: true, // X ์ถ• ์ด๋™ ์ž ๊ธˆ - lockMovementY: true, // Y ์ถ• ์ด๋™ ์ž ๊ธˆ - lockRotation: true, // ํšŒ์ „ ์ž ๊ธˆ - lockScalingX: true, // X ์ถ• ํฌ๊ธฐ ์กฐ์ • ์ž ๊ธˆ - lockScalingY: true, // Y ์ถ• ํฌ๊ธฐ ์กฐ์ • ์ž ๊ธˆ - }) - canvasProps?.add(cloned) - }) - } + }, [contextRef, contextMenuList, currentObject]) return ( <> diff --git a/src/components/common/select/QSelectBox.jsx b/src/components/common/select/QSelectBox.jsx index f1041da3..a027cb55 100644 --- a/src/components/common/select/QSelectBox.jsx +++ b/src/components/common/select/QSelectBox.jsx @@ -87,7 +87,7 @@ export default function QSelectBox({
    {options?.length > 0 && options?.map((option, index) => ( -
  • handleClickSelectOption(option)}> +
  • handleClickSelectOption(option)}>
  • ))} diff --git a/src/components/estimate/Estimate.jsx b/src/components/estimate/Estimate.jsx index a44af878..a544ced7 100644 --- a/src/components/estimate/Estimate.jsx +++ b/src/components/estimate/Estimate.jsx @@ -13,7 +13,7 @@ import dayjs from 'dayjs' import { useCommonCode } from '@/hooks/common/useCommonCode' import { useEstimateController } from '@/hooks/floorPlan/estimate/useEstimateController' import { SessionContext } from '@/app/SessionProvider' -import Select from 'react-select' +import Select, {components} from 'react-select' import { convertNumberToPriceDecimal, convertNumberToPriceDecimalToFixed } from '@/util/common-utils' import ProductFeaturesPop from './popup/ProductFeaturesPop' import { v4 as uuidv4 } from 'uuid' @@ -175,7 +175,10 @@ export default function Estimate({}) { row.check = false estimateOption.map((row2) => { if (row.pkgYn === '0') { - if (row2 === row.code) { + // if (row2 === row.code) { + // row.check = true + // } + if (row.code.split('ใ€').includes(row2)) { row.check = true } } else { @@ -217,7 +220,10 @@ export default function Estimate({}) { row.check = false estimateOption.map((row2) => { if (row.pkgYn === '0') { - if (row2 === row.code) { + // if (row2 === row.code) { + // row.check = true + // } + if (row.code.split('ใ€').includes(row2)) { row.check = true } } else { @@ -240,7 +246,6 @@ export default function Estimate({}) { } } }) - setSpecialNoteList(res) setSpecialNoteFirstFlg(true) @@ -377,8 +382,8 @@ export default function Estimate({}) { useEffect(() => { if (estimateContextState.estimateType !== '') { const param = { - saleStoreId: session.storeId, - sapSalesStoreCd: session.custCd, + saleStoreId: estimateContextState.sapSaleStoreId, + sapSalesStoreCd: estimateContextState.sapSalesStoreCd, docTpCd: estimateContextState?.estimateType, } @@ -387,6 +392,8 @@ export default function Estimate({}) { if (isNotEmptyArray(res?.data)) { setStorePriceList(res.data) } + + setItemChangeYn(true) }) if (estimateContextState.estimateType === 'YJSS') { @@ -416,8 +423,6 @@ export default function Estimate({}) { handlePricing('UNIT_PRICE') } } - - setItemChangeYn(true) } }, [estimateContextState?.estimateType]) @@ -469,6 +474,21 @@ export default function Estimate({}) { } else { item.check = false } + } else { + let codes = item.code.split('ใ€') + let flg = '0' + if (codes.length > 1) { + for (let i = 0; i < pushData.length; i++) { + if (codes.indexOf(pushData[i]) > -1) { + flg = '1' + } + } + if (flg === '1') { + item.check = true + } else { + item.check = false + } + } } }) @@ -481,8 +501,8 @@ export default function Estimate({}) { //Pricing ๋ฒ„ํŠผ const handlePricing = async (showPriceCd) => { const param = { - saleStoreId: session.storeId, - sapSalesStoreCd: session.custCd, + saleStoreId: estimateContextState.sapSaleStoreId, + sapSalesStoreCd: estimateContextState.sapSalesStoreCd, docTpCd: estimateContextState.estimateType, priceCd: showPriceCd, itemIdList: estimateContextState.itemList.filter((item) => item.delFlg === '0' && item.paDispOrder === null), @@ -506,7 +526,6 @@ export default function Estimate({}) { }) } } - setIsGlobalLoading(true) await promisePost({ url: '/api/estimate/price/item-price-list', data: param }).then((res) => { let updateList = [] @@ -1342,7 +1361,12 @@ export default function Estimate({}) {
    -
    + {/*pkgRank is null, empty ์ธ ๊ฒฝ์šฐ : ์‚ฌ์šฉ๋ถˆ๊ฐ€, ์ด์ „์— ๋“ฑ๋ก๋œ ๊ฒฝ์šฐ ์‚ฌ์šฉ๊ฐ€๋Šฅ, style๋กœ ์ œ์–ด*/} +
    0 + && storePriceList[0].pkgRank !== null + && storePriceList[0].pkgRank !== '' + || estimateContextState?.estimateType === 'YJSS') ? "" : "none"}}> x.itemName} + getOptionLabel={(x) => x.itemName + " (" + x.itemNo + ")"} getOptionValue={(x) => x.itemNo} + components={{ + SingleValue:({children, ...props}) =>{ + return ( + + {props.data.itemName} + + ) + } + }} isClearable={false} isDisabled={!!item?.paDispOrder} value={displayItemList.filter(function (option) { @@ -1880,8 +1913,17 @@ export default function Estimate({}) { placeholder="Select" options={cableItemList} menuPlacement={'auto'} - getOptionLabel={(x) => x.clRefChr3} + getOptionLabel={(x) => x.clRefChr3 + " (" + x.clRefChr1 + ")"} getOptionValue={(x) => x.clRefChr1} + components={{ + SingleValue:({children, ...props}) =>{ + return ( + + {props.data.clRefChr3} + + ) + } + }} isClearable={false} isDisabled={true} value={cableItemList.filter(function (option) { diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 85bb03dd..a471b5be 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -45,8 +45,11 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { options.sort = options.sort ?? true options.parentId = options.parentId ?? null + this.isSortedPoints = false + if (!options.sort && points.length <= 8) { points = sortedPointLessEightPoint(points) + this.isSortedPoints = true } else { let isDiagonal = false points.forEach((point, i) => { @@ -62,6 +65,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { if (!isDiagonal) { points = sortedPoints(points) + this.isSortedPoints = true } } @@ -119,10 +123,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.addLengthText() this.on('moving', () => { + this.initLines() this.addLengthText() }) this.on('modified', (e) => { + this.initLines() this.addLengthText() }) @@ -136,8 +142,20 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.on('removed', () => { // const children = getAllRelatedObjects(this.id, this.canvas) const children = this.canvas.getObjects().filter((obj) => obj.parentId === this.id) + children.forEach((child) => { this.canvas.remove(child) + + //๊ทธ๋ฃน์ผ๋•Œ + if (child.hasOwnProperty('_objects')) { + child._objects.forEach((obj) => { + if (obj.hasOwnProperty('texts')) { + obj.texts.forEach((text) => { + this.canvas?.remove(text) + }) + } + }) + } }) }) @@ -171,8 +189,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.lines = [] - this.points.forEach((point, i) => { - const nextPoint = this.points[(i + 1) % this.points.length] + this.getCurrentPoints().forEach((point, i) => { + const nextPoint = this.getCurrentPoints()[(i + 1) % this.points.length] const line = new QLine([point.x, point.y, nextPoint.x, nextPoint.y], { stroke: this.stroke, strokeWidth: this.strokeWidth, diff --git a/src/components/floor-plan/CanvasFrame.jsx b/src/components/floor-plan/CanvasFrame.jsx index ddce32a6..c98f686b 100644 --- a/src/components/floor-plan/CanvasFrame.jsx +++ b/src/components/floor-plan/CanvasFrame.jsx @@ -30,11 +30,14 @@ import { useCanvasSetting } from '@/hooks/option/useCanvasSetting' import { useCanvasMenu } from '@/hooks/common/useCanvasMenu' import { useEvent } from '@/hooks/useEvent' import { compasDegAtom } from '@/store/orientationAtom' +import { hotkeyStore } from '@/store/hotkeyAtom' +import { usePopup } from '@/hooks/usePopup' export default function CanvasFrame() { const canvasRef = useRef(null) const { canvas } = useCanvas('canvas') const { canvasLoadInit, gridInit } = useCanvasConfigInitialize() + const { closeAll } = usePopup() const currentMenu = useRecoilValue(currentMenuState) const { floorPlanState } = useContext(FloorPlanContext) const { contextMenu, handleClick } = useContextMenu() @@ -92,6 +95,8 @@ export default function CanvasFrame() { useEffect(() => { setIsGlobalLoading(false) + // ํ˜น์‹œ ๋ชจ๋ฅผ ํŒ์—…์ด ๋– ์žˆ๋Š” ๊ฒฝ์šฐ ๋‹ซ๊ณ  ์‹œ์ž‘ํ•œ๋‹ค. + closeAll() return () => { canvas?.clear() @@ -110,6 +115,38 @@ export default function CanvasFrame() { resetPcsCheckState() } + /** + * ์บ”๋ฒ„์Šค๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ํ•ซํ‚ค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ + * hotkeyStore์— ํ•ซํ‚ค ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ์ถ”๊ฐ€ + * + * const hotkeys = [ + { key: 'c', fn: () => asdf() }, + { key: 'v', fn: () => qwer() }, + ] + setHotkeyStore(hotkeys) + */ + const hotkeyState = useRecoilValue(hotkeyStore) + const hotkeyHandlerRef = useRef(null) + + useEffect(() => { + hotkeyHandlerRef.current = (e) => { + hotkeyState.forEach((hotkey) => { + if (e.key === hotkey.key) { + hotkey.fn() + } + }) + } + + document.addEventListener('keyup', hotkeyHandlerRef.current) + + return () => { + if (hotkeyHandlerRef.current) { + document.removeEventListener('keyup', hotkeyHandlerRef.current) + } + } + }, [hotkeyState]) + /** ํ•ซํ‚ค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌ ๋ */ + return (
    diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index 0d9ddc0a..a268604e 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -634,7 +634,7 @@ export default function CanvasMenu(props) { onClick={() => setEstimatePopupOpen(true)} > - {getMessage('plan.menu.estimate.docDown')} + {getMessage('plan.menu.estimate.docDownload')}