Compare commits
No commits in common. "d341b4a52deb24b1c708186c792defc298ce48f6" and "b7ba6d8fc17ab599144e93350d8b821922ee7796" have entirely different histories.
d341b4a52d
...
b7ba6d8fc1
@ -8,7 +8,6 @@ import { useRecoilState } from 'recoil'
|
|||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
import Cookies from 'js-cookie'
|
|
||||||
|
|
||||||
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
import GlobalSpinner from '@/components/common/spinner/GlobalSpinner'
|
||||||
|
|
||||||
@ -32,7 +31,6 @@ export default function AutoLoginPage({ autoLoginParam }) {
|
|||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
const res = response.data
|
const res = response.data
|
||||||
if (res.sessionId) Cookies.set('sessionId', res.sessionId)
|
|
||||||
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
|
const result = { ...res, storeLvl: res.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
|
||||||
setSession(result)
|
setSession(result)
|
||||||
setSessionState(result)
|
setSessionState(result)
|
||||||
|
|||||||
@ -48,7 +48,6 @@ export default function Login() {
|
|||||||
post({ url: '/api/login/v1.0/user', data: { loginId: res.data } }).then((response) => {
|
post({ url: '/api/login/v1.0/user', data: { loginId: res.data } }).then((response) => {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
if (response) {
|
if (response) {
|
||||||
if (response.sessionId) Cookies.set('sessionId', response.sessionId)
|
|
||||||
const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
|
const result = { ...response, storeLvl: response.groupId === '60000' ? '1' : '2', pwdInitYn: 'Y' }
|
||||||
setSession(result)
|
setSession(result)
|
||||||
setSessionState(result)
|
setSessionState(result)
|
||||||
@ -108,7 +107,6 @@ export default function Login() {
|
|||||||
if (res) {
|
if (res) {
|
||||||
setIsLoading(false)
|
setIsLoading(false)
|
||||||
if (res.data.result.resultCode === 'S') {
|
if (res.data.result.resultCode === 'S') {
|
||||||
if (res.data.sessionId) Cookies.set('sessionId', res.data.sessionId)
|
|
||||||
setSession(res.data.data)
|
setSession(res.data.data)
|
||||||
setSessionState(res.data.data)
|
setSessionState(res.data.data)
|
||||||
// ID SAVE 체크되어 있는 경우, 쿠키 저장
|
// ID SAVE 체크되어 있는 경우, 쿠키 저장
|
||||||
|
|||||||
@ -8,8 +8,7 @@ import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
|||||||
import { dimmedStore, sessionStore } from '@/store/commonAtom'
|
import { dimmedStore, sessionStore } from '@/store/commonAtom'
|
||||||
|
|
||||||
import { useMessage } from '@/hooks/useMessage'
|
import { useMessage } from '@/hooks/useMessage'
|
||||||
import { checkSession } from '@/lib/authActions'
|
import { checkSession, logout } from '@/lib/authActions'
|
||||||
import { useLogout } from '@/hooks/useLogout'
|
|
||||||
|
|
||||||
import QSelectBox from '@/components/common/select/QSelectBox'
|
import QSelectBox from '@/components/common/select/QSelectBox'
|
||||||
|
|
||||||
@ -50,7 +49,6 @@ export default function Header(props) {
|
|||||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
||||||
|
|
||||||
const { closeAll } = usePopup()
|
const { closeAll } = usePopup()
|
||||||
const { logoutProcess } = useLogout()
|
|
||||||
|
|
||||||
const { userSession } = props
|
const { userSession } = props
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||||
@ -374,7 +372,14 @@ export default function Header(props) {
|
|||||||
<div className="sign-out-box">
|
<div className="sign-out-box">
|
||||||
<button
|
<button
|
||||||
className="sign-out"
|
className="sign-out"
|
||||||
onClick={() => logoutProcess()}
|
onClick={() => {
|
||||||
|
setStuffSearch({
|
||||||
|
...stuffSearch,
|
||||||
|
code: 'DELETE',
|
||||||
|
})
|
||||||
|
logout()
|
||||||
|
router.replace('/login', undefined, { shallow: true })
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{getMessage('header.logout')}
|
{getMessage('header.logout')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -5,10 +5,10 @@ import { sessionStore } from '@/store/commonAtom'
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
import { useAxios } from '@/hooks/useAxios'
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
import { globalLocaleStore } from '@/store/localeAtom'
|
||||||
import { login, setSession } from '@/lib/authActions'
|
import { login, logout, setSession } from '@/lib/authActions'
|
||||||
import { useSwal } from '@/hooks/useSwal'
|
import { useSwal } from '@/hooks/useSwal'
|
||||||
import { QcastContext } from '@/app/QcastProvider'
|
import { QcastContext } from '@/app/QcastProvider'
|
||||||
import { useLogout } from '@/hooks/useLogout'
|
import { useRouter } from 'next/navigation'
|
||||||
|
|
||||||
export default function ChangePasswordPop(props) {
|
export default function ChangePasswordPop(props) {
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
||||||
@ -19,7 +19,7 @@ export default function ChangePasswordPop(props) {
|
|||||||
const { patch } = useAxios(globalLocaleState)
|
const { patch } = useAxios(globalLocaleState)
|
||||||
const { getMessage } = useMessage()
|
const { getMessage } = useMessage()
|
||||||
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
const [sessionState, setSessionState] = useRecoilState(sessionStore)
|
||||||
const { logoutProcess } = useLogout()
|
const router = useRouter()
|
||||||
const formInitValue = {
|
const formInitValue = {
|
||||||
password1: '',
|
password1: '',
|
||||||
password2: '',
|
password2: '',
|
||||||
@ -129,13 +129,15 @@ export default function ChangePasswordPop(props) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
logoutProcess()
|
logout()
|
||||||
|
router.replace('/login', undefined, { shallow: true })
|
||||||
console.log('code not 200 error')
|
console.log('code not 200 error')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
setIsGlobalLoading(false)
|
setIsGlobalLoading(false)
|
||||||
logoutProcess()
|
logout()
|
||||||
|
router.replace('/login', undefined, { shallow: true })
|
||||||
console.log('catch::::::::', error)
|
console.log('catch::::::::', error)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -207,7 +209,10 @@ export default function ChangePasswordPop(props) {
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="btn-origin grey"
|
className="btn-origin grey"
|
||||||
onClick={() => logoutProcess()}
|
onClick={() => {
|
||||||
|
logout()
|
||||||
|
router.replace('/login', undefined, { shallow: true })
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{getMessage('main.popup.login.btn2')}
|
{getMessage('main.popup.login.btn2')}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@ -1868,7 +1868,7 @@ export default function StuffDetail() {
|
|||||||
})}
|
})}
|
||||||
{/* 라디오끝 */}
|
{/* 라디오끝 */}
|
||||||
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
||||||
<input type="text" className="input-light" maxLength={50} {...form.register('objectName')} />
|
<input type="text" className="input-light" {...form.register('objectName')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="select-wrap" style={{ width: '120px' }}>
|
<div className="select-wrap" style={{ width: '120px' }}>
|
||||||
<Select
|
<Select
|
||||||
@ -2461,7 +2461,7 @@ export default function StuffDetail() {
|
|||||||
})}
|
})}
|
||||||
{/* 상세라디오끝 */}
|
{/* 상세라디오끝 */}
|
||||||
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
<div className="input-wrap mr5" style={{ width: '545px' }}>
|
||||||
<input type="text" className="input-light" maxLength={50} {...form.register('objectName')} />
|
<input type="text" className="input-light" {...form.register('objectName')} />
|
||||||
</div>
|
</div>
|
||||||
<div className="select-wrap" style={{ width: '120px' }}>
|
<div className="select-wrap" style={{ width: '120px' }}>
|
||||||
<Select
|
<Select
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
|
||||||
import { sessionStore } from '@/store/commonAtom'
|
|
||||||
import { globalLocaleStore } from '@/store/localeAtom'
|
|
||||||
import { stuffSearchState } from '@/store/stuffAtom'
|
|
||||||
import { useAxios } from '@/hooks/useAxios'
|
|
||||||
import { logout } from '@/lib/authActions'
|
|
||||||
import Cookies from 'js-cookie'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 로그아웃 처리 Hook
|
|
||||||
* - 로그아웃 API 호출 후 성공/실패 상관없이 로그아웃 처리
|
|
||||||
* - Header, ChangePasswordPop 등에서 공통으로 사용
|
|
||||||
*/
|
|
||||||
export function useLogout() {
|
|
||||||
const [sessionState] = useRecoilState(sessionStore)
|
|
||||||
const [stuffSearch, setStuffSearch] = useRecoilState(stuffSearchState)
|
|
||||||
const globalLocaleState = useRecoilValue(globalLocaleStore)
|
|
||||||
const { promisePost } = useAxios(globalLocaleState)
|
|
||||||
|
|
||||||
const logoutProcess = async () => {
|
|
||||||
const param = {
|
|
||||||
loginId: sessionState.userId,
|
|
||||||
requestId: Cookies.get('sessionId'), // 로그인 시 저장한 sessionId
|
|
||||||
}
|
|
||||||
|
|
||||||
// 로그아웃 API 호출 (실패해도 로그아웃 진행)
|
|
||||||
try {
|
|
||||||
await promisePost({ url: '/api/login/v1.0/logout', data: param })
|
|
||||||
} catch (error) {
|
|
||||||
console.log('logout api error:', error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 물건검색 상태 초기화
|
|
||||||
setStuffSearch({
|
|
||||||
...stuffSearch,
|
|
||||||
code: 'DELETE',
|
|
||||||
})
|
|
||||||
Cookies.remove('sessionId') // sessionId 쿠키 삭제
|
|
||||||
await logout() // iron-session 세션 파기
|
|
||||||
window.location.href = '/login' // 로그인 페이지로 이동 (full reload)
|
|
||||||
}
|
|
||||||
|
|
||||||
return { logoutProcess }
|
|
||||||
}
|
|
||||||
@ -253,12 +253,13 @@ export default class SkeletonBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static CorrectBisectorDirection(bisector: LineParametric2d, beginNextVertex: Vertex, endPreviousVertex: Vertex, beginEdge: Edge, endEdge: Edge) {
|
private static CorrectBisectorDirection(bisector: LineParametric2d, beginNextVertex: Vertex, endPreviousVertex: Vertex, beginEdge: Edge, endEdge: Edge) {
|
||||||
// Use the vertex's actual edges — chain edges can become stale
|
const beginEdge2 = beginNextVertex.PreviousEdge;
|
||||||
// when previous iterations of MultiSplitEvent modify the LAV
|
const endEdge2 = endPreviousVertex.NextEdge;
|
||||||
const actualBeginEdge = beginNextVertex.PreviousEdge;
|
|
||||||
const actualEndEdge = endPreviousVertex.NextEdge;
|
|
||||||
|
|
||||||
if (actualBeginEdge.Norm.Dot(actualEndEdge.Norm) < -0.97) {
|
if (beginEdge !== beginEdge2 || endEdge !== endEdge2)
|
||||||
|
throw new Error();
|
||||||
|
|
||||||
|
if (beginEdge.Norm.Dot(endEdge.Norm) < -0.97) {
|
||||||
const n1 = PrimitiveUtils.FromTo(endPreviousVertex.Point, bisector.A).Normalized();
|
const n1 = PrimitiveUtils.FromTo(endPreviousVertex.Point, bisector.A).Normalized();
|
||||||
const n2 = PrimitiveUtils.FromTo(bisector.A, beginNextVertex.Point).Normalized();
|
const n2 = PrimitiveUtils.FromTo(bisector.A, beginNextVertex.Point).Normalized();
|
||||||
const bisectorPrediction = this.CalcVectorBisector(n1, n2);
|
const bisectorPrediction = this.CalcVectorBisector(n1, n2);
|
||||||
@ -886,7 +887,7 @@ export default class SkeletonBuilder {
|
|||||||
private static GetEdgeInLav(lav: CircularList<Vertex>, oppositeEdge: Edge): Vertex {
|
private static GetEdgeInLav(lav: CircularList<Vertex>, oppositeEdge: Edge): Vertex {
|
||||||
for (const node of lav.Iterate())
|
for (const node of lav.Iterate())
|
||||||
if (oppositeEdge === node.PreviousEdge ||
|
if (oppositeEdge === node.PreviousEdge ||
|
||||||
oppositeEdge === (node.Previous as Vertex).NextEdge)
|
oppositeEdge === node.Previous.Next)
|
||||||
return node;
|
return node;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@ -16,39 +16,6 @@ import wallLine from '@/components/floor-plan/modal/wallLineOffset/type/WallLine
|
|||||||
*/
|
*/
|
||||||
const EPSILON = 0.1
|
const EPSILON = 0.1
|
||||||
|
|
||||||
/**
|
|
||||||
* 오목(concave) 폴리곤인지 판별합니다.
|
|
||||||
* cross product의 부호가 혼재하면 오목 폴리곤입니다.
|
|
||||||
*/
|
|
||||||
const isConcavePolygon = (points) => {
|
|
||||||
if (!points || points.length < 4) return false
|
|
||||||
let positive = 0, negative = 0
|
|
||||||
for (let i = 0; i < points.length; i++) {
|
|
||||||
const prev = points[(i - 1 + points.length) % points.length]
|
|
||||||
const curr = points[i]
|
|
||||||
const next = points[(i + 1) % points.length]
|
|
||||||
const cross = (curr.x - prev.x) * (next.y - curr.y) - (curr.y - prev.y) * (next.x - curr.x)
|
|
||||||
if (cross > 0) positive++
|
|
||||||
else if (cross < 0) negative++
|
|
||||||
}
|
|
||||||
return positive > 0 && negative > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 오목 폴리곤에만 미세한 perturbation을 적용하여 동시 이벤트(MultiSplitEvent)를 방지합니다.
|
|
||||||
* 대칭적 변 길이(예: 45.5, 45.5)가 동일 거리의 이벤트를 만들어 LAV 불일치 오류를 유발하므로
|
|
||||||
* 각 꼭짓점에 인덱스 기반 미세 오프셋을 적용하면 이벤트가 개별 SplitEvent로 처리됩니다.
|
|
||||||
* 볼록 폴리곤은 이 문제가 없으므로 원본을 그대로 반환합니다.
|
|
||||||
*/
|
|
||||||
const perturbPolygonPoints = (points, eps = 1e-4) => {
|
|
||||||
if (!points || points.length < 3) return points
|
|
||||||
if (!isConcavePolygon(points)) return points
|
|
||||||
return points.map((p, i) => ({
|
|
||||||
x: p.x + (i + 1) * eps,
|
|
||||||
y: p.y + (i + 1) * eps
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => {
|
export const drawSkeletonRidgeRoof = (roofId, canvas, textMode) => {
|
||||||
|
|
||||||
// 2. 스켈레톤 생성 및 그리기
|
// 2. 스켈레톤 생성 및 그리기
|
||||||
@ -536,13 +503,11 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
// changRoofLinePoints를 roof.skeletonPoints에 저장 (roof.points 원본은 유지)
|
// changRoofLinePoints를 roof.skeletonPoints에 저장 (roof.points 원본은 유지)
|
||||||
roof.skeletonPoints = changRoofLinePoints.map(p => ({ x: p.x, y: p.y }))
|
roof.skeletonPoints = changRoofLinePoints.map(p => ({ x: p.x, y: p.y }))
|
||||||
|
|
||||||
const perturbedPoints = perturbPolygonPoints(changRoofLinePoints)
|
const geoJSONPolygon = toGeoJSON(changRoofLinePoints)
|
||||||
const geoJSONPolygon = toGeoJSON(perturbedPoints)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
|
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
|
||||||
geoJSONPolygon.pop()
|
geoJSONPolygon.pop()
|
||||||
console.log('[SkeletonBuilder] geoJSONPolygon:', JSON.stringify(geoJSONPolygon, null, 2))
|
|
||||||
const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]])
|
const skeleton = SkeletonBuilder.BuildFromGeoJSON([[geoJSONPolygon]])
|
||||||
|
|
||||||
// 스켈레톤 데이터를 기반으로 내부선 생성
|
// 스켈레톤 데이터를 기반으로 내부선 생성
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user