이 영역은 테스트입니다.
+
+
+
+
+
+
diff --git a/src/components/auth/NewLogin.jsx b/src/components/auth/NewLogin.jsx
new file mode 100644
index 00000000..cf56378e
--- /dev/null
+++ b/src/components/auth/NewLogin.jsx
@@ -0,0 +1,240 @@
+'use client'
+
+import { useState, useRef, useEffect } from 'react'
+import Image from 'next/image'
+import Link from 'next/link'
+import { redirect } from 'next/navigation'
+import { useRecoilState } from 'recoil'
+import { useAxios } from '@/hooks/useAxios'
+import { setSession } from '@/lib/authActions'
+import { useMessage } from '@/hooks/useMessage'
+import { globalLocaleStore } from '@/store/localeAtom'
+import { sessionStore } from '@/store/commonAtom'
+import { modalContent, modalState } from '@/store/modalAtom'
+import '@/styles/style.scss'
+import { useRouter } from 'next/navigation'
+
+export default function NewLogin() {
+ const [passwordVisible, setPasswordVisible] = useState(false)
+ const passwordRef = useRef(null)
+ const router = useRouter()
+
+ useEffect(() => {
+ if (passwordVisible) {
+ passwordRef.current.type = 'text'
+ } else {
+ passwordRef.current.type = 'password'
+ }
+ }, [passwordVisible])
+
+ const { patch } = useAxios()
+
+ const { getMessage } = useMessage()
+ const [globalLocaleState, setGlbalLocaleState] = useRecoilState(globalLocaleStore)
+ const [sessionState, setSessionState] = useRecoilState(sessionStore)
+ const [isSelected, setIsSelected] = useState(globalLocaleState === 'ko' ? true : false)
+
+ const handleSelected = () => {
+ if (isSelected) {
+ setGlbalLocaleState('ja')
+ } else {
+ setGlbalLocaleState('ko')
+ }
+
+ setIsSelected(!isSelected)
+ }
+
+ // login process
+ const loginProcess = async (formData) => {
+ const param = {
+ // langCd: currentLocale
+ langCd: globalLocaleState,
+ lastEditUser: formData.get('id'),
+ loginId: formData.get('id'),
+ pwd: formData.get('password'),
+ }
+
+ // await post({ url: '/api/login/v1.0/login', data: param }).then((res) => {
+ // if (res) {
+ // if (res.result.resultCode == 'S') {
+ // // console.log('res.data', res.data)
+ // // 비밀번호 초기화가 필요한 경우
+ // // if (res.data.pwdInitYn != 'Y') {
+ // // alert('비밀번호 초기화가 필요한 경우')
+ // // } else {
+ // setSession(res.data)
+ // redirect('/')
+ // // }
+ // } else {
+ // alert(res.result.resultMsg)
+ // }
+ // }
+ // })
+
+ // 임시 로그인 처리
+ setSession({
+ userId: 'NEW016610',
+ 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: 'N',
+ })
+
+ setSessionState({
+ userId: 'NEW016610',
+ 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: 'N',
+ })
+
+ // redirect('/')
+ router.push('/')
+ // 임시 로그인 처리 끝
+ }
+
+ // 비밀번호 초기화 관련
+ const [open, setOpen] = useRecoilState(modalState)
+ const [contents, setContent] = useRecoilState(modalContent)
+
+ const initPasswordProcess = async (formData) => {
+ const param = {
+ langCd: currentLocale,
+ lastEditUser: formData.get('checkId'),
+ loginId: formData.get('checkId'),
+ email: formData.get('checkEmail'),
+ }
+
+ await patch({ url: '/api/login/v1.0/user/init-password', data: param }).then((res) => {
+ if (res) {
+ if (res.result.resultCode == 'S') {
+ alert(getMessage('login.init_password.complete_message'))
+ redirect('/login')
+ } else {
+ alert(res.result.resultMsg)
+ }
+ }
+ })
+ }
+
+ const initPasswordContent = (
+
+ )
+
+ return (
+
+
+
+
+
+
+
+ ※当サイトをご利用の際は、事前申請が必要です。
+
+ IDがない方は ID申請 クリックしてください。
+
+
+
COPYRIGHT©2024 Hanwha Japan All Rights Reserved.
+
+ )
+}
diff --git a/src/components/common/input/QInput.jsx b/src/components/common/input/QInput.jsx
new file mode 100644
index 00000000..484d9859
--- /dev/null
+++ b/src/components/common/input/QInput.jsx
@@ -0,0 +1,60 @@
+'use client'
+
+export default function QInput({ type, readOnly = false, options, value, onChange }) {
+ // options = options || [
+ // {
+ // id: 'one',
+ // name: 'Option 1',
+ // value: '111',
+ // },
+ // {
+ // id: 'two',
+ // name: 'Option 2',
+ // value: '222',
+ // },
+ // {
+ // id: 'three',
+ // name: 'Option 3',
+ // value: '333',
+ // },
+ // ]
+
+ const handleChange = (e, optionValue) => {
+ if (type === 'radio') {
+ onChange(e.target.value)
+ } else {
+ const newValue = value.includes(optionValue) ? value.filter((v) => v !== optionValue) : [...value, optionValue]
+ onChange(newValue)
+ }
+ }
+
+ return (
+
+
+
+ {type === 'text' ? (
+
+ onChange(e.target.value)} />
+
+ ) : type === 'radio' || type === 'checkbox' ? (
+
+ {options?.map((option) => (
+
+ handleChange(e, option.value)}
+ />
+
+
+ ))}
+
+ ) : null}
+
+
+
+ )
+}
diff --git a/src/components/floor-plan/CanvasLayout.jsx b/src/components/floor-plan/CanvasLayout.jsx
index 3abebd85..98f5f36d 100644
--- a/src/components/floor-plan/CanvasLayout.jsx
+++ b/src/components/floor-plan/CanvasLayout.jsx
@@ -12,7 +12,7 @@ import { sessionStore } from '@/store/commonAtom'
export default function CanvasLayout() {
const [objectNo, setObjectNo] = useState('test123240822001') // 이후 삭제 필요
- const [addCanvasPlans, setAddCanvasPlans] = useState([])
+ const [plans, setPlans] = useState([])
const [planNum, setPlanNum] = useState(0)
const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
@@ -21,25 +21,54 @@ export default function CanvasLayout() {
const { getMessage } = useMessage()
const { swalFire } = useSwal()
- const { getCanvasByObjectNo, delCanvasById } = usePlan()
+ const { getCanvasByObjectNo, delCanvasById, checkModifiedCanvasPlan, saveCanvas } = usePlan()
const handleCurrentPlan = (newCurrentId) => {
+ // console.log('currentPlan newCurrentId: ', newCurrentId)
+
if (!currentCanvasPlan?.id || currentCanvasPlan.id !== newCurrentId) {
- setInitCanvasPlans((plans) =>
- plans.map((plan) => {
- return { ...plan, isCurrent: plan.id === newCurrentId }
- }),
- )
- setAddCanvasPlans((plans) =>
- plans.map((plan) => {
- return { ...plan, isCurrent: plan.id === newCurrentId }
- }),
- )
+ if (currentCanvasPlan?.id && checkModifiedCanvasPlan()) {
+ swalFire({
+ html: getMessage('common.message.confirm.save') + `${currentCanvasPlan.name}`,
+ type: 'confirm',
+ confirmFn: async () => {
+ saveCanvas(sessionState.userId)
+ /**
+ * TODO: 신규 canvas plan 저장 시 id, name 등 데이터 동기화 필요 (40~51Line)
+ */
+ initCanvasPlans.map((initPlan) => {
+ if ('isNew' in initPlan) {
+ // console.log('================ isNew initPlan: ', initPlan)
+ setPlans((plans) =>
+ plans.map((plan) => {
+ initPlan.isNew === plan.id
+ ? { ...plan, id: initPlan.id, name: initPlan.name, canvasStatus: initPlan.canvasStatus, isCurrent: plan.id === newCurrentId }
+ : { ...plan, isCurrent: plan.id === newCurrentId }
+ }),
+ )
+ }
+ })
+ },
+ denyFn: () => {
+ setPlans((plans) =>
+ plans.map((plan) => {
+ return { ...plan, isCurrent: plan.id === newCurrentId }
+ }),
+ )
+ },
+ })
+ } else {
+ setPlans((plans) =>
+ plans.map((plan) => {
+ return { ...plan, isCurrent: plan.id === newCurrentId }
+ }),
+ )
+ }
}
}
useEffect(() => {
- setCurrentCanvasPlan([...initCanvasPlans, ...addCanvasPlans].find((plan) => plan.isCurrent) || null)
- }, [initCanvasPlans, addCanvasPlans])
+ setCurrentCanvasPlan(plans.find((plan) => plan.isCurrent) || null)
+ }, [plans])
const handleDeletePlan = (e, id) => {
e.stopPropagation() // 이벤트 버블링 방지
@@ -48,20 +77,21 @@ export default function CanvasLayout() {
delCanvasById(id)
.then((res) => {
swalFire({ text: getMessage('common.message.delete') })
- console.log('[DELETE] canvas-statuses res :::::::: %o', res)
+ // console.log('[DELETE] canvas-statuses res :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) => initCanvasPlans.filter((plan) => plan.id !== id))
+ setPlans((plans) => plans.filter((plan) => plan.id !== id))
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
- console.error('[DELETE] canvas-statuses res error :::::::: %o', error)
+ // console.error('[DELETE] canvas-statuses res error :::::::: %o', error)
})
} else {
- setAddCanvasPlans(addCanvasPlans.filter((plan) => plan.id !== id))
+ setPlans(plans.filter((plan) => plan.id !== id))
swalFire({ text: getMessage('common.message.delete') })
}
// 삭제 후 last 데이터에 포커싱
- const lastPlan = [...initCanvasPlans, ...addCanvasPlans].filter((plan) => plan.id !== id).at(-1)
+ const lastPlan = plans.filter((plan) => plan.id !== id).at(-1)
if (!lastPlan) {
setPlanNum(0)
setCurrentCanvasPlan(null)
@@ -71,7 +101,7 @@ export default function CanvasLayout() {
}
const addNewPlan = () => {
- setAddCanvasPlans([...addCanvasPlans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }])
+ setPlans([...plans, { id: planNum, name: `Plan ${planNum + 1}`, objectNo: `${objectNo}` }])
handleCurrentPlan(planNum)
setPlanNum(planNum + 1)
}
@@ -81,6 +111,7 @@ export default function CanvasLayout() {
console.log('canvas 목록 ', res)
if (res.length > 0) {
setInitCanvasPlans(res)
+ setPlans(res)
handleCurrentPlan(res.at(-1).id) // last 데이터에 포커싱
setPlanNum(res.length)
} else {
@@ -93,7 +124,7 @@ export default function CanvasLayout() {
- {[...initCanvasPlans, ...addCanvasPlans].map((plan) => (
+ {plans.map((plan) => (
-
plan.isCurrent === true)} />
+ plan.isCurrent === true)} />
)
}
diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx
index b0fedf65..350434f0 100644
--- a/src/components/floor-plan/CanvasMenu.jsx
+++ b/src/components/floor-plan/CanvasMenu.jsx
@@ -41,6 +41,8 @@ export default function CanvasMenu(props) {
setShowRoofShapeSettingModal,
setShowRoofShapePassivitySettingModal,
setShowAuxiliaryModal,
+ setShowEavesGableEditModal,
+ setShowWallLineOffsetSettingModal,
} = props
const [menuNumber, setMenuNumber] = useState(null)
@@ -88,8 +90,10 @@ export default function CanvasMenu(props) {
setShowRoofShapeSettingModal,
setShowRoofShapePassivitySettingModal,
setShowAuxiliaryModal,
+ setShowEavesGableEditModal,
setShowSlopeSettingModal,
setShowPlaceShapeDrawingModal,
+ setShowWallLineOffsetSettingModal,
type,
}
@@ -116,6 +120,7 @@ export default function CanvasMenu(props) {
const onClickPlacementInitialMenu = () => {
setShowOutlineModal(false)
setShowCanvasSettingModal(false)
+ setShowEavesGableEditModal(false)
setShowPlaceShapeModal(true)
}
diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx
index a827482b..4cf10a8a 100644
--- a/src/components/floor-plan/FloorPlan.jsx
+++ b/src/components/floor-plan/FloorPlan.jsx
@@ -19,6 +19,8 @@ import PlacementShapeDrawing from '@/components/floor-plan/modal/placementShape/
import Slope from '@/components/floor-plan/modal/Slope'
import AuxiliaryDrawing from '@/components/floor-plan/modal/auxiliary/AuxiliaryDrawing'
+import EavesGableEdit from '@/components/floor-plan/modal/eavesGable/EavesGableEdit'
+import WallLineOffsetSetting from '@/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting'
export default function FloorPlan() {
const [showCanvasSettingModal, setShowCanvasSettingModal] = useState(false)
@@ -30,6 +32,8 @@ export default function FloorPlan() {
const [showAuxiliaryModal, setShowAuxiliaryModal] = useState(false)
const [showSlopeSettingModal, setShowSlopeSettingModal] = useState(false)
const [showPlaceShapeDrawingModal, setShowPlaceShapeDrawingModal] = useState(false)
+ const [showEavesGableEditModal, setShowEavesGableEditModal] = useState(false)
+ const [showWallLineOffsetSettingModal, setShowWallLineOffsetSettingModal] = useState(false)
const globalLocaleState = useRecoilValue(globalLocaleStore)
const { get } = useAxios(globalLocaleState)
@@ -62,6 +66,8 @@ export default function FloorPlan() {
setShowRoofShapeSettingModal,
setShowRoofShapePassivitySettingModal,
setShowAuxiliaryModal,
+ setShowEavesGableEditModal,
+ setShowWallLineOffsetSettingModal,
}
useEffect(() => {
@@ -125,6 +131,9 @@ export default function FloorPlan() {
{showAuxiliaryModal &&
}
{showSlopeSettingModal &&
}
{showPlaceShapeDrawingModal &&
}
+ {showEavesGableEditModal &&
}
+ {/*
*/}
+ {showWallLineOffsetSettingModal &&
}
>
diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx
index 4148ac5a..e5c88e9b 100644
--- a/src/components/floor-plan/MenuDepth01.jsx
+++ b/src/components/floor-plan/MenuDepth01.jsx
@@ -14,8 +14,10 @@ export default function MenuDepth01(props) {
setShowRoofShapeSettingModal,
setShowRoofShapePassivitySettingModal,
setShowAuxiliaryModal,
+ setShowEavesGableEditModal,
setShowSlopeSettingModal,
setShowPlaceShapeDrawingModal,
+ setShowWallLineOffsetSettingModal,
} = props
const { getMessage } = useMessage()
const [activeMenu, setActiveMenu] = useState()
@@ -31,6 +33,8 @@ export default function MenuDepth01(props) {
setShowRoofShapeSettingModal(id === 1)
setShowRoofShapePassivitySettingModal(id === 2)
setShowAuxiliaryModal(id === 3)
+ setShowEavesGableEditModal(id === 4)
+ setShowWallLineOffsetSettingModal(id === 6)
setShowPlaceShapeDrawingModal(false)
}
diff --git a/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx
new file mode 100644
index 00000000..e144e717
--- /dev/null
+++ b/src/components/floor-plan/modal/eavesGable/EavesGableEdit.jsx
@@ -0,0 +1,46 @@
+import { useMessage } from '@/hooks/useMessage'
+import WithDraggable from '@/components/common/draggable/withDraggable'
+import { useState } from 'react'
+import Eaves from '@/components/floor-plan/modal/eavesGable/type/Eaves'
+import Gable from '@/components/floor-plan/modal/eavesGable/type/Gable'
+import WallMerge from '@/components/floor-plan/modal/eavesGable/type/WallMerge'
+import Shed from '@/components/floor-plan/modal/eavesGable/type/Shed'
+
+export default function EavesGableEdit({ setShowEavesGableEditModal }) {
+ const { getMessage } = useMessage()
+ const [buttonAct, setButtonAct] = useState(1)
+ const buttonMenu = [
+ { id: 1, name: getMessage('eaves') },
+ { id: 2, name: getMessage('gable') },
+ { id: 3, name: getMessage('wall.merge') },
+ { id: 4, name: getMessage('shed') },
+ ]
+ return (
+
- {Array(4)
- .fill()
- .map((_, i) => {
- if (i === 0) {
- return (
-
- {
- setObjectNo(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schObjectNo: e.target.value })
- }}
- />
- {
- setSaleStoreId(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreId: e.target.value })
- }}
- />
- {
- setAddress(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schAddress: e.target.value })
- }}
- />
-
- )
- } else if (i === 1) {
- return (
-
-
{
- setobjectName(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schObjectName: e.target.value })
- }}
- />
-
{
- setSaleStoreName(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schSaleStoreName: e.target.value })
- }}
- />
-
- {
- setSpecDateYn(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schSpecDateYn: e.target.value })
- }}
- />
-
-
-
- {
- setSpecDateYn(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schSpecDateYn: e.target.value })
- }}
- />
-
-
-
- {
- setSpecDateYn(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schSpecDateYn: e.target.value })
- }}
- />
-
-
-
- )
- } else if (i === 2) {
- return (
-
- )
- } else {
- return (
-
-
- {
- setDateType(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
- }}
- />
-
-
-
- {
- setDateType(e.target.value)
- setStuffSearch({ ...stuffSearch, code: 'S', schDateType: e.target.value })
- }}
- />
-
-
-
-
- )
- }
- })}
-
*/}
>
)
}
diff --git a/src/hooks/useAxios.js b/src/hooks/useAxios.js
index 1a7cd03e..39b71769 100644
--- a/src/hooks/useAxios.js
+++ b/src/hooks/useAxios.js
@@ -5,32 +5,40 @@ const AxiosType = {
EXTERNAL: 'External',
}
+/**
+ * axios 인스턴스 생성 후 반환
+ * @param {String} lang
+ * @returns http request instance - get, post, put, patch, delete (promise 접수사가 붙은 함수는 promise 반환)
+ */
export function useAxios(lang = '') {
const getInstances = (url) => {
+ /**
+ * url이 http로 시작하면 외부 서버로 판단
+ */
let type = AxiosType.INTERNAL
url.startsWith('http') ? (type = AxiosType.EXTERNAL) : ''
+ /**
+ * 내부 서버로 요청 시 lang 헤더 추가
+ */
+ let headerValue = {
+ Accept: 'application/json',
+ }
+ url.startsWith('https') ? '' : (headerValue['lang'] = lang)
+
return axios.create({
baseURL: type === AxiosType.INTERNAL ? process.env.NEXT_PUBLIC_API_SERVER_PATH : '',
- headers: {
- Accept: 'application/json',
- lang,
- },
+ headers: headerValue,
})
}
+ // request 추가 로직
axios.interceptors.request.use((config) => {
- // config['Authorization'] = localStorage.getItem('token')
- //TODO: 인터셉터에서 추가 로직 구현
return config
})
- axios.interceptors.request.use(undefined, (error) => {
- //TODO: 인터셉터에서 에러 처리 로직 구현
- // if (error.isAxiosError && e.response?.status === 401) {
- // localStorage.removeItem('token')
- // }
- })
+ // response 추가 로직
+ axios.interceptors.request.use(undefined, (error) => {})
const get = async ({ url }) => {
return await getInstances(url)
diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js
index 0f506fd3..a33c0de2 100644
--- a/src/hooks/useCanvas.js
+++ b/src/hooks/useCanvas.js
@@ -11,7 +11,7 @@ import { defineQLine } from '@/util/qline-utils'
import { defineQPloygon } from '@/util/qpolygon-utils'
import { writeImage } from '@/lib/canvas'
import { useCanvasEvent } from '@/hooks/useCanvasEvent'
-import { post } from '@/lib/Axios'
+import { useAxios } from '@/hooks/useAxios'
import { v4 as uuidv4 } from 'uuid'
export function useCanvas(id) {
@@ -22,6 +22,7 @@ export function useCanvas(id) {
const [canvasSize] = useRecoilState(canvasSizeState)
const [fontSize] = useRecoilState(fontSizeState)
const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent()
+ const { post } = useAxios()
/**
* 처음 셋팅
diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js
index ec0842b8..04ec7685 100644
--- a/src/hooks/useMode.js
+++ b/src/hooks/useMode.js
@@ -4873,13 +4873,14 @@ export function useMode() {
let turfPolygon
let manualDrawCells = drewRoofCells // 앞에서 자동으로 했을때 추가됨
let direction
-
+ let trestlePolygon
canvas.on('mouse:move', (e) => {
//마우스 이벤트 삭제 후 재추가
const mousePoint = canvas.getPointer(e.e)
for (let i = 0; i < trestlePolygons.length; i++) {
turfPolygon = polygonToTurfPolygon(trestlePolygons[i])
+ trestlePolygon = trestlePolygons[i]
direction = trestlePolygons[i].direction //도형의 방향
let width = direction === 'south' || direction === 'north' ? 172 : 113
let height = direction === 'south' || direction === 'north' ? 113 : 172
@@ -5050,6 +5051,7 @@ export function useMode() {
fabricPolygon.setCoords()
canvas?.renderAll()
inside = true
+ break
} else {
inside = false
}
@@ -5078,12 +5080,12 @@ export function useMode() {
fabricPolygon.set({ points: rectPoints })
const tempTurfModule = polygonToTurfPolygon(fabricPolygon)
+ //도머 객체를 가져옴
if (dormerTrestlePolygons) {
dormerTrestlePolygons.forEach((dormerTrestle) => {
- const dormerTurfPolygon = polygonToTurfPolygon(dormerTrestle)
-
- const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule]))
-
+ const dormerTurfPolygon = polygonToTurfPolygon(dormerTrestle) //turf객체로 변환
+ const intersection = turf.intersect(turf.featureCollection([dormerTurfPolygon, tempTurfModule])) //겹치는지 확인
+ //겹치면 안됨
if (intersection) {
alert('도머위에 모듈을 올릴 수 없습니다.')
isIntersection = false
@@ -5097,18 +5099,21 @@ export function useMode() {
if (turf.booleanWithin(tempTurfModule, turfPolygon)) {
//마우스 클릭시 set으로 해당 위치에 셀을 넣음
-
- const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell)))
+ const isOverlap = manualDrawCells.some((cell) => turf.booleanOverlap(tempTurfModule, polygonToTurfPolygon(cell))) //겹치는지 확인
if (!isOverlap) {
//안겹치면 넣는다
fabricPolygon.setCoords()
fabricPolygon.set({ name: 'cell', fill: '#BFFD9F' })
- manualDrawCells.push(fabricPolygon)
+ manualDrawCells.push(fabricPolygon) //모듈배열에 추가
+ //해당 모듈에 프로퍼티로 넣는다
+ trestlePolygon.set({
+ modules: manualDrawCells,
+ })
} else {
alert('셀끼리 겹치면 안되죠?')
}
} else {
- alert('나갔으요!!')
+ alert('나갔죠?!!')
}
setDrewRoofCells(manualDrawCells)
}
diff --git a/src/hooks/usePlan.js b/src/hooks/usePlan.js
index c719ba24..e861f9f8 100644
--- a/src/hooks/usePlan.js
+++ b/src/hooks/usePlan.js
@@ -6,7 +6,7 @@ import { useSwal } from '@/hooks/useSwal'
export function usePlan() {
const [canvas, setCanvas] = useRecoilState(canvasState)
- const [currentCanvasPlan, setcurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
+ const [currentCanvasPlan, setCurrentCanvasPlan] = useRecoilState(currentCanvasPlanState)
const [initCanvasPlans, setInitCanvasPlans] = useRecoilState(initCanvasPlansState)
const { swalFire } = useSwal()
const { getMessage } = useMessage()
@@ -70,6 +70,24 @@ export function usePlan() {
// }, 1000)
}
+ /**
+ * 실시간 캔버스 상태와 DB에 저장된 캔버스 상태를 비교하여 수정 여부를 판단
+ */
+ const checkModifiedCanvasPlan = () => {
+ removeMouseLines()
+ const canvasStr = addCanvas()
+ const canvasStatus = dbToCanvasFormat(canvasToDbFormat(canvasStr))
+ if (JSON.parse(canvasStr).objects.length === 0 && currentCanvasPlan.canvasStatus === undefined) {
+ // 빈 캔버스
+ return false
+ } else if (canvasStatus === currentCanvasPlan.canvasStatus) {
+ // 변경사항 없는 캔버스
+ return false
+ } else {
+ return true
+ }
+ }
+
/**
* DB에 저장된 데이터를 canvas에서 사용할 수 있도록 포맷화
*/
@@ -106,14 +124,14 @@ export function usePlan() {
await promisePut({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('common.message.save') })
- console.log('[PUT] canvas-statuses res :::::::: %o', res)
+ // console.log('[PUT] canvas-statuses res :::::::: %o', res)
setInitCanvasPlans((initCanvasPlans) =>
initCanvasPlans.map((plan) => (plan.id === currentCanvasPlan.id ? { ...plan, canvasStatus: canvasStatus } : plan)),
)
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
- console.error('[PUT] canvas-statuses error :::::::: %o', error)
+ // console.error('[PUT] canvas-statuses error :::::::: %o', error)
})
} else {
// canvas 신규 등록
@@ -127,11 +145,15 @@ export function usePlan() {
await promisePost({ url: '/api/canvas-management/canvas-statuses', data: planData })
.then((res) => {
swalFire({ text: getMessage('common.message.save') })
- console.log('[POST] canvas-statuses response :::::::: %o', res)
+ setInitCanvasPlans([
+ ...initCanvasPlans,
+ { id: res.data, name: currentCanvasPlan.objectNo + '-' + res.data, userId: userId, canvasStatus: canvasStatus, isNew: currentCanvasPlan.id },
+ ])
+ // console.log('[POST] canvas-statuses response :::::::: %o', res)
})
.catch((error) => {
swalFire({ text: error.message, icon: 'error' })
- console.error('[POST] canvas-statuses res error :::::::: %o', error)
+ // console.error('[POST] canvas-statuses res error :::::::: %o', error)
})
}
}
@@ -170,6 +192,7 @@ export function usePlan() {
removeMouseLines,
saveCanvas,
addCanvas,
+ checkModifiedCanvasPlan,
getCanvasByObjectNo,
delCanvasById,
}
diff --git a/src/lib/Axios.js b/src/lib/Axios.js
deleted file mode 100644
index d773d352..00000000
--- a/src/lib/Axios.js
+++ /dev/null
@@ -1,56 +0,0 @@
-'use client'
-
-import axios from 'axios'
-
-axios.defaults.baseURL = process.env.NEXT_PUBLIC_API_SERVER_PATH
-
-const axiosInstance = axios.create({
- // baseURL: process.env.API_SERVER_URL,
- headers: {
- Accept: 'application/json',
- },
-})
-
-axiosInstance.interceptors.request.use((config) => {
- // config['Authorization'] = localStorage.getItem('token')
- //TODO: 인터셉터에서 추가 로직 구현
- return config
-})
-
-axiosInstance.interceptors.request.use(undefined, (error) => {
- //TODO: 인터셉터에서 에러 처리 로직 구현
- // if (error.isAxiosError && e.response?.status === 401) {
- // localStorage.removeItem('token')
- // }
-})
-
-export const get = ({ url }) =>
- axiosInstance
- .get(url)
- .then((res) => res.data)
- .catch(console.error)
-
-export const post = ({ url, data }) =>
- axiosInstance
- .post(url, data)
- .then((res) => res.data)
- .catch(console.error)
-
-export const put = ({ url, data }) =>
- axiosInstance
- .put(url, data)
- .then((res) => res.data)
- .catch(console.error)
-
-export const patch = ({ url, data }) =>
- axiosInstance
- .patch(url, data)
-
- .then((res) => res.data)
- .catch(console.error)
-
-export const del = ({ url }) =>
- axiosInstance
- .delete(url)
- .then((res) => res.data)
- .catch(console.error)
diff --git a/src/locales/ja.json b/src/locales/ja.json
index 4337ad21..5250a0fa 100644
--- a/src/locales/ja.json
+++ b/src/locales/ja.json
@@ -39,6 +39,7 @@
"plan.menu.roof.cover": "지붕덮개",
"plan.menu.roof.cover.outline.drawing": "外壁線を描",
"plan.menu.roof.cover.roof.shape.setting": "屋根形状設定",
+ "plan.menu.roof.cover.roof.shape.passivity.setting": "屋根形状設定",
"plan.menu.roof.cover.roof.shape.edit": "지붕형상 편집",
"plan.menu.roof.cover.auxiliary.line.drawing": "補助線を描",
"plan.menu.roof.cover.roof.surface.alloc": "지붕면 할당",
@@ -144,6 +145,15 @@
"modal.canvas.setting.wallline.properties.setting.info": "※属性を変更する外壁線を選択し、軒で設定またはケラバで設定 ボタンをクリックして設定値を適用します。",
"modal.canvas.setting.wallline.properties.setting.eaves": "軒で設定",
"modal.canvas.setting.wallline.properties.setting.edge": "ケラバに設定",
+ "modal.eaves.gable.edit": "軒・ケラバ変更",
+ "modal.eaves.gable.edit.basic": "通常",
+ "modal.eaves.gable.edit.wall.merge.info": "家屋などの壁に面する屋根を作成します。",
+ "modal.wallline.offset.setting": "外壁の編集とオフセット",
+ "modal.wallline.offset.setting.wallline.edit": "外壁の編集",
+ "modal.wallline.offset.setting.wallline.edit.info": "辺と始点を選択して長さと方向を指定してください。",
+ "modal.wallline.offset.setting.wallline.edit.position": "支店",
+ "modal.wallline.offset.setting.offset": "オフセット",
+ "modal.wallline.offset.setting.offset.info": "オフセットしたい外壁を選択してください。",
"setting": "設定",
"common.message.no.data": "No data",
"common.message.no.dataDown": "ダウンロードするデータがありません",
@@ -285,26 +295,29 @@
"join.complete.contents": "※ 신청한 ID가 승인되면, 담당자 정보에 입력한 이메일 주소로 로그인 관련 안내 메일이 전송됩니다.",
"join.complete.email_comment": "담당자 이메일 주소",
"join.complete.email": "test@naver.com",
- "stuff.gridHeader.lastEditDatetime": "갱신일시",
- "stuff.gridHeader.objectNo": "물건번호",
- "stuff.gridHeader.planTotCnt": "플랜 수",
- "stuff.gridHeader.objectName": "물건명",
- "stuff.gridHeader.saleStoreId": "대리점ID",
- "stuff.gridHeader.saleStoreName": "대리점명",
- "stuff.gridHeader.address": "물건주소",
- "stuff.gridHeader.dispCompanyName": "견적처",
- "stuff.gridHeader.receiveUser": "담당자",
- "stuff.gridHeader.specDate": "사양확인",
- "stuff.gridHeader.createDatetime": "등록일",
+ "stuff.gridHeader.lastEditDatetime": "更新日時",
+ "stuff.gridHeader.objectNo": "品番",
+ "stuff.gridHeader.planTotCnt": "プラン数",
+ "stuff.gridHeader.objectName": "商品名",
+ "stuff.gridHeader.saleStoreId": "代理店ID",
+ "stuff.gridHeader.saleStoreName": "代理店名",
+ "stuff.gridHeader.address": "商品アドレス",
+ "stuff.gridHeader.dispCompanyName": "見積もり",
+ "stuff.gridHeader.receiveUser": "担当者",
+ "stuff.gridHeader.specDate": "仕様確認日",
+ "stuff.gridHeader.createDatetime": "登録日",
+ "stuff.message.periodError": "最大1年間閲覧可能.",
"length": "長さ",
"slope": "傾斜",
"eaves.offset": "軒の",
"gable.offset": "ケラバ出幅",
+ "offset": "出幅",
"size": "寸",
"size.angle": "寸(度)",
"eaves": "軒",
"gable": "ケラバ",
"wall": "壁",
+ "wall.merge": "壁取り",
"hajebichi": "ハゼビーチ",
"straight.line": "直線",
"right.angle": "直角",
@@ -321,5 +334,6 @@
"jerkinhead.width": "半折先幅",
"jerkinhead.slope": "半折先傾斜",
"shed.width": "片流幅",
+ "windage": "漂流",
"windage.width": "漂流の出幅"
}
diff --git a/src/locales/ko.json b/src/locales/ko.json
index 167aaa11..d29df3d0 100644
--- a/src/locales/ko.json
+++ b/src/locales/ko.json
@@ -151,6 +151,15 @@
"modal.canvas.setting.wallline.properties.setting.info": "※ 속성을 변경할 외벽선을 선택하고, 처마로 설정 또는 케라바로 설정\n 버튼을 클릭하여 설정값을 적용하십시오.\n",
"modal.canvas.setting.wallline.properties.setting.eaves": "처마로 설정",
"modal.canvas.setting.wallline.properties.setting.edge": "케라바로 설정",
+ "modal.eaves.gable.edit": "처마・케라바 변경",
+ "modal.eaves.gable.edit.basic": "통상",
+ "modal.eaves.gable.edit.wall.merge.info": "하옥 등 벽에 접하는 지붕을 작성합니다.",
+ "modal.wallline.offset.setting": "외벽선 편집 및 오프셋",
+ "modal.wallline.offset.setting.wallline.edit": "외벽선 편집",
+ "modal.wallline.offset.setting.wallline.edit.info": "변과 시작점을 선택하고 길이와 방향을 지정해 주세요.",
+ "modal.wallline.offset.setting.wallline.edit.position": "지점",
+ "modal.wallline.offset.setting.offset": "오프셋",
+ "modal.wallline.offset.setting.offset.info": "오프셋 하고 싶은 외벽선을 선택하세요.",
"setting": "설정",
"common.message.no.data": "No data",
"common.message.no.dataDown": "No data to download",
@@ -301,17 +310,20 @@
"stuff.gridHeader.address": "물건주소",
"stuff.gridHeader.dispCompanyName": "견적처",
"stuff.gridHeader.receiveUser": "담당자",
- "stuff.gridHeader.specDate": "사양확인",
+ "stuff.gridHeader.specDate": "사양확인일",
"stuff.gridHeader.createDatetime": "등록일",
+ "stuff.message.periodError": "최대1년 조회 가능합니다.",
"length": "길이",
"slope": "경사",
"eaves.offset": "처마 출폭",
"gable.offset": "케라바 출폭",
+ "offset": "출폭",
"size": "치수",
"size.angle": "寸(度)",
"eaves": "처마",
"gable": "케라바",
"wall": "벽",
+ "wall.merge": "벽취합",
"hajebichi": "하제비치",
"straight.line": "직선",
"right.angle": "직각",
@@ -328,5 +340,6 @@
"jerkinhead.width": "반절처 폭",
"jerkinhead.slope": "반절처 경사",
"shed.width": "한쪽흐름 폭",
+ "windage": "편류",
"windage.width": "편류의 출폭"
}
diff --git a/src/store/stuffAtom.js b/src/store/stuffAtom.js
index 5de2c04e..c47321ae 100644
--- a/src/store/stuffAtom.js
+++ b/src/store/stuffAtom.js
@@ -1,16 +1,13 @@
import { atom } from 'recoil'
import dayjs from 'dayjs'
-import isLeapYear from 'dayjs/plugin/isLeapYear' // 윤년 판단 플러그인
-dayjs.extend(isLeapYear)
+import { v1 } from 'uuid'
export const stuffSearchState = atom({
- key: 'stuffSearchState',
+ key: `stuffSearchState/${v1()}`,
default: {
schObjectNo: '', //물건번호
- schSaleStoreId: '', //판매대리점ID
schAddress: '', //물건주소
schObjectName: '', //물건명
schSaleStoreName: '', //판매대리점명
- schSpecDateYn: '', //사양타입 ('', 'Y', 'N')
schReceiveUser: '', //담당자
schDispCompanyName: '', //견적처
schDateType: 'U', //갱신일(U)/등록일(R)
diff --git a/src/styles/_contents.scss b/src/styles/_contents.scss
index 3d503b0b..034955f5 100644
--- a/src/styles/_contents.scss
+++ b/src/styles/_contents.scss
@@ -842,4 +842,85 @@
color: #344356;
font-weight: 500;
}
+}
+
+// 패널 배치 집계
+
+.penal-wrap{
+ position: fixed;
+ top: 200px;
+ left: 50px;
+ z-index: 999999;
+ width: 237px;
+ height: 40px;
+ line-height: 40px;
+ background-color: #fff;
+ border: 1px solid #DFDFDF;
+ padding: 0 34px 0 10px;
+ border-radius: 2px;
+ box-shadow: 0px 7px 14px 0px rgba(0, 0, 0, 0.05);
+ cursor: pointer;
+ &::before{
+ content: '';
+ position: absolute;
+ top: 50%;
+ right: 12px;
+ transform: translateY(-50%);
+ width: 10px;
+ height: 6px;
+ background: url(../../public/static/images/canvas/penal_arr.svg)no-repeat center;
+ background-size: cover;
+ }
+ h2{
+ font-size: 12px;
+ font-weight: 500;
+ color: #3D3D3D;
+ }
+ .penal-table-wrap{
+ display: none;
+ position: absolute;
+ top: 100%;
+ left: -1px;
+ min-width: calc(100% + 2px);
+ background-color: #3D3D3D;
+ border: 1px solid #3D3D3D;
+ padding: 20px;
+ .penal-table{
+ table-layout: fixed;
+ border-collapse: collapse;
+ thead{
+ th{
+ text-align: center;
+ background-color:rgba(255, 255, 255, 0.05);
+ font-size: 12px;
+ font-weight: 500;
+ color: #fff;
+ border: 1px solid #505050;
+ }
+ }
+ tbody{
+ td{
+ font-size: 12px;
+ color: #fff;
+ font-weight: 400;
+ text-align: center;
+ padding: 0 10px;
+ border: 1px solid #505050;
+ }
+ }
+ }
+ }
+ &.act{
+ border: 1px solid #3D3D3D;
+ background-color: #3D3D3D;
+ h2{
+ color: #fff;
+ }
+ &::before{
+ background: url(../../public/static/images/canvas/penal_arr_white.svg)no-repeat center;
+ }
+ .penal-table-wrap{
+ display: block;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/styles/_modal.scss b/src/styles/_modal.scss
index be8b4185..705550d5 100644
--- a/src/styles/_modal.scss
+++ b/src/styles/_modal.scss
@@ -77,6 +77,10 @@ $alert-color: #101010;
width: 640px;
}
+ &.lx-2 {
+ width: 740px;
+ }
+
&.lx {
width: 770px;
}
@@ -1543,3 +1547,115 @@ $alert-color: #101010;
}
}
}
+
+
+// 지붕모듈선택
+.roof-module-tab {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 14px;
+
+ .module-tab-bx {
+ flex: 1;
+ height: 34px;
+ line-height: 31px;
+ border: 1px solid #484848;
+ border-radius: 2px;
+ background-color: transparent;
+ font-size: 12px;
+ color: #AAA;
+ text-align: center;
+ cursor: default;
+ transition: all .15s ease-in-out;
+
+ &.act {
+ background-color: #1083E3;
+ border: 1px solid #1083E3;
+ color: #fff;
+ font-weight: 500;
+ }
+ }
+
+ .tab-arr {
+ display: block;
+ width: 9px;
+ height: 14px;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: cover;
+ background-image: url(../../public/static/images/canvas/module_tab_arr.svg);
+ transition: all .15s ease-in-out;
+
+ &.act {
+ background-image: url(../../public/static/images/canvas/module_tab_arr_white.svg);
+ }
+ }
+}
+
+.roof-module-compas {
+ margin-bottom: 24px;
+
+ .compas-box-inner {
+ width: 280px;
+ height: 253px;
+
+ .circle {
+ top: 86%;
+
+ &:nth-child(1),
+ &:nth-child(7),
+ &:nth-child(13),
+ &:nth-child(19) {
+ &::before {
+ content: '';
+ position: absolute;
+ top: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 1px;
+ height: 6px;
+ background-color: #8B8B8B;
+ }
+ }
+
+ i {
+ top: 32px;
+ }
+
+ &.act {
+ i {
+ color: #8B8B8B;
+ }
+ }
+ }
+ }
+}
+
+.center-wrap {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 20px;
+}
+
+.module-table-flex-wrap {
+ display: flex;
+ gap: 10px;
+}
+
+.module-table-box {
+ flex: 1;
+ background-color: #3D3D3D;
+ border-radius: 2px;
+
+ .module-table-inneer {
+ padding: 10px;
+
+ .outline-form {
+ span {
+ width: auto;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/styles/_reset.scss b/src/styles/_reset.scss
index 6d899458..7804b800 100644
--- a/src/styles/_reset.scss
+++ b/src/styles/_reset.scss
@@ -144,6 +144,12 @@ button{
.ml10{margin-left: 10px !important;}
.ml15{margin-left: 15px !important;}
+// align
+.al-l{text-align: left !important;}
+.al-r{text-align: right !important;}
+.al-c{text-align: center !important;}
+
+
// button
.btn-frame{
display: inline-block;
@@ -410,6 +416,11 @@ input[type=text]{
&:read-only{
color: #AAA;
}
+ &.plane{
+ font-family: 'Noto Sans JP', sans-serif;
+ border: 1px solid #525252;
+ background-color: transparent;
+ }
}
&.input-light{
display: block;
diff --git a/src/styles/_table.scss b/src/styles/_table.scss
index d3c207a7..a87bfc5b 100644
--- a/src/styles/_table.scss
+++ b/src/styles/_table.scss
@@ -174,4 +174,32 @@ table{
}
}
}
+}
+
+.roof-module-table{
+ table{
+ border-collapse: collapse;
+ thead{
+ th{
+ height: 40px;
+ padding: 0 10px;
+ font-size: 12px;
+ color: #fff;
+ font-weight: 500;
+ border: 1px solid #505050;
+ vertical-align: middle;
+ background-color: rgba(255, 255, 255, 0.05);
+ text-align: center;
+ word-break: keep-all;
+ }
+ }
+ tbody{
+ td{
+ font-size: 12px;
+ color: #fff;
+ font-weight: 400;
+ border: 1px solid #505050;
+ }
+ }
+ }
}
\ No newline at end of file