Compare commits
No commits in common. "da0d77724dcf3778ef772b2ed86e101af29e50eb" and "68408eb3c92b44af4dee90777e427d1e42e14421" have entirely different histories.
da0d77724d
...
68408eb3c9
4
.env
4
.env
@ -5,8 +5,8 @@
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
# DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
|
||||
DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
|
||||
# DATABASE_URL="mysql://root:root@localhost:3306/onsitesurvey"
|
||||
# DATABASE_URL="sqlserver://3team.devgrr.kr:1433;database=onsitesurvey;user=sa;password=1q2w3e4r!;encrypt=true;trustServerCertificate=true;"
|
||||
DATABASE_URL="mysql://root:root@localhost:3306/onsitesurvey"
|
||||
|
||||
# SESSION_PASSWORD="QWERASDFZXCV1234567890REWQFDSAVCXZ"
|
||||
SESSION_PASSWORD="This application is for mobile field research"
|
||||
@ -12,10 +12,10 @@ const nextConfig: NextConfig = {
|
||||
source: '/api/user/login',
|
||||
destination: `${process.env.NEXT_PUBLIC_QSP_API_URL}/api/user/login`,
|
||||
},
|
||||
// {
|
||||
// source: '/api/:path*',
|
||||
// destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
|
||||
// },
|
||||
{
|
||||
source: '/api/:path*',
|
||||
destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
|
||||
},
|
||||
]
|
||||
},
|
||||
}
|
||||
|
||||
@ -3,10 +3,11 @@ generator client {
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "sqlserver"
|
||||
provider = "mysql"
|
||||
url = env("DATABASE_URL")
|
||||
}
|
||||
|
||||
// 사용자 정보
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
@ -19,161 +20,186 @@ model User {
|
||||
updated_at DateTime @updatedAt
|
||||
}
|
||||
|
||||
// 지붕재 적합성 정보
|
||||
model MS_SUITABLE {
|
||||
//일련번호
|
||||
id Int @id @default(autoincrement())
|
||||
//제품명
|
||||
product_name String @db.VarChar(200)
|
||||
//제조업체명
|
||||
manufacturer String? @db.VarChar(200)
|
||||
//지붕재
|
||||
roof_material String? @db.VarChar(100)
|
||||
//금구형태(쇠붙이형)
|
||||
shape String? @db.VarChar(200)
|
||||
support_roof_tile String? @db.VarChar(2)
|
||||
//지지 기와
|
||||
support_roof_tile String? @db.VarChar(1)
|
||||
//지지 기와 메모
|
||||
support_roof_tile_memo String? @db.VarChar(500)
|
||||
//지지 금구
|
||||
support_roof_bracket String? @db.VarChar(200)
|
||||
//지지 금구 메모
|
||||
support_roof_bracket_memo String? @db.VarChar(500)
|
||||
//yg 앵커
|
||||
yg_anchor String? @db.VarChar(200)
|
||||
//yg 앵커 메모
|
||||
yg_anchor_memo String? @db.VarChar(500)
|
||||
//rg 지붕판
|
||||
rg_roof_tile_part String? @db.VarChar(200)
|
||||
//rg 지붕판 메모
|
||||
rg_roof_tile_part_memo String? @db.VarChar(500)
|
||||
//다이도헌트 지지 기와2
|
||||
dido_hunt_support_tile_2 String? @db.VarChar(200)
|
||||
//다이도헌트 지지 기와2 메모
|
||||
dido_hunt_support_tile_2_memo String? @db.VarChar(500)
|
||||
//타카시마 파워 베이스
|
||||
takashima_power_base String? @db.VarChar(200)
|
||||
//타카시마 파워 베이스 메모
|
||||
takashima_power_base_memo String? @db.VarChar(500)
|
||||
//타카시마용 금구
|
||||
takashima_tile_bracket String? @db.VarChar(200)
|
||||
//타카시마용 금구 메모
|
||||
takashima_tile_bracket_memo String? @db.VarChar(500)
|
||||
//슬레이트 금구4
|
||||
slate_bracket_4 String? @db.VarChar(200)
|
||||
//슬레이트 금구4 메모
|
||||
slate_bracket_4_memo String? @db.VarChar(500)
|
||||
//슬레이트 판금 금구(슬레이트, 싱글)
|
||||
slate_single_metal_bracket String? @db.VarChar(200)
|
||||
//슬레이트 판금 금구 메모(슬레이트, 싱글)
|
||||
slate_single_metal_bracket_memo String? @db.VarChar(500)
|
||||
//다이도헌트 짧은 트랙4
|
||||
dido_hunt_short_rack_4 String? @db.VarChar(200)
|
||||
//다이도헌트 짧은 트랙4 메모
|
||||
dido_hunt_short_rack_4_memo String? @db.VarChar(500)
|
||||
//타카시마 슬레이트 금구
|
||||
takashima_slate_bracket_slate_single String? @db.VarChar(200)
|
||||
//타카시마 슬레이트 금구 메모
|
||||
takashima_slate_bracket_slate_single_memo String? @db.VarChar(500)
|
||||
//df 판금 금구
|
||||
df_metal_bracket String? @db.VarChar(200)
|
||||
//df 판금 금구 메모
|
||||
df_metal_bracket_memo String? @db.VarChar(500)
|
||||
//슬레이트 판금 금구(금속 지붕)
|
||||
slate_metal_bracket String? @db.VarChar(200)
|
||||
//슬레이트 판금 금구(금속 지붕) 메모
|
||||
slate_metal_bracket_memo String? @db.VarChar(500)
|
||||
//타카시마 슬레이트 금구(금속 지붕)
|
||||
takashima_slate_bracket_metal_roof String? @db.VarChar(200)
|
||||
//타카시마 슬레이트 금구(금속 지붕) 메모
|
||||
takashima_slate_bracket_metal_roof_memo String? @db.VarChar(500)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
}
|
||||
|
||||
// 조사 매물 기본 정보
|
||||
model SD_SERVEY_SALES_BASIC_INFO {
|
||||
//일련번호
|
||||
id Int @id @default(autoincrement())
|
||||
//담당자명
|
||||
representative String @db.VarChar(200)
|
||||
//판매점
|
||||
store String? @db.VarChar(200)
|
||||
//시공점
|
||||
construction_point String? @db.VarChar(200)
|
||||
//현재 조사일
|
||||
investigation_date String? @db.VarChar(10)
|
||||
//건물명
|
||||
building_name String? @db.VarChar(200)
|
||||
//고객명
|
||||
customer_name String? @db.VarChar(200)
|
||||
//우편번호
|
||||
post_code String? @db.VarChar(10)
|
||||
//주소
|
||||
address String? @db.VarChar(200)
|
||||
//상세주소
|
||||
address_detail String? @db.VarChar(300)
|
||||
//제출상태
|
||||
submission_status Boolean @default(false)
|
||||
//제출일
|
||||
submission_date DateTime? @db.Date
|
||||
//상세정보
|
||||
detail_info SD_SERVEY_SALES_DETAIL_INFO?
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
detail_info SD_SERVEY_SALES_DETAIL_INFO?
|
||||
}
|
||||
|
||||
// 조사 매물 전기 지붕 정보
|
||||
model SD_SERVEY_SALES_DETAIL_INFO {
|
||||
//일련번호
|
||||
id Int @id @default(autoincrement())
|
||||
//전기계약 용량
|
||||
contract_capacity String? @db.VarChar(20)
|
||||
//전기 소매 회사
|
||||
retail_company String? @db.VarChar(100)
|
||||
//전기 부대 설비
|
||||
supplementary_facilities String? @db.VarChar(20)
|
||||
//전기 부대 설비 기타
|
||||
supplementary_facilities_etc String? @db.VarChar(200)
|
||||
installation_system Int?
|
||||
//설치 희망 시스템
|
||||
installation_system Int? @db.Int
|
||||
//설치 희망 시스템 기타
|
||||
installation_system_etc String? @db.VarChar(200)
|
||||
construction_year Int?
|
||||
//건축 연수
|
||||
construction_year Int? @db.Int
|
||||
//건축 연수 기타
|
||||
construction_year_etc String? @db.VarChar(200)
|
||||
//지붕재
|
||||
roof_material String? @db.VarChar(20)
|
||||
//지붕재 기타
|
||||
roof_material_etc String? @db.VarChar(200)
|
||||
roof_shape Int?
|
||||
//지붕 모양
|
||||
roof_shape Int? @db.Int
|
||||
//지붕 모양 기타
|
||||
roof_shape_etc String? @db.VarChar(200)
|
||||
//지붕 경사도
|
||||
roof_slope String? @db.VarChar(5)
|
||||
house_structure Int?
|
||||
//주택 구조
|
||||
house_structure Int? @db.Int
|
||||
//주택 구조 기타
|
||||
house_structure_etc String? @db.VarChar(200)
|
||||
rafter_material Int?
|
||||
//서까래 재질
|
||||
rafter_material Int? @db.Int
|
||||
//서까래 재질 기타
|
||||
rafter_material_etc String? @db.VarChar(200)
|
||||
rafter_size Int?
|
||||
//서까래 크기
|
||||
rafter_size Int? @db.Int
|
||||
//서까래 크기 기타
|
||||
rafter_size_etc String? @db.VarChar(200)
|
||||
rafter_pitch Int?
|
||||
//서까래 피치
|
||||
rafter_pitch Int? @db.Int
|
||||
//서까래 피치 기타
|
||||
rafter_pitch_etc String? @db.VarChar(200)
|
||||
rafter_direction Int?
|
||||
open_field_plate_kind Int?
|
||||
//서까래 방향
|
||||
rafter_direction Int? @db.Int
|
||||
//노지판 종류
|
||||
open_field_plate_kind Int? @db.Int
|
||||
//노지판 종류 기타
|
||||
open_field_plate_kind_etc String? @db.VarChar(200)
|
||||
//노지판 두께
|
||||
open_field_plate_thickness String? @db.VarChar(5)
|
||||
//누수 흔적
|
||||
leak_trace Boolean? @default(false)
|
||||
waterproof_material Int?
|
||||
//방수재 종류
|
||||
waterproof_material Int? @db.Int
|
||||
//방수재 종류 기타
|
||||
waterproof_material_etc String? @db.VarChar(200)
|
||||
insulation_presence Int?
|
||||
//단열재 여부
|
||||
insulation_presence Int? @db.Int
|
||||
//단열재 여부 기타
|
||||
insulation_presence_etc String? @db.VarChar(200)
|
||||
structure_order Int?
|
||||
//지붕 구조 순서
|
||||
structure_order Int? @db.Int
|
||||
//지붕 구조 순서 기타
|
||||
structure_order_etc String? @db.VarChar(200)
|
||||
installation_availability Int?
|
||||
//설치 가능 여부
|
||||
installation_availability Int? @db.Int
|
||||
//설치 가능 여부 기타
|
||||
installation_availability_etc String? @db.VarChar(200)
|
||||
//메모
|
||||
memo String? @db.VarChar(500)
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
basic_info_id Int @unique
|
||||
basic_info SD_SERVEY_SALES_BASIC_INFO @relation(fields: [basic_info_id], references: [id])
|
||||
}
|
||||
|
||||
model BC_COMM_H {
|
||||
HEAD_CD String @id(map: "PK_BC_COMM_H") @db.NVarChar(6)
|
||||
HEAD_ID String @db.NVarChar(100)
|
||||
HEAD_NM String @db.NVarChar(100)
|
||||
HEAD_JP String @db.NVarChar(100)
|
||||
HEAD_4TH String @db.NVarChar(100)
|
||||
REF_CHR1 String @db.NVarChar(100)
|
||||
REF_CHR2 String @db.NVarChar(100)
|
||||
REF_CHR3 String @db.NVarChar(100)
|
||||
REF_CHR4 String @db.NVarChar(100)
|
||||
REF_CHR5 String @db.NVarChar(100)
|
||||
REF_NUM1 String @db.NVarChar(100)
|
||||
REF_NUM2 String @db.NVarChar(100)
|
||||
REF_NUM3 String @db.NVarChar(100)
|
||||
REF_NUM4 String @db.NVarChar(100)
|
||||
REF_NUM5 String @db.NVarChar(100)
|
||||
REMARKS String @db.NVarChar(200)
|
||||
SAP_YN String @db.NVarChar(1)
|
||||
STAT_CD String @db.NVarChar(1)
|
||||
DEL_YN String @db.NVarChar(1)
|
||||
REG_DT DateTime? @db.DateTime
|
||||
REG_ID String @db.NVarChar(50)
|
||||
UPT_DT DateTime? @db.DateTime
|
||||
UPT_ID String @db.NVarChar(50)
|
||||
QC_COMM_YN String? @default("N", map: "DF__BC_COMM_H__QC_CO__48CFD27E") @db.NVarChar(1)
|
||||
BC_COMM_L BC_COMM_L[]
|
||||
|
||||
@@index([HEAD_ID], map: "BC_COMM_H_HEAD_ID_IDX")
|
||||
}
|
||||
|
||||
model BC_COMM_L {
|
||||
HEAD_CD String @db.NVarChar(6)
|
||||
CODE String @db.NVarChar(50)
|
||||
READ_CD String? @db.NVarChar(50)
|
||||
CODE_NM String? @db.NVarChar(100)
|
||||
CODE_JP String? @db.NVarChar(100)
|
||||
CODE_4TH String? @db.NVarChar(100)
|
||||
REF_CHR1 String? @db.NVarChar(150)
|
||||
REF_CHR2 String? @db.NVarChar(150)
|
||||
REF_CHR3 String? @db.NVarChar(150)
|
||||
REF_CHR4 String? @db.NVarChar(150)
|
||||
REF_CHR5 String? @db.NVarChar(150)
|
||||
REF_NUM1 Decimal? @db.Decimal(22, 5)
|
||||
REF_NUM2 Decimal? @db.Decimal(22, 5)
|
||||
REF_NUM3 Decimal? @db.Decimal(22, 5)
|
||||
REF_NUM4 Decimal? @db.Decimal(22, 5)
|
||||
REF_NUM5 Decimal? @db.Decimal(22, 5)
|
||||
PRIORITY Decimal? @db.Decimal(3, 0)
|
||||
REF_CNT String? @db.NVarChar(5)
|
||||
STAT_CD String? @db.NVarChar(1)
|
||||
DEL_YN String? @db.NVarChar(1)
|
||||
REG_DT DateTime? @db.DateTime
|
||||
REG_ID String? @db.NVarChar(50)
|
||||
UPT_DT DateTime? @db.DateTime
|
||||
UPT_ID String? @db.NVarChar(50)
|
||||
BC_COMM_H BC_COMM_H @relation(fields: [HEAD_CD], references: [HEAD_CD], onUpdate: NoAction, map: "FK_BC_COMM_L")
|
||||
|
||||
@@id([HEAD_CD, CODE], map: "PK_BC_COMM_L")
|
||||
basic_info_id Int @unique
|
||||
}
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.5 2.00372C10.6049 1.99039 9.7047 2.01289 8.8294 2.07107C4.64639 2.34913 1.31441 5.72838 1.04024 9.97072C0.986587 10.8009 0.986587 11.6607 1.04024 12.4909C1.1401 14.036 1.82343 15.4666 2.62791 16.6746C3.09501 17.5203 2.78674 18.5758 2.30021 19.4978C1.94941 20.1626 1.77401 20.495 1.91484 20.7351C2.05568 20.9752 2.37026 20.9829 2.99943 20.9982C4.24367 21.0285 5.08268 20.6757 5.74868 20.1846C6.1264 19.9061 6.31527 19.7668 6.44544 19.7508C6.5756 19.7348 6.83177 19.8403 7.34401 20.0513C7.8044 20.2409 8.33896 20.3579 8.8294 20.3905C10.2536 20.4852 11.7435 20.4854 13.1706 20.3905C17.3536 20.1125 20.6856 16.7332 20.9598 12.4909C21.0021 11.836 21.011 11.1627 20.9866 10.5" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M7.5 14H14.5M7.5 9H11" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<path d="M14 4.5H21M17.5 1L17.5 8" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@ -1,4 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0.5" y="0.5" width="21" height="21" rx="10.5" stroke="#A8B6C7"/>
|
||||
<path d="M7 13L11 9L15 13" stroke="#A8B6C7" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 289 B |
@ -1,4 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 11C20 6.02944 15.9706 2 11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C15.9706 20 20 15.9706 20 11Z" fill="#F1FFF4" stroke="#6CB67C"/>
|
||||
<path d="M7 11.5875L9.5625 14.15L15.2 8" stroke="#6CB67C" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 387 B |
@ -1,4 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 11C20 6.02944 15.9706 2 11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C15.9706 20 20 15.9706 20 11Z" fill="#EFF7FF" stroke="#67A9EF"/>
|
||||
<path d="M10.9219 14.7871C11.3002 14.8253 11.5956 15.1448 11.5957 15.5332C11.5957 15.9217 11.3002 16.2411 10.9219 16.2793L10.8457 16.2832H10.833C10.419 16.283 10.083 15.9473 10.083 15.5332C10.0831 15.1192 10.4191 14.7834 10.833 14.7832H10.8457L10.9219 14.7871ZM10.835 5C12.4218 5.00022 13.6687 6.32107 13.6689 7.90039C13.6689 8.4001 13.545 8.87321 13.3252 9.28613L13.2256 9.45898C13.0473 9.74483 12.8377 10.0187 12.6436 10.2676C12.4417 10.5263 12.2576 10.7567 12.0908 10.9932C11.7534 11.4716 11.5851 11.8465 11.585 12.2002V12.7383C11.5847 13.1522 11.2489 13.4881 10.835 13.4883C10.4209 13.4883 10.0852 13.1523 10.085 12.7383V12.2002C10.0851 11.3669 10.4817 10.6716 10.8652 10.1279C11.059 9.85327 11.2756 9.58331 11.4609 9.3457C11.6538 9.09844 11.8192 8.87814 11.9521 8.66504L12.0439 8.49316C12.1238 8.3143 12.1689 8.11368 12.1689 7.90039C12.1687 7.10541 11.55 6.50023 10.835 6.5C10.1198 6.50002 9.50021 7.10528 9.5 7.90039C9.49985 8.31448 9.16412 8.65039 8.75 8.65039C8.33589 8.65037 8.00015 8.31447 8 7.90039C8.00021 6.32095 9.24791 5.00002 10.835 5Z" fill="#67A9EF"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,4 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 11C20 6.02944 15.9706 2 11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C15.9706 20 20 15.9706 20 11Z" fill="#F6F6F6" stroke="#999999"/>
|
||||
<path d="M11.0088 13.75C11.5611 13.75 12.0088 14.1977 12.0088 14.75C12.0088 15.3023 11.5611 15.75 11.0088 15.75H11C10.4477 15.75 10 15.3023 10 14.75C10 14.1977 10.4477 13.75 11 13.75H11.0088ZM11 6C11.4142 6 11.75 6.33579 11.75 6.75V11.75C11.75 12.1642 11.4142 12.5 11 12.5C10.5858 12.5 10.25 12.1642 10.25 11.75V6.75C10.25 6.33579 10.5858 6 11 6Z" fill="#999999"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 626 B |
@ -1,4 +0,0 @@
|
||||
<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20 11C20 6.02944 15.9706 2 11 2C6.02944 2 2 6.02944 2 11C2 15.9706 6.02944 20 11 20C15.9706 20 20 15.9706 20 11Z" fill="#FFF3F7" stroke="#EB6F94"/>
|
||||
<path d="M8 8L11 11M11 11L14 14M11 11L14 8M11 11L8 14" stroke="#EB6F94" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 401 B |
@ -14,7 +14,7 @@ export async function POST(request: Request) {
|
||||
loginId,
|
||||
pwd,
|
||||
})
|
||||
// console.log('🚀 ~ result ~ result:', result)
|
||||
console.log('🚀 ~ result ~ result:', result)
|
||||
|
||||
if (result.data.result.code === 200) {
|
||||
const cookieStore = await cookies()
|
||||
@ -54,5 +54,5 @@ export async function POST(request: Request) {
|
||||
await session.save()
|
||||
}
|
||||
|
||||
return NextResponse.json({ code: 200, message: 'Login is Succecss!!', result: result.data.data })
|
||||
return NextResponse.json({ code: 200, message: 'Login is Succecss!!' })
|
||||
}
|
||||
|
||||
72
src/app/api/survey-sale/[id]/route.ts
Normal file
72
src/app/api/survey-sale/[id]/route.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
const body = await request.json()
|
||||
const { id } = await context.params
|
||||
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
export async function GET(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.findUnique({
|
||||
where: { id: Number(id) },
|
||||
include: {
|
||||
detail_info: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json(survey)
|
||||
}
|
||||
|
||||
export async function PUT(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
const body = await request.json()
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
...body,
|
||||
detail_info: {
|
||||
update: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json(survey)
|
||||
}
|
||||
|
||||
export async function DELETE(request: Request, context: { params: { id: string; detail_id: string } }) {
|
||||
const { id, detail_id } = await context.params
|
||||
if (detail_id) {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_DETAIL_INFO.delete({
|
||||
where: { id: Number(detail_id) },
|
||||
})
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.delete({
|
||||
where: { id: Number(id) },
|
||||
})
|
||||
}
|
||||
return NextResponse.json({ message: 'Survey deleted successfully' })
|
||||
}
|
||||
|
||||
export async function PATCH(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
}
|
||||
33
src/app/api/survey-sale/route.ts
Normal file
33
src/app/api/survey-sale/route.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const body = await request.json()
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.create({
|
||||
data: body,
|
||||
})
|
||||
return NextResponse.json(res)
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany()
|
||||
return NextResponse.json(res)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
const body = await request.json()
|
||||
console.log('🚀 ~ PUT ~ body:', body)
|
||||
const detailInfo = { ...body.detail_info, basic_info_id: body.id }
|
||||
console.log('🚀 ~ PUT ~ detailInfo:', detailInfo)
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_DETAIL_INFO.create({
|
||||
data: detailInfo,
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey sales updated successfully' })
|
||||
}
|
||||
@ -1,5 +1,21 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
|
||||
export async function POST(request: Request, context: { params: { id: string } }) {
|
||||
const body = await request.json()
|
||||
const { id } = await context.params
|
||||
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
|
||||
export async function GET(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
// @ts-ignore
|
||||
@ -64,45 +80,12 @@ export async function DELETE(request: Request, context: { params: { id: string }
|
||||
|
||||
export async function PATCH(request: Request, context: { params: { id: string } }) {
|
||||
const { id } = await context.params
|
||||
const body = await request.json()
|
||||
if (body.submit) {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
submission_date: new Date(),
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const hasDetails = await prisma.SD_SERVEY_SALES_DETAIL_INFO.findUnique({
|
||||
where: { basic_info_id: Number(id) },
|
||||
})
|
||||
if (hasDetails) {
|
||||
//@ts-ignore
|
||||
const result = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
updated_at: new Date(),
|
||||
detail_info: {
|
||||
update: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json(result)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
detail_info: {
|
||||
create: body.detail_info,
|
||||
},
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey detail created successfully' })
|
||||
}
|
||||
}
|
||||
// @ts-ignore
|
||||
const survey = await prisma.SD_SERVEY_SALES_BASIC_INFO.update({
|
||||
where: { id: Number(id) },
|
||||
data: {
|
||||
submission_status: true,
|
||||
},
|
||||
})
|
||||
return NextResponse.json({ message: 'Survey confirmed successfully' })
|
||||
}
|
||||
|
||||
@ -10,75 +10,32 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json(res)
|
||||
}
|
||||
|
||||
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 searchOptions = ['building_name', 'representative', 'store', 'construction_point', 'customer_name', 'post_code', 'address', 'address_detail']
|
||||
export async function GET() {
|
||||
try {
|
||||
const where: any = {}
|
||||
|
||||
if (isMySurvey !== null && isMySurvey !== undefined) {
|
||||
where.representative = isMySurvey
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
|
||||
where,
|
||||
orderBy: sort === 'created' ? { created_at: 'desc' } : { updated_at: 'desc' },
|
||||
skip: Number(offset),
|
||||
take: 10,
|
||||
})
|
||||
return NextResponse.json(res)
|
||||
} else {
|
||||
// @ts-ignore
|
||||
const count = await prisma.SD_SERVEY_SALES_BASIC_INFO.count({
|
||||
where,
|
||||
})
|
||||
return NextResponse.json(count)
|
||||
}
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany()
|
||||
return NextResponse.json(res)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// export async function GET(request: Request) {
|
||||
// // @ts-ignore
|
||||
// const res = await prisma.SD_SERVEY_SALES_BASIC_INFO.findMany({
|
||||
// include: {
|
||||
// detail_info: true,
|
||||
// },
|
||||
// })
|
||||
// return NextResponse.json(res)
|
||||
// }
|
||||
|
||||
export async function PUT(request: Request) {
|
||||
const body = await request.json()
|
||||
console.log('🚀 ~ PUT ~ body:', body)
|
||||
const detailInfo = { ...body.detail_info, basic_info_id: body.id }
|
||||
console.log('🚀 ~ PUT ~ detailInfo:', detailInfo)
|
||||
// @ts-ignore
|
||||
const res = await prisma.SD_SERVEY_SALES_DETAIL_INFO.create({
|
||||
data: detailInfo,
|
||||
|
||||
@ -2,8 +2,7 @@ import { NextResponse } from 'next/server'
|
||||
import { prisma } from '@/libs/prisma'
|
||||
import { getIronSession } from 'iron-session'
|
||||
import { cookies } from 'next/headers'
|
||||
import { sessionOptions } from '@/libs/session'
|
||||
import type { SessionData } from '@/types/Auth'
|
||||
import { SessionData, sessionOptions } from '@/libs/session'
|
||||
|
||||
export async function POST(request: Request) {
|
||||
const { username, password } = await request.json()
|
||||
@ -26,7 +25,7 @@ export async function POST(request: Request) {
|
||||
const cookieStore = await cookies()
|
||||
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||
console.log('start session edit!')
|
||||
session.userNm = user.username!
|
||||
session.username = user.username!
|
||||
session.email = user.email!
|
||||
session.isLoggedIn = true
|
||||
console.log('end session edit!')
|
||||
|
||||
9
src/app/inquiry/write/page.tsx
Normal file
9
src/app/inquiry/write/page.tsx
Normal file
@ -0,0 +1,9 @@
|
||||
import InquiryWriteForm from '@/components/inquiry/InquiryWriteForm'
|
||||
|
||||
export default function InquiryWrite() {
|
||||
return (
|
||||
<div>
|
||||
<InquiryWriteForm />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import type { Metadata } from 'next'
|
||||
|
||||
import ReactQueryProviders from '@/providers/ReactQueryProvider'
|
||||
import EdgeProvider from '@/providers/EdgeProvider'
|
||||
import PopupController from '@/components/ui/PopupController'
|
||||
|
||||
import '@/styles/style.scss'
|
||||
@ -18,24 +17,22 @@ interface RootLayoutProps {
|
||||
header: ReactNode
|
||||
footer: ReactNode
|
||||
floatBtn: ReactNode
|
||||
}6
|
||||
}
|
||||
|
||||
export default async function RootLayout({ children, header, footer, floatBtn }: RootLayoutProps): Promise<ReactNode> {
|
||||
return (
|
||||
<ReactQueryProviders>
|
||||
<EdgeProvider>
|
||||
<html lang="ja" suppressHydrationWarning>
|
||||
<body>
|
||||
<div className="wrap">
|
||||
{header}
|
||||
{children}
|
||||
{footer}
|
||||
{floatBtn}
|
||||
</div>
|
||||
<PopupController />
|
||||
</body>
|
||||
</html>
|
||||
</EdgeProvider>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<div className="wrap">
|
||||
{header}
|
||||
{children}
|
||||
{footer}
|
||||
{floatBtn}
|
||||
</div>
|
||||
<PopupController />
|
||||
</body>
|
||||
</html>
|
||||
</ReactQueryProviders>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,240 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function page() {
|
||||
const [fileName, setFileName] = useState<File | null>(null) //file name value
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="form-design-wrap">
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Input</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<input className="input-frame" type="text" placeholder="Input Frame Text" />
|
||||
<input className="input-frame" type="number" placeholder="Input Frame Number" />
|
||||
<input className="input-frame" type="password" placeholder="Input Frame Password" />
|
||||
<input className="input-frame" type="email" placeholder="Input Frame Email" />
|
||||
<input className="input-frame" type="text" placeholder="Input Frame Disabled" defaultValue={'defaultValue'} disabled />
|
||||
<input className="input-frame" type="text" placeholder="Input Frame ReadOnly" readOnly />
|
||||
<div className="filebox">
|
||||
<label className="btn-frame l-blue icon" htmlFor="file">
|
||||
<i className="btn-clip"></i>Upload File
|
||||
</label>
|
||||
<input type="file" id="file" onChange={(e) => setFileName(e.target.files?.[0] ?? null)} />
|
||||
</div>
|
||||
<div className="search-input">
|
||||
<input type="text" className="search-frame" placeholder="Input Frame Search" />
|
||||
<button className="search-icon"></button>
|
||||
</div>
|
||||
<div className="date-input">
|
||||
<button className="date-btn">
|
||||
<i className="date-icon"></i>
|
||||
</button>
|
||||
<input type="text" className="date-frame" placeholder="Input Frame Date" />
|
||||
</div>
|
||||
<div className="login-input id">
|
||||
<input type="text" className="login-frame" placeholder="Input Frame ID" />
|
||||
<button className="login-icon">
|
||||
<i className="del-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="login-input pw">
|
||||
<input type="password" className="login-frame" placeholder="Input Frame PW" />
|
||||
<button className="login-icon act">
|
||||
<i className="show-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="login-input pw change">
|
||||
<input type="password" className="login-frame" placeholder="Input Frame PW" />
|
||||
<button className="login-icon act">
|
||||
<i className="show-icon"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Button</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<button className="btn-frame d-blue min">Block Button</button>
|
||||
<button className="btn-frame n-blue min">Block Button</button>
|
||||
<button className="btn-frame red min">Block Button</button>
|
||||
<button className="btn-frame d-blue">Block Button</button>
|
||||
<button className="btn-frame n-blue">Block Button</button>
|
||||
<button className="btn-frame l-blue">Block Button</button>
|
||||
<button className="btn-frame red">Block Button</button>
|
||||
<button className="btn-frame d-blue icon">
|
||||
Block Icon Button<i className="btn-arr"></i>
|
||||
</button>
|
||||
<button className="btn-frame n-blue icon">
|
||||
Block Icon Button<i className="btn-arr"></i>
|
||||
</button>
|
||||
<button className="btn-frame n-blue icon">
|
||||
Block Icon Button<i className="btn-edit"></i>
|
||||
</button>
|
||||
<button className="btn-frame red icon">
|
||||
Block Icon Button<i className="btn-arr"></i>
|
||||
</button>
|
||||
<button className="btn-frame l-blue icon">
|
||||
<i className="btn-clip"></i>Block Icon Button
|
||||
</button>
|
||||
<button className="btn-frame icon login">
|
||||
Block Big Button <i className="btn-arr"></i>
|
||||
</button>
|
||||
<button className="btn-frame" disabled>
|
||||
Block Button
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Check Box</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch01" />
|
||||
<label htmlFor="ch01">Check Box</label>
|
||||
</div>
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch02" />
|
||||
<label htmlFor="ch02">Check Box</label>
|
||||
</div>
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch03" defaultChecked />
|
||||
<label htmlFor="ch03">Check Box</label>
|
||||
</div>
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch04" defaultChecked />
|
||||
<label htmlFor="ch04">Check Box</label>
|
||||
</div>
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch05" disabled />
|
||||
<label htmlFor="ch05">Check Box</label>
|
||||
</div>
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id="ch06" disabled />
|
||||
<label htmlFor="ch06">Check Box</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Radio Button</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<div className="radio-form-box">
|
||||
<input type="radio" name="radio01" id="ra01" />
|
||||
<label htmlFor="ra01">Radio Button</label>
|
||||
</div>
|
||||
<div className="radio-form-box">
|
||||
<input type="radio" name="radio01" id="ra02" defaultChecked />
|
||||
<label htmlFor="ra02">Radio Button</label>
|
||||
</div>
|
||||
<div className="radio-form-box">
|
||||
<input type="radio" name="radio03" id="ra05" disabled />
|
||||
<label htmlFor="ra05">Radio Button</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Toggle Button</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<div>
|
||||
<label className="toggle-btn">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
<div className="toggle-form">
|
||||
<label className="toggle-btn">
|
||||
<input type="checkbox" />
|
||||
<span className="slider"></span>
|
||||
</label>
|
||||
<div className="toggle-name">Q.PARTNERS</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">Select Box</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">Select01</option>
|
||||
<option value="">Select02</option>
|
||||
<option value="">Select03</option>
|
||||
<option value="">Select04</option>
|
||||
<option value="">Select05</option>
|
||||
</select>
|
||||
<select className="select-form" name="" id="" disabled>
|
||||
<option value="">Select01</option>
|
||||
<option value="">Select02</option>
|
||||
<option value="">Select03</option>
|
||||
<option value="">Select04</option>
|
||||
<option value="">Select05</option>
|
||||
</select>
|
||||
<div className="select-flex-form">
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">Select01</option>
|
||||
<option value="">Select02</option>
|
||||
<option value="">Select03</option>
|
||||
<option value="">Select04</option>
|
||||
<option value="">Select05</option>
|
||||
</select>
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">Select01</option>
|
||||
<option value="">Select02</option>
|
||||
<option value="">Select03</option>
|
||||
<option value="">Select04</option>
|
||||
<option value="">Select05</option>
|
||||
</select>
|
||||
<select className="select-form" name="" id="" disabled>
|
||||
<option value="">Select01</option>
|
||||
<option value="">Select02</option>
|
||||
<option value="">Select03</option>
|
||||
<option value="">Select04</option>
|
||||
<option value="">Select05</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="design-box">
|
||||
<h1 className="tit">TextArea</h1>
|
||||
<div className="design-grid-wrap">
|
||||
<textarea className="textarea-form" name="" id="" placeholder="TextArea Filed"></textarea>
|
||||
<textarea className="textarea-form" name="" id="" disabled></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<style jsx>
|
||||
{`
|
||||
.form-design-wrap {
|
||||
max-width: 1300px;
|
||||
margin: 0px auto;
|
||||
padding: 25px 20px;
|
||||
}
|
||||
.design-box {
|
||||
padding: 15px;
|
||||
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
.tit {
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
color: #000;
|
||||
border-bottom: 1px solid #000;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.design-grid-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
.icon-bx {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 5px;
|
||||
background-color: #000;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
interface SuitableLayoutProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export default function layout({ children }: SuitableLayoutProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="container">
|
||||
<div className="sale-contents">
|
||||
<div className="border-frame">
|
||||
<div className="pw-guide">
|
||||
<div className="pw-guide-txt">この適合表は参考資料として使用してください.</div>
|
||||
<div className="pw-guide-txt">詳細やお問い合わせは1:1お問い合わせをご利用ください.</div>
|
||||
<div className="pw-guide-txt">屋根材の選択or屋根材名を直接入力してください.</div>
|
||||
</div>
|
||||
</div>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
import Suitable from '@/components/suitable/Suitable'
|
||||
|
||||
export default function page() {
|
||||
return (
|
||||
<>
|
||||
<Suitable />
|
||||
<h1>지붕재 적합성</h1>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ export default function page() {
|
||||
return (
|
||||
<>
|
||||
<DataTable />
|
||||
<DetailForm />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import ListTable from '@/components/survey-sale/list/ListTable'
|
||||
import SearchForm from '@/components/survey-sale/list/SearchForm'
|
||||
|
||||
export default function page() {
|
||||
return (
|
||||
|
||||
@ -3,9 +3,10 @@
|
||||
interface LoadMoreButtonProps {
|
||||
hasMore: boolean
|
||||
onLoadMore: () => void
|
||||
onScrollToTop: () => void
|
||||
}
|
||||
|
||||
export default function LoadMoreButton({ hasMore, onLoadMore }: LoadMoreButtonProps) {
|
||||
export default function LoadMoreButton({ hasMore, onLoadMore, onScrollToTop }: LoadMoreButtonProps) {
|
||||
return (
|
||||
<>
|
||||
{hasMore ? (
|
||||
@ -14,7 +15,10 @@ export default function LoadMoreButton({ hasMore, onLoadMore }: LoadMoreButtonPr
|
||||
<i className="btn-edit"></i>
|
||||
</button>
|
||||
) : (
|
||||
<></>
|
||||
<button onClick={onScrollToTop} className="btn-frame n-blue icon">
|
||||
トップシフト
|
||||
<i className="btn-arr-up"></i>
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,11 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import type { SessionData } from '@/types/Auth'
|
||||
import { useEffect, useReducer, useState } from 'react'
|
||||
import { useReducer, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { axiosInstance } from '@/libs/axios'
|
||||
import { useSessionStore } from '@/store/session'
|
||||
|
||||
interface AccountState {
|
||||
loginId: string
|
||||
@ -23,8 +21,6 @@ export default function Login() {
|
||||
//로그인 상태
|
||||
const [isLogin, setIsLogin] = useState(false)
|
||||
|
||||
const { session, setSession } = useSessionStore()
|
||||
|
||||
const reducer = (state: AccountState, newState: Partial<AccountState>) => ({ ...state, ...newState })
|
||||
const [account, setAccount] = useReducer(reducer, {
|
||||
loginId: '',
|
||||
@ -34,7 +30,6 @@ export default function Login() {
|
||||
interface LoginData {
|
||||
code: number
|
||||
message: string | null
|
||||
result: SessionData
|
||||
}
|
||||
|
||||
const {
|
||||
@ -48,7 +43,7 @@ export default function Login() {
|
||||
loginId: account.loginId,
|
||||
pwd: account.pwd,
|
||||
})
|
||||
// router.push('/')
|
||||
router.push('/')
|
||||
|
||||
return data
|
||||
},
|
||||
@ -57,17 +52,6 @@ export default function Login() {
|
||||
retry: false,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
setIsLogin(false)
|
||||
if (loginData?.code === 200) {
|
||||
setSession({
|
||||
...session,
|
||||
...loginData?.result,
|
||||
})
|
||||
router.push('/')
|
||||
}
|
||||
}, [loginData])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="login-form-wrap">
|
||||
|
||||
@ -23,6 +23,11 @@ export default function InquiryWriteForm() {
|
||||
const file = Array.from(e.target.files || [])
|
||||
setFormData({ ...formData, file: [...formData.file, ...file] })
|
||||
}
|
||||
|
||||
const handleFileDelete = (fileToDelete: File) => {
|
||||
setFormData({ ...formData, file: formData.file.filter((f) => f !== fileToDelete) })
|
||||
}
|
||||
|
||||
const handleSubmit = () => {
|
||||
console.log('submit: ', formData)
|
||||
// router.push(`/inquiry`)
|
||||
@ -55,7 +60,7 @@ export default function InquiryWriteForm() {
|
||||
<div key={f.name}>
|
||||
<div>{f.name}</div>
|
||||
<div>
|
||||
<button>delete</button>
|
||||
<button onClick={() => handleFileDelete(f)}>delete</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -1,13 +1,10 @@
|
||||
'use client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
|
||||
export default function ListForm() {
|
||||
const router = useRouter()
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
<div className="sale-form-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/inquiry/regist')}>
|
||||
<button className="btn-frame n-blue icon">
|
||||
お問い合わせ<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,52 +1,5 @@
|
||||
'use client'
|
||||
|
||||
import { use, useEffect, useState } from 'react'
|
||||
import LoadMoreButton from '../LoadMoreButton'
|
||||
|
||||
const inquiryDummy = [
|
||||
{ id: 1, category: '屋根', title: '屋根材適合性確認依頼', date: '2025.04.02', status: 'completed' },
|
||||
{ id: 2, category: '外壁', title: '外壁仕上げ材確認', date: '2025.04.03', status: 'completed' },
|
||||
{ id: 3, category: '設備', title: '換気システム図面確認', date: '2025.04.04', status: 'completed' },
|
||||
{ id: 4, category: '基礎', title: '基礎配筋検査依頼', date: '2025.04.05', status: 'completed' },
|
||||
{ id: 5, category: '内装', title: 'クロス仕様確認', date: '2025.04.06', status: 'waiting' },
|
||||
{ id: 6, category: '構造', title: '耐震壁位置変更申請', date: '2025.04.07', status: 'completed' },
|
||||
{ id: 7, category: '屋根', title: '雨樋取付方法確認', date: '2025.04.08', status: 'completed' },
|
||||
{ id: 8, category: '外構', title: 'フェンス高さ変更相談', date: '2025.04.09', status: 'completed' },
|
||||
{ id: 9, category: '設備', title: '給湯器設置位置確認', date: '2025.04.10', status: 'completed' },
|
||||
{ id: 10, category: '外壁', title: 'タイル割付案確認依頼', date: '2025.04.11', status: 'waiting' },
|
||||
{ id: 11, category: '内装', title: '照明配置図面確認', date: '2025.04.12', status: 'completed' },
|
||||
{ id: 12, category: '構造', title: '梁補強案確認', date: '2025.04.13', status: 'completed' },
|
||||
{ id: 13, category: '基礎', title: '杭長設計確認依頼', date: '2025.04.14', status: 'completed' },
|
||||
{ id: 14, category: '屋根', title: '断熱材施工方法確認', date: '2025.04.15', status: 'completed' },
|
||||
{ id: 15, category: '外構', title: '駐車場勾配図確認', date: '2025.04.16', status: 'completed' },
|
||||
]
|
||||
|
||||
const badgeStyle = [
|
||||
{
|
||||
id: 'completed',
|
||||
label: '回答完了',
|
||||
color: 'blue',
|
||||
},
|
||||
{
|
||||
id: 'waiting',
|
||||
label: '回答待ち',
|
||||
color: 'orange',
|
||||
},
|
||||
]
|
||||
export default function ListTable() {
|
||||
const [offset, setOffset] = useState(0)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
|
||||
const inquiryList = inquiryDummy.slice(0, offset + 10)
|
||||
|
||||
useEffect(() => {
|
||||
if (inquiryDummy.length > offset + 10) {
|
||||
setHasMore(true)
|
||||
} else {
|
||||
setHasMore(false)
|
||||
}
|
||||
}, [inquiryList])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
@ -60,8 +13,10 @@ export default function ListTable() {
|
||||
<div className="filter-select">
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">全体</option>
|
||||
<option value="">回答待ち</option>
|
||||
<option value="">回答完了</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -70,21 +25,43 @@ export default function ListTable() {
|
||||
合計 <span>98</span>個
|
||||
</div>
|
||||
<ul className="inquiry-list">
|
||||
{inquiryList.map((inquiry) => (
|
||||
<li className="inquiry-item" key={inquiry.id}>
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">{inquiry.category}</div>
|
||||
<div className="inquiry-item-tit">{inquiry.title}</div>
|
||||
<div className="inquiry-item-date">{inquiry.date}</div>
|
||||
<div className={`inquiry-badge badge ${badgeStyle.find((badge) => badge.id === inquiry.status)?.color}`}>
|
||||
{badgeStyle.find((badge) => badge.id === inquiry.status)?.label}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">屋根</div>
|
||||
<div className="inquiry-item-tit">屋根材適合性確認依頼</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge blue">回答待ち</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">設計</div>
|
||||
<div className="inquiry-item-tit">設置可能ですか?</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge orange">回答完了</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">屋根</div>
|
||||
<div className="inquiry-item-tit">屋根材適合性確認依頼屋根材適合性確認依頼屋根材適合性確認依頼屋根材適合性確認依頼</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge blue">回答待ち</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="inquiry-item">
|
||||
<div className="inquiry-item-bx">
|
||||
<div className="inquiry-item-category">設計</div>
|
||||
<div className="inquiry-item-tit">設置可能ですか?</div>
|
||||
<div className="inquiry-item-date">2025.04.02</div>
|
||||
<div className="inquiry-badge badge orange">回答完了</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<div className="sale-edit-btn">
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
|
||||
<button className="btn-frame n-blue icon">
|
||||
もっと見る<i className="btn-edit"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,32 +0,0 @@
|
||||
export default function SuitableDetailPopup() {
|
||||
return (
|
||||
<div className="modal-popup">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="modal-header">
|
||||
<div className="modal-header-inner">
|
||||
<div className="modal-name-wrap">
|
||||
<div className="modal-img">
|
||||
<img src="/assets/images/layout/modal_header_icon03.svg" alt="" />
|
||||
</div>
|
||||
<div className="modal-name">屋根材適合性詳細 </div>
|
||||
</div>
|
||||
<button className="modal-close"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="modal-body">
|
||||
<div className={`compliance-check-bx act`}>
|
||||
<div className="check-name-wrap">
|
||||
<div className="check-name">アースティ40</div>
|
||||
<div className="check-name-btn">
|
||||
<button className="bx-btn"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="compliance-check-pop-contents"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,51 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { useAddressStore } from '@/store/addressStore'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import { useState } from 'react'
|
||||
|
||||
const dummyData = [
|
||||
{
|
||||
post_code: '123-567',
|
||||
pref: '東京都',
|
||||
city: '千代田区',
|
||||
detail: '永田町ハイツ101号室',
|
||||
},
|
||||
{
|
||||
post_code: '987-654',
|
||||
pref: '大阪府',
|
||||
city: '北区',
|
||||
detail: '梅田スカイビル23階',
|
||||
},
|
||||
{
|
||||
post_code: '456-789',
|
||||
pref: '福岡県',
|
||||
city: '博多区',
|
||||
detail: '中洲マンション305号',
|
||||
},
|
||||
]
|
||||
|
||||
export default function ZipCodePopup() {
|
||||
const [searchValue, setSearchValue] = useState('') //search 데이터 유무
|
||||
const [selected, setSelected] = useState('')
|
||||
|
||||
const { setAddressData } = useAddressStore()
|
||||
|
||||
//search 데이터 value 추가
|
||||
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearchValue(e.target.value)
|
||||
}
|
||||
|
||||
const popupController = usePopupController()
|
||||
|
||||
const handleApply = () => {
|
||||
const addressData = dummyData.find((item) => item.post_code === selected)
|
||||
setAddressData({
|
||||
post_code: addressData?.post_code || '',
|
||||
address: addressData?.pref || '',
|
||||
address_detail: addressData?.city + ' ' + addressData?.detail || '',
|
||||
})
|
||||
popupController.setZipCodePopup(false)
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="modal-popup">
|
||||
@ -83,23 +50,31 @@ export default function ZipCodePopup() {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{dummyData.map((item, index) => (
|
||||
<tr key={`${item.post_code}-${index}`} onClick={() => setSelected(item.post_code)}>
|
||||
<td>{item.pref}</td>
|
||||
<td>{item.city}</td>
|
||||
<td>{item.detail}</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>東京都</td>
|
||||
<td>浜松</td>
|
||||
<td>浜松町</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div className="btn-flex-wrap">
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon" onClick={handleApply}>
|
||||
<button className="btn-frame red icon">
|
||||
住所の適用<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(false)}>
|
||||
<button className="btn-frame n-blue icon">
|
||||
閉じる<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import SuitableCheckData from './SuitableCheckData'
|
||||
import SuitableNoData from './SuitableNoData'
|
||||
|
||||
export default function Suitable() {
|
||||
const [reference, setReference] = useState(false)
|
||||
|
||||
return (
|
||||
<div className="border-frame">
|
||||
<div className="sale-form-bx">
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">屋根材を選択してください.</option>
|
||||
<option value="">屋根材を選択してください.</option>
|
||||
<option value="">屋根材を選択してください.</option>
|
||||
<option value="">屋根材を選択してください.</option>
|
||||
<option value="">屋根材を選択してください.</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="sale-form-bx">
|
||||
<div className="search-input">
|
||||
<input type="text" className="search-frame" placeholder="屋根材 製品名を入力してください." />
|
||||
<button className="search-icon"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="compliance-check-wrap">
|
||||
<div className={`compliance-check-bx ${reference ? 'act' : ''}`}>
|
||||
<div className="check-name-wrap">
|
||||
<div className="check-name">凡例</div>
|
||||
<div className="check-name-btn">
|
||||
<button className="bx-btn" onClick={() => setReference(!reference)}></button>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="reference-list">
|
||||
<li className="reference-item">
|
||||
<div className="reference-item-bx">
|
||||
<i className="compliance-icon check"></i>設置可能
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="reference-item-bx">
|
||||
<i className="compliance-icon x"></i>設置可能
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="reference-item-bx">
|
||||
<i className="compliance-icon quest"></i>設置可能
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="reference-item-bx">
|
||||
<i className="compliance-icon tip"></i>設置可能
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{/* checkData */}
|
||||
{/* 데이터 없을경우 버튼 영역 안보여야함 */}
|
||||
<SuitableCheckData />
|
||||
<SuitableCheckData />
|
||||
<SuitableCheckData />
|
||||
<SuitableCheckData />
|
||||
</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">
|
||||
選択ダウンロード<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
{/* 검색기록 없을떄 위에 두 영역 안보이고 이 부분만 보여야 함*/}
|
||||
{/* <SuitableNoData /> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
'use client'
|
||||
|
||||
export default function SuitableCheckData() {
|
||||
return (
|
||||
<>
|
||||
<div className={`compliance-check-bx act`}>
|
||||
<div className="check-name-wrap">
|
||||
<div className="check-form-box com-tit">
|
||||
<input type="checkbox" id="ch01" />
|
||||
<label htmlFor="ch01">アースティ40</label>
|
||||
</div>
|
||||
<div className="check-name-btn">
|
||||
<button className="bx-btn"></button>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="reference-list check">
|
||||
<li className="reference-item">
|
||||
<div className="check-item-wrap">
|
||||
<div className="check-form-box com-txt">
|
||||
<input type="checkbox" id="ch02" />
|
||||
<label htmlFor="ch02">屋根技研 支持瓦</label>
|
||||
</div>
|
||||
<div className="compliance-icon-wrap">
|
||||
<i className="compliance-icon check"></i>
|
||||
<i className="compliance-icon x"></i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="check-item-wrap">
|
||||
<div className="check-form-box com-txt">
|
||||
<input type="checkbox" id="ch03" />
|
||||
<label htmlFor="ch03">屋根技研 支持金具</label>
|
||||
</div>
|
||||
<div className="compliance-icon-wrap">
|
||||
<i className="compliance-icon x"></i>
|
||||
<i className="compliance-icon tip"></i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="check-item-wrap">
|
||||
<div className="check-form-box com-txt">
|
||||
<input type="checkbox" id="ch04" />
|
||||
<label htmlFor="ch04">屋根技研YGアンカー</label>
|
||||
</div>
|
||||
<div className="compliance-icon-wrap">
|
||||
<i className="compliance-icon tip"></i>
|
||||
<i className="compliance-icon quest"></i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
<li className="reference-item">
|
||||
<div className="check-item-wrap">
|
||||
<div className="check-form-box com-txt">
|
||||
<input type="checkbox" id="ch05" />
|
||||
<label htmlFor="ch05">ダイドハント支持瓦Ⅱ</label>
|
||||
</div>
|
||||
<div className="compliance-icon-wrap">
|
||||
<i className="compliance-icon check"></i>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
export default function SuitableNoData() {
|
||||
return (
|
||||
<>
|
||||
<div className="compliace-nosearch">
|
||||
<span className="mb10">検索結果はありません。</span>
|
||||
<span className="mb10">屋根材適合性表にない製品の情報を入力してください。 今後返信いたします。</span>
|
||||
<span>
|
||||
<button className="btn-frame n-blue icon">
|
||||
お問い合わせ登録<i className="btn-arr"></i>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import { usePathname, useRouter, useSearchParams, useParams } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
|
||||
export default function NavTab() {
|
||||
const router = useRouter()
|
||||
@ -10,7 +11,6 @@ export default function NavTab() {
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const id = searchParams.get('id')
|
||||
const isTemp = searchParams.get('isTemp')
|
||||
|
||||
const params = useParams()
|
||||
const detailId = params.id
|
||||
@ -33,17 +33,13 @@ export default function NavTab() {
|
||||
return
|
||||
}
|
||||
if (detailId) {
|
||||
router.push(`/survey-sale/${detailId}`)
|
||||
router.push(`/survey-sale/${detailId}?tab=basic-info`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
const handleRoofInfoClick = () => {
|
||||
if (id) {
|
||||
if (isTemp === 'true') {
|
||||
alert('基本情報が一時保存された状態です。')
|
||||
return
|
||||
}
|
||||
router.push(`/survey-sale/roof-info?id=${id}`)
|
||||
return
|
||||
}
|
||||
@ -52,7 +48,8 @@ export default function NavTab() {
|
||||
return
|
||||
}
|
||||
if (pathname === '/survey-sale/basic-info') {
|
||||
alert('基本情報を先に保存してください。')
|
||||
// TODO: 팝업 추가
|
||||
alert('Save essential information first')
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,34 +1,22 @@
|
||||
'use client'
|
||||
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useParams, useSearchParams } from 'next/navigation'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
import { useState } from 'react'
|
||||
import DetailForm from './DetailForm'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import RoofDetailForm from './RoofDetailForm'
|
||||
|
||||
export default function DataTable() {
|
||||
const params = useParams()
|
||||
const id = params.id
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const tab = searchParams.get('tab')
|
||||
|
||||
const { surveyDetail, isLoadingSurveyDetail } = useServey(Number(id))
|
||||
const [isTemporary, setIsTemporary] = useState(true)
|
||||
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
|
||||
const [isTemporary, setIsTemporary] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyDetail?.representative && surveyDetail?.store && surveyDetail?.construction_point) {
|
||||
setIsTemporary(false)
|
||||
if (!surveyDetail?.representative || !surveyDetail?.store || !surveyDetail?.construction_point) {
|
||||
setIsTemporary(true)
|
||||
}
|
||||
if (tab === 'roof-info') {
|
||||
setRoofInfoSelected()
|
||||
} else {
|
||||
setBasicInfoSelected()
|
||||
}
|
||||
}, [surveyDetail, tab, setBasicInfoSelected, setRoofInfoSelected])
|
||||
}, [surveyDetail])
|
||||
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
@ -64,15 +52,9 @@ export default function DataTable() {
|
||||
<tr>
|
||||
<th>提出可否</th>
|
||||
<td>
|
||||
{surveyDetail?.submission_status && surveyDetail?.submission_date ? (
|
||||
<>
|
||||
{/* TODO: 제출한 판매점 ID 추가 필요 */}
|
||||
<div>{new Date(surveyDetail.submission_date).toLocaleString()}</div>
|
||||
<div>販売店 ID...</div>
|
||||
</>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
{surveyDetail?.submission_status && surveyDetail?.submission_date
|
||||
? new Date(surveyDetail.submission_date).toLocaleString()
|
||||
: '-'}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -86,11 +68,6 @@ export default function DataTable() {
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{tab === 'roof-info' ? (
|
||||
<RoofDetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
|
||||
) : (
|
||||
<DetailForm surveyDetail={surveyDetail} isLoadingSurveyDetail={isLoadingSurveyDetail} />
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,65 +0,0 @@
|
||||
'use client'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
|
||||
export default function DetailButton({ isTemporary, surveyId }: { isTemporary: boolean; surveyId: number }) {
|
||||
const router = useRouter()
|
||||
|
||||
const { submitSurvey, deleteSurvey } = useServey(surveyId)
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (isTemporary) {
|
||||
alert('一時保存されたデータは提出できません。')
|
||||
return
|
||||
}
|
||||
if (confirm('提出しますか??')) {
|
||||
if (surveyId) {
|
||||
// TODO: 제출 페이지 추가
|
||||
alert('SUBMIT POPUP!!!!!!!!!!!')
|
||||
await submitSurvey()
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleUpdate = () => {
|
||||
router.push(`/survey-sale/basic-info?id=${surveyId}&isTemp=${isTemporary}`)
|
||||
}
|
||||
const handleDelete = async () => {
|
||||
if (confirm('削除しますか?')) {
|
||||
if (surveyId) {
|
||||
await deleteSurvey()
|
||||
router.push('/survey-sale')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="btn-flex-wrap">
|
||||
{isTemporary ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
|
||||
リスト<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon" onClick={handleSubmit}>
|
||||
提出<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
|
||||
修正<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleDelete}>
|
||||
削除<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,27 +1,60 @@
|
||||
'use client'
|
||||
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import { useEffect, useState } from 'react'
|
||||
import DetailButton from './DetailButton'
|
||||
import { SurveyBasicInfo } from '@/types/Survey'
|
||||
import { useParams, useRouter, useSearchParams } from 'next/navigation'
|
||||
|
||||
export default function DetailForm({
|
||||
surveyDetail,
|
||||
isLoadingSurveyDetail,
|
||||
}: {
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
isLoadingSurveyDetail: boolean
|
||||
}) {
|
||||
export default function DetailForm() {
|
||||
const router = useRouter()
|
||||
const params = useParams()
|
||||
const id = params.id
|
||||
|
||||
const searchParams = useSearchParams()
|
||||
const tab = searchParams.get('tab')
|
||||
|
||||
const { surveyDetail, deleteSurvey, submitSurvey, isLoadingSurveyDetail } = useServey(Number(id))
|
||||
const { setBasicInfoSelected, setRoofInfoSelected } = useSurveySaleTabState()
|
||||
const [isTemporary, setIsTemporary] = useState(true)
|
||||
|
||||
// TODO: 조사매물 지붕정보 퍼블 구현되면 탭 화면 변경 추가
|
||||
useEffect(() => {
|
||||
if (tab === 'basic-info') {
|
||||
setBasicInfoSelected()
|
||||
}
|
||||
if (tab === 'roof-info') {
|
||||
setRoofInfoSelected()
|
||||
}
|
||||
if (surveyDetail?.representative && surveyDetail?.store && surveyDetail?.construction_point) {
|
||||
setIsTemporary(false)
|
||||
}
|
||||
}, [surveyDetail])
|
||||
}, [tab, setBasicInfoSelected, setRoofInfoSelected, surveyDetail])
|
||||
|
||||
console.log('isTemporary', isTemporary)
|
||||
console.log('surveyDetail', surveyDetail)
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (confirm('submit?')) {
|
||||
if (surveyDetail?.id) {
|
||||
await submitSurvey()
|
||||
}
|
||||
}
|
||||
}
|
||||
const handleUpdate = () => {
|
||||
router.push(`/survey-sale/basic-info?id=${id}`)
|
||||
}
|
||||
const handleDelete = async () => {
|
||||
if (confirm('delete?')) {
|
||||
if (surveyDetail?.id) {
|
||||
await deleteSurvey()
|
||||
router.push('/survey-sale')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
@ -56,7 +89,34 @@ export default function DetailForm({
|
||||
<input type="text" className="input-frame" disabled defaultValue={surveyDetail?.customer_name ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
<DetailButton isTemporary={isTemporary} surveyId={Number(surveyDetail?.id)} />
|
||||
<div className="btn-flex-wrap">
|
||||
{isTemporary ? (
|
||||
<></>
|
||||
) : (
|
||||
<>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={() => router.push('/survey-sale')}>
|
||||
リスト<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame red icon" onClick={handleSubmit}>
|
||||
提出<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleUpdate}>
|
||||
修正<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div className="btn-bx">
|
||||
<button className="btn-frame n-blue icon" onClick={handleDelete}>
|
||||
削除<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
|
||||
@ -1,256 +0,0 @@
|
||||
import { SurveyBasicInfo, SurveyDetailInfo } from '@/types/Survey'
|
||||
import DetailButton from './DetailButton'
|
||||
import { roof_material, supplementary_facilities } from './form/MultiCheckEtc'
|
||||
import { selectBoxOptions } from './form/SelectBoxEtc'
|
||||
import { radioEtcData } from './form/RadioEtc'
|
||||
|
||||
export default function RoofDetailForm({
|
||||
surveyDetail,
|
||||
isLoadingSurveyDetail,
|
||||
}: {
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
isLoadingSurveyDetail: boolean
|
||||
}) {
|
||||
console.log(surveyDetail)
|
||||
|
||||
const makeNumArr = (value: string) => {
|
||||
return value
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0)
|
||||
}
|
||||
|
||||
if (isLoadingSurveyDetail) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<div className="sale-frame">
|
||||
<div className="data-form-wrap">
|
||||
<div className="data-input-form-bx">
|
||||
{/* 전기 계약 용량 */}
|
||||
<div className="data-input-form-tit">電気契約容量</div>
|
||||
<input className="input-frame" type="text" placeholder="-" readOnly value={surveyDetail?.detail_info?.contract_capacity ?? ''} />
|
||||
</div>
|
||||
{/* 전기 소매 회사 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">電気小売会社</div>
|
||||
<input className="input-frame" type="text" placeholder="-" readOnly value={surveyDetail?.detail_info?.retail_company ?? ''} />
|
||||
</div>
|
||||
{/* 전기 부대 설비 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">電気附属設備</div>
|
||||
<div className="data-check-wrap">
|
||||
{supplementary_facilities.map((item) => (
|
||||
<div className="check-form-box" key={item.id}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`${item.id}`}
|
||||
checked={makeNumArr(surveyDetail?.detail_info?.supplementary_facilities ?? '').includes(String(item.id))}
|
||||
readOnly
|
||||
/>
|
||||
<label htmlFor={`${item.id}`}>{item.name}</label>
|
||||
</div>
|
||||
))}
|
||||
<div className="check-form-box">
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`supplementary_facilities_etc`}
|
||||
checked={surveyDetail?.detail_info?.supplementary_facilities_etc !== null}
|
||||
readOnly
|
||||
/>
|
||||
<label htmlFor={`supplementary_facilities_etc`}>その他 (直接入力)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="data-input">
|
||||
<input
|
||||
type="text"
|
||||
className="input-frame"
|
||||
placeholder="-"
|
||||
readOnly
|
||||
value={surveyDetail?.detail_info?.supplementary_facilities_etc ?? ''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 설치 희망 시스템 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">設置希望システム</div>
|
||||
<SelectedBox column="installation_system" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 건축 연수 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">建築年数</div>
|
||||
<SelectedBox column="construction_year" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 지붕재 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">屋根材</div>
|
||||
<div className="data-check-wrap">
|
||||
{roof_material.map((item) => (
|
||||
<div className="check-form-box" key={item.id}>
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`${item.id}`}
|
||||
checked={makeNumArr(surveyDetail?.detail_info?.roof_material ?? '').includes(String(item.id))}
|
||||
readOnly
|
||||
/>
|
||||
<label htmlFor={`${item.id}`}>{item.name}</label>
|
||||
</div>
|
||||
))}
|
||||
<div className="check-form-box">
|
||||
<input type="checkbox" id={`roof_material_etc`} checked={surveyDetail?.detail_info?.roof_material_etc !== null} readOnly />
|
||||
<label htmlFor={`roof_material_etc`}>その他 (直接入力)</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="data-input">
|
||||
<input type="text" className="input-frame" placeholder="-" readOnly value={surveyDetail?.detail_info?.roof_material_etc ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
{/* 지붕 모양 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">屋根の形状</div>
|
||||
<SelectedBox column="roof_shape" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 지붕 경사도 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">屋根の斜面</div>
|
||||
<div className="data-input flex">
|
||||
<input className="input-frame" type="text" placeholder="-" readOnly value={surveyDetail?.detail_info?.roof_slope ?? ''} />
|
||||
<span>寸</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* 주택 구조 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">住宅構造</div>
|
||||
<RadioSelected column="house_structure" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 서까래 재질 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">垂木の材質</div>
|
||||
<RadioSelected column="rafter_material" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 서까래 크기 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">垂木の大きさ</div>
|
||||
<SelectedBox column="rafter_size" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 서까래 피치 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">垂木のピッチ</div>
|
||||
<SelectedBox column="rafter_pitch" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 서까래 방향 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">垂木の方向</div>
|
||||
<RadioSelected column="rafter_direction" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 노지판 종류 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">路地板の種類</div>
|
||||
<SelectedBox column="open_field_plate_kind" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 노지판 두께 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">路地板厚</div>
|
||||
<div className="data-input flex">
|
||||
<input
|
||||
className="input-frame"
|
||||
type="text"
|
||||
placeholder="-"
|
||||
readOnly
|
||||
value={surveyDetail?.detail_info?.open_field_plate_thickness ?? ''}
|
||||
/>
|
||||
<span>mm</span>
|
||||
</div>
|
||||
</div>
|
||||
{/* 누수 흔적 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">水漏れの痕跡</div>
|
||||
<RadioSelected column="leak_trace" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 방수재 종류 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">防水材の種類</div>
|
||||
<RadioSelected column="waterproof_material" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 단열재 유무 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">断熱材の有無</div>
|
||||
<RadioSelected column="insulation_presence" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 구조 순서 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">屋根構造の順序</div>
|
||||
<SelectedBox column="structure_order" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 설치 가능 여부 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">設置可能な場合</div>
|
||||
<SelectedBox column="installation_availability" detailInfoData={surveyDetail?.detail_info ?? null} />
|
||||
</div>
|
||||
{/* 메모 */}
|
||||
<div className="data-input-form-bx">
|
||||
<div className="data-input-form-tit">メモ</div>
|
||||
<div className="data-input">
|
||||
<textarea className="textarea-form" placeholder="-" readOnly value={surveyDetail?.detail_info?.memo ?? ''} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<DetailButton isTemporary={false} surveyId={Number(surveyDetail?.id)} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const SelectedBox = ({ column, detailInfoData }: { column: string; detailInfoData: SurveyDetailInfo | null }) => {
|
||||
const selectedId = detailInfoData?.[column as keyof SurveyDetailInfo]
|
||||
const etcValue = detailInfoData?.[`${column}_etc` as keyof SurveyDetailInfo]
|
||||
|
||||
return (
|
||||
<>
|
||||
<select className="select-form mb10" name={column} id={column} disabled value={selectedId ? 'selected' : etcValue ? 'etc' : ''}>
|
||||
<option value="">-</option>
|
||||
<option value="etc">その他 (直接入力)</option>
|
||||
<option value="selected">
|
||||
{typeof selectedId === 'number' ? selectBoxOptions[column as keyof typeof selectBoxOptions][selectedId]?.name : ''}
|
||||
</option>
|
||||
</select>
|
||||
{etcValue && <input type="text" className="input-frame" readOnly value={etcValue.toString()} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const RadioSelected = ({ column, detailInfoData }: { column: string; detailInfoData: SurveyDetailInfo | null }) => {
|
||||
let selectedId = detailInfoData?.[column as keyof SurveyDetailInfo]
|
||||
if (column === 'leak_trace') {
|
||||
selectedId = Number(selectedId)
|
||||
}
|
||||
|
||||
let etcValue = null
|
||||
if (column !== 'rafter_direction') {
|
||||
etcValue = detailInfoData?.[`${column}_etc` as keyof SurveyDetailInfo]
|
||||
}
|
||||
const etcChecked = etcValue !== null && etcValue !== undefined && etcValue !== ''
|
||||
|
||||
return (
|
||||
<>
|
||||
{radioEtcData[column as keyof typeof radioEtcData].map((item) => (
|
||||
<div className="radio-form-box mb10" key={item.id}>
|
||||
<input type="radio" name={column} id={`${item.id}`} disabled checked={selectedId === item.id} />
|
||||
<label htmlFor={`${item.id}`}>{item.label}</label>
|
||||
</div>
|
||||
))}
|
||||
{column !== 'rafter_direction' && column !== 'leak_trace' && column !== 'insulation_presence' && (
|
||||
<div className="radio-form-box mb10">
|
||||
<input type="radio" name={column} id={`${column}_etc`} value="etc" disabled checked={etcChecked} />
|
||||
<label htmlFor={`${column}_etc`}>その他 (直接入力)</label>
|
||||
</div>
|
||||
)}
|
||||
{etcChecked && (
|
||||
<div className="data-input">
|
||||
<input type="text" className="input-frame" placeholder="-" readOnly value={etcValue?.toString() ?? ''} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -5,15 +5,12 @@ import { SurveyBasicRequest } from '@/types/Survey'
|
||||
import { useRouter, useSearchParams } from 'next/navigation'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useSurveySaleTabState } from '@/store/surveySaleTabState'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import { useAddressStore } from '@/store/addressStore'
|
||||
import { useSessionStore } from '@/store/session'
|
||||
|
||||
const defaultBasicInfoForm: SurveyBasicRequest = {
|
||||
representative: '',
|
||||
store: null,
|
||||
construction_point: null,
|
||||
investigation_date: new Date().toLocaleDateString('en-CA'),
|
||||
investigation_date: null,
|
||||
building_name: null,
|
||||
customer_name: null,
|
||||
post_code: null,
|
||||
@ -35,25 +32,16 @@ export default function BasicForm() {
|
||||
|
||||
const [basicInfoData, setBasicInfoData] = useState<SurveyBasicRequest>(defaultBasicInfoForm)
|
||||
|
||||
const { addressData } = useAddressStore()
|
||||
|
||||
const popupController = usePopupController()
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyDetail) {
|
||||
const { id, updated_at, created_at, detail_info, ...rest } = surveyDetail
|
||||
setBasicInfoData(rest)
|
||||
}
|
||||
if (addressData) {
|
||||
setBasicInfoData({
|
||||
...basicInfoData,
|
||||
post_code: addressData.post_code,
|
||||
address: addressData.address,
|
||||
address_detail: addressData.address_detail,
|
||||
})
|
||||
}
|
||||
}, [surveyDetail])
|
||||
|
||||
useEffect(() => {
|
||||
setBasicInfoSelected()
|
||||
}, [surveyDetail, addressData])
|
||||
}, [])
|
||||
|
||||
const focusInput = (input: keyof SurveyBasicRequest) => {
|
||||
const inputElement = document.getElementById(input)
|
||||
@ -79,17 +67,16 @@ export default function BasicForm() {
|
||||
const handleSave = async (isTemporary: boolean) => {
|
||||
if (id) {
|
||||
updateSurvey(basicInfoData)
|
||||
alert('保存しました。')
|
||||
router.push(`/survey-sale/${id}?tab=basic-info`)
|
||||
}
|
||||
if (isTemporary) {
|
||||
const saveId = await createSurvey(basicInfoData)
|
||||
alert('一時保存されました。')
|
||||
alert('save success temporary id: ' + saveId)
|
||||
router.push(`/survey-sale/${saveId}?tab=basic-info`)
|
||||
} else {
|
||||
if (validateSurvey(basicInfoData)) {
|
||||
const saveId = await createSurvey(basicInfoData)
|
||||
alert('保存しました。 登録番号: ' + saveId)
|
||||
alert('save success id: ' + saveId)
|
||||
router.push(`/survey-sale/${saveId}?tab=basic-info`)
|
||||
}
|
||||
}
|
||||
@ -111,6 +98,7 @@ export default function BasicForm() {
|
||||
id="representative"
|
||||
value={basicInfoData.representative}
|
||||
onChange={(e) => handleChange('representative', e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="data-input-form-bx">
|
||||
@ -121,6 +109,7 @@ export default function BasicForm() {
|
||||
id="store"
|
||||
value={basicInfoData.store ?? ''}
|
||||
onChange={(e) => handleChange('store', e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="data-input-form-bx">
|
||||
@ -131,6 +120,7 @@ export default function BasicForm() {
|
||||
id="construction_point"
|
||||
value={basicInfoData.construction_point ?? ''}
|
||||
onChange={(e) => handleChange('construction_point', e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@ -148,7 +138,7 @@ export default function BasicForm() {
|
||||
type="date"
|
||||
className="date-frame"
|
||||
id="investigation_date"
|
||||
value={basicInfoData.investigation_date ?? ''}
|
||||
value={basicInfoData.investigation_date ?? new Date().toISOString().split('T')[0]}
|
||||
onChange={(e) => handleChange('investigation_date', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
@ -202,7 +192,7 @@ export default function BasicForm() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="form-btn">
|
||||
<button className="btn-frame n-blue icon" onClick={() => popupController.setZipCodePopup(true)}>
|
||||
<button className="btn-frame n-blue icon">
|
||||
郵便番号<i className="btn-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
import { SurveyDetailRequest } from '@/types/Survey'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
|
||||
export const supplementary_facilities = [
|
||||
const supplementary_facilities = [
|
||||
{ id: 1, name: 'エコキュート' }, //에코큐트
|
||||
{ id: 2, name: 'エネパーム' }, //에네팜
|
||||
{ id: 3, name: '蓄電池システム' }, //축전지시스템
|
||||
{ id: 4, name: '太陽光発電' }, //태양광발전
|
||||
]
|
||||
|
||||
export const roof_material = [
|
||||
const roof_material = [
|
||||
{ id: 1, name: 'スレート' }, //슬레이트
|
||||
{ id: 2, name: 'アスファルトシングル' }, //아스팔트 싱글
|
||||
{ id: 3, name: '瓦' }, //기와
|
||||
@ -29,22 +29,11 @@ export default function MultiCheckbox({
|
||||
const [isOtherChecked, setIsOtherChecked] = useState(false)
|
||||
const [otherValue, setOtherValue] = useState('')
|
||||
|
||||
const makeNumArr = (value: string) => {
|
||||
return value
|
||||
const handleCheckbox = (dataIndex: number) => {
|
||||
const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsOtherChecked(true)
|
||||
setOtherValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
|
||||
const handleCheckbox = (dataIndex: number) => {
|
||||
const value = makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? ''))
|
||||
|
||||
let newValue: string[]
|
||||
if (value.includes(String(dataIndex))) {
|
||||
@ -74,7 +63,11 @@ export default function MultiCheckbox({
|
||||
|
||||
const handleOtherCheckbox = () => {
|
||||
if (column === 'roof_material') {
|
||||
const value = makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? ''))
|
||||
const value = String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v.length > 0)
|
||||
|
||||
// 현재 선택된 항목 수
|
||||
const currentSelected = value.length
|
||||
|
||||
@ -126,7 +119,10 @@ export default function MultiCheckbox({
|
||||
<input
|
||||
type="checkbox"
|
||||
id={`${column}_ch${item.id}`}
|
||||
checked={makeNumArr(String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')).includes(String(item.id))}
|
||||
checked={String(detailInfoData[column as keyof SurveyDetailRequest] ?? '')
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.includes(String(item.id))}
|
||||
onChange={() => handleCheckbox(item.id)}
|
||||
/>
|
||||
<label htmlFor={`${column}_ch${item.id}`}>{item.name}</label>
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
'use client'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { SurveyDetailRequest } from '@/types/Survey'
|
||||
|
||||
type RadioEtcKeys = 'house_structure' | 'rafter_material' | 'waterproof_material' | 'insulation_presence' | 'rafter_direction' | 'leak_trace'
|
||||
type RadioEtcKeys = 'house_structure' | 'rafter_material' | 'waterproof_material' | 'insulation_presence'
|
||||
|
||||
const translateJapanese: Record<RadioEtcKeys, string> = {
|
||||
house_structure: '住宅構造',
|
||||
rafter_material: '垂木材質',
|
||||
waterproof_material: '防水材の種類',
|
||||
insulation_presence: '断熱材の有無',
|
||||
rafter_direction: '垂木の方向',
|
||||
leak_trace: '水漏れの痕跡',
|
||||
}
|
||||
|
||||
export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]> = {
|
||||
const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]> = {
|
||||
house_structure: [
|
||||
{
|
||||
id: 1,
|
||||
@ -46,26 +44,6 @@ export const radioEtcData: Record<RadioEtcKeys, { id: number; label: string }[]>
|
||||
label: 'あり',
|
||||
},
|
||||
],
|
||||
rafter_direction: [
|
||||
{
|
||||
id: 1,
|
||||
label: '垂直垂木',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: '水平垂木',
|
||||
},
|
||||
],
|
||||
leak_trace: [
|
||||
{
|
||||
id: 1,
|
||||
label: 'あり',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
label: 'なし',
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default function RadioEtc({
|
||||
@ -80,13 +58,6 @@ export default function RadioEtc({
|
||||
const [isEtcSelected, setIsEtcSelected] = useState(false)
|
||||
const [etcValue, setEtcValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsEtcSelected(true)
|
||||
setEtcValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
|
||||
const handleRadioChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
if (column === 'insulation_presence') {
|
||||
@ -139,7 +110,7 @@ export default function RadioEtc({
|
||||
))}
|
||||
{column !== 'insulation_presence' && (
|
||||
<div className="radio-form-box mb10">
|
||||
<input type="radio" name={column} id={`${column}_etc`} value="etc" onChange={handleRadioChange} checked={isEtcSelected} />
|
||||
<input type="radio" name={column} id={`${column}`} value="etc" onChange={handleRadioChange} />
|
||||
<label htmlFor={`${column}_etc`}>その他 (直接入力)</label>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -65,26 +65,15 @@ export default function RoofInfoForm() {
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyDetail?.detail_info) {
|
||||
const { id, updated_at, created_at, basic_info_id, ...rest } = surveyDetail.detail_info
|
||||
const { id, updated_at, created_at, ...rest } = surveyDetail.detail_info
|
||||
setDetailInfoData(rest)
|
||||
}
|
||||
}, [surveyDetail])
|
||||
|
||||
const handleNumberInput = (key: keyof SurveyDetailRequest, value: number | string) => {
|
||||
if (key === 'roof_slope' || key === 'open_field_plate_thickness') {
|
||||
const stringValue = value.toString()
|
||||
if (stringValue.length > 5) {
|
||||
alert('保存できるサイズを超えました。')
|
||||
return
|
||||
}
|
||||
if (stringValue.includes('.')) {
|
||||
const decimalPlaces = stringValue.split('.')[1].length
|
||||
if (decimalPlaces > 1) {
|
||||
alert('小数点以下1桁までしか許されません。')
|
||||
return
|
||||
}
|
||||
}
|
||||
setDetailInfoData({ ...detailInfoData, [key]: value.toString() })
|
||||
if (typeof value === 'string') {
|
||||
const numberValue = value === '' ? null : Number(value)
|
||||
setDetailInfoData({ ...detailInfoData, [key]: numberValue })
|
||||
} else {
|
||||
setDetailInfoData({ ...detailInfoData, [key]: value })
|
||||
}
|
||||
@ -106,30 +95,19 @@ export default function RoofInfoForm() {
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: 조사매물 저장 요구사항 정립 이후 수정 필요
|
||||
const handleSave = async () => {
|
||||
if (id) {
|
||||
const emptyField = validateSurveyDetail(detailInfoData)
|
||||
if (emptyField.trim() === '') {
|
||||
const updatedBasicInfoData = {
|
||||
detail_info: detailInfoData,
|
||||
}
|
||||
try {
|
||||
createSurveyDetail({
|
||||
surveyId: Number(id),
|
||||
surveyDetail: updatedBasicInfoData,
|
||||
})
|
||||
alert('調査物件を保存しました。')
|
||||
} catch (error) {
|
||||
alert(error)
|
||||
throw new Error('failed to create survey detail: ' + error)
|
||||
}
|
||||
createSurveyDetail({ surveyId: Number(id), surveyDetail: detailInfoData })
|
||||
router.push(`/survey-sale`)
|
||||
} else {
|
||||
alert(emptyField + ' は必須項目です。')
|
||||
alert(emptyField + ' is required')
|
||||
focusOnInput(emptyField)
|
||||
}
|
||||
} else {
|
||||
alert('基本情報を作成した後、屋根情報を作成することができます。')
|
||||
alert('save essential information first')
|
||||
}
|
||||
}
|
||||
const focusOnInput = (field: string) => {
|
||||
@ -148,11 +126,10 @@ export default function RoofInfoForm() {
|
||||
<div className="data-input-form-tit">電気契約容量</div>
|
||||
<div className="data-input mb5">
|
||||
<input
|
||||
type="number"
|
||||
inputMode="decimal"
|
||||
type="text"
|
||||
className="input-frame"
|
||||
value={detailInfoData.contract_capacity?.split(' ')[0] ?? ''}
|
||||
onChange={(e) => handleNumberInput('contract_capacity', e.target.value)}
|
||||
onChange={(e) => handleTextInput('contract_capacity', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="data-input">
|
||||
@ -161,11 +138,7 @@ export default function RoofInfoForm() {
|
||||
name="contract_capacity_unit"
|
||||
id="contract_capacity_unit"
|
||||
onChange={(e) => handleUnitInput(e.target.value)}
|
||||
value={detailInfoData.contract_capacity?.split(' ')[1] ?? ''}
|
||||
>
|
||||
<option value="" hidden>
|
||||
選択してください
|
||||
</option>
|
||||
<option value="kVA">kVA</option>
|
||||
<option value="A">A</option>
|
||||
</select>
|
||||
@ -206,12 +179,10 @@ export default function RoofInfoForm() {
|
||||
<div className="data-input-form-tit">屋根の斜面</div>
|
||||
<div className="data-input flex">
|
||||
<input
|
||||
type="number"
|
||||
step={0.1}
|
||||
inputMode="decimal"
|
||||
type="text"
|
||||
className="input-frame"
|
||||
value={detailInfoData.roof_slope ?? ''}
|
||||
onChange={(e) => handleNumberInput('roof_slope', e.target.value)}
|
||||
onChange={(e) => handleTextInput('roof_slope', e.target.value)}
|
||||
/>
|
||||
<span>寸</span>
|
||||
</div>
|
||||
@ -234,7 +205,7 @@ export default function RoofInfoForm() {
|
||||
name="rafter_direction"
|
||||
id="rafter_direction_1"
|
||||
value={1}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', Number(e.target.value))}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', e.target.value)}
|
||||
checked={detailInfoData.rafter_direction === 1}
|
||||
/>
|
||||
<label htmlFor="rafter_direction_1">垂直垂木</label>
|
||||
@ -245,7 +216,7 @@ export default function RoofInfoForm() {
|
||||
name="rafter_direction"
|
||||
id="rafter_direction_2"
|
||||
value={2}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', Number(e.target.value))}
|
||||
onChange={(e) => handleNumberInput('rafter_direction', e.target.value)}
|
||||
checked={detailInfoData.rafter_direction === 2}
|
||||
/>
|
||||
<label htmlFor="rafter_direction_2">水平垂木</label>
|
||||
@ -261,12 +232,10 @@ export default function RoofInfoForm() {
|
||||
</div>
|
||||
<div className="data-input flex">
|
||||
<input
|
||||
type="number"
|
||||
step={0.1}
|
||||
inputMode="decimal"
|
||||
type="text"
|
||||
className="input-frame"
|
||||
value={detailInfoData.open_field_plate_thickness ?? ''}
|
||||
onChange={(e) => handleNumberInput('open_field_plate_thickness', e.target.value)}
|
||||
onChange={(e) => handleTextInput('open_field_plate_thickness', e.target.value)}
|
||||
/>
|
||||
<span>mm</span>
|
||||
</div>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { SurveyDetailRequest } from '@/types/Survey'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export type SelectBoxKeys =
|
||||
type SelectBoxKeys =
|
||||
| 'installation_system'
|
||||
| 'construction_year'
|
||||
| 'roof_shape'
|
||||
@ -33,7 +33,7 @@ const translateJapanese: Record<SelectBoxKeys, string> = {
|
||||
installation_availability: '屋根製品名 設置可否確認',
|
||||
}
|
||||
|
||||
export const selectBoxOptions: Record<SelectBoxKeys, { id: number; name: string }[]> = {
|
||||
const selectBoxOptions: Record<SelectBoxKeys, { id: number; name: string }[]> = {
|
||||
installation_system: [
|
||||
{
|
||||
id: 1,
|
||||
@ -165,28 +165,25 @@ export default function SelectBoxForm({
|
||||
const [etcValue, setEtcValue] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
if (detailInfoData[`${column}_etc` as keyof SurveyDetailRequest]) {
|
||||
setIsEtcSelected(true)
|
||||
setEtcValue(detailInfoData[`${column}_etc` as keyof SurveyDetailRequest] as string)
|
||||
}
|
||||
}, [detailInfoData])
|
||||
setEtcValue(detailInfoData[`${column}_etc`] ?? '')
|
||||
}, [detailInfoData[`${column}_etc`]])
|
||||
|
||||
const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = e.target.value
|
||||
if (column === 'installation_availability' || column === 'construction_year') {
|
||||
setIsEtcSelected(value === '2') // 건축 연수 & 설치 가능 여부 컬럼 2번 선택 시 input 활성화
|
||||
setIsEtcSelected(value === '2') // 既築(2) 또는 未確認(2) 선택 시 input 활성화
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
[column]: Number(value),
|
||||
})
|
||||
} else if (value === 'etc') {
|
||||
setIsEtcSelected(true) // 기타 선택 시 input 활성화
|
||||
setIsEtcSelected(true)
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
[column]: null,
|
||||
})
|
||||
} else {
|
||||
setIsEtcSelected(false) // 기타 선택 해제 시 input 비활성화
|
||||
setIsEtcSelected(false)
|
||||
setEtcValue('')
|
||||
setDetailInfoData({
|
||||
...detailInfoData,
|
||||
@ -210,15 +207,9 @@ export default function SelectBoxForm({
|
||||
<div className="data-input-form-bx">
|
||||
<div className={font[column]}>{translateJapanese[column]}</div>
|
||||
<div className="data-input mb5">
|
||||
<select
|
||||
className="select-form"
|
||||
name={column}
|
||||
id={column}
|
||||
onChange={handleSelectChange}
|
||||
value={detailInfoData[column] ? detailInfoData[column] : detailInfoData[`${column}_etc`] ? 'etc' : ''}
|
||||
>
|
||||
<select className="select-form" name={column} id={column} onChange={handleSelectChange} value={detailInfoData[column] ?? ''}>
|
||||
<option value="" hidden>
|
||||
選択してください
|
||||
선택해주세요
|
||||
</option>
|
||||
{selectBoxOptions[column].map((option) => (
|
||||
<option key={option.id} value={option.id}>
|
||||
@ -234,13 +225,7 @@ export default function SelectBoxForm({
|
||||
className="input-frame"
|
||||
value={etcValue ?? ''}
|
||||
onChange={handleEtcInputChange}
|
||||
disabled={
|
||||
column === 'installation_availability'
|
||||
? !Boolean(detailInfoData[column])
|
||||
: column === 'construction_year'
|
||||
? detailInfoData[column] !== 2 // 既築(2)가 아닐 때 비활성화
|
||||
: !isEtcSelected
|
||||
}
|
||||
disabled={column === 'installation_availability' || column === 'construction_year' ? !Boolean(detailInfoData[column]) : !isEtcSelected}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -2,79 +2,91 @@
|
||||
|
||||
import LoadMoreButton from '@/components/LoadMoreButton'
|
||||
import { useServey } from '@/hooks/useSurvey'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import SearchForm from './SearchForm'
|
||||
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
|
||||
import { useSessionStore } from '@/store/session'
|
||||
|
||||
export default function ListTable() {
|
||||
const router = useRouter()
|
||||
const { surveyList, isLoadingSurveyList, surveyListCount } = useServey()
|
||||
const { offset, setOffset } = useSurveyFilterStore()
|
||||
const { surveyList, isLoadingSurveyList } = useServey()
|
||||
|
||||
const [heldSurveyList, setHeldSurveyList] = useState<typeof surveyList>([])
|
||||
const [visibleItems, setVisibleItems] = useState(5)
|
||||
const [search, setSearch] = useState('')
|
||||
const [isMyPostsOnly, setIsMyPostsOnly] = useState(false)
|
||||
const [hasMore, setHasMore] = useState(false)
|
||||
|
||||
const { session } = useSessionStore()
|
||||
console.log('session:: ', session)
|
||||
// TODO: 로그인 구현 이후 USERNAME 변경
|
||||
const username = 'test'
|
||||
|
||||
const filteredSurveyList = useMemo(() => {
|
||||
let filtered = surveyList
|
||||
if (search.trim().length > 0) {
|
||||
filtered = filtered.filter((survey) => survey.building_name?.includes(search))
|
||||
}
|
||||
if (isMyPostsOnly) {
|
||||
filtered = filtered.filter((survey) => survey.representative === username)
|
||||
}
|
||||
return filtered
|
||||
}, [surveyList, search, isMyPostsOnly, username])
|
||||
|
||||
useEffect(() => {
|
||||
if (surveyList && surveyList.length > 0) {
|
||||
if (offset === 0) {
|
||||
setHeldSurveyList(surveyList)
|
||||
} else {
|
||||
const remainingList = heldSurveyList.slice(offset, offset + 10)
|
||||
if (JSON.stringify(remainingList) !== JSON.stringify(surveyList)) {
|
||||
setHeldSurveyList((prev) => [...prev, ...surveyList])
|
||||
}
|
||||
}
|
||||
setHasMore(surveyListCount > offset + 10)
|
||||
}
|
||||
}, [surveyList, surveyListCount, offset])
|
||||
setHasMore(filteredSurveyList.length > visibleItems)
|
||||
}, [filteredSurveyList, visibleItems])
|
||||
|
||||
const handleLoadMore = () => {
|
||||
const newVisibleItems = Math.min(visibleItems + 5, filteredSurveyList.length)
|
||||
setVisibleItems(newVisibleItems)
|
||||
setHasMore(newVisibleItems < filteredSurveyList.length)
|
||||
}
|
||||
|
||||
const handleScrollToTop = () => {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
|
||||
const handleDetailClick = (id: number) => {
|
||||
router.push(`/survey-sale/${id}`)
|
||||
}
|
||||
const handleItemsInit = () => {
|
||||
setHeldSurveyList([])
|
||||
setOffset(0)
|
||||
|
||||
const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setSearch(e.target.value)
|
||||
setVisibleItems(5)
|
||||
}
|
||||
|
||||
// TODO: 로딩 처리 필요
|
||||
const handleMyPostsToggle = () => {
|
||||
setIsMyPostsOnly((prev) => !prev)
|
||||
setVisibleItems(5)
|
||||
}
|
||||
|
||||
if (isLoadingSurveyList) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<SearchForm onItemsInit={handleItemsInit} />
|
||||
{heldSurveyList.length > 0 ? (
|
||||
<div className="sale-frame">
|
||||
<ul className="sale-list-wrap">
|
||||
{heldSurveyList.map((survey) => (
|
||||
<li className="sale-list-item cursor-pointer" key={survey.id} onClick={() => handleDetailClick(survey.id)}>
|
||||
<div className="sale-item-bx">
|
||||
<div className="sale-item-date-bx">
|
||||
<div className="sale-item-num">{survey.id}</div>
|
||||
<div className="sale-item-date">{survey.investigation_date}</div>
|
||||
</div>
|
||||
<div className="sale-item-tit">{survey.building_name}</div>
|
||||
<div className="sale-item-customer">{survey.customer_name}</div>
|
||||
<div className="sale-item-update-bx">
|
||||
<div className="sale-item-name">{survey.representative}</div>
|
||||
<div className="sale-item-update">{new Date(survey.updated_at).toLocaleString()}</div>
|
||||
</div>
|
||||
<SearchForm handleSearch={handleSearchChange} handleMyPosts={handleMyPostsToggle} />
|
||||
<div className="sale-frame">
|
||||
<ul className="sale-list-wrap">
|
||||
{filteredSurveyList.slice(0, visibleItems).map((survey) => (
|
||||
<li className="sale-list-item cursor-pointer" key={survey.id} onClick={() => handleDetailClick(survey.id)}>
|
||||
<div className="sale-item-bx">
|
||||
<div className="sale-item-date-bx">
|
||||
<div className="sale-item-num">{survey.id}</div>
|
||||
<div className="sale-item-date">{survey.investigation_date}</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="sale-edit-btn">
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={() => setOffset(offset + 10)} />
|
||||
</div>
|
||||
<div className="sale-item-tit">{survey.building_name}</div>
|
||||
<div className="sale-item-customer">{survey.customer_name}</div>
|
||||
<div className="sale-item-update-bx">
|
||||
<div className="sale-item-name">{survey.representative}</div>
|
||||
<div className="sale-item-update">{new Date(survey.updated_at).toLocaleString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="sale-edit-btn">
|
||||
<LoadMoreButton hasMore={hasMore} onLoadMore={handleLoadMore} onScrollToTop={handleScrollToTop} />
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<p>作成された物件はありません。</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,25 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { SEARCH_OPTIONS, SEARCH_OPTIONS_ENUM, useSurveyFilterStore } from '@/store/surveyFilterStore'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useState } from 'react'
|
||||
|
||||
export default function SearchForm({ onItemsInit }: { onItemsInit: () => void }) {
|
||||
export default function SearchForm({ handleSearch, handleMyPosts }: { handleSearch: (e: React.ChangeEvent<HTMLInputElement>) => void, handleMyPosts: () => void }) {
|
||||
const router = useRouter()
|
||||
const { setSearchOption, setSort, setIsMySurvey, setKeyword, isMySurvey, keyword, searchOption, sort } = useSurveyFilterStore()
|
||||
const [searchKeyword, setSearchKeyword] = useState(keyword)
|
||||
|
||||
const username = 'test'
|
||||
|
||||
const handleSearch = () => {
|
||||
if (searchKeyword.trim().length < 2) {
|
||||
alert('2文字以上入力してください')
|
||||
return
|
||||
}
|
||||
setKeyword(searchKeyword)
|
||||
onItemsInit()
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="sale-frame">
|
||||
<div className="sale-form-bx">
|
||||
@ -28,64 +12,33 @@ export default function SearchForm({ onItemsInit }: { onItemsInit: () => void })
|
||||
</button>
|
||||
</div>
|
||||
<div className="sale-form-bx">
|
||||
<select
|
||||
className="select-form"
|
||||
name="search-option"
|
||||
id="search-option"
|
||||
value={searchOption}
|
||||
onChange={(e) => setSearchOption(e.target.value as SEARCH_OPTIONS_ENUM)}
|
||||
>
|
||||
{SEARCH_OPTIONS.map((option) => (
|
||||
<option key={option.id} value={option.id}>
|
||||
{option.label}
|
||||
</option>
|
||||
))}
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
<option value="">全体</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="sale-form-bx">
|
||||
<div className="search-input">
|
||||
<input
|
||||
type="text"
|
||||
className="search-frame"
|
||||
value={searchKeyword}
|
||||
placeholder="タイトルを入力してください. (2文字以上)"
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSearch()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button className="search-icon" onClick={handleSearch}></button>
|
||||
<input type="text" className="search-frame" placeholder="タイトルを入力してください. (2文字以上)" onChange={handleSearch} />
|
||||
<button className="search-icon"></button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sale-form-bx">
|
||||
<div className="check-form-box">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="ch01"
|
||||
checked={isMySurvey === username}
|
||||
onChange={() => {
|
||||
setIsMySurvey(isMySurvey === username ? null : username)
|
||||
onItemsInit()
|
||||
}}
|
||||
/>
|
||||
<input type="checkbox" id="ch01" onClick={handleMyPosts} />
|
||||
<label htmlFor="ch01">私が書いた物件</label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sale-form-bx">
|
||||
<select
|
||||
className="select-form"
|
||||
name="sort-option"
|
||||
id="sort-option"
|
||||
value={sort}
|
||||
onChange={(e) => {
|
||||
setSort(e.target.value as 'created' | 'updated')
|
||||
onItemsInit()
|
||||
}}
|
||||
>
|
||||
<option value="created">最近の登録日</option>
|
||||
<option value="updated">最新の更新日</option>
|
||||
<select className="select-form" name="" id="">
|
||||
<option value="">最近の登録日</option>
|
||||
<option value="">最近の登録日</option>
|
||||
<option value="">最近の登録日</option>
|
||||
<option value="">最近の登録日</option>
|
||||
<option value="">最近の登録日</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,8 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useHeaderStore } from '@/store/header'
|
||||
import { useSideNavState } from '@/store/sideNavState'
|
||||
import { usePathname, useRouter } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
export default function Main() {
|
||||
const router = useRouter()
|
||||
const pathname = usePathname()
|
||||
const { setBackBtn } = useHeaderStore()
|
||||
const { reset } = useSideNavState()
|
||||
|
||||
/**
|
||||
* 헤더 뒤로가기 버튼 컨트롤
|
||||
* 사이드바 초기화 컨트롤
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (pathname === '/') {
|
||||
setBackBtn(false)
|
||||
}
|
||||
//사이드바 초기화
|
||||
reset()
|
||||
}, [pathname])
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -11,7 +30,7 @@ export default function Main() {
|
||||
<div className="head-block-tit">屋根材の照会</div>
|
||||
<div className="head-block-text">ご使用の屋根材の適合性をご確認いただけます</div>
|
||||
<div className="head-block-link-wrap">
|
||||
<button className="head-block-link" onClick={() => router.push('/suitable')}>
|
||||
<button className="head-block-link">
|
||||
照会まさに移動<i className="block-arr"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@ -1,12 +1,8 @@
|
||||
'use client'
|
||||
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
|
||||
import MemberInfomationPopup from '../popup/MemberInformationPopup'
|
||||
import ZipCodePopup from '../popup/ZipCodePopup'
|
||||
import Alert from './common/Alert'
|
||||
import DoubleBtnAlert from './common/DoubleBtnAlert'
|
||||
import SuitableDetailPopup from '../popup/SuitableDetailPopup'
|
||||
|
||||
export default function PopupController() {
|
||||
const popupController = usePopupController()
|
||||
@ -15,9 +11,6 @@ export default function PopupController() {
|
||||
<>
|
||||
{popupController.memberInfomationPopup && <MemberInfomationPopup />}
|
||||
{popupController.zipCodePopup && <ZipCodePopup />}
|
||||
{popupController.alert && <Alert />}
|
||||
{popupController.alert2 && <DoubleBtnAlert />}
|
||||
{popupController.suitableDetailPopup && <SuitableDetailPopup />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@ -1,24 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
|
||||
export default function Alert() {
|
||||
const { alertMsg, alertBtn } = usePopupController()
|
||||
|
||||
return (
|
||||
<div className="modal-popup alert">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="alert-tit">{alertMsg}</div>
|
||||
<div className="alert-btn-wrap">
|
||||
<div className="alert-btn-bx">
|
||||
<button className="btn-frame red min" onClick={() => alertBtn()}>
|
||||
確認
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import React from 'react'
|
||||
|
||||
export default function DoubleBtnAlert() {
|
||||
const { alertMsg, alert2BtnYes, alert2BtnNo } = usePopupController()
|
||||
|
||||
return (
|
||||
<div className="modal-popup alert">
|
||||
<div className="modal-dialog">
|
||||
<div className="modal-content">
|
||||
<div className="alert-tit">{alertMsg}</div>
|
||||
<div className="alert-btn-wrap">
|
||||
<div className="alert-btn-bx">
|
||||
<button className="btn-frame red min" onClick={() => alert2BtnYes()}>
|
||||
確認
|
||||
</button>
|
||||
</div>
|
||||
<div className="alert-btn-bx">
|
||||
<button className="btn-frame n-blue min" onClick={() => alert2BtnNo()}>
|
||||
キャンセル
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@ -12,8 +12,6 @@ import type { HeaderProps } from '@/types/Header'
|
||||
|
||||
import 'swiper/css'
|
||||
import { axiosInstance } from '@/libs/axios'
|
||||
import { useSessionStore } from '@/store/session'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
|
||||
// type HeaderProps = {
|
||||
// name: string //header 이름
|
||||
@ -26,18 +24,13 @@ export default function Header({ name }: HeaderProps) {
|
||||
const { sideNavIsOpen, setSideNavIsOpen } = useSideNavState()
|
||||
const { backBtn } = useHeaderStore()
|
||||
|
||||
const { session, reset } = useSessionStore()
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
if (pathname === '/login') {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleLogout = async () => {
|
||||
reset()
|
||||
const { data } = await axiosInstance(null).get('/api/auth/logout')
|
||||
if (data.code === 200) {
|
||||
queryClient.clear()
|
||||
router.push('/login')
|
||||
}
|
||||
}
|
||||
@ -67,8 +60,8 @@ export default function Header({ name }: HeaderProps) {
|
||||
<img src="/assets/images/layout/side_nav_profile.svg" alt="profile" />
|
||||
</div>
|
||||
<div className="profile-group">
|
||||
<div className="profile-name">{session.userNm}</div>
|
||||
<div className="profile-company">{session.category}</div>
|
||||
<div className="profile-name">HONG GILDONG</div>
|
||||
<div className="profile-company">Interplug corp.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="side-close-wrap">
|
||||
|
||||
@ -1,33 +1,28 @@
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import type { SurveyBasicInfo, SurveyBasicRequest, SurveyDetailInfo, SurveyDetailRequest, SurveyDetailCoverRequest } from '@/types/Survey'
|
||||
import { axiosInstance } from '@/libs/axios'
|
||||
import { useSurveyFilterStore } from '@/store/surveyFilterStore'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
|
||||
import type { SurveyBasicInfo, SurveyBasicRequest, SurveyDetailInfo, SurveyDetailRequest } from '@/types/Survey'
|
||||
|
||||
export function useServey(id?: number): {
|
||||
surveyList: SurveyBasicInfo[] | []
|
||||
surveyDetail: SurveyBasicInfo | null
|
||||
surveyListCount: number
|
||||
isLoadingSurveyList: boolean
|
||||
isLoadingSurveyDetail: boolean
|
||||
isCreatingSurvey: boolean
|
||||
isUpdatingSurvey: boolean
|
||||
isDeletingSurvey: boolean
|
||||
createSurvey: (survey: SurveyBasicRequest) => Promise<number>
|
||||
createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => void
|
||||
createSurveyDetail: (params: { surveyId: number; surveyDetail: SurveyDetailRequest }) => void
|
||||
updateSurvey: (survey: SurveyBasicRequest) => void
|
||||
deleteSurvey: () => Promise<boolean>
|
||||
submitSurvey: () => void
|
||||
validateSurveyDetail: (surveyDetail: SurveyDetailRequest) => string
|
||||
} {
|
||||
const queryClient = useQueryClient()
|
||||
const { keyword, searchOption, isMySurvey, sort, offset } = useSurveyFilterStore()
|
||||
|
||||
const { data: surveyList, isLoading: isLoadingSurveyList } = useQuery({
|
||||
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort, offset],
|
||||
queryKey: ['survey', 'list'],
|
||||
queryFn: async () => {
|
||||
const resp = await axiosInstance(null).get<SurveyBasicInfo[]>('/api/survey-sales', {
|
||||
params: { keyword, searchOption, isMySurvey, sort, offset },
|
||||
})
|
||||
const resp = await axiosInstance.get<SurveyBasicInfo[]>('/api/survey-sales')
|
||||
return resp.data
|
||||
},
|
||||
})
|
||||
@ -37,25 +32,15 @@ export function useServey(id?: number): {
|
||||
queryFn: async () => {
|
||||
if (id === undefined) throw new Error('id is required')
|
||||
if (id === null) return null
|
||||
const resp = await axiosInstance(null).get<SurveyBasicInfo>(`/api/survey-sales/${id}`)
|
||||
const resp = await axiosInstance.get<SurveyBasicInfo>(`/api/survey-sales/${id}`)
|
||||
return resp.data
|
||||
},
|
||||
enabled: id !== undefined,
|
||||
})
|
||||
|
||||
const { data: surveyListCount } = useQuery({
|
||||
queryKey: ['survey', 'list', keyword, searchOption, isMySurvey, sort],
|
||||
queryFn: async () => {
|
||||
const resp = await axiosInstance(null).get<number>('/api/survey-sales', {
|
||||
params: { keyword, searchOption, isMySurvey, sort },
|
||||
})
|
||||
return resp.data
|
||||
},
|
||||
})
|
||||
|
||||
const { mutateAsync: createSurvey, isPending: isCreatingSurvey } = useMutation({
|
||||
mutationFn: async (survey: SurveyBasicRequest) => {
|
||||
const resp = await axiosInstance(null).post<SurveyBasicInfo>('/api/survey-sales', survey)
|
||||
const resp = await axiosInstance.post<SurveyBasicInfo>('/api/survey-sales', survey)
|
||||
return resp.data.id ?? 0
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
@ -69,7 +54,7 @@ export function useServey(id?: number): {
|
||||
mutationFn: async (survey: SurveyBasicRequest) => {
|
||||
console.log('updateSurvey:: ', survey)
|
||||
if (id === undefined) throw new Error('id is required')
|
||||
const resp = await axiosInstance(null).put<SurveyBasicInfo>(`/api/survey-sales/${id}`, survey)
|
||||
const resp = await axiosInstance.put<SurveyBasicInfo>(`/api/survey-sales/${id}`, survey)
|
||||
return resp.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
@ -81,7 +66,7 @@ export function useServey(id?: number): {
|
||||
const { mutateAsync: deleteSurvey, isPending: isDeletingSurvey } = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (id === null) throw new Error('id is required')
|
||||
const resp = await axiosInstance(null).delete<boolean>(`/api/survey-sales/${id}`)
|
||||
const resp = await axiosInstance.delete<boolean>(`/api/survey-sales/${id}`)
|
||||
return resp.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
@ -90,9 +75,9 @@ export function useServey(id?: number): {
|
||||
},
|
||||
})
|
||||
|
||||
const { mutateAsync: createSurveyDetail } = useMutation({
|
||||
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailCoverRequest }) => {
|
||||
const resp = await axiosInstance(null).patch<SurveyDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
|
||||
const { mutateAsync: createSurveyDetail, isPending: isCreatingSurveyDetail } = useMutation({
|
||||
mutationFn: async ({ surveyId, surveyDetail }: { surveyId: number; surveyDetail: SurveyDetailRequest }) => {
|
||||
const resp = await axiosInstance.post<SurveyDetailInfo>(`/api/survey-sales/${surveyId}`, surveyDetail)
|
||||
return resp.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
@ -104,9 +89,7 @@ export function useServey(id?: number): {
|
||||
const { mutateAsync: submitSurvey } = useMutation({
|
||||
mutationFn: async () => {
|
||||
if (id === undefined) throw new Error('id is required')
|
||||
const resp = await axiosInstance(null).patch<boolean>(`/api/survey-sales/${id}`, {
|
||||
submit: true,
|
||||
})
|
||||
const resp = await axiosInstance.patch<boolean>(`/api/survey-sales/${id}`)
|
||||
return resp.data
|
||||
},
|
||||
onSuccess: () => {
|
||||
@ -125,29 +108,31 @@ export function useServey(id?: number): {
|
||||
'waterproof_material',
|
||||
'insulation_presence',
|
||||
'structure_order',
|
||||
] as const
|
||||
] as const;
|
||||
|
||||
const etcFields = ['installation_system', 'construction_year', 'rafter_size', 'rafter_pitch', 'waterproof_material', 'structure_order'] as const
|
||||
const etcFields = [
|
||||
'installation_system',
|
||||
'construction_year',
|
||||
'rafter_size',
|
||||
'rafter_pitch',
|
||||
'waterproof_material',
|
||||
'structure_order',
|
||||
] as const;
|
||||
|
||||
const emptyField = requiredFields.find((field) => {
|
||||
if (etcFields.includes(field as (typeof etcFields)[number])) {
|
||||
return surveyDetail[field as keyof SurveyDetailRequest] === null && surveyDetail[`${field}_etc` as keyof SurveyDetailRequest] === null
|
||||
return surveyDetail[field as keyof SurveyDetailRequest] === null &&
|
||||
surveyDetail[`${field}_etc` as keyof SurveyDetailRequest] === null;
|
||||
}
|
||||
return surveyDetail[field as keyof SurveyDetailRequest] === null
|
||||
})
|
||||
return surveyDetail[field as keyof SurveyDetailRequest] === null;
|
||||
});
|
||||
|
||||
const contractCapacity = surveyDetail.contract_capacity
|
||||
if (contractCapacity && contractCapacity.trim() !== '' && contractCapacity.split(' ')?.length === 1) {
|
||||
return 'contract_capacity_unit'
|
||||
}
|
||||
|
||||
return emptyField || ''
|
||||
}
|
||||
return emptyField || '';
|
||||
};
|
||||
|
||||
return {
|
||||
surveyList: surveyList || [],
|
||||
surveyDetail: surveyDetail || null,
|
||||
surveyListCount: surveyListCount || 0,
|
||||
isLoadingSurveyList,
|
||||
isLoadingSurveyDetail,
|
||||
isCreatingSurvey,
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import { useAlertSwitch } from '@/store/alertSwitch'
|
||||
import { useHeaderStore } from '@/store/header'
|
||||
import { usePopupController } from '@/store/popupController'
|
||||
import { useSideNavState } from '@/store/sideNavState'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
alert2: (msg: string, alert2BtnYes?: () => void, alert2BtnNo?: () => void) => void
|
||||
}
|
||||
}
|
||||
|
||||
export default function EdgeProvider({ children }: React.PropsWithChildren) {
|
||||
const pathname = usePathname()
|
||||
const { setBackBtn } = useHeaderStore()
|
||||
const { reset } = useSideNavState()
|
||||
const { setAlertMsg, setAlertBtn, setAlert, setAlert2, setAlert2BtnYes, setAlert2BtnNo } = usePopupController()
|
||||
const { alertKind } = useAlertSwitch()
|
||||
|
||||
const alertFunc = (msg: string, alertBtn: Function) => {
|
||||
console.log('🚀 ~ alertFunc ~ msg:', msg)
|
||||
setAlertMsg(msg)
|
||||
setAlertBtn(alertBtn)
|
||||
setAlert(true)
|
||||
}
|
||||
/**
|
||||
* alert2 함수
|
||||
* @param msg alert 메시지
|
||||
* @param alertBtn2Yes alert 확인 버튼 클릭시 실행되는 함수
|
||||
* @param alertBtn2No alert 취소 버튼 클릭시 실행되는 함수
|
||||
*/
|
||||
const alertFunc2 = (msg: string, alertBtn2Yes: Function, alertBtn2No: Function) => {
|
||||
console.log('🚀 ~ alertFunc ~ msg:', msg)
|
||||
setAlertMsg(msg)
|
||||
setAlert2BtnYes(alertBtn2Yes)
|
||||
setAlert2BtnNo(alertBtn2No)
|
||||
setAlert2(true)
|
||||
}
|
||||
|
||||
//alert 함수 변경해서 바인딩
|
||||
useEffect(() => {
|
||||
if (alertKind === 'single') {
|
||||
window.alert = function (msg, alertBtn = () => setAlert(false)) {
|
||||
alertFunc(msg, alertBtn)
|
||||
}
|
||||
} else if (alertKind === 'multi') {
|
||||
window.alert = function (msg, alert2BtnYes = () => setAlert2(false), alert2BtnNo = () => setAlert2(false)) {
|
||||
alertFunc2(msg, alert2BtnYes, alert2BtnNo)
|
||||
}
|
||||
}
|
||||
}, [alertKind])
|
||||
|
||||
/**
|
||||
* 헤더 뒤로가기 버튼 컨트롤
|
||||
* 사이드바 초기화 컨트롤
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (pathname === '/') {
|
||||
setBackBtn(false)
|
||||
} else {
|
||||
setBackBtn(true)
|
||||
}
|
||||
//사이드바 초기화
|
||||
reset()
|
||||
}, [pathname])
|
||||
|
||||
return <>{children}</>
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
type AddressData = {
|
||||
post_code: string
|
||||
address: string
|
||||
address_detail: string
|
||||
}
|
||||
|
||||
interface AddressState {
|
||||
addressData: AddressData | null
|
||||
setAddressData: (address: AddressData) => void
|
||||
resetAddressData: () => void
|
||||
}
|
||||
|
||||
export const useAddressStore = create<AddressState>((set) => ({
|
||||
addressData: null,
|
||||
setAddressData: (address) => set({ addressData: address }),
|
||||
resetAddressData: () => set({ addressData: null }),
|
||||
}))
|
||||
@ -1,21 +0,0 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
type AlertSwitchState = {
|
||||
alertKind: string
|
||||
setAlertKind: (value: string) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
type InitialState = {
|
||||
alertKind: string
|
||||
}
|
||||
|
||||
const initialState: InitialState = {
|
||||
alertKind: 'single',
|
||||
}
|
||||
|
||||
export const useAlertSwitch = create<AlertSwitchState>((set) => ({
|
||||
...initialState,
|
||||
setAlertKind: (value: string) => set((state) => ({ ...state, alertKind: value })),
|
||||
reset: () => set(initialState),
|
||||
}))
|
||||
@ -1,41 +0,0 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
export const FILTER_OPTIONS = [
|
||||
{
|
||||
id: 'all',
|
||||
label: '全体',
|
||||
},
|
||||
{
|
||||
id: 'completed',
|
||||
label: '回答完了',
|
||||
},
|
||||
{
|
||||
id: 'waiting',
|
||||
label: '回答待ち',
|
||||
},
|
||||
]
|
||||
export type FILTER_OPTIONS_ENUM = (typeof FILTER_OPTIONS)[number]['id']
|
||||
|
||||
type InquiryFilterState = {
|
||||
keyword: string
|
||||
filter: FILTER_OPTIONS_ENUM
|
||||
isMySurvey: string | null
|
||||
offset: number
|
||||
setKeyword: (keyword: string) => void
|
||||
setFilter: (filter: FILTER_OPTIONS_ENUM) => void
|
||||
setIsMySurvey: (isMySurvey: string | null) => void
|
||||
setOffset: (offset: number) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
export const useInquiryFilterStore = create<InquiryFilterState>((set) => ({
|
||||
keyword: '',
|
||||
filter: 'all',
|
||||
isMySurvey: null,
|
||||
offset: 0,
|
||||
setKeyword: (keyword) => set({ keyword }),
|
||||
setFilter: (filter) => set({ filter }),
|
||||
setIsMySurvey: (isMySurvey) => set({ isMySurvey }),
|
||||
setOffset: (offset) => set({ offset }),
|
||||
reset: () => set({ keyword: '', filter: 'all', isMySurvey: null, offset: 0 }),
|
||||
}))
|
||||
@ -3,59 +3,23 @@ import { create } from 'zustand'
|
||||
type PoupControllerState = {
|
||||
memberInfomationPopup: boolean
|
||||
zipCodePopup: boolean
|
||||
alertMsg: string
|
||||
setAlertMsg: (value: string) => void
|
||||
alert: boolean
|
||||
alertBtn: Function
|
||||
alert2: boolean
|
||||
alert2BtnYes: Function
|
||||
alert2BtnNo: Function
|
||||
setMemberInfomationPopup: (value: boolean) => void
|
||||
setZipCodePopup: (value: boolean) => void
|
||||
setAlert: (value: boolean) => void
|
||||
setAlertBtn: (value: Function) => void
|
||||
setAlert2: (value: boolean) => void
|
||||
setAlert2BtnYes: (value: Function) => void
|
||||
setAlert2BtnNo: (value: Function) => void
|
||||
suitableDetailPopup: boolean
|
||||
setSuitableDetailPopup: (value: boolean) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
type InitialState = {
|
||||
memberInfomationPopup: boolean
|
||||
zipCodePopup: boolean
|
||||
alertMsg: string
|
||||
alert: boolean
|
||||
alertBtn: Function
|
||||
alert2: boolean
|
||||
alert2BtnYes: Function
|
||||
alert2BtnNo: Function
|
||||
suitableDetailPopup: boolean
|
||||
}
|
||||
|
||||
const initialState: InitialState = {
|
||||
memberInfomationPopup: false,
|
||||
zipCodePopup: false,
|
||||
alertMsg: '',
|
||||
alert: false,
|
||||
alertBtn: () => {},
|
||||
alert2: false,
|
||||
alert2BtnYes: () => {},
|
||||
alert2BtnNo: () => {},
|
||||
suitableDetailPopup: false,
|
||||
}
|
||||
|
||||
export const usePopupController = create<PoupControllerState>((set) => ({
|
||||
...initialState,
|
||||
setMemberInfomationPopup: (value: boolean) => set((state) => ({ ...state, memberInfomationPopup: value })),
|
||||
setZipCodePopup: (value: boolean) => set((state) => ({ ...state, zipCodePopup: value })),
|
||||
setAlertMsg: (value: string) => set((state) => ({ ...state, alertMsg: value })),
|
||||
setAlert: (value: boolean) => set((state) => ({ ...state, alert: value })),
|
||||
setAlertBtn: (value: Function) => set((state) => ({ ...state, alertBtn: value })),
|
||||
setAlert2: (value: boolean) => set((state) => ({ ...state, alert2: value })),
|
||||
setAlert2BtnYes: (value: Function) => set((state) => ({ ...state, alert2BtnYes: value })),
|
||||
setAlert2BtnNo: (value: Function) => set((state) => ({ ...state, alert2BtnNo: value })),
|
||||
setSuitableDetailPopup: (value: boolean) => set((state) => ({ ...state, suitableDetailPopup: value })),
|
||||
reset: () => set(initialState),
|
||||
}))
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
import type { SessionData } from '@/types/Auth'
|
||||
import { create } from 'zustand'
|
||||
|
||||
type SessionState = {
|
||||
session: SessionData
|
||||
setSession: (session: SessionData) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
type InitialState = {
|
||||
session: SessionData
|
||||
}
|
||||
|
||||
const initialState: InitialState = {
|
||||
session: {
|
||||
langCd: null,
|
||||
currPage: 0,
|
||||
rowCount: 0,
|
||||
startRow: null,
|
||||
endRow: null,
|
||||
compCd: null,
|
||||
agencyStoreId: null,
|
||||
storeId: null,
|
||||
userId: null,
|
||||
category: null,
|
||||
userNm: null,
|
||||
userNmKana: null,
|
||||
telNo: null,
|
||||
fax: null,
|
||||
email: null,
|
||||
lastEditUser: null,
|
||||
storeGubun: null,
|
||||
pwCurr: null,
|
||||
pwdInitYn: null,
|
||||
apprStatCd: null,
|
||||
loginFailCnt: 0,
|
||||
loginFailMinYn: null,
|
||||
priceViewStatCd: null,
|
||||
groupId: null,
|
||||
storeLvl: null,
|
||||
custCd: null,
|
||||
builderNo: null,
|
||||
isLoggedIn: false,
|
||||
},
|
||||
}
|
||||
|
||||
export const useSessionStore = create<SessionState>((set) => ({
|
||||
...initialState,
|
||||
setSession: (value: SessionData) => set((state) => ({ ...state, session: { ...state.session, ...value } })),
|
||||
reset: () => set(initialState),
|
||||
}))
|
||||
@ -1,94 +1,27 @@
|
||||
import { create } from 'zustand'
|
||||
|
||||
// export type SEARCH_OPTIONS_ENUM = 'all' | 'id' | 'building_name' | 'representative' | 'store' | 'construction_point'
|
||||
// | 'store_id' | 'construction_id'
|
||||
// export type SEARCH_OPTIONS_PARTNERS_ENUM = 'all' | 'id' | 'building_name' | 'representative'
|
||||
|
||||
// export type SORT_OPTIONS_ENUM = 'created' | 'updated'
|
||||
|
||||
export const SEARCH_OPTIONS = [
|
||||
{
|
||||
id: 'all',
|
||||
label: '全体',
|
||||
},
|
||||
{
|
||||
id: 'id',
|
||||
label: '登録番号',
|
||||
},
|
||||
{
|
||||
id: 'building_name',
|
||||
label: '建物名',
|
||||
},
|
||||
{
|
||||
id: 'representative',
|
||||
label: '作成者',
|
||||
},
|
||||
{
|
||||
id: 'store',
|
||||
label: '販売店名',
|
||||
},
|
||||
// {
|
||||
// id: 'store_id',
|
||||
// label: '販売店ID',
|
||||
// },
|
||||
{
|
||||
id: 'construction_point',
|
||||
label: '施工店名',
|
||||
},
|
||||
// {
|
||||
// id: 'construction_id',
|
||||
// label: '施工店ID',
|
||||
// },
|
||||
]
|
||||
|
||||
export const SEARCH_OPTIONS_PARTNERS = [
|
||||
{
|
||||
id: 'all',
|
||||
label: '全体',
|
||||
},
|
||||
{
|
||||
id: 'id',
|
||||
label: '登録番号',
|
||||
},
|
||||
{
|
||||
id: 'building_name',
|
||||
label: '建物名',
|
||||
},
|
||||
{
|
||||
id: 'representative',
|
||||
label: '作成者',
|
||||
},
|
||||
]
|
||||
|
||||
export type MEMBER_TYPE = 'hwj' | 'order' | 'musubi' | 'partner'
|
||||
export type SEARCH_OPTIONS_ENUM = (typeof SEARCH_OPTIONS)[number]['id']
|
||||
export type SEARCH_OPTIONS_PARTNERS_ENUM = (typeof SEARCH_OPTIONS_PARTNERS)[number]['id']
|
||||
export type SORT_OPTIONS_ENUM = 'created' | 'updated'
|
||||
|
||||
type SurveyFilterState = {
|
||||
keyword: string
|
||||
searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM
|
||||
isMySurvey: string | null
|
||||
sort: SORT_OPTIONS_ENUM
|
||||
offset: number
|
||||
setKeyword: (keyword: string) => void
|
||||
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => void
|
||||
setIsMySurvey: (isMySurvey: string | null) => void
|
||||
setSort: (sort: SORT_OPTIONS_ENUM) => void
|
||||
setOffset: (offset: number) => void
|
||||
reset: () => void
|
||||
keyword: string;
|
||||
searchOption: string;
|
||||
isMySurvey: boolean;
|
||||
sort: 'recent' | 'updated';
|
||||
setKeyword: (keyword: string) => void;
|
||||
setSearchOption: (searchOption: string) => void;
|
||||
setIsMySurvey: (isMySurvey: boolean) => void;
|
||||
setSort: (sort: 'recent' | 'updated') => void;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
export const useSurveyFilterStore = create<SurveyFilterState>((set) => ({
|
||||
keyword: '',
|
||||
searchOption: 'all',
|
||||
isMySurvey: null,
|
||||
sort: 'created',
|
||||
offset: 0,
|
||||
setKeyword: (keyword: string) => set({ keyword }),
|
||||
setSearchOption: (searchOption: SEARCH_OPTIONS_ENUM | SEARCH_OPTIONS_PARTNERS_ENUM) => set({ searchOption }),
|
||||
setIsMySurvey: (isMySurvey: string | null) => set({ isMySurvey }),
|
||||
setSort: (sort: SORT_OPTIONS_ENUM) => set({ sort }),
|
||||
setOffset: (offset: number) => set({ offset }),
|
||||
reset: () => set({ keyword: '', searchOption: 'all', isMySurvey: null, sort: 'created', offset: 0 }),
|
||||
keyword: '',
|
||||
searchOption: '',
|
||||
isMySurvey: false,
|
||||
sort: 'recent',
|
||||
setKeyword: (keyword: string) => set({ keyword }),
|
||||
setSearchOption: (searchOption: string) => set({ searchOption }),
|
||||
setIsMySurvey: (isMySurvey: boolean) => set({ isMySurvey }),
|
||||
setSort: (sort: 'recent' | 'updated') => set({ sort }),
|
||||
reset: () => set({ keyword: '', searchOption: '', isMySurvey: false, sort: 'recent' }),
|
||||
}))
|
||||
|
||||
|
||||
|
||||
@ -82,13 +82,13 @@ input::-webkit-inner-spin-button {
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
height: 40px;
|
||||
font-size: $font-s-13;
|
||||
color: $font-c;
|
||||
font-weight: $font-w-400;
|
||||
background-color: $white-fff;
|
||||
border: 1px solid #D5DEE8;
|
||||
border-radius: 4px;
|
||||
input{
|
||||
font-size: $font-s-13;
|
||||
color: $font-c;
|
||||
font-weight: $font-w-400;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: transparent;
|
||||
@ -194,11 +194,4 @@ input::-webkit-inner-spin-button {
|
||||
}
|
||||
}
|
||||
}
|
||||
&.change{
|
||||
height: 40px;
|
||||
padding-left: 10px;
|
||||
&::before{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,10 +16,4 @@
|
||||
margin-top: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px){
|
||||
.login-contents{
|
||||
padding: 70px 34px 0;
|
||||
}
|
||||
}
|
||||
@ -88,12 +88,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px){
|
||||
.main-grid-wrap{
|
||||
.main-grid-bx{
|
||||
padding: 40px 20px 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,11 +33,4 @@
|
||||
.btn-flex-wrap{
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
// 지붕재 적합성 상세
|
||||
.compliance-check-pop-contents{
|
||||
padding: 14px;
|
||||
border-top: 1px solid #ECECEC;
|
||||
margin-top: 10px;
|
||||
}
|
||||
@ -17,10 +17,6 @@
|
||||
@include defaultFont($font-s-13, $font-w-400, #A8B6C7);
|
||||
}
|
||||
}
|
||||
.data-input-guide{
|
||||
margin-top: 8px;
|
||||
@include defaultFont($font-s-13, $font-w-400, #A8B6C7);
|
||||
}
|
||||
}
|
||||
|
||||
.btn-flex-wrap{
|
||||
@ -29,14 +25,6 @@
|
||||
.btn-bx{
|
||||
flex: 1;
|
||||
}
|
||||
&.com{
|
||||
.btn-bx{
|
||||
flex: 1 1 auto;
|
||||
button{
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 매물 common
|
||||
@ -189,13 +177,6 @@
|
||||
padding-left: 6px;
|
||||
}
|
||||
}
|
||||
&.nodata{
|
||||
.sale-item-nodata{
|
||||
padding: 5px 0;
|
||||
text-align: center;
|
||||
@include defaultFont($font-s-15, $font-w-500, $font-c);
|
||||
}
|
||||
}
|
||||
}
|
||||
.sale-edit-btn{
|
||||
margin-top: 24px;
|
||||
@ -354,14 +335,6 @@
|
||||
top: 0;
|
||||
right: 0;
|
||||
}
|
||||
&.nodata{
|
||||
padding-right: 0;
|
||||
.inquiry-item-nodata{
|
||||
padding: 10px 0;
|
||||
text-align: center;
|
||||
@include defaultFont($font-s-15, $font-w-500, $font-c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -387,12 +360,9 @@
|
||||
@include flex(0px);
|
||||
align-items: center;
|
||||
.file-item-name{
|
||||
@include ellipsis(1);
|
||||
@include defaultFont($font-s-13, $font-w-400, $font-c);
|
||||
padding-right: 10px;
|
||||
}
|
||||
.file-del{
|
||||
flex: none;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
width: 16px;
|
||||
@ -446,177 +416,4 @@
|
||||
.inquiry-answer-date{
|
||||
@include defaultFont($font-s-13, $font-w-400, #F86A56);
|
||||
}
|
||||
}
|
||||
|
||||
// 비밀번호 변경
|
||||
.border-frame{
|
||||
padding: 20px;
|
||||
border-top: 1px solid #ECECEC;
|
||||
border-bottom: 1px solid #ECECEC;
|
||||
background-color: #fff;
|
||||
margin-bottom: 10px;
|
||||
&:last-child{
|
||||
border-bottom: none;
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.pw-guide{
|
||||
.pw-guide-tit{
|
||||
@include defaultFont($font-s-16, $font-w-500, #1259CB);
|
||||
}
|
||||
.pw-guide-txt{
|
||||
@include defaultFont($font-s-13, $font-w-400, #417DDC);
|
||||
}
|
||||
}
|
||||
|
||||
// 지붕재 적합성
|
||||
.compliance-icon{
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
&.check{
|
||||
background-image: url(/assets/images/sub/compliance_check_icon.svg);
|
||||
}
|
||||
&.x{
|
||||
background-image: url(/assets/images/sub/compliance_x_icon.svg);
|
||||
}
|
||||
&.quest{
|
||||
background-image: url(/assets/images/sub/compliance_quest_icon.svg);
|
||||
}
|
||||
&.tip{
|
||||
background-image: url(/assets/images/sub/compliance_tip_icon.svg);
|
||||
}
|
||||
}
|
||||
.compliance-check-wrap{
|
||||
padding-top: 10px;
|
||||
}
|
||||
.compliance-check-bx{
|
||||
position: relative;
|
||||
padding: 14px 18px;
|
||||
border: 1px solid #EFEFEF;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
&:last-child{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.act{
|
||||
.bx-btn{
|
||||
transform: rotate(0) !important;
|
||||
}
|
||||
.reference-list{
|
||||
display: block
|
||||
}
|
||||
}
|
||||
}
|
||||
.check-name-wrap{
|
||||
@include flex(0px);
|
||||
align-items: center;
|
||||
.check-name{
|
||||
@include defaultFont($font-s-13, $font-w-500, $font-c);
|
||||
}
|
||||
.check-name-btn{
|
||||
margin-left: auto;
|
||||
.bx-btn{
|
||||
display: block;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: url(/assets/images/sub/compliance_bx_icon.svg)no-repeat center;
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
.reference-list{
|
||||
display: none;
|
||||
margin-top: 10px;
|
||||
padding-top: 14px;
|
||||
border-top: 1px solid #ECECEC;
|
||||
transition: all .15s ease-in-out;
|
||||
.reference-item{
|
||||
margin-bottom: 8px;
|
||||
padding-left: 14px;
|
||||
.reference-item-bx{
|
||||
@include flex(10px);
|
||||
@include defaultFont($font-s-13, $font-w-400, $font-c);
|
||||
align-items: center;
|
||||
}
|
||||
&:last-child{
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
&.check{
|
||||
.reference-item{
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.compliace-nodata{
|
||||
margin-top: 24px;
|
||||
padding: 30px 0;
|
||||
@include defaultFont($font-s-13, $font-w-400, $font-c);
|
||||
text-align: center;
|
||||
}
|
||||
.compliace-nosearch{
|
||||
padding: 30px 0;
|
||||
span{
|
||||
display: block;
|
||||
@include defaultFont($font-s-13, $font-w-400, $font-c);
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
.check-form-box{
|
||||
&.com-tit{
|
||||
label{
|
||||
&:before{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
input[type="checkbox"]:checked + label{
|
||||
font-weight: 500;
|
||||
&::after{
|
||||
left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.com-txt{
|
||||
label{
|
||||
@include defaultFont($font-s-13, $font-w-400, #8595A7);
|
||||
&:before{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
input[type="checkbox"]:checked + label{
|
||||
&::after{
|
||||
left: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.check-item-wrap{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.compliance-icon-wrap{
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 360px){
|
||||
.btn-flex-wrap{
|
||||
flex-direction: column;
|
||||
}
|
||||
.data-check-wrap{
|
||||
.radio-form-box,
|
||||
.check-form-box{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +31,6 @@ $default-color: #2C2C2C;
|
||||
|
||||
.p-body{
|
||||
margin-top: 30px;
|
||||
padding-bottom: 70px;
|
||||
flex: 1 1 auto;
|
||||
.p-contents{
|
||||
max-width: 1480px;
|
||||
|
||||
@ -9,11 +9,11 @@ export interface SessionData {
|
||||
storeId: null
|
||||
userId: null
|
||||
category: null
|
||||
userNm: null | string
|
||||
userNmKana: null | string
|
||||
userNm: null
|
||||
userNmKana: null
|
||||
telNo: null
|
||||
fax: null
|
||||
email: null | string
|
||||
email: null
|
||||
lastEditUser: null
|
||||
storeGubun: null
|
||||
pwCurr: null
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
import { SEARCH_OPTIONS_ENUM, SEARCH_OPTIONS_PARTNERS_ENUM, SORT_OPTIONS_ENUM } from "@/store/surveyFilterStore"
|
||||
|
||||
export type SurveyBasicInfo = {
|
||||
id: number
|
||||
representative: string
|
||||
@ -20,7 +18,6 @@ export type SurveyBasicInfo = {
|
||||
|
||||
export type SurveyDetailInfo = {
|
||||
id: number
|
||||
basic_info_id: number
|
||||
contract_capacity: string | null
|
||||
retail_company: string | null
|
||||
supplementary_facilities: string | null // number 배열
|
||||
@ -111,7 +108,3 @@ export type SurveyDetailRequest = {
|
||||
installation_availability_etc: string | null
|
||||
memo: string | null
|
||||
}
|
||||
|
||||
export type SurveyDetailCoverRequest = {
|
||||
detail_info: SurveyDetailRequest
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user