Compare commits

..

4 Commits

Author SHA1 Message Date
d718012f6f feat: 지붕재적합성 상세팝업 마크업 적용 및 팝업 on/off 연결 2025-05-23 13:31:42 +09:00
e833e25048 feat: Add representative and store ID fields to SD_SURVEY_SALES_BASIC_INFO model in Prisma schema 2025-05-23 11:22:02 +09:00
916946956a Merge pull request 'feature/suitable' (#44) from feature/suitable into dev
Reviewed-on: #44
2025-05-23 10:50:07 +09:00
29e7675ada refactor: Enhance authentication API and login component functionality
- Updated authentication API to improve response handling and session management.
- Refactored login component to include validation and keyboard event handling for login submission.
- Improved user feedback by displaying alerts for login success and failure messages.
2025-05-23 10:06:45 +09:00
5 changed files with 298 additions and 54 deletions

View File

@ -62,6 +62,8 @@ model SD_SURVEY_SALES_BASIC_INFO {
SUBMISSION_TARGET_ID String? @db.VarChar(200)
REG_DT DateTime @default(now())
UPT_DT DateTime @updatedAt
REPRESENTATIVE_ID String? @db.VarChar(100)
STORE_ID String? @db.VarChar(100)
DETAIL_INFO SD_SURVEY_SALES_DETAIL_INFO?
}

View File

@ -15,7 +15,9 @@ export async function POST(request: Request) {
})
console.log('🚀 ~ result ~ result:', result.data)
if (result.data.result.code === 200) {
let finalResult = {}
if (result.data.result.resultCode === 'S') {
tracking({
url: `/api/auth/login`,
data: JSON.stringify({
@ -72,52 +74,64 @@ export async function POST(request: Request) {
console.log('end session edit!')
await session.save()
}
const resultForSession = {
LANG_CD: result.data.data.langCd,
CURR_PAGE: result.data.data.currPage,
ROW_COUNT: result.data.data.rowCount,
START_ROW: result.data.data.startRow,
END_ROW: result.data.data.endRow,
COMP_CD: result.data.data.compCd,
AGENCY_STORE_ID: result.data.data.agencyStoreId,
STORE_ID: result.data.data.storeId,
STORE_NM: result.data.data.storeNm,
USER_ID: result.data.data.userId,
CATEGORY: result.data.data.category,
USER_NM: result.data.data.userNm,
USER_NM_KANA: result.data.data.userNmKana,
TEL_NO: result.data.data.telNo,
FAX: result.data.data.fax,
EMAIL: result.data.data.email,
LAST_EDIT_USER: result.data.data.lastEditUser,
STORE_GUBUN: result.data.data.storeGubun,
PW_CURR: result.data.data.pwCurr,
PWD_INIT_YN: result.data.data.pwdInitYn,
APPR_STAT_CD: result.data.data.apprStatCd,
LOGIN_FAIL_CNT: result.data.data.loginFailCnt,
LOGIN_FAIL_MIN_YN: result.data.data.loginFailMinYn,
PRICE_VIEW_STAT_CD: result.data.data.priceViewStatCd,
GROUP_ID: result.data.data.groupId,
STORE_LVL: result.data.data.storeLvl,
CUST_CD: result.data.data.custCd,
BUILDER_NO: result.data.data.builderNo,
IS_LOGGED_IN: true,
ROLE: '',
}
const resultForSession = {
LANG_CD: result.data.data.langCd,
CURR_PAGE: result.data.data.currPage,
ROW_COUNT: result.data.data.rowCount,
START_ROW: result.data.data.startRow,
END_ROW: result.data.data.endRow,
COMP_CD: result.data.data.compCd,
AGENCY_STORE_ID: result.data.data.agencyStoreId,
STORE_ID: result.data.data.storeId,
STORE_NM: result.data.data.storeNm,
USER_ID: result.data.data.userId,
CATEGORY: result.data.data.category,
USER_NM: result.data.data.userNm,
USER_NM_KANA: result.data.data.userNmKana,
TEL_NO: result.data.data.telNo,
FAX: result.data.data.fax,
EMAIL: result.data.data.email,
LAST_EDIT_USER: result.data.data.lastEditUser,
STORE_GUBUN: result.data.data.storeGubun,
PW_CURR: result.data.data.pwCurr,
PWD_INIT_YN: result.data.data.pwdInitYn,
APPR_STAT_CD: result.data.data.apprStatCd,
LOGIN_FAIL_CNT: result.data.data.loginFailCnt,
LOGIN_FAIL_MIN_YN: result.data.data.loginFailMinYn,
PRICE_VIEW_STAT_CD: result.data.data.priceViewStatCd,
GROUP_ID: result.data.data.groupId,
STORE_LVL: result.data.data.storeLvl,
CUST_CD: result.data.data.custCd,
BUILDER_NO: result.data.data.builderNo,
IS_LOGGED_IN: true,
ROLE: '',
}
if (result.data.data.userId === 'T01') {
resultForSession.ROLE = 'T01'
} else if (result.data.data.groupId === '60000') {
resultForSession.ROLE = 'Admin'
} else if (result.data.data.groupId === '70000' && result.data.data.builderNo === null) {
resultForSession.ROLE = 'Admin_Sub'
} else if (result.data.data.groupId === '70000' && result.data.data.builderNo !== null) {
resultForSession.ROLE = 'Builder'
if (result.data.data.userId === 'T01') {
resultForSession.ROLE = 'T01'
} else if (result.data.data.groupId === '60000') {
resultForSession.ROLE = 'Admin'
} else if (result.data.data.groupId === '70000' && result.data.data.builderNo === null) {
resultForSession.ROLE = 'Admin_Sub'
} else if (result.data.data.groupId === '70000' && result.data.data.builderNo !== null) {
resultForSession.ROLE = 'Builder'
} else {
resultForSession.ROLE = 'User'
}
finalResult = {
code: 200,
message: 'Login is Succecss!!',
result: resultForSession,
}
} else {
resultForSession.ROLE = 'User'
finalResult = {
code: 400,
message: 'Login is Failed!!',
result: {},
}
}
return NextResponse.json({ code: 200, message: 'Login is Succecss!!', result: resultForSession })
return NextResponse.json(finalResult)
}

View File

@ -39,6 +39,26 @@ export default function Login() {
return emailRegex.test(email)
}
const handleLogin = () => {
if (validateLogin()) {
setIsLogin(true)
}
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter') {
handleLogin()
}
}
const validateLogin = () => {
if (account.loginId === '' || account.pwd === '') {
alert('Please enter your ID and password.')
return false
}
return true
}
interface LoginData {
code: number
message: string | null
@ -85,6 +105,9 @@ export default function Login() {
...loginData?.result,
})
router.push('/')
} else if (loginData?.code === 400) {
alert(loginData?.message)
setAccount({ loginId: '', pwd: '' })
}
}, [loginData])
@ -106,6 +129,7 @@ export default function Login() {
className="login-frame"
placeholder="Input Frame ID"
value={account.loginId}
onKeyDown={(e) => handleKeyDown(e)}
onChange={(e) => setAccount({ loginId: e.target.value })}
/>
<button className="login-icon" onClick={() => setAccount({ loginId: '' })}>
@ -120,6 +144,7 @@ export default function Login() {
className="login-frame"
placeholder="Input Frame PW"
value={account.pwd}
onKeyDown={(e) => handleKeyDown(e)}
onChange={(e) => setAccount({ pwd: e.target.value })}
/>
<button className={`login-icon ${pwShow ? 'act' : ''}`} onClick={() => setPwShow(!pwShow)}>
@ -141,7 +166,7 @@ export default function Login() {
</div>
</div>
<div className="login-btn-wrap">
<button className="btn-frame icon login" onClick={() => setIsLogin(true)}>
<button className="btn-frame icon login" onClick={handleLogin}>
<i className="btn-arr"></i>
</button>
</div>

View File

@ -1,4 +1,11 @@
'use client'
import Image from 'next/image'
import { usePopupController } from '@/store/popupController'
export default function SuitableDetailPopup() {
const popupController = usePopupController()
return (
<div className="modal-popup">
<div className="modal-dialog">
@ -7,22 +14,214 @@ export default function SuitableDetailPopup() {
<div className="modal-header-inner">
<div className="modal-name-wrap">
<div className="modal-img">
<img src="/assets/images/layout/modal_header_icon03.svg" alt="" />
<Image src="/assets/images/layout/modal_header_icon03.svg" width={22} height={22} alt="" />
</div>
<div className="modal-name"> </div>
</div>
<button className="modal-close"></button>
<button className="modal-close" onClick={() => popupController.setSuitableDetailPopup(false)}></button>
</div>
</div>
<div className="modal-body">
<div className={`compliance-check-bx act`}>
<div className="check-name-wrap">
<div className="check-name"></div>
<div className="check-name-btn">
<button className="bx-btn"></button>
<div className="compliance-check-pop-wrap">
<div className={`compliance-check-bx act`}>
<div className="check-name-wrap">
<div className="check-name"></div>
<div className="check-name-btn">
<button className="bx-btn"></button>
</div>
</div>
<div className="compliance-check-pop-contents">
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"> </div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"></div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"></div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-table-wrap">
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"> </div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_check_icon.svg'} width={22} height={22} alt=""></Image>
</div>
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_tip_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data">
使
</div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"></div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_x_icon.svg'} width={22} height={22} alt=""></Image>
</div>
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_tip_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name">YGアンカー</div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_quest_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"></div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_check_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"> () </div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
</div>
</div>
</div>
<div className="compliance-check-pop-contents"></div>
<div className={`compliance-check-bx`}>
<div className="check-name-wrap">
<div className="check-name"></div>
<div className="check-name-btn">
<button className="bx-btn"></button>
</div>
</div>
<div className="compliance-check-pop-contents">
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"> </div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"></div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-wrap">
<div className="check-pop-data-tit"></div>
<div className="check-pop-data-txt"></div>
</div>
<div className="check-pop-data-table-wrap">
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"> </div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_check_icon.svg'} width={22} height={22} alt=""></Image>
</div>
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_tip_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"></div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_x_icon.svg'} width={22} height={22} alt=""></Image>
</div>
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_tip_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name">YGアンカー</div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_quest_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"></div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
<div className="check-pop-data-table">
<div className="pop-data-table-head">
<div className="pop-data-table-head-name"></div>
<div className="pop-data-table-head-icon">
<div className="compliance-icon">
<Image src={'/assets/images/sub/compliance_check_icon.svg'} width={22} height={22} alt=""></Image>
</div>
</div>
</div>
<div className="pop-data-table-body"> () </div>
<div className="pop-data-table-footer">
<div className="pop-data-table-footer-unit"></div>
<div className="pop-data-table-footer-data"></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="btn-flex-wrap com">
<div className="btn-bx">
<button className="btn-frame n-blue icon">
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame red icon">
<i className="btn-arr"></i>
</button>
</div>
<div className="btn-bx">
<button className="btn-frame n-blue icon">
11<i className="btn-arr"></i>
</button>
</div>
</div>
</div>
</div>

View File

@ -1,6 +1,10 @@
'use client'
import { usePopupController } from '@/store/popupController'
export default function SuitableButton() {
const popupController = usePopupController()
return (
<div className="float-btn-wrap">
<div className="btn-flex-wrap com">
@ -10,7 +14,7 @@ export default function SuitableButton() {
</button>
</div>
<div className="btn-bx">
<button className="btn-frame red icon">
<button className="btn-frame red icon" onClick={() => popupController.setSuitableDetailPopup(true)}>
<i className="btn-arr"></i>
</button>
</div>