feat: enhance survey sales API and component integration

- 조사매물 조회 시 권한 확인 로직 추가
This commit is contained in:
Dayoung 2025-06-02 18:09:42 +09:00
parent c6664e9827
commit e756465250
5 changed files with 83 additions and 29 deletions

View File

@ -1,18 +1,60 @@
import { NextRequest, NextResponse } from 'next/server' import { NextRequest, NextResponse } from 'next/server'
import { prisma } from '@/libs/prisma' import { prisma } from '@/libs/prisma'
import { convertToSnakeCase } from '@/utils/common-utils' import { convertToSnakeCase } from '@/utils/common-utils'
import { SessionData } from '@/types/Auth'
type SessionParams = {
role: string | null
storeId: string | null
builderNo: string | null
isLoggedIn: string | null
}
const checkRole = (survey: any, sessionParams: SessionParams) => {
switch (sessionParams.role) {
case 'T01':
return true
case 'Admin':
if (survey.SUBMISSION_STATUS) {
return survey.SUBMISSION_TARGET_ID === sessionParams.storeId
}
return survey.STORE_ID === sessionParams.storeId
case 'Admin_Sub':
if (survey.SUBMISSION_STATUS) {
return survey.SUBMISSION_TARGET_ID === sessionParams.builderNo
}
return survey.STORE_ID === sessionParams.storeId && survey.CONSTRUCTION_POINT_ID === sessionParams.builderNo
case 'Partner':
case 'Builder':
return survey.CONSTRUCTION_POINT_ID === sessionParams.builderNo
default:
return false
}
}
export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) { export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
try { try {
const { id } = await params const { id } = await params
const { searchParams } = new URL(request.url)
const sessionParams: SessionParams = {
role: searchParams.get('role'),
storeId: searchParams.get('storeId'),
builderNo: searchParams.get('builderNo'),
isLoggedIn: searchParams.get('isLoggedIn'),
}
// @ts-ignore // @ts-ignore
const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findUnique({ const survey = await prisma.SD_SURVEY_SALES_BASIC_INFO.findFirst({
where: { ID: Number(id) }, where: {
ID: Number(id),
},
include: { include: {
DETAIL_INFO: true, DETAIL_INFO: true,
}, },
}) })
return NextResponse.json(survey) if (checkRole(survey, sessionParams)) {
return NextResponse.json(survey)
}
} catch (error) { } catch (error) {
console.error('Error fetching survey:', error) console.error('Error fetching survey:', error)
return NextResponse.json({ error: 'Failed to fetch survey' }, { status: 500 }) return NextResponse.json({ error: 'Failed to fetch survey' }, { status: 500 })

View File

@ -4,16 +4,20 @@ import { useEffect, useState } from 'react'
import { useSurveySaleTabState } from '@/store/surveySaleTabState' import { useSurveySaleTabState } from '@/store/surveySaleTabState'
import type { SurveyBasicRequest } from '@/types/Survey' import type { SurveyBasicRequest } from '@/types/Survey'
import type { Mode } from 'fs' import type { Mode } from 'fs'
import { useSessionStore } from '@/store/session'
import { usePopupController } from '@/store/popupController' import { usePopupController } from '@/store/popupController'
import { useAddressStore } from '@/store/addressStore' import { useAddressStore } from '@/store/addressStore'
import { SessionData } from '@/types/Auth'
export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBasicInfo: (basicInfo: SurveyBasicRequest) => void; mode: Mode }) { export default function BasicForm(props: {
const { basicInfo, setBasicInfo, mode } = props basicInfo: SurveyBasicRequest
setBasicInfo: (basicInfo: SurveyBasicRequest) => void
mode: Mode
session: SessionData
}) {
const { basicInfo, setBasicInfo, mode, session } = props
const { setBasicInfoSelected } = useSurveySaleTabState() const { setBasicInfoSelected } = useSurveySaleTabState()
const [isFlip, setIsFlip] = useState<boolean>(true) const [isFlip, setIsFlip] = useState<boolean>(true)
const { session } = useSessionStore()
const { addressData } = useAddressStore() const { addressData } = useAddressStore()
useEffect(() => { useEffect(() => {

View File

@ -32,10 +32,12 @@ export default function ButtonForm(props: {
// -------------------------------------------------------------- // --------------------------------------------------------------
// 권한 // 권한
// 제출권한 // 제출권한 있는 회원
const [isSubmiter, setIsSubmiter] = useState(false) const [isSubmiter, setIsSubmiter] = useState(false)
// 작성자 // 작성자
const [isWriter, setIsWriter] = useState(false) const [isWriter, setIsWriter] = useState(false)
// 제출받은 대상자
const [isReceiver, setIsReceiver] = useState(false)
const isSubmit = props.data.basic.submissionStatus const isSubmit = props.data.basic.submissionStatus
useEffect(() => { useEffect(() => {
@ -44,11 +46,13 @@ export default function ButtonForm(props: {
// T01 제출권한 없음 // T01 제출권한 없음
case 'T01': case 'T01':
setIsSubmiter(false) setIsSubmiter(false)
setIsReceiver(!props.data.basic.submissionTargetId && !props.data.basic.submissionTargetNm)
break break
// 1차 판매점(Order) + 2차 판매점(Musubi) => 같은 판매점 제출권한 // 1차 판매점(Order) + 2차 판매점(Musubi) => 같은 판매점 제출권한
case 'Admin': case 'Admin':
case 'Admin_Sub': case 'Admin_Sub':
setIsSubmiter(session.storeNm === props.data.basic.store && session.builderNo === props.data.basic.constructionPointId) setIsSubmiter(session.storeNm === props.data.basic.store && session.builderNo === props.data.basic.constructionPointId)
setIsReceiver(session?.storeId === props.data.basic.submissionTargetId)
break break
// 시공권한 User(Musubi) + Partner => 같은 시공ID 제출권한 // 시공권한 User(Musubi) + Partner => 같은 시공ID 제출권한
case 'Builder': case 'Builder':
@ -59,7 +63,6 @@ export default function ButtonForm(props: {
setIsSubmiter(false) setIsSubmiter(false)
break break
} }
setIsWriter(session.userNm === props.data.basic.representative) setIsWriter(session.userNm === props.data.basic.representative)
} }
setSaveData({ setSaveData({
@ -175,19 +178,11 @@ export default function ButtonForm(props: {
// ------------------------------------------------------------ // ------------------------------------------------------------
// 제출 완료 된 매물의 경우 제출 권한 있으면 수정/삭제 불가능 if (!session?.isLoggedIn) {
if (mode === 'READ' && isSubmit && isSubmiter) { return <></>
return (
<>
<div className="sale-form-btn-wrap">
<div className="btn-flex-wrap">
<ListButton />
</div>
</div>
</>
)
} }
if (mode === 'READ' && session?.role === 'T01' && (!isSubmit || (props.data.basic.submissionTargetId && props.data.basic.submissionTargetNm))) {
if (mode === 'READ' && isSubmit && isSubmiter) {
return ( return (
<> <>
<div className="sale-form-btn-wrap"> <div className="sale-form-btn-wrap">
@ -205,8 +200,8 @@ export default function ButtonForm(props: {
<div className="sale-form-btn-wrap"> <div className="sale-form-btn-wrap">
<div className="btn-flex-wrap"> <div className="btn-flex-wrap">
<ListButton /> <ListButton />
<EditButton setMode={setMode} id={id.toString()} mode={mode} /> {(isSubmiter || (isReceiver && isSubmit)) && <EditButton setMode={setMode} id={id.toString()} mode={mode} />}
{(isWriter || !isSubmiter) && <DeleteButton handleDelete={handleDelete} />} {(isWriter || (isReceiver && isSubmit)) && <DeleteButton handleDelete={handleDelete} />}
{!isSubmit && isSubmiter && <SubmitButton handleSubmit={handleSubmit} />} {!isSubmit && isSubmiter && <SubmitButton handleSubmit={handleSubmit} />}
</div> </div>
</div> </div>
@ -218,7 +213,7 @@ export default function ButtonForm(props: {
<ListButton /> <ListButton />
<TempButton setMode={setMode} handleSave={() => handleSave(true, false)} /> <TempButton setMode={setMode} handleSave={() => handleSave(true, false)} />
<SaveButton handleSave={() => handleSave(false, false)} /> <SaveButton handleSave={() => handleSave(false, false)} />
{session?.role === 'T01' || props.data.basic.submissionStatus ? <></> : <SubmitButton handleSubmit={handleSubmit} />} {session?.role === 'T01' || isSubmit ? <></> : <SubmitButton handleSubmit={handleSubmit} />}
</div> </div>
</div> </div>
)} )}

View File

@ -7,6 +7,7 @@ import BasicForm from './BasicForm'
import RoofForm from './RoofForm' import RoofForm from './RoofForm'
import { useParams, useSearchParams } from 'next/navigation' import { useParams, useSearchParams } from 'next/navigation'
import { useSurvey } from '@/hooks/useSurvey' import { useSurvey } from '@/hooks/useSurvey'
import { useSessionStore } from '@/store/session'
const roofInfoForm: SurveyDetailRequest = { const roofInfoForm: SurveyDetailRequest = {
contractCapacity: null, contractCapacity: null,
@ -74,6 +75,7 @@ export default function DetailForm() {
const id = Number(routeId) ? Number(routeId) : Number(idParam) const id = Number(routeId) ? Number(routeId) : Number(idParam)
const { surveyDetail, validateSurveyDetail } = useSurvey(Number(id)) const { surveyDetail, validateSurveyDetail } = useSurvey(Number(id))
const { session } = useSessionStore()
const [mode, setMode] = useState<Mode>(modeset) const [mode, setMode] = useState<Mode>(modeset)
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(basicInfoForm) const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(basicInfoForm)
@ -81,7 +83,7 @@ export default function DetailForm() {
useEffect(() => { useEffect(() => {
if (Number(idParam) !== 0 || surveyDetail === null) { if (Number(idParam) !== 0 || surveyDetail === null) {
alert('データが見つかりません。') alert('権限がありません。')
window.location.href = '/survey-sale' window.location.href = '/survey-sale'
} }
if (surveyDetail && (mode === 'EDIT' || mode === 'READ')) { if (surveyDetail && (mode === 'EDIT' || mode === 'READ')) {
@ -94,7 +96,7 @@ export default function DetailForm() {
} }
} }
} }
}, [surveyDetail, id]) }, [surveyDetail, id, mode, session])
const data = { const data = {
basic: basicInfoData, basic: basicInfoData,
@ -107,7 +109,7 @@ export default function DetailForm() {
<> <>
<div className="sale-detail-toggle-wrap"> <div className="sale-detail-toggle-wrap">
{/* 기본정보 */} {/* 기본정보 */}
<BasicForm basicInfo={basicInfoData} setBasicInfo={setBasicInfoData} mode={mode} /> <BasicForm basicInfo={basicInfoData} setBasicInfo={setBasicInfoData} mode={mode} session={session} />
{/* 전기/지붕정보 */} {/* 전기/지붕정보 */}
<RoofForm roofInfo={roofInfoData} setRoofInfo={setRoofInfoData} mode={mode} /> <RoofForm roofInfo={roofInfoData} setRoofInfo={setRoofInfoData} mode={mode} />
<ButtonForm {...buttonFormProps} /> <ButtonForm {...buttonFormProps} />

View File

@ -5,6 +5,7 @@ import { useSurveyFilterStore } from '@/store/surveyFilterStore'
import { useSessionStore } from '@/store/session' import { useSessionStore } from '@/store/session'
import { useAxios } from './useAxios' import { useAxios } from './useAxios'
import { queryStringFormatter } from '@/utils/common-utils' import { queryStringFormatter } from '@/utils/common-utils'
import { AxiosError } from 'axios'
export const requiredFields = [ export const requiredFields = [
{ {
@ -111,8 +112,18 @@ export function useSurvey(id?: number): {
queryFn: async () => { queryFn: async () => {
if (id === undefined) throw new Error('id is required') if (id === undefined) throw new Error('id is required')
if (id === null || isNaN(id)) return null if (id === null || isNaN(id)) return null
const resp = await axiosInstance(null).get<SurveyBasicInfo>(`/api/survey-sales/${id}`) if (session?.isLoggedIn) {
return resp.data const resp = await axiosInstance(null).get<SurveyBasicInfo>(`/api/survey-sales/${id}`, {
params: {
role: session?.role,
storeId: session?.storeId,
builderNo: session?.builderNo,
isLoggedIn: session?.isLoggedIn,
},
})
return resp.data
}
return null
}, },
enabled: id !== undefined, enabled: id !== undefined,
}) })