From 5be7237aa634ecd204d90f3ebe18f00e7180555c Mon Sep 17 00:00:00 2001 From: Daseul Kim Date: Thu, 29 May 2025 10:07:05 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=A7=80=EB=B6=95=EC=9E=AC?= =?UTF-8?q?=EC=A0=81=ED=95=A9=EC=84=B1=20=EC=83=81=EC=84=B8=ED=8C=9D?= =?UTF-8?q?=EC=97=85=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=91=9C=EC=B6=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20TODO=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +-- src/app/api/suitable/route.ts | 30 ++-- src/components/popup/SuitableDetailPopup.tsx | 141 ++++++------------ .../popup/SuitableDetailPopupButton.tsx | 16 +- src/components/suitable/SuitableList.tsx | 32 ++-- src/hooks/useSuitable.ts | 36 ++++- 6 files changed, 132 insertions(+), 147 deletions(-) diff --git a/README.md b/README.md index 6d332e0..c6869cd 100644 --- a/README.md +++ b/README.md @@ -64,15 +64,21 @@ session에 있는 role 키로 구분한다 # 지붕재 적합성 TODO ``` -const suitableCheck = (value: string) => { - if (value === '×') { - return - } else if (value === 'ー') { - return - } else { - return - } +const suitableCheckIcon = (value: string): string => { + const iconMap: Record = { + '×': '/assets/images/sub/compliance_x_icon.svg', + 'ー': '/assets/images/sub/compliance_quest_icon.svg', + default: '/assets/images/sub/compliance_check_icon.svg', } + return iconMap[value] || iconMap.default +} +const suitableCheckMemo = (value: string): string => { + if (value === '○') return '設置可' + if (value === '×') return '設置不可' + if (value === 'ー') return 'お問い合わせください' + return `${value}で設置可` +} ``` -- 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요 +- src/hooks/useSuitable.ts > suitableCheckIcon(), suitableCheckMemo() + - 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요 diff --git a/src/app/api/suitable/route.ts b/src/app/api/suitable/route.ts index df42e1e..173b6ea 100644 --- a/src/app/api/suitable/route.ts +++ b/src/app/api/suitable/route.ts @@ -2,12 +2,15 @@ import { NextRequest, NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' import { Suitable } from '@/types/Suitable' -export async function GET(request: NextRequest) { +export async function POST(request: NextRequest) { try { - const searchParams = request.nextUrl.searchParams + const body: Record = await request.json() + const ids = body.ids + const detailIds = body.detailIds - const ids = searchParams.get('ids') - const detailIds = searchParams.get('subIds') + if (ids === '' || detailIds === '') { + return NextResponse.json({ error: '필수 파라미터가 누락되었습니다' }, { status: 400 }) + } let query = ` SELECT @@ -29,28 +32,22 @@ export async function GET(request: NextRequest) { , msd_json.memo FROM ms_suitable_detail msd_json WHERE msd.main_id = msd_json.main_id + AND msd_json.id IN (:detailIds) FOR JSON PATH ) AS detail FROM ms_suitable_detail msd GROUP BY msd.main_id ) AS details ON msm.id = details.main_id - --ids AND details.main_id IN (:mainIds) - --detailIds AND details.id IN (:detailIds) - WHERE 1=1 - --ids AND msm.id IN (:mainIds) + AND details.main_id IN (:mainIds) + WHERE + msm.id IN (:mainIds) ORDER BY msm.product_name; ` // 검색 조건 설정 - if (ids) { - query = query.replaceAll('--ids ', '') - query = query.replaceAll(':mainIds', ids) - if (detailIds) { - query = query.replaceAll('--detailIds ', '') - query = query.replaceAll(':detailIds', detailIds) - } - } + query = query.replaceAll(':mainIds', ids) + query = query.replaceAll(':detailIds', detailIds) const suitable: Suitable[] = await prisma.$queryRawUnsafe(query) @@ -60,4 +57,3 @@ export async function GET(request: NextRequest) { return NextResponse.json({ error: '데이터 조회 중 오류가 발생했습니다' }, { status: 500 }) } } - diff --git a/src/components/popup/SuitableDetailPopup.tsx b/src/components/popup/SuitableDetailPopup.tsx index 80524d0..4baf4ec 100644 --- a/src/components/popup/SuitableDetailPopup.tsx +++ b/src/components/popup/SuitableDetailPopup.tsx @@ -3,15 +3,13 @@ import Image from 'next/image' import { useCallback, useEffect, useState } from 'react' import { usePopupController } from '@/store/popupController' -import { useSuitableStore } from '@/store/useSuitableStore' import SuitableDetailPopupButton from './SuitableDetailPopupButton' import { useSuitable } from '@/hooks/useSuitable' -import { Suitable } from '@/types/Suitable' +import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable' export default function SuitableDetailPopup() { const popupController = usePopupController() - const { getSuitableDetails, serializeSelectedItems } = useSuitable() - const { selectedItems } = useSuitableStore() + const { getSelectedItemsData, toCodeName, toSuitableDetail, suitableCheckIcon, suitableCheckMemo } = useSuitable() const [openItems, setOpenItems] = useState>(new Set()) const [suitableDetails, setSuitableDetails] = useState([]) @@ -25,14 +23,9 @@ export default function SuitableDetailPopup() { }) }, []) - // 선택된 아이템 상세 데이터 가져오기 - const getSelectedItemsData = async () => { - const serialized: Map = serializeSelectedItems() - setSuitableDetails(await getSuitableDetails(serialized.get('ids') ?? '', serialized.get('detailIds') ?? '')) - } - useEffect(() => { - getSelectedItemsData() + // TODO: 로딩 처리 필요 + getSelectedItemsData().then((data) => setSuitableDetails(data)) }, []) return ( @@ -52,98 +45,56 @@ export default function SuitableDetailPopup() {
-
-
-
アースティ40
-
- -
-
-
-
-
屋根技研 支持瓦
-
㈱ダイトー
-
-
-
屋根材
-
-
-
-
金具タイプ
-
木ねじ打ち込み式
-
-
-
-
-
屋根技研 支持瓦
-
-
- -
-
- -
-
-
-
Dで設置可
-
-
備考
-
- 桟木なしの場合は支持金具平ー1で設置可能。その場合水返しが高い為、レベルプレート使用。桟木ありの場合は支持金具平ー2で設置可能 -
-
+ {suitableDetails.map((item: Suitable) => ( +
+
+
{item.productName}
+
+
-
-
-
屋根技研支持金具
-
-
- -
-
- -
-
-
-
設置不可
-
-
備考
-
入手困難
-
+
+
+
+
屋根技研 支持瓦
+
{toCodeName(SUITABLE_HEAD_CODE.MANU_FT_CD, item.manuFtCd)}
-
-
-
屋根技研YGアンカー
-
-
- -
-
-
-
お問い合わせください
-
-
備考
-
入手困難
-
+
+
屋根材
+
{toCodeName(SUITABLE_HEAD_CODE.ROOF_MT_CD, item.roofMtCd)}
-
-
-
ダイドーハント支持瓦Ⅱ
-
-
- +
+
金具タイプ
+
{toCodeName(SUITABLE_HEAD_CODE.ROOF_SH_CD, item.roofShCd)}
+
+
+ {toSuitableDetail(item.detail).map((subItem: SuitableDetail) => ( +
+
+
{toCodeName(SUITABLE_HEAD_CODE.TRESTLE_MFPC_CD, subItem.trestleMfpcCd)}
+
+
+ +
+ {subItem.memo && ( +
+ +
+ )} +
+
{suitableCheckMemo(subItem.trestleManufacturerProductName)}
+ {subItem.memo && ( +
+
備考
+
{subItem.memo}
+
+ )}
-
-
Ⅳ (D) で設置可
-
-
備考
-
入手困難
-
+ ))}
-
+ ))}
diff --git a/src/components/popup/SuitableDetailPopupButton.tsx b/src/components/popup/SuitableDetailPopupButton.tsx index 52e1bae..01f88ce 100644 --- a/src/components/popup/SuitableDetailPopupButton.tsx +++ b/src/components/popup/SuitableDetailPopupButton.tsx @@ -1,10 +1,16 @@ 'use client' +import { useRouter } from 'next/navigation' +import { usePopupController } from '@/store/popupController' + export default function SuitableDetailPopupButton() { + const popupController = usePopupController() + const router = useRouter() + return (
-
@@ -14,7 +20,13 @@ export default function SuitableDetailPopupButton() {
-
diff --git a/src/components/suitable/SuitableList.tsx b/src/components/suitable/SuitableList.tsx index 9936494..7aaeb5e 100644 --- a/src/components/suitable/SuitableList.tsx +++ b/src/components/suitable/SuitableList.tsx @@ -9,7 +9,17 @@ import { useSuitableStore } from '@/store/useSuitableStore' import { SUITABLE_HEAD_CODE, type Suitable, type SuitableDetail } from '@/types/Suitable' export default function SuitableList() { - const { toCodeName, toSuitableDetail, toSuitableDetailIds, suitables, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = useSuitable() + const { + toCodeName, + toSuitableDetail, + toSuitableDetailIds, + suitables, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + suitableCheckIcon, + } = useSuitable() const { selectedItems, addSelectedItem, removeSelectedItem } = useSuitableStore() const [openItems, setOpenItems] = useState>(new Set()) const observerTarget = useRef(null) @@ -52,20 +62,6 @@ export default function SuitableList() { }) }, []) - // TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요 - const suitableCheck = useCallback((value: string) => { - const iconMap: Record = { - '×': '/assets/images/sub/compliance_x_icon.svg', - ー: '/assets/images/sub/compliance_quest_icon.svg', - default: '/assets/images/sub/compliance_check_icon.svg', - } - return ( -
- -
- ) - }, []) - // 아이템 렌더링 const renderItem = useCallback( (item: Suitable) => { @@ -99,7 +95,9 @@ export default function SuitableList() {
- {suitableCheck(subItem.trestleManufacturerProductName)} +
+ +
{subItem.memo && (
@@ -113,7 +111,7 @@ export default function SuitableList() {
) }, - [isItemSelected, openItems, handleItemClick, toggleItemOpen, suitableCheck, toCodeName, toSuitableDetail], + [isItemSelected, openItems, handleItemClick, toggleItemOpen, toCodeName, toSuitableDetail], ) // 아이템 리스트 diff --git a/src/hooks/useSuitable.ts b/src/hooks/useSuitable.ts index 5acad45..23b89b5 100644 --- a/src/hooks/useSuitable.ts +++ b/src/hooks/useSuitable.ts @@ -62,7 +62,7 @@ export function useSuitable() { try { const params: Record = { ids: ids } if (detailIds) params.detailIds = detailIds - const response = await axiosInstance(null).get('/api/suitable', { params }) + const response = await axiosInstance(null).post('/api/suitable', params) return response.data } catch (error) { console.error('지붕재 상세 데이터 로드 실패:', error) @@ -134,17 +134,19 @@ export function useSuitable() { enabled: selectedCategory !== '' || searchKeyword !== '', }) - const serializeSelectedItems = (): Map => { + const serializeSelectedItems = (): { ids: string; detailIds: string } => { const ids: string[] = [] const detailIds: string[] = [] for (const [key, value] of selectedItems) { ids.push(String(key)) for (const id of value) detailIds.push(String(id)) } - return new Map([ - ['ids', ids.join(',')], - ['detailIds', detailIds.join(',')], - ]) + return { ids: ids.join(','), detailIds: detailIds.length > 0 ? detailIds.join(',') : '' } + } + + const getSelectedItemsData = async (): Promise => { + const { ids, detailIds } = serializeSelectedItems() + return await getSuitableDetails(ids, detailIds) } const clearSuitableSearch = ({ items = false, category = false, keyword = false }: { items?: boolean; category?: boolean; keyword?: boolean }) => { @@ -153,6 +155,24 @@ export function useSuitable() { if (keyword) clearSearchKeyword() } + // TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요 + const suitableCheckIcon = (value: string): string => { + const iconMap: Record = { + '×': '/assets/images/sub/compliance_x_icon.svg', + 'ー': '/assets/images/sub/compliance_quest_icon.svg', + default: '/assets/images/sub/compliance_check_icon.svg', + } + return iconMap[value] || iconMap.default + } + + // TODO: 추후 지붕재 적합성 데이터 CUD 구현 시 ○, ×, ー 데이터 관리 필요 + const suitableCheckMemo = (value: string): string => { + if (value === '○') return '設置可' + if (value === '×') return '設置不可' + if (value === 'ー') return 'お問い合わせください' + return `${value}で設置可` + } + return { getSuitables, getSuitableIds, @@ -166,7 +186,9 @@ export function useSuitable() { hasNextPage, isFetchingNextPage, isLoading, - serializeSelectedItems, + getSelectedItemsData, clearSuitableSearch, + suitableCheckIcon, + suitableCheckMemo, } } From f1617045a5147ad4801a5471f2ecd0bd6e785273 Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 29 May 2025 10:23:35 +0900 Subject: [PATCH 2/3] chore: update environment files to use localhost for API URLs and add nodemailer types to package-lock --- .env.development | 2 +- .env.localhost | 2 +- .env.production | 2 +- package-lock.json | 10 + src/components/pdf/SuitableDownloadPdf.tsx | 2066 ++++++-------------- src/config/config.local.ts | 2 +- src/styles/components/_pdfview.scss | 81 + src/styles/components/_pop-contents.scss | 9 + 8 files changed, 748 insertions(+), 1426 deletions(-) diff --git a/.env.development b/.env.development index 2034c56..205b2a4 100644 --- a/.env.development +++ b/.env.development @@ -2,7 +2,7 @@ NEXT_PUBLIC_RUN_MODE=development # 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경 # 다시 로컬에서 개발할때는 localhost로 변경 #route handler -NEXT_PUBLIC_API_URL=http://172.30.1.23:3000 +NEXT_PUBLIC_API_URL=http://localhost:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 diff --git a/.env.localhost b/.env.localhost index 844bc76..5f61c6f 100644 --- a/.env.localhost +++ b/.env.localhost @@ -2,7 +2,7 @@ NEXT_PUBLIC_RUN_MODE=local # 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경 # 다시 로컬에서 개발할때는 localhost로 변경 #route handler -NEXT_PUBLIC_API_URL=http://172.30.1.23:3000 +NEXT_PUBLIC_API_URL=http://localhost:3000 #qsp 로그인 api NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 diff --git a/.env.production b/.env.production index 72caa0d..fc11d33 100644 --- a/.env.production +++ b/.env.production @@ -1,6 +1,6 @@ NEXT_PUBLIC_RUN_MODE=production #route handler -NEXT_PUBLIC_API_URL=http://1.248.227.176:3000 +NEXT_PUBLIC_API_URL=http://localhost:3000 #qsp 로그인 api # NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120 diff --git a/package-lock.json b/package-lock.json index 6a3c17f..669f7b5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@prisma/client": "^6.7.0", "@tanstack/react-query": "^5.71.0", "@tanstack/react-query-devtools": "^5.71.0", + "@types/nodemailer": "^6.4.17", "axios": "^1.8.4", "env-cmd": "^10.1.0", "iron-session": "^8.0.4", @@ -1964,6 +1965,15 @@ "undici-types": "~6.19.2" } }, + "node_modules/@types/nodemailer": { + "version": "6.4.17", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.4.17.tgz", + "integrity": "sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/raf": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", diff --git a/src/components/pdf/SuitableDownloadPdf.tsx b/src/components/pdf/SuitableDownloadPdf.tsx index 86f5c60..60ffc07 100644 --- a/src/components/pdf/SuitableDownloadPdf.tsx +++ b/src/components/pdf/SuitableDownloadPdf.tsx @@ -33,1431 +33,653 @@ export default function SuitableDownloadPdf() { } return ( <> - -
-
-
-
- ハンファジャパン株式会社 -
-
(瓦) 屋根材適合表
-
- 2025年4月30日 10:40 -
+
+
+
+
ハンファジャパン株式会社
+
(瓦) 屋根材適合表
+
2025年4月30日 10:40
-
-

- 本適合表は参考資料としてご使用下さい。 -

-

- 屋根材製品の形状・仕様はメーカーより変更される場合が御座います。 -

-

- 又、現場環境(働き、勾配、瓦桟木条件等)により本適合表と異なる適合結果となる場合が御座います。予めご了承下さい。 -

-

- 屋根材以外の設置条件(垂木、野地板等の設置基準)も必ずご確認下さい。 -

+
+

本適合表は参考資料としてご使用下さい。

+

屋根材製品の形状・仕様はメーカーより変更される場合が御座います。

+

又、現場環境(働き、勾配、瓦桟木条件等)により本適合表と異なる適合結果となる場合が御座います。予めご了承下さい。

+

屋根材以外の設置条件(垂木、野地板等の設置基準)も必ずご確認下さい。

-
- - - - - - - - - - - - - - - - - - - - -
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
-
- - 屋根材製品名 - - - メーカー名 - - - 屋根材の種類 - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- 金具タイプ - - 金具名 - - 設置可否 - - 備考 -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
- 木ねじ打ち込み式 - - 屋根技研支持瓦 - - C で設置可 - - 支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身){' '} -
-
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
+
+ 屋根材製品名 + メーカー名 + 屋根材の種類 +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
金具タイプ金具名設置可否備考
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
木ねじ打ち込み式屋根技研支持瓦C で設置可支持瓦はアンダーラップの先端を削って納める(Try-U40はこの瓦の前身)
+
+
+
diff --git a/src/config/config.local.ts b/src/config/config.local.ts index 8fbf68b..ac02c97 100644 --- a/src/config/config.local.ts +++ b/src/config/config.local.ts @@ -1,7 +1,7 @@ import getConfigs from '@/config/config.common' // 환경마다 달라져야 할 변수, 값들을 정의합니다. (여기는 local 환경에 맞는 값을 지정합니다.) -const baseUrl = 'http://172.30.1.23:3000' +const baseUrl = 'http://localhost:3000' const mode = 'local' // 환경마다 달라져야 할 값들을 getConfig 함수에 전달합니다. diff --git a/src/styles/components/_pdfview.scss b/src/styles/components/_pdfview.scss index 8db6cf7..039d1f5 100644 --- a/src/styles/components/_pdfview.scss +++ b/src/styles/components/_pdfview.scss @@ -1,5 +1,6 @@ @use "../abstracts" as *; +// 조사매물 .pdf-contents{ padding: 0 20px; border-top: 1px solid #ececec; @@ -54,4 +55,84 @@ @include defaultFont($font-s-11, $font-w-400, #FF5656); border: 1px solid $black-1010; min-height: 150px; +} + +// 지붕재 적합성 +.pdf-intro-page{ + height: 1080px; + padding: 80px 40px ; + background-color: #fff; +} +.pdf-intro-tit-wrap{ + text-align: center; + .pdf-intro-tit{ + @include defaultFont($font-s-24, $font-w-500, #101010); + } + .pdf-intro-date{ + @include defaultFont($font-s-22, $font-w-400, #101010); + } +} +.pdf-intro-cont-wrap{ + margin-top: 70px; + p{ + @include defaultFont($font-s-18, $font-w-400, #101010); + } +} + +.pdf-table-content{ + padding: 20px; +} +.pdf-table-grid-wrap{ + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 10px 20px; +} +.pdf-table-card{ + .pdf-table-tit-wrap{ + margin-bottom: 5px; + span{ + position: relative; + @include defaultFont($font-s-13, $font-w-500, #101010); + padding: 0 10px; + &:first-child{ + padding-left: 0; + } + &:last-child{ + padding-right: 0; + &::before{ + display: none; + } + } + &::before{ + content: ''; + position: absolute; + top: 50%; + right: 0; + transform: translateY(-50%); + width: 1px; + height: 14px; + background-color: #101010; + } + } + } +} +.pdf-roof-table{ + table{ + width: 100%; + table-layout: fixed; + border-collapse: collapse; + th{ + padding: 0px 5px; + text-align: center; + @include defaultFont($font-s-11, $font-w-500, #fff); + background-color: #18B490; + border: 1px solid #18B490; + } + td{ + padding: 0px 5px; + @include defaultFont($font-s-11, $font-w-300, #101010); + border: 1px solid #CBCBCB; + line-height: 1; + } + } } \ No newline at end of file diff --git a/src/styles/components/_pop-contents.scss b/src/styles/components/_pop-contents.scss index b598397..afa4b3a 100644 --- a/src/styles/components/_pop-contents.scss +++ b/src/styles/components/_pop-contents.scss @@ -112,4 +112,13 @@ padding: 10px; @include defaultFont($font-s-13, $font-w-400, $font-c); } +} + +// 제출팝업 +.submit-content{ + padding: 15px 10px; + border: 1px solid #D5DEE8; + border-radius: 4px; + background-color: #f5f6fa; + cursor: default; } \ No newline at end of file From 37435d2cca714bb9873553dd8577943b5f65cd7e Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Thu, 29 May 2025 10:32:10 +0900 Subject: [PATCH 3/3] feat: add documentation for Login component, project structure, and login process - Introduced diagrams for the Login component structure, project architecture, and login sequence. - Detailed state management, external hooks, event handling, and UI components in the Login documentation. - Provided an overview of the component relationships and roles within the application architecture. --- diagram/Login.md | 88 +++++++++++++++++++++++++ diagram/mermaid.md | 98 +++++++++++++++++++++++++++ diagram/mermaid2.md | 125 +++++++++++++++++++++++++++++++++++ diagram/mermaid3.md | 157 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 468 insertions(+) create mode 100644 diagram/Login.md create mode 100644 diagram/mermaid.md create mode 100644 diagram/mermaid2.md create mode 100644 diagram/mermaid3.md diff --git a/diagram/Login.md b/diagram/Login.md new file mode 100644 index 0000000..2e816ca --- /dev/null +++ b/diagram/Login.md @@ -0,0 +1,88 @@ +# Login Component Structure + +## Component Diagram + +```mermaid +graph TD + A[Login Component] --> B[State Management] + B --> B1[useState Hooks] + B1 --> B1a[pwShow: 비밀번호 표시 여부] + B1 --> B1b[idSave: ID 저장 여부] + B1 --> B1c[isPartners: Q.PARTNERS 여부] + B1 --> B1d[isLogin: 로그인 상태] + + A --> C[Account Management] + C --> C1[useReducer] + C1 --> C1a[loginId] + C1 --> C1b[pwd] + + A --> D[External Hooks] + D --> D1[useRouter] + D --> D2[useLocalStorage] + D --> D3[useSessionStore] + D --> D4[useAxios] + D --> D5[useQuery] + + A --> E[Event Handlers] + E --> E1[handleLogin] + E --> E2[handleKeyDown] + E --> E3[validateLogin] + + A --> F[Effects] + F --> F1[Login Success Effect] + F1 --> F1a[세션 저장] + F1 --> F1b[라우팅] + F --> F2[Email Validation Effect] + F2 --> F2a[Partners 모드 전환] + + A --> G[UI Components] + G --> G1[Login Form] + G1 --> G1a[ID Input] + G1 --> G1b[Password Input] + G1 --> G1c[Checkboxes] + G1 --> G1d[Login Button] +``` + +## 주요 특징과 동작 방식 + +### 1. 상태 관리 + +- `useState`를 사용하여 UI 상태 관리 (비밀번호 표시, ID 저장, Partners 모드) +- `useReducer`를 사용하여 계정 정보(loginId, pwd) 관리 + +### 2. 외부 훅 통합 + +- `useRouter`: 페이지 라우팅 +- `useLocalStorage`: 로컬 스토리지 데이터 관리 +- `useSessionStore`: 세션 상태 관리 +- `useAxios`: API 통신 +- `useQuery`: 로그인 API 호출 및 상태 관리 + +### 3. 이벤트 처리 + +- `handleLogin`: 로그인 시도 +- `handleKeyDown`: Enter 키 입력 처리 +- `validateLogin`: 입력값 유효성 검사 + +### 4. 효과 처리 + +- 로그인 성공 시 세션 저장 및 라우팅 +- 이메일 형식에 따른 Partners 모드 자동 전환 + +### 5. UI 구성 + +- ID/PW 입력 필드 +- 비밀번호 표시/숨김 토글 +- ID 저장 체크박스 +- Q.PARTNERS 토글 +- 로그인 버튼 + +### 6. 보안 및 유효성 검사 + +- 이메일 형식 검증 +- 필수 입력값 검증 +- API 응답 코드에 따른 처리 (200: 성공, 400: 실패) + +## 특징 + +이 컴포넌트는 클라이언트 사이드에서 동작하며('use client'), 사용자 인증과 관련된 모든 로직을 포함하고 있습니다. 특히 Q.PARTNERS 모드와 일반 모드를 구분하여 다른 API 엔드포인트를 사용하는 특징이 있습니다. diff --git a/diagram/mermaid.md b/diagram/mermaid.md new file mode 100644 index 0000000..e6b5ef8 --- /dev/null +++ b/diagram/mermaid.md @@ -0,0 +1,98 @@ +# Project Structure Documentation + +## Component Relationship Diagram + +```mermaid +graph TD + subgraph Root + A[RootLayout] --> B[ReactQueryProviders] + B --> C[EdgeProvider] + C --> D[HTML Structure] + end + + subgraph Layout Components + D --> E[Header] + D --> F[Main Content] + D --> G[Footer] + D --> H[Float Button] + D --> I[PopupController] + end + + subgraph Pages + F --> J[Login] + F --> K[Survey Sale] + F --> L[Suitable] + F --> M[Inquiry] + F --> N[Password Reset] + F --> O[PDF] + end + + subgraph Providers + P1[ReactQueryProvider] + P2[EdgeProvider] + end + + subgraph Components + C1[UI Components] + C2[Popup Components] + C3[PDF Components] + C4[Survey Components] + C5[Inquiry Components] + end + + subgraph Utils + U1[Session Management] + U2[Mailer] + U3[API Routes] + end + + %% Relationships + A --> P1 + A --> P2 + J --> U1 + K --> C4 + L --> C4 + M --> C5 + N --> U2 + O --> C3 +``` + +## Structure Explanation + +### 1. Root Layout + +- `RootLayout`이 전체 애플리케이션의 기본 구조를 정의 +- `ReactQueryProviders`와 `EdgeProvider`로 감싸져 있음 + +### 2. Layout Components + +- Header, Footer, Float Button 등 공통 레이아웃 컴포넌트 +- `PopupController`로 팝업 관리 + +### 3. Pages + +- Next.js App Router 기반의 페이지 구조 +- Login, Survey Sale, Suitable, Inquiry 등 주요 페이지들 + +### 4. Providers + +- `ReactQueryProvider`: 데이터 페칭 관리 +- `EdgeProvider`: 세션 및 상태 관리 + +### 5. Components + +- UI Components: 공통 UI 요소 +- Popup Components: 팝업 관련 컴포넌트 +- PDF Components: PDF 생성/관리 컴포넌트 +- Survey Components: 설문 관련 컴포넌트 +- Inquiry Components: 문의 관련 컴포넌트 + +### 6. Utils + +- Session Management: 세션 관리 +- Mailer: 이메일 발송 기능 +- API Routes: 백엔드 API 엔드포인트 + +## Architecture Overview + +이 구조는 Next.js의 App Router를 기반으로 하며, 컴포넌트 기반 아키텍처를 따르고 있습니다. 각 기능별로 모듈화가 잘 되어있고, 공통 컴포넌트와 유틸리티를 효율적으로 재사용할 수 있도록 구성되어 있습니다. diff --git a/diagram/mermaid2.md b/diagram/mermaid2.md new file mode 100644 index 0000000..5d8e561 --- /dev/null +++ b/diagram/mermaid2.md @@ -0,0 +1,125 @@ +# Login Process Documentation + +## Login Sequence Diagram + +```mermaid +sequenceDiagram + actor User + participant Login as Login Component + participant API as Auth API + participant QSP as QSP API + participant Session as Session Store + participant Router as Next Router + + User->>Login: Enter credentials + Login->>Login: Validate input + alt Invalid Input + Login->>User: Show error message + else Valid Input + Login->>API: POST /api/auth + API->>QSP: POST /api/user/login + QSP-->>API: Return user data + + alt Login Success + API->>Session: Create session + Session->>Session: Set user data + Session->>Session: Set role + API-->>Login: Return success response + Login->>Router: Redirect to home + Router->>User: Show home page + else Login Failed + API-->>Login: Return error response + Login->>User: Show error message + end + end +``` + +## Login Process Flow + +1. **User Input** + + - User enters login credentials (ID and password) + - Optional: User can toggle Q.PARTNERS mode + - Optional: User can save ID + +2. **Input Validation** + + - Checks if ID and password are not empty + - Validates email format for Q.PARTNERS mode + +3. **Authentication Request** + + - Sends credentials to Auth API + - Auth API forwards request to QSP API + - QSP API validates credentials + +4. **Session Management** + + - On successful login: + - Creates new session + - Stores user data + - Sets user role based on permissions + - Saves session to cookies + +5. **Response Handling** + - Success: Redirects to home page + - Failure: Shows error message + +## Role Assignment Logic + +The system assigns roles based on the following rules: + +- `T01`: If userId is 'T01' +- `Admin`: If groupId is '60000' +- `Admin_Sub`: If groupId is '70000' and builderNo is null +- `Builder`: If groupId is '70000' and builderNo is not null +- `User`: Default role for all other cases + +```mermaid +graph TD +subgraph Root +A[RootLayout] --> B[ReactQueryProviders] +B --> C[EdgeProvider] +C --> D[HTML Structure] +end + subgraph Layout Components + D --> E[Header] + D --> F[Main Content] + D --> G[Footer] + D --> H[Float Button] + D --> I[PopupController] + end + subgraph Pages + F --> J[Login] + F --> K[Survey Sale] + F --> L[Suitable] + F --> M[Inquiry] + F --> N[Password Reset] + F --> O[PDF] + end + subgraph Providers + P1[ReactQueryProvider] + P2[EdgeProvider] + end + subgraph Components + C1[UI Components] + C2[Popup Components] + C3[PDF Components] + C4[Survey Components] + C5[Inquiry Components] + end + subgraph Utils + U1[Session Management] + U2[Mailer] + U3[API Routes] + end + %% Relationships + A --> P1 + A --> P2 + J --> U1 + K --> C4 + L --> C4 + M --> C5 + N --> U2 + O --> C3 +``` diff --git a/diagram/mermaid3.md b/diagram/mermaid3.md new file mode 100644 index 0000000..cf9fd15 --- /dev/null +++ b/diagram/mermaid3.md @@ -0,0 +1,157 @@ +# Pages and Components Class Diagram + +## Class Diagram + +```mermaid +classDiagram + class RootLayout { + +ReactNode children + +ReactNode header + +ReactNode footer + +ReactNode floatBtn + +ReactQueryProviders + +EdgeProvider + +PopupController + } + + class Page { + <> + +ReactNode render() + } + + class LoginPage { + +Login component + +handleLogin() + +validateInput() + } + + class SurveySalePage { + +SurveySaleList + +SurveySaleDetail + +handleSurveySubmit() + } + + class SuitablePage { + +SuitableList + +SuitableDetail + +handleSuitableSubmit() + } + + class InquiryPage { + +InquiryList + +InquiryDetail + +handleInquirySubmit() + } + + class PasswordResetPage { + +PasswordResetForm + +handlePasswordReset() + } + + class PDFPage { + +PDFViewer + +PDFGenerator + +handlePDFGeneration() + } + + class BaseComponent { + <> + +ReactNode render() + } + + class UIComponent { + +Button + +Input + +Select + +Modal + } + + class PopupComponent { + +PopupController + +PopupContent + +handlePopup() + } + + class PDFComponent { + +PDFViewer + +PDFGenerator + +handlePDF() + } + + class SurveyComponent { + +SurveyForm + +SurveyList + +handleSurvey() + } + + class InquiryComponent { + +InquiryForm + +InquiryList + +handleInquiry() + } + + %% Relationships + RootLayout --> Page + Page <|-- LoginPage + Page <|-- SurveySalePage + Page <|-- SuitablePage + Page <|-- InquiryPage + Page <|-- PasswordResetPage + Page <|-- PDFPage + + BaseComponent <|-- UIComponent + BaseComponent <|-- PopupComponent + BaseComponent <|-- PDFComponent + BaseComponent <|-- SurveyComponent + BaseComponent <|-- InquiryComponent + + LoginPage --> BaseComponent + SurveySalePage --> SurveyComponent + SuitablePage --> SurveyComponent + InquiryPage --> InquiryComponent + PasswordResetPage --> UIComponent + PDFPage --> PDFComponent + + RootLayout --> PopupComponent +``` + +## Component Hierarchy + +1. **Root Layout** + + - 최상위 레이아웃 컴포넌트 + - ReactQuery와 Edge Provider를 포함 + - 공통 레이아웃 요소 관리 + +2. **Pages** + + - LoginPage: 로그인 기능 + - SurveySalePage: 설문 판매 관리 + - SuitablePage: 적합성 관리 + - InquiryPage: 문의 관리 + - PasswordResetPage: 비밀번호 재설정 + - PDFPage: PDF 생성 및 관리 + +3. **Base Components** + - UIComponent: 기본 UI 요소 + - PopupComponent: 팝업 관리 + - PDFComponent: PDF 관련 기능 + - SurveyComponent: 설문 관련 기능 + - InquiryComponent: 문의 관련 기능 + +## Component Relationships + +1. **Page-Component Relationship** + + - 각 페이지는 필요한 컴포넌트들을 조합하여 구성 + - 페이지별로 특화된 컴포넌트 사용 + +2. **Component Inheritance** + + - 모든 컴포넌트는 BaseComponent 인터페이스 구현 + - 각 컴포넌트 타입별로 특화된 기능 제공 + +3. **Layout Integration** + - RootLayout이 전체 페이지 구조 관리 + - PopupComponent를 통한 모달 관리 + - 공통 UI 요소의 일관성 유지