Compare commits
7 Commits
5cc1cc9247
...
68a0c15c61
| Author | SHA1 | Date | |
|---|---|---|---|
| 68a0c15c61 | |||
| 799b0025c4 | |||
| 3088c88122 | |||
| 721edddbe0 | |||
| 510f886220 | |||
| 941c0921c9 | |||
| e35cc50660 |
105
README.md
105
README.md
@ -1,4 +1,71 @@
|
|||||||
# prisma 연결
|
# 모바일 현장 조사 애플리케이션 (onsitesurvey)
|
||||||
|
|
||||||
|
## 개요
|
||||||
|
|
||||||
|
Next.js와 TypeScript로 구축된 현장 조사용 모바일 애플리케이션입니다.
|
||||||
|
|
||||||
|
## 기술 스택
|
||||||
|
|
||||||
|
- **프레임워크**: Next.js (App Router)
|
||||||
|
- **언어**: TypeScript
|
||||||
|
- **UI 컴포넌트**: Shadcn UI, Radix UI
|
||||||
|
- **스타일링**: Tailwind CSS
|
||||||
|
- **상태 관리**: React Query
|
||||||
|
- **데이터베이스**: Prisma ORM
|
||||||
|
- **인증**: 세션 기반 인증
|
||||||
|
|
||||||
|
## 시작하기
|
||||||
|
|
||||||
|
### 필수 요구사항
|
||||||
|
|
||||||
|
- Node.js (LTS 버전)
|
||||||
|
- npm 또는 yarn 또는 pnpm 또는 burn
|
||||||
|
- Prisma CLI
|
||||||
|
|
||||||
|
### 설치 방법
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 의존성 설치
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 환경 변수 설정
|
||||||
|
cp .env.example .env.local
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 개발 서버 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 개발 서버 실행
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 프로덕션 빌드
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 애플리케이션 빌드
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 프로덕션 서버 실행
|
||||||
|
npm start
|
||||||
|
```
|
||||||
|
|
||||||
|
## 프로젝트 구조
|
||||||
|
|
||||||
|
[프로젝트 구조 Diagram](./diagram/mermaid.md)
|
||||||
|
|
||||||
|
```
|
||||||
|
├── app/ # Next.js 앱 디렉토리
|
||||||
|
├── components/ # React 컴포넌트
|
||||||
|
├── hooks/ # 커스텀 React 훅
|
||||||
|
├── lib/ # 유틸리티 함수 및 설정
|
||||||
|
├── prisma/ # 데이터베이스 스키마 및 마이그레이션
|
||||||
|
└── public/ # 정적 자산
|
||||||
|
```
|
||||||
|
|
||||||
|
## 참고
|
||||||
|
|
||||||
|
### prisma 연결
|
||||||
|
|
||||||
```
|
```
|
||||||
npx prisma migrate dev
|
npx prisma migrate dev
|
||||||
@ -11,38 +78,38 @@ npx prisma db push
|
|||||||
|
|
||||||
generate 를 진행해야 로컬에 연결 파일들이 생성이되고 pull push 를 사용할 수 있게 됨.
|
generate 를 진행해야 로컬에 연결 파일들이 생성이되고 pull push 를 사용할 수 있게 됨.
|
||||||
|
|
||||||
# react query cache data 가져오기
|
### react query cache data 가져오기
|
||||||
|
|
||||||
```
|
```
|
||||||
const cache = useQueryClient()
|
const cache = useQueryClient()
|
||||||
const data = cache.getQueryData(['user', 'info']) as UserState
|
const data = cache.getQueryData(['user', 'info']) as UserState
|
||||||
```
|
```
|
||||||
|
|
||||||
# 팝업 컨트롤러 제어
|
### 팝업 컨트롤러 제어
|
||||||
|
|
||||||
### open
|
[open]
|
||||||
|
|
||||||
```
|
```javascript
|
||||||
const popupController = usePopupController()
|
const popupController = usePopupController()
|
||||||
|
|
||||||
onClick={() => popupController.setMemberInformationPopup(true)}
|
onClick={() => popupController.setMemberInformationPopup(true)}
|
||||||
onClick={() => popupController.setZipCodePopup(true)}
|
onClick={() => popupController.setZipCodePopup(true)}
|
||||||
```
|
```
|
||||||
|
|
||||||
### close
|
[close]
|
||||||
|
|
||||||
```
|
```javascript
|
||||||
const popupController = usePopupController()
|
const popupController = usePopupController()
|
||||||
|
|
||||||
onClick={() => popupController.setMemberInformationPopup(false)}
|
onClick={() => popupController.setMemberInformationPopup(false)}
|
||||||
onClick={() => popupController.setZipCodePopup(false)}
|
onClick={() => popupController.setZipCodePopup(false)}
|
||||||
```
|
```
|
||||||
|
|
||||||
# useEffect 정리
|
### useEffect 정리
|
||||||
|
|
||||||
- client url pathname 변경시 -> @/components/ui/Header.tsx
|
- client url pathname 변경시 -> @/components/ui/Header.tsx
|
||||||
|
|
||||||
# User Role 구분
|
### User Role 구분
|
||||||
|
|
||||||
session에 있는 role 키로 구분한다
|
session에 있는 role 키로 구분한다
|
||||||
|
|
||||||
@ -61,7 +128,7 @@ session에 있는 role 키로 구분한다
|
|||||||
- 이외의 경우 -> 굳이 체크할 필요 없어보임\
|
- 이외의 경우 -> 굳이 체크할 필요 없어보임\
|
||||||
session.role === 'User'
|
session.role === 'User'
|
||||||
|
|
||||||
# 지붕재 적합성 TODO
|
### 지붕재 적합성 TODO
|
||||||
|
|
||||||
```
|
```
|
||||||
const suitableCheckIcon = (value: string): string => {
|
const suitableCheckIcon = (value: string): string => {
|
||||||
@ -82,3 +149,21 @@ const suitableCheckMemo = (value: string): string => {
|
|||||||
|
|
||||||
- src/hooks/useSuitable.ts > suitableCheckIcon(), suitableCheckMemo()
|
- src/hooks/useSuitable.ts > suitableCheckIcon(), suitableCheckMemo()
|
||||||
- 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요
|
- 추후 지붕재 적합성 데이터 CUD 구현 시 ×, ー 데이터 관리 필요
|
||||||
|
|
||||||
|
# 주의
|
||||||
|
|
||||||
|
## <span style="color: red">Prisma ORM 사용 시 주의사항</span>
|
||||||
|
|
||||||
|
현재 프로젝트는 Prisma ORM을 통해 데이터베이스의 일부 테이블만 관리하고 있습니다. 이로 인해 `prisma db pull` 또는 `prisma db push` 명령어 사용 시 주의가 필요합니다.
|
||||||
|
|
||||||
|
### <span style="color: red">잠재적 위험성</span>
|
||||||
|
|
||||||
|
- `schema.prisma` 파일에 정의된 모델이 의도치 않게 수정될 수 있습니다.
|
||||||
|
- 기존에 정의된 모델의 속성이나 관계가 손상될 수 있습니다.
|
||||||
|
- 데이터베이스 스키마와 Prisma 스키마 간의 불일치가 발생할 수 있습니다.
|
||||||
|
|
||||||
|
### <span style="color: red">권장 사항</span>
|
||||||
|
|
||||||
|
- 데이터베이스 스키마 변경이 필요한 경우, 반드시 팀 내 논의 후 진행하시기 바랍니다.
|
||||||
|
- `prisma db pull` 실행 전 현재 `schema.prisma` 파일의 백업을 권장합니다.
|
||||||
|
- 변경 사항 적용 전 staging 환경에서 충분한 테스트를 진행하시기 바랍니다.
|
||||||
|
|||||||
@ -7,103 +7,65 @@ datasource db {
|
|||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
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_memo String? @db.VarChar(500)
|
|
||||||
support_roof_bracket String? @db.VarChar(200)
|
|
||||||
support_roof_bracket_memo String? @db.VarChar(500)
|
|
||||||
yg_anchor String? @db.VarChar(200)
|
|
||||||
yg_anchor_memo String? @db.VarChar(500)
|
|
||||||
rg_roof_tile_part String? @db.VarChar(200)
|
|
||||||
rg_roof_tile_part_memo String? @db.VarChar(500)
|
|
||||||
dido_hunt_support_tile_2 String? @db.VarChar(200)
|
|
||||||
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)
|
|
||||||
slate_bracket_4 String? @db.VarChar(200)
|
|
||||||
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)
|
|
||||||
dido_hunt_short_rack_4 String? @db.VarChar(200)
|
|
||||||
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_metal_bracket String? @db.VarChar(200)
|
|
||||||
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_SURVEY_SALES_BASIC_INFO {
|
model SD_SURVEY_SALES_BASIC_INFO {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
SRL_NO String @db.VarChar(20)
|
SRL_NO String @db.NVarChar(20)
|
||||||
REPRESENTATIVE String @db.VarChar(200)
|
REPRESENTATIVE String @db.NVarChar(200)
|
||||||
STORE String? @db.VarChar(200)
|
STORE String? @db.NVarChar(200)
|
||||||
CONSTRUCTION_POINT String? @db.VarChar(200)
|
CONSTRUCTION_POINT String? @db.NVarChar(200)
|
||||||
INVESTIGATION_DATE String? @db.VarChar(10)
|
INVESTIGATION_DATE String? @db.NVarChar(10)
|
||||||
BUILDING_NAME String? @db.VarChar(200)
|
BUILDING_NAME String? @db.NVarChar(200)
|
||||||
CUSTOMER_NAME String? @db.VarChar(200)
|
CUSTOMER_NAME String? @db.NVarChar(200)
|
||||||
POST_CODE String? @db.VarChar(10)
|
POST_CODE String? @db.NVarChar(10)
|
||||||
ADDRESS String? @db.VarChar(200)
|
ADDRESS String? @db.NVarChar(200)
|
||||||
ADDRESS_DETAIL String? @db.VarChar(300)
|
ADDRESS_DETAIL String? @db.NVarChar(300)
|
||||||
SUBMISSION_STATUS Boolean @default(false)
|
SUBMISSION_STATUS Boolean @default(false)
|
||||||
SUBMISSION_DATE DateTime? @db.Date
|
SUBMISSION_DATE DateTime? @db.Date
|
||||||
SUBMISSION_TARGET_ID String? @db.VarChar(200)
|
SUBMISSION_TARGET_ID String? @db.NVarChar(200)
|
||||||
REG_DT DateTime @default(now())
|
REG_DT DateTime @default(now())
|
||||||
UPT_DT DateTime @updatedAt
|
UPT_DT DateTime @updatedAt
|
||||||
REPRESENTATIVE_ID String? @db.VarChar(100)
|
REPRESENTATIVE_ID String? @db.NVarChar(100)
|
||||||
STORE_ID String? @db.VarChar(100)
|
STORE_ID String? @db.NVarChar(100)
|
||||||
DETAIL_INFO SD_SURVEY_SALES_DETAIL_INFO?
|
DETAIL_INFO SD_SURVEY_SALES_DETAIL_INFO?
|
||||||
}
|
}
|
||||||
|
|
||||||
model SD_SURVEY_SALES_DETAIL_INFO {
|
model SD_SURVEY_SALES_DETAIL_INFO {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
CONTRACT_CAPACITY String? @db.VarChar(20)
|
CONTRACT_CAPACITY String? @db.NVarChar(20)
|
||||||
RETAIL_COMPANY String? @db.VarChar(100)
|
RETAIL_COMPANY String? @db.NVarChar(100)
|
||||||
SUPPLEMENTARY_FACILITIES String? @db.VarChar(20)
|
SUPPLEMENTARY_FACILITIES String? @db.NVarChar(20)
|
||||||
SUPPLEMENTARY_FACILITIES_ETC String? @db.VarChar(200)
|
SUPPLEMENTARY_FACILITIES_ETC String? @db.NVarChar(200)
|
||||||
INSTALLATION_SYSTEM String? @db.VarChar(20)
|
INSTALLATION_SYSTEM String? @db.NVarChar(20)
|
||||||
INSTALLATION_SYSTEM_ETC String? @db.VarChar(200)
|
INSTALLATION_SYSTEM_ETC String? @db.NVarChar(200)
|
||||||
CONSTRUCTION_YEAR String? @db.VarChar(200)
|
CONSTRUCTION_YEAR String? @db.NVarChar(200)
|
||||||
CONSTRUCTION_YEAR_ETC String? @db.VarChar(200)
|
CONSTRUCTION_YEAR_ETC String? @db.NVarChar(200)
|
||||||
ROOF_MATERIAL String? @db.VarChar(20)
|
ROOF_MATERIAL String? @db.NVarChar(20)
|
||||||
ROOF_MATERIAL_ETC String? @db.VarChar(200)
|
ROOF_MATERIAL_ETC String? @db.NVarChar(200)
|
||||||
ROOF_SHAPE String? @db.VarChar(20)
|
ROOF_SHAPE String? @db.NVarChar(20)
|
||||||
ROOF_SHAPE_ETC String? @db.VarChar(200)
|
ROOF_SHAPE_ETC String? @db.NVarChar(200)
|
||||||
ROOF_SLOPE String? @db.VarChar(5)
|
ROOF_SLOPE String? @db.NVarChar(5)
|
||||||
HOUSE_STRUCTURE String? @db.VarChar(20)
|
HOUSE_STRUCTURE String? @db.NVarChar(20)
|
||||||
HOUSE_STRUCTURE_ETC String? @db.VarChar(200)
|
HOUSE_STRUCTURE_ETC String? @db.NVarChar(200)
|
||||||
RAFTER_MATERIAL String? @db.VarChar(20)
|
RAFTER_MATERIAL String? @db.NVarChar(20)
|
||||||
RAFTER_MATERIAL_ETC String? @db.VarChar(200)
|
RAFTER_MATERIAL_ETC String? @db.NVarChar(200)
|
||||||
RAFTER_SIZE String? @db.VarChar(20)
|
RAFTER_SIZE String? @db.NVarChar(20)
|
||||||
RAFTER_SIZE_ETC String? @db.VarChar(200)
|
RAFTER_SIZE_ETC String? @db.NVarChar(200)
|
||||||
RAFTER_PITCH String? @db.VarChar(20)
|
RAFTER_PITCH String? @db.NVarChar(20)
|
||||||
RAFTER_PITCH_ETC String? @db.VarChar(200)
|
RAFTER_PITCH_ETC String? @db.NVarChar(200)
|
||||||
RAFTER_DIRECTION String? @db.VarChar(20)
|
RAFTER_DIRECTION String? @db.NVarChar(20)
|
||||||
OPEN_FIELD_PLATE_KIND String? @db.VarChar(20)
|
OPEN_FIELD_PLATE_KIND String? @db.NVarChar(20)
|
||||||
OPEN_FIELD_PLATE_KIND_ETC String? @db.VarChar(200)
|
OPEN_FIELD_PLATE_KIND_ETC String? @db.NVarChar(200)
|
||||||
OPEN_FIELD_PLATE_THICKNESS String? @db.VarChar(5)
|
OPEN_FIELD_PLATE_THICKNESS String? @db.NVarChar(5)
|
||||||
LEAK_TRACE Boolean? @default(false)
|
LEAK_TRACE Boolean? @default(false)
|
||||||
WATERPROOF_MATERIAL String? @db.VarChar(20)
|
WATERPROOF_MATERIAL String? @db.NVarChar(20)
|
||||||
WATERPROOF_MATERIAL_ETC String? @db.VarChar(200)
|
WATERPROOF_MATERIAL_ETC String? @db.NVarChar(200)
|
||||||
INSULATION_PRESENCE String? @db.VarChar(20)
|
INSULATION_PRESENCE String? @db.NVarChar(20)
|
||||||
INSULATION_PRESENCE_ETC String? @db.VarChar(200)
|
INSULATION_PRESENCE_ETC String? @db.NVarChar(200)
|
||||||
STRUCTURE_ORDER String? @db.VarChar(20)
|
STRUCTURE_ORDER String? @db.NVarChar(20)
|
||||||
STRUCTURE_ORDER_ETC String? @db.VarChar(200)
|
STRUCTURE_ORDER_ETC String? @db.NVarChar(200)
|
||||||
INSTALLATION_AVAILABILITY String? @db.VarChar(20)
|
INSTALLATION_AVAILABILITY String? @db.NVarChar(20)
|
||||||
INSTALLATION_AVAILABILITY_ETC String? @db.VarChar(200)
|
INSTALLATION_AVAILABILITY_ETC String? @db.NVarChar(200)
|
||||||
MEMO String? @db.VarChar(500)
|
MEMO String? @db.NVarChar(500)
|
||||||
REG_DT DateTime @default(now())
|
REG_DT DateTime @default(now())
|
||||||
UPT_DT DateTime @updatedAt
|
UPT_DT DateTime @updatedAt
|
||||||
BASIC_INFO_ID Int @unique
|
BASIC_INFO_ID Int @unique
|
||||||
@ -171,20 +133,20 @@ model BC_COMM_L {
|
|||||||
}
|
}
|
||||||
|
|
||||||
model MS_SUITABLE_ROOF_MATERIAL_GROUP {
|
model MS_SUITABLE_ROOF_MATERIAL_GROUP {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
ROOF_MATERIAL_GROUP String @db.VarChar(200)
|
ROOF_MATL_GRP_CD String @db.NVarChar(200)
|
||||||
ROOF_MT_CD String @db.VarChar(200)
|
ROOF_MT_CD String @db.NVarChar(200)
|
||||||
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__creat__4F7CD00D")
|
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__creat__4F7CD00D")
|
||||||
UPT_DT DateTime?
|
UPT_DT DateTime?
|
||||||
}
|
}
|
||||||
|
|
||||||
model MS_SUITABLE_DETAIL {
|
model MS_SUITABLE_DETAIL {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
MAIN_ID Int
|
MAIN_ID Int
|
||||||
TRESTLE_MFPC_CD String? @db.VarChar(200)
|
TRESTLE_MFPC_CD String? @db.NVarChar(200)
|
||||||
TRESTLE_MANUFACTURER_PRODUCT_NAME String? @db.VarChar(200)
|
TRESTLE_MANUFACTURER_PRODUCT_NAME String? @db.NVarChar(200)
|
||||||
MEMO String? @db.VarChar(500)
|
MEMO String? @db.NVarChar(500)
|
||||||
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__creat__571DF1D5")
|
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__REG_D__1332DBDC")
|
||||||
UPT_DT DateTime?
|
UPT_DT DateTime?
|
||||||
MS_SUITABLE_MAIN MS_SUITABLE_MAIN @relation(fields: [MAIN_ID], references: [ID], onUpdate: NoAction, map: "MS_SUITABLE_DETAIL_MS_SUITABLE_MAIN_FK")
|
MS_SUITABLE_MAIN MS_SUITABLE_MAIN @relation(fields: [MAIN_ID], references: [ID], onUpdate: NoAction, map: "MS_SUITABLE_DETAIL_MS_SUITABLE_MAIN_FK")
|
||||||
|
|
||||||
@ -193,24 +155,25 @@ model MS_SUITABLE_DETAIL {
|
|||||||
|
|
||||||
model MS_SUITABLE_MAIN {
|
model MS_SUITABLE_MAIN {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
PRODUCT_NAME String @db.VarChar(200)
|
PRODUCT_NAME String @db.NVarChar(200)
|
||||||
MANU_FT_CD String? @db.VarChar(200)
|
MANU_FT_CD String? @db.NVarChar(200)
|
||||||
ROOF_MT_CD String? @db.VarChar(100)
|
ROOF_MT_CD String? @db.NVarChar(100)
|
||||||
ROOF_SH_CD String? @db.VarChar(200)
|
ROOF_SH_CD String? @db.NVarChar(200)
|
||||||
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__creat__5441852A")
|
REG_DT DateTime @default(now(), map: "DF__MS_SUITAB__REG_D__10566F31")
|
||||||
UPT_DT DateTime?
|
UPT_DT DateTime?
|
||||||
|
UPT_ID String? @db.NVarChar(50)
|
||||||
|
DEL_YN String? @db.NVarChar(1)
|
||||||
MS_SUITABLE_DETAIL MS_SUITABLE_DETAIL[]
|
MS_SUITABLE_DETAIL MS_SUITABLE_DETAIL[]
|
||||||
|
|
||||||
@@index([PRODUCT_NAME], map: "MS_SUITABLE_MAIN_PRODUCT_NAME_IDX")
|
@@index([PRODUCT_NAME], map: "MS_SUITABLE_MAIN_PRODUCT_NAME_IDX")
|
||||||
@@index([ROOF_MT_CD, PRODUCT_NAME], map: "MS_SUITABLE_MAIN_ROOF_MT_CD_IDX")
|
@@index([ROOF_MT_CD, PRODUCT_NAME], map: "MS_SUITABLE_MAIN_ROOF_MT_CD_IDX")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The underlying table does not contain a valid unique identifier and can therefore currently not be handled by Prisma Client.
|
|
||||||
model MS_USR_TRK {
|
model MS_USR_TRK {
|
||||||
ID Int @id @default(autoincrement())
|
ID Int @id @default(autoincrement())
|
||||||
OWNER String @db.VarChar(100)
|
OWNER String @db.NVarChar(100)
|
||||||
TYPE String @db.VarChar(50)
|
TYPE String @db.NVarChar(50)
|
||||||
URL String? @db.VarChar(200)
|
URL String? @db.NVarChar(200)
|
||||||
REG_DT DateTime @default(now())
|
REG_DT DateTime @default(now())
|
||||||
DATA String? @db.VarChar(200)
|
DATA String? @db.NVarChar(200)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,9 +90,9 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => {
|
|||||||
// MUSUBI (시공권한 X) 같은 판매점에서 작성한 제출/제출되지 않은 매물
|
// MUSUBI (시공권한 X) 같은 판매점에서 작성한 제출/제출되지 않은 매물
|
||||||
AND: [
|
AND: [
|
||||||
{ STORE_ID: { equals: params.store } },
|
{ STORE_ID: { equals: params.store } },
|
||||||
{
|
// {
|
||||||
OR: [{ CONSTRUCTION_POINT: { equals: null } }, { CONSTRUCTION_POINT: { equals: '' } }],
|
// OR: [{ CONSTRUCTION_POINT: { equals: null } }, { CONSTRUCTION_POINT: { equals: '' } }],
|
||||||
},
|
// },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -109,10 +109,10 @@ const createMemberRoleCondition = (params: SearchParams): WhereCondition => {
|
|||||||
|
|
||||||
case 'Builder': // MUSUBI (시공권한 O)
|
case 'Builder': // MUSUBI (시공권한 O)
|
||||||
case 'Partner': // PARTNER
|
case 'Partner': // PARTNER
|
||||||
// 시공점이 있고 STORE_ID가 시공ID와 같은 매물
|
// 시공점이 있고 STORE_ID 가 시공ID와 같은 매물
|
||||||
where.AND?.push({
|
where.AND?.push({
|
||||||
CONSTRUCTION_POINT: { not: null },
|
// CONSTRUCTION_POINT: { not: null },
|
||||||
STORE_ID: { equals: params.builderNo },
|
CONSTRUCTION_POINT: { equals: params.builderNo },
|
||||||
})
|
})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@ -35,13 +35,28 @@ export default function SurveySaleSubmitPopup() {
|
|||||||
const { getCommCode } = useCommCode()
|
const { getCommCode } = useCommCode()
|
||||||
const { surveyDetail } = useSurvey(Number(routeId))
|
const { surveyDetail } = useSurvey(Number(routeId))
|
||||||
|
|
||||||
|
const [submitData, setSubmitData] = useState<SubmitFormData>({
|
||||||
|
saleBase: null,
|
||||||
|
store: '',
|
||||||
|
sender: session?.email ?? '',
|
||||||
|
receiver: [],
|
||||||
|
reference: null,
|
||||||
|
title: '[HANASYS現地調査] 調査物件が提出.',
|
||||||
|
contents: '',
|
||||||
|
})
|
||||||
|
|
||||||
const [commCodeList, setCommCodeList] = useState<CommCode[]>([])
|
const [commCodeList, setCommCodeList] = useState<CommCode[]>([])
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.isLoggedIn && session?.role === 'Admin') {
|
if (!session?.isLoggedIn) return
|
||||||
|
if (session?.role === 'Admin') {
|
||||||
getCommCode('SALES_OFFICE_CD').then((codes) => {
|
getCommCode('SALES_OFFICE_CD').then((codes) => {
|
||||||
setCommCodeList(codes)
|
setCommCodeList(codes)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
setSubmitData({
|
||||||
|
...submitData,
|
||||||
|
sender: session?.email ?? '',
|
||||||
|
})
|
||||||
}, [session])
|
}, [session])
|
||||||
|
|
||||||
const FORM_FIELDS: FormField[] = [
|
const FORM_FIELDS: FormField[] = [
|
||||||
@ -54,16 +69,6 @@ export default function SurveySaleSubmitPopup() {
|
|||||||
{ id: 'contents', name: '内容', required: false },
|
{ id: 'contents', name: '内容', required: false },
|
||||||
]
|
]
|
||||||
|
|
||||||
const [submitData, setSubmitData] = useState<SubmitFormData>({
|
|
||||||
saleBase: null,
|
|
||||||
store: '',
|
|
||||||
sender: session?.email ?? '',
|
|
||||||
receiver: [],
|
|
||||||
reference: null,
|
|
||||||
title: '[HANASYS現地調査] 調査物件が提出.',
|
|
||||||
contents: '',
|
|
||||||
})
|
|
||||||
|
|
||||||
const { submitSurvey, isSubmittingSurvey } = useSurvey(Number(routeId))
|
const { submitSurvey, isSubmittingSurvey } = useSurvey(Number(routeId))
|
||||||
|
|
||||||
const handleInputChange = (field: keyof SubmitFormData, value: string) => {
|
const handleInputChange = (field: keyof SubmitFormData, value: string) => {
|
||||||
@ -108,6 +113,7 @@ export default function SurveySaleSubmitPopup() {
|
|||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsShow(false)
|
setIsShow(false)
|
||||||
|
submitSurvey({ targetId: submitData.store })
|
||||||
popupController.setSurveySaleSubmitPopup(false)
|
popupController.setSurveySaleSubmitPopup(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
|
|||||||
representative: session.userNm ?? '',
|
representative: session.userNm ?? '',
|
||||||
representativeId: session.userId ?? null,
|
representativeId: session.userId ?? null,
|
||||||
store: session.role === 'Partner' ? null : session.storeNm ?? null,
|
store: session.role === 'Partner' ? null : session.storeNm ?? null,
|
||||||
storeId: session.role === 'Partner' || session.role === 'Builder' ? session.builderNo : session.storeId ?? null,
|
storeId: session.role === 'Partner' ? session.builderNo : session.storeId ?? null,
|
||||||
constructionPoint: session.builderNo ?? null,
|
constructionPoint: session.builderNo ?? null,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -67,29 +67,15 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
|
|||||||
onChange={(e) => setBasicInfo({ ...basicInfo, representative: e.target.value })}
|
onChange={(e) => setBasicInfo({ ...basicInfo, representative: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{(session?.role === 'Builder' || session?.role?.includes('Admin')) && (
|
{mode === 'READ' || session?.role === 'Builder' ? (
|
||||||
<div className="data-input-form-bx">
|
<>
|
||||||
<div className="data-input-form-tit">販売店</div>
|
{storeInput(basicInfo, setBasicInfo, mode)}
|
||||||
<input
|
{builderInput(basicInfo, setBasicInfo, mode)}
|
||||||
type="text"
|
</>
|
||||||
className="input-frame"
|
) : session?.role === 'Partner' ? (
|
||||||
readOnly
|
<>{builderInput(basicInfo, setBasicInfo, mode)}</>
|
||||||
value={basicInfo?.store ?? ''}
|
) : (
|
||||||
onChange={(e) => setBasicInfo({ ...basicInfo, store: e.target.value })}
|
<>{storeInput(basicInfo, setBasicInfo, mode)}</>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{(session?.role === 'Builder' || session?.role === 'Partner') && (
|
|
||||||
<div className="data-input-form-bx">
|
|
||||||
<div className="data-input-form-tit">施工店</div>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
className="input-frame"
|
|
||||||
readOnly
|
|
||||||
value={basicInfo?.constructionPoint ?? ''}
|
|
||||||
onChange={(e) => setBasicInfo({ ...basicInfo, constructionPoint: e.target.value })}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -169,3 +155,33 @@ export default function BasicForm(props: { basicInfo: SurveyBasicRequest; setBas
|
|||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const storeInput = (basicInfo: SurveyBasicRequest, setBasicInfo: (basicInfo: SurveyBasicRequest) => void, mode: Mode) => {
|
||||||
|
return (
|
||||||
|
<div className="data-input-form-bx">
|
||||||
|
<div className="data-input-form-tit">販売店</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-frame"
|
||||||
|
readOnly
|
||||||
|
value={basicInfo?.store ?? ''}
|
||||||
|
onChange={(e) => setBasicInfo({ ...basicInfo, store: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const builderInput = (basicInfo: SurveyBasicRequest, setBasicInfo: (basicInfo: SurveyBasicRequest) => void, mode: Mode) => {
|
||||||
|
return (
|
||||||
|
<div className="data-input-form-bx">
|
||||||
|
<div className="data-input-form-tit">施工店</div>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="input-frame"
|
||||||
|
readOnly
|
||||||
|
value={basicInfo?.constructionPoint ?? ''}
|
||||||
|
onChange={(e) => setBasicInfo({ ...basicInfo, constructionPoint: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { useEffect, useState } from 'react'
|
|||||||
import { useParams, useRouter, useSearchParams } from 'next/navigation'
|
import { useParams, useRouter, useSearchParams } from 'next/navigation'
|
||||||
import { requiredFields, useSurvey } from '@/hooks/useSurvey'
|
import { requiredFields, useSurvey } from '@/hooks/useSurvey'
|
||||||
import { usePopupController } from '@/store/popupController'
|
import { usePopupController } from '@/store/popupController'
|
||||||
|
import { useSpinnerStore } from '@/store/spinnerStore'
|
||||||
|
|
||||||
export default function ButtonForm(props: {
|
export default function ButtonForm(props: {
|
||||||
mode: Mode
|
mode: Mode
|
||||||
@ -29,6 +30,8 @@ export default function ButtonForm(props: {
|
|||||||
...props.data.basic,
|
...props.data.basic,
|
||||||
detailInfo: props.data.roof,
|
detailInfo: props.data.roof,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { setIsShow } = useSpinnerStore()
|
||||||
// --------------------------------------------------------------
|
// --------------------------------------------------------------
|
||||||
// 권한
|
// 권한
|
||||||
|
|
||||||
@ -75,7 +78,16 @@ export default function ButtonForm(props: {
|
|||||||
const { deleteSurvey, updateSurvey, isDeletingSurvey, isUpdatingSurvey } = useSurvey(Number(id))
|
const { deleteSurvey, updateSurvey, isDeletingSurvey, isUpdatingSurvey } = useSurvey(Number(id))
|
||||||
const { validateSurveyDetail, createSurvey, isCreatingSurvey } = useSurvey()
|
const { validateSurveyDetail, createSurvey, isCreatingSurvey } = useSurvey()
|
||||||
|
|
||||||
const handleSave = (isTemporary: boolean, isSubmitProcess = false) => {
|
useEffect(() => {
|
||||||
|
if (isCreatingSurvey || isUpdatingSurvey || isDeletingSurvey) {
|
||||||
|
setIsShow(true)
|
||||||
|
}
|
||||||
|
if (!isCreatingSurvey && !isUpdatingSurvey && !isDeletingSurvey) {
|
||||||
|
setIsShow(false)
|
||||||
|
}
|
||||||
|
}, [isCreatingSurvey, isUpdatingSurvey, isDeletingSurvey])
|
||||||
|
|
||||||
|
const handleSave = (isTemporary: boolean, isSubmitProcess: boolean) => {
|
||||||
const emptyField = validateSurveyDetail(props.data.roof)
|
const emptyField = validateSurveyDetail(props.data.roof)
|
||||||
const hasEmptyField = emptyField?.trim() !== ''
|
const hasEmptyField = emptyField?.trim() !== ''
|
||||||
|
|
||||||
@ -89,14 +101,18 @@ export default function ButtonForm(props: {
|
|||||||
const tempSaveProcess = async () => {
|
const tempSaveProcess = async () => {
|
||||||
if (idParam) {
|
if (idParam) {
|
||||||
await updateSurvey({ survey: saveData, isTemporary: true })
|
await updateSurvey({ survey: saveData, isTemporary: true })
|
||||||
router.push(`/survey-sale/${idParam}`)
|
if (!isUpdatingSurvey) {
|
||||||
|
router.push(`/survey-sale/${idParam}`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const updatedData = {
|
const updatedData = {
|
||||||
...saveData,
|
...saveData,
|
||||||
srlNo: '一時保存',
|
srlNo: '一時保存',
|
||||||
}
|
}
|
||||||
const id = await createSurvey(updatedData)
|
const id = await createSurvey(updatedData)
|
||||||
router.push(`/survey-sale/${id}`)
|
if (!isCreatingSurvey) {
|
||||||
|
router.push(`/survey-sale/${id}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
alert('一時保存されました。')
|
alert('一時保存されました。')
|
||||||
}
|
}
|
||||||
@ -112,10 +128,14 @@ export default function ButtonForm(props: {
|
|||||||
if (emptyField?.trim() === '') {
|
if (emptyField?.trim() === '') {
|
||||||
if (idParam) {
|
if (idParam) {
|
||||||
await updateSurvey({ survey: saveData, isTemporary: false, storeId: session.storeId ?? '' })
|
await updateSurvey({ survey: saveData, isTemporary: false, storeId: session.storeId ?? '' })
|
||||||
router.push(`/survey-sale/${idParam}`)
|
if (!isUpdatingSurvey) {
|
||||||
|
router.push(`/survey-sale/${idParam}`)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const id = await createSurvey(saveData)
|
const id = await createSurvey(saveData)
|
||||||
router.push(`/survey-sale/${id}`)
|
if (!isCreatingSurvey) {
|
||||||
|
router.push(`/survey-sale/${id}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isSubmitProcess) {
|
if (isSubmitProcess) {
|
||||||
if (!isCreatingSurvey && !isUpdatingSurvey) {
|
if (!isCreatingSurvey && !isUpdatingSurvey) {
|
||||||
@ -196,9 +216,9 @@ export default function ButtonForm(props: {
|
|||||||
<div className="sale-form-btn-wrap">
|
<div className="sale-form-btn-wrap">
|
||||||
<div className="btn-flex-wrap">
|
<div className="btn-flex-wrap">
|
||||||
<ListButton />
|
<ListButton />
|
||||||
<TempButton setMode={setMode} handleSave={handleSave} />
|
<TempButton setMode={setMode} handleSave={() => handleSave(true, false)} />
|
||||||
<SaveButton handleSave={handleSave} />
|
<SaveButton handleSave={() => handleSave(false, false)} />
|
||||||
{session?.role !== 'T01' && <SubmitButton handleSubmit={handleSubmit} />}
|
{session?.role === 'T01' || props.data.basic.submissionStatus ? <></> : <SubmitButton handleSubmit={handleSubmit} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export default function DataTable() {
|
|||||||
const { surveyDetail, isLoadingSurveyDetail } = useSurvey(Number(id))
|
const { surveyDetail, isLoadingSurveyDetail } = useSurvey(Number(id))
|
||||||
|
|
||||||
if (isLoadingSurveyDetail) {
|
if (isLoadingSurveyDetail) {
|
||||||
return <div>Loading...</div>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user