diff --git a/src/app/api/survey-sales/route.ts b/src/app/api/survey-sales/route.ts index ce6ce6f..1343880 100644 --- a/src/app/api/survey-sales/route.ts +++ b/src/app/api/survey-sales/route.ts @@ -1,105 +1,211 @@ import { NextResponse } from 'next/server' import { prisma } from '@/libs/prisma' +// Types +type SearchParams = { + keyword?: string | null + searchOption?: string | null + isMySurvey?: string | null + sort?: string | null + offset?: string | null + memberRole?: string | null + store?: string | null + builderNo?: string | null +} + +type WhereCondition = { + AND?: any[] + OR?: any[] + [key: string]: any +} + +// Constants +const SEARCH_OPTIONS = [ + 'BUILDING_NAME', + 'REPRESENTATIVE', + 'STORE', + 'CONSTRUCTION_POINT', + 'CUSTOMER_NAME', + 'POST_CODE', + 'ADDRESS', + 'ADDRESS_DETAIL', +] as const + +const ITEMS_PER_PAGE = 10 + +// Helper functions +const createKeywordSearchCondition = (keyword: string, searchOption: string): WhereCondition => { + const where: WhereCondition = {} + + if (searchOption === 'all') { + where.OR = [] + + // ID 검색 조건 추가 + if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) { + where.OR.push({ + ID: { equals: Number(keyword) }, + }) + } + + // 다른 필드 검색 조건 추가 + where.OR.push( + ...SEARCH_OPTIONS.map((field) => ({ + [field]: { contains: keyword }, + })), + ) + } else if (SEARCH_OPTIONS.includes(searchOption as any)) { + where[searchOption] = { contains: keyword } + } else if (searchOption === 'id') { + where.ID = { equals: Number(keyword) } + } + + return where +} + +const createMemberRoleCondition = (params: SearchParams): WhereCondition => { + const where: WhereCondition = { AND: [] } + + switch (params.memberRole) { + // 1차점: 같은 판매점에서 작성된 매물 + 2차점에서 제출받은 매물 + case 'Admin': + where.OR = [ + { + AND: [ + { STORE: { equals: params.store } }, + { SUBMISSION_STATUS: { equals: false } } + ] + }, + { + AND: [ + { STORE: { endsWith: params.store } }, + { SUBMISSION_STATUS: { equals: true } } + ] + } + ] + break + + // 2차점: 같은 판매점에서 작성된 매물 + Builder에게 제출받은 매물 + case 'Admin_Sub': + where.OR = [ + { + AND: [ + { STORE: { equals: params.store } }, + { CONSTRUCTION_POINT: { equals: null } }, + { SUBMISSION_STATUS: { equals: false } } + ] + }, + { + AND: [ + { STORE: { equals: params.store } }, + { CONSTRUCTION_POINT: { not: null } }, + { SUBMISSION_STATUS: { equals: true } } + ] + } + ] + break + + // 2차점 시공권한: 같은 시공ID에서 작성된 매물 + case 'Builder': + where.AND?.push({ + CONSTRUCTION_POINT: { equals: params.builderNo }, + SUBMISSION_STATUS: { equals: false }, + }) + break + + // 시공점: 같은 시공ID에서 작성된 매물 + case 'Partner': + where.AND?.push({ + CONSTRUCTION_POINT: { equals: params.builderNo }, + SUBMISSION_STATUS: { equals: false }, + }) + break + + // 모든 매물 조회 가능 + case 'T01': + case 'User': + break + } + + return where +} + +// API Routes export async function POST(request: Request) { - const body = await request.json() - // @ts-ignore - const res = await prisma.SD_SURVEY_SALES_BASIC_INFO.create({ - data: body, - }) - return NextResponse.json(res) + try { + const body = await request.json() + // @ts-ignore + const res = await prisma.SD_SURVEY_SALES_BASIC_INFO.create({ + data: body, + }) + return NextResponse.json(res) + } catch (error) { + console.error('Error creating survey:', error) + return NextResponse.json({ error: 'Failed to create survey' }, { status: 500 }) + } } export async function GET(request: Request) { - const { searchParams } = new URL(request.url) - const keyword = searchParams.get('keyword') - const searchOption = searchParams.get('searchOption') - const isMySurvey = searchParams.get('isMySurvey') - const sort = searchParams.get('sort') - const offset = searchParams.get('offset') - const store = searchParams.get('store') - const construction_point = searchParams.get('construction_point') - - const searchOptions = ['BUILDING_NAME', 'REPRESENTATIVE', 'STORE', 'CONSTRUCTION_POINT', 'CUSTOMER_NAME', 'POST_CODE', 'ADDRESS', 'ADDRESS_DETAIL'] try { - const where: any = {} - - if (isMySurvey !== null && isMySurvey !== undefined) { - where.REPRESENTATIVE = isMySurvey + const { searchParams } = new URL(request.url) + const params: SearchParams = { + keyword: searchParams.get('keyword'), + searchOption: searchParams.get('searchOption'), + isMySurvey: searchParams.get('isMySurvey'), + sort: searchParams.get('sort'), + offset: searchParams.get('offset'), + memberRole: searchParams.get('memberRole'), + store: searchParams.get('store'), + builderNo: searchParams.get('builderNo'), } - if (keyword && keyword.trim() !== '' && searchOption) { - if (searchOption === 'all') { - where.OR = [] - if (keyword.match(/^\d+$/) || !isNaN(Number(keyword))) { - where.OR.push({ - ID: { - equals: Number(keyword), - }, - }) - } - where.OR.push( - ...searchOptions.map((field) => ({ - [field]: { - contains: keyword, - }, - })), - ) - } else if (searchOptions.includes(searchOption)) { - where[searchOption] = { - contains: keyword, - } - } else if (searchOption === 'id') { - where[searchOption] = { - equals: Number(keyword), - } - } + const where: WhereCondition = {} + + // 내가 작성한 매물 조건 + if (params.isMySurvey) { + where.REPRESENTATIVE = params.isMySurvey } - where.AND = [] - if (store) { - where.AND.push({ - STORE: { - equals: store, - }, - }) - } - if (construction_point) { - where.AND.push({ - CONSTRUCTION_POINT: { - equals: construction_point, - }, - }) + // 키워드 검색 조건 + if (params.keyword && params.searchOption) { + Object.assign(where, createKeywordSearchCondition(params.keyword, params.searchOption)) } - if (offset) { + // 회원 유형 조건 + Object.assign(where, createMemberRoleCondition(params)) + + // 데이터 조회 + if (params.offset) { // @ts-ignore const res = await prisma.SD_SURVEY_SALES_BASIC_INFO.findMany({ where, - orderBy: sort === 'created' ? { REG_DT: 'desc' } : { UPT_DT: 'desc' }, - skip: Number(offset), - take: 10, + orderBy: params.sort === 'created' ? { REG_DT: 'desc' } : { UPT_DT: 'desc' }, + skip: Number(params.offset), + take: ITEMS_PER_PAGE, }) return NextResponse.json(res) - } else { - // @ts-ignore - const count = await prisma.SD_SURVEY_SALES_BASIC_INFO.count({ - where, - }) - return NextResponse.json(count) } + + // 전체 개수 조회 + // @ts-ignore + const count = await prisma.SD_SURVEY_SALES_BASIC_INFO.count({ where }) + return NextResponse.json(count) } catch (error) { - console.error(error) - throw error + console.error('Error fetching surveys:', error) + return NextResponse.json({ error: 'Failed to fetch surveys' }, { status: 500 }) } } export async function PUT(request: Request) { - const body = await request.json() - const detailInfo = { ...body.detail_info, BASIC_INFO_ID: body.id } - // @ts-ignore - const res = await prisma.SD_SURVEY_SALES_DETAIL_INFO.create({ - data: detailInfo, - }) - return NextResponse.json({ message: 'Survey sales updated successfully' }) + try { + const body = await request.json() + const detailInfo = { ...body.detail_info, BASIC_INFO_ID: body.id } + // @ts-ignore + const res = await prisma.SD_SURVEY_SALES_DETAIL_INFO.create({ + data: detailInfo, + }) + return NextResponse.json({ message: 'Survey sales updated successfully' }) + } catch (error) { + console.error('Error updating survey:', error) + return NextResponse.json({ error: 'Failed to update survey' }, { status: 500 }) + } } diff --git a/src/components/survey-sale/detail/DetailButton.tsx b/src/components/survey-sale/detail/DetailButton.tsx index a2fc44f..d8471c9 100644 --- a/src/components/survey-sale/detail/DetailButton.tsx +++ b/src/components/survey-sale/detail/DetailButton.tsx @@ -8,20 +8,20 @@ export default function DetailButton({ isTemporary, surveyId, representative }: const router = useRouter() const { session } = useSessionStore() const { submitSurvey, deleteSurvey } = useServey(surveyId) - const [userNm, setUserNm] = useState('') + const [userId, setUserId] = useState('') useEffect(() => { if (session?.isLoggedIn) { - setUserNm(session?.userNm ?? '') + setUserId(session?.userId ?? '') } - }, [session, setUserNm]) + }, [session, setUserId]) const handleSubmit = async () => { if (isTemporary) { alert('一時保存されたデータは提出できません。') return } - if (userNm === representative) { + if (userId === representative) { if (confirm('提出しますか??')) { if (surveyId) { // TODO: 제출 페이지 추가 @@ -34,7 +34,7 @@ export default function DetailButton({ isTemporary, surveyId, representative }: } } const handleUpdate = () => { - if (userNm === representative) { + if (userId === representative) { router.push(`/survey-sale/basic-info?id=${surveyId}&isTemp=${isTemporary}`) } else { alert('担当者のみ修正可能です。') @@ -43,7 +43,7 @@ export default function DetailButton({ isTemporary, surveyId, representative }: const handleDelete = async () => { if (confirm('削除しますか?')) { if (surveyId) { - if (userNm === representative) { + if (userId === representative) { await deleteSurvey() router.push('/survey-sale') } else { diff --git a/src/components/survey-sale/detail/form/BasicForm.tsx b/src/components/survey-sale/detail/form/BasicForm.tsx index 6ed3d28..a8dd228 100644 --- a/src/components/survey-sale/detail/form/BasicForm.tsx +++ b/src/components/survey-sale/detail/form/BasicForm.tsx @@ -37,7 +37,7 @@ export default function BasicForm() { const [basicInfoData, setBasicInfoData] = useState(defaultBasicInfoForm) const { addressData } = useAddressStore() - const { userType, store, construction_point } = useUserType() + const { memberRole, store, builderNo } = useUserType() const { session } = useSessionStore() const popupController = usePopupController() @@ -58,11 +58,13 @@ export default function BasicForm() { if (session?.isLoggedIn) { setBasicInfoData((prev) => ({ ...prev, - REPRESENTATIVE: session?.userNm ?? '', + REPRESENTATIVE: session?.userId ?? '', + STORE: store ?? '', + CONSTRUCTION_POINT: builderNo ?? '', })) } setBasicInfoSelected() - }, [surveyDetail, addressData, session?.isLoggedIn, session?.userNm]) + }, [surveyDetail, addressData, session?.isLoggedIn, session?.userNm, store, builderNo]) const focusInput = (input: keyof SurveyBasicRequest) => { const inputElement = document.getElementById(input) @@ -117,11 +119,11 @@ export default function BasicForm() { type="text" className="input-frame" id="representative" - value={session?.userNm ? session?.userNm : basicInfoData.REPRESENTATIVE} + value={session?.userId ? session?.userId : basicInfoData.REPRESENTATIVE} onChange={(e) => handleChange('REPRESENTATIVE', e.target.value)} /> - {(userType === 'order' || userType?.includes('musubi')) && ( + {(memberRole === 'Builder' || memberRole?.includes('Admin')) && ( <>
販売店
@@ -135,14 +137,14 @@ export default function BasicForm() {
)} - {(userType === 'partner' || userType === 'musubi_con') && ( + {(memberRole === 'Partner' || memberRole === 'Builder') && (
施工店
handleChange('CONSTRUCTION_POINT', e.target.value)} />
diff --git a/src/components/survey-sale/list/ListTable.tsx b/src/components/survey-sale/list/ListTable.tsx index 63eb41b..2523aaa 100644 --- a/src/components/survey-sale/list/ListTable.tsx +++ b/src/components/survey-sale/list/ListTable.tsx @@ -6,7 +6,8 @@ import { useEffect, useState } from 'react' import { useRouter } from 'next/navigation' import SearchForm from './SearchForm' import { useSurveyFilterStore } from '@/store/surveyFilterStore' -import { UserType } from '@/hooks/useUserType' +import { MemberRole } from '@/hooks/useUserType' +import { useSessionStore } from '@/store/session' export default function ListTable() { const router = useRouter() @@ -15,7 +16,9 @@ export default function ListTable() { const [heldSurveyList, setHeldSurveyList] = useState([]) const [hasMore, setHasMore] = useState(false) - const [memberType, setMemberType] = useState('hwj') + const [memberRole, setMemberRole] = useState() + + const { session } = useSessionStore() useEffect(() => { if (surveyList && surveyList.length > 0) { @@ -29,9 +32,8 @@ export default function ListTable() { } setHasMore(surveyListCount > offset + 10) } - // 회원 유형 설정 이후 변경 - setMemberType('hwj') - }, [surveyList, surveyListCount, offset]) + setMemberRole(session.role as MemberRole) + }, [surveyList, surveyListCount, offset, session]) const handleDetailClick = (id: number) => { router.push(`/survey-sale/${id}`) @@ -45,7 +47,7 @@ export default function ListTable() { return ( <> - + {heldSurveyList.length > 0 ? (
    diff --git a/src/components/survey-sale/list/SearchForm.tsx b/src/components/survey-sale/list/SearchForm.tsx index 6cd8b57..1b46349 100644 --- a/src/components/survey-sale/list/SearchForm.tsx +++ b/src/components/survey-sale/list/SearchForm.tsx @@ -1,11 +1,11 @@ 'use client' import { SEARCH_OPTIONS, SEARCH_OPTIONS_ENUM, SEARCH_OPTIONS_PARTNERS, useSurveyFilterStore } from '@/store/surveyFilterStore' -import { UserType } from '@/hooks/useUserType' +import { MemberRole } from '@/hooks/useUserType' import { useRouter } from 'next/navigation' import { useState } from 'react' -export default function SearchForm({ onItemsInit, memberType }: { onItemsInit: () => void; memberType: UserType }) { +export default function SearchForm({ onItemsInit, memberRole }: { onItemsInit: () => void; memberRole: MemberRole }) { const router = useRouter() const { setSearchOption, setSort, setIsMySurvey, setKeyword, isMySurvey, keyword, searchOption, sort } = useSurveyFilterStore() const [searchKeyword, setSearchKeyword] = useState(keyword) @@ -20,7 +20,7 @@ export default function SearchForm({ onItemsInit, memberType }: { onItemsInit: ( setKeyword(searchKeyword) onItemsInit() } - const searchOptions = memberType === 'partner' ? SEARCH_OPTIONS_PARTNERS : SEARCH_OPTIONS + const searchOptions = memberRole === 'Partner' ? SEARCH_OPTIONS_PARTNERS : SEARCH_OPTIONS return (
    diff --git a/src/hooks/useSurvey.ts b/src/hooks/useSurvey.ts index ea04db4..909d220 100644 --- a/src/hooks/useSurvey.ts +++ b/src/hooks/useSurvey.ts @@ -41,13 +41,13 @@ export function useServey(id?: number): { } { const queryClient = useQueryClient() const { keyword, searchOption, isMySurvey, sort, offset } = useSurveyFilterStore() - const { store, construction_point } = useUserType() + const { store, builderNo, memberRole } = useUserType() const { data: surveyList, isLoading: isLoadingSurveyList } = useQuery({ - queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort, offset, store, construction_point], + queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort, offset, store, builderNo, memberRole], queryFn: async () => { const resp = await axiosInstance(null).get('/api/survey-sales', { - params: { keyword, searchOption, isMySurvey, sort, offset, store, construction_point }, + params: { keyword, searchOption, isMySurvey, sort, offset, store, builderNo, memberRole }, }) return resp.data }, diff --git a/src/hooks/useUserType.ts b/src/hooks/useUserType.ts index e8a9dcd..5f38e58 100644 --- a/src/hooks/useUserType.ts +++ b/src/hooks/useUserType.ts @@ -1,24 +1,31 @@ import { useSessionStore } from '@/store/session' import { useEffect, useState } from 'react' -export type UserType = 'hwj' | 'order' | 'musubi' | 'musubi_con' | 'partner' +export type MemberRole = 'T01' | 'Admin' | 'Admin_Sub' | 'Builder' | 'Partner' | 'User' +// TO1 - else +// Admin - 1차점, Admin_Sub - 2차점, Builder - 2차점 시공사, Partner - 파트너사, User - else // 로그인 된 회원 유형에 따라 조사 매물 목록 변경됨 export function useUserType() { const { session } = useSessionStore() - const [userType, setUserType] = useState(null) + const [memberRole, setMemberRole] = useState(null) const [store, setStore] = useState(null) - const [construction_point, setConstructionPoint] = useState(null) + const [builderNo, setBuilderNo] = useState(null) - // TODO: 회원 유형 설정 useEffect(() => { if (!session?.isLoggedIn) return - setUserType('musubi_con') + setMemberRole(session.role as MemberRole) + if (session.role === 'T01') { + setStore(null) + } else { + setStore(session.storeId) + } + setBuilderNo(session.builderNo) }, [session]) return { - userType, + memberRole, store, - construction_point, + builderNo, } }