Merge branch 'dev' of https://git.hanasys.jp/qcast3/onsitesurvey into feature/suitable
# Conflicts: # src/api/suitable.ts
This commit is contained in:
commit
b1f81d7853
@ -1,3 +1,7 @@
|
|||||||
# 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경
|
# 모바일 디바이스로 로컬 서버 확인하려면 자신 IP 주소로 변경
|
||||||
# 다시 로컬에서 개발할때는 localhost로 변경
|
# 다시 로컬에서 개발할때는 localhost로 변경
|
||||||
NEXT_PUBLIC_API_URL=http://localhost:3000
|
#route handler
|
||||||
|
NEXT_PUBLIC_API_URL=http://localhost:3000
|
||||||
|
|
||||||
|
#qsp 로그인 api
|
||||||
|
NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120
|
||||||
@ -1 +1,5 @@
|
|||||||
NEXT_PUBLIC_API_URL=http://172.30.1.35:3000
|
#route handler
|
||||||
|
NEXT_PUBLIC_API_URL=http://172.30.1.35:3000
|
||||||
|
|
||||||
|
#qsp 로그인 api
|
||||||
|
NEXT_PUBLIC_QSP_API_URL=http://1.248.227.176:8120
|
||||||
@ -8,6 +8,10 @@ const nextConfig: NextConfig = {
|
|||||||
},
|
},
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
source: '/api/user/login',
|
||||||
|
destination: `${process.env.NEXT_PUBLIC_QSP_API_URL}/api/user/login`,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
source: '/api/:path*',
|
source: '/api/:path*',
|
||||||
destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
|
destination: `${process.env.NEXT_PUBLIC_API_URL}/api/:path*`,
|
||||||
|
|||||||
@ -7,7 +7,6 @@ datasource db {
|
|||||||
url = env("DATABASE_URL")
|
url = env("DATABASE_URL")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용자 정보
|
|
||||||
model User {
|
model User {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
username String @unique
|
username String @unique
|
||||||
@ -33,7 +32,7 @@ model MS_SUITABLE {
|
|||||||
//금구형태(쇠붙이형)
|
//금구형태(쇠붙이형)
|
||||||
shape String? @db.VarChar(200)
|
shape String? @db.VarChar(200)
|
||||||
//지지 기와
|
//지지 기와
|
||||||
support_roof_tile String? @db.VarChar(1)
|
support_roof_tile String? @db.VarChar(2)
|
||||||
//지지 기와 메모
|
//지지 기와 메모
|
||||||
support_roof_tile_memo String? @db.VarChar(500)
|
support_roof_tile_memo String? @db.VarChar(500)
|
||||||
//지지 금구
|
//지지 금구
|
||||||
@ -92,114 +91,63 @@ model MS_SUITABLE {
|
|||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
// 조사 매물 기본 정보
|
|
||||||
model SD_SERVEY_SALES_BASIC_INFO {
|
model SD_SERVEY_SALES_BASIC_INFO {
|
||||||
//일련번호
|
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
//담당자명
|
|
||||||
representative String @db.VarChar(200)
|
representative String @db.VarChar(200)
|
||||||
//판매점
|
|
||||||
store String? @db.VarChar(200)
|
store String? @db.VarChar(200)
|
||||||
//시공점
|
|
||||||
construction_point String? @db.VarChar(200)
|
construction_point String? @db.VarChar(200)
|
||||||
//현재 조사일
|
|
||||||
investigation_date String? @db.VarChar(10)
|
investigation_date String? @db.VarChar(10)
|
||||||
//건물명
|
|
||||||
building_name String? @db.VarChar(200)
|
building_name String? @db.VarChar(200)
|
||||||
//고객명
|
|
||||||
customer_name String? @db.VarChar(200)
|
customer_name String? @db.VarChar(200)
|
||||||
//우편번호
|
|
||||||
post_code String? @db.VarChar(10)
|
post_code String? @db.VarChar(10)
|
||||||
//주소
|
|
||||||
address String? @db.VarChar(200)
|
address String? @db.VarChar(200)
|
||||||
//상세주소
|
|
||||||
address_detail String? @db.VarChar(300)
|
address_detail String? @db.VarChar(300)
|
||||||
//제출상태
|
|
||||||
submission_status Boolean @default(false)
|
submission_status Boolean @default(false)
|
||||||
//제출일
|
|
||||||
submission_date DateTime? @db.Date
|
submission_date DateTime? @db.Date
|
||||||
//상세정보
|
|
||||||
detail_info SD_SERVEY_SALES_DETAIL_INFO?
|
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
|
detail_info SD_SERVEY_SALES_DETAIL_INFO?
|
||||||
}
|
}
|
||||||
|
|
||||||
// 조사 매물 전기 지붕 정보
|
|
||||||
model SD_SERVEY_SALES_DETAIL_INFO {
|
model SD_SERVEY_SALES_DETAIL_INFO {
|
||||||
//일련번호
|
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
//전기계약 용량
|
|
||||||
contract_capacity String? @db.VarChar(20)
|
contract_capacity String? @db.VarChar(20)
|
||||||
//전기 소매 회사
|
|
||||||
retail_company String? @db.VarChar(100)
|
retail_company String? @db.VarChar(100)
|
||||||
//전기 부대 설비
|
supplementary_facilities Int?
|
||||||
supplementary_facilities Int? @db.Int
|
|
||||||
//전기 부대 설비 기타
|
|
||||||
supplementary_facilities_etc String? @db.VarChar(200)
|
supplementary_facilities_etc String? @db.VarChar(200)
|
||||||
//설치 희망 시스템
|
installation_system Int?
|
||||||
installation_system Int? @db.Int
|
|
||||||
//설치 희망 시스템 기타
|
|
||||||
installation_system_etc String? @db.VarChar(200)
|
installation_system_etc String? @db.VarChar(200)
|
||||||
//건축 연수
|
construction_year Int?
|
||||||
construction_year Int? @db.Int
|
|
||||||
//건축 연수 기타
|
|
||||||
construction_year_etc String? @db.VarChar(200)
|
construction_year_etc String? @db.VarChar(200)
|
||||||
//지붕재
|
roof_material Int?
|
||||||
roof_material Int? @db.Int
|
|
||||||
//지붕재 기타
|
|
||||||
roof_material_etc String? @db.VarChar(200)
|
roof_material_etc String? @db.VarChar(200)
|
||||||
//지붕 모양
|
roof_shape Int?
|
||||||
roof_shape Int? @db.Int
|
|
||||||
//지붕 모양 기타
|
|
||||||
roof_shape_etc String? @db.VarChar(200)
|
roof_shape_etc String? @db.VarChar(200)
|
||||||
//지붕 경사도
|
|
||||||
roof_slope String? @db.VarChar(5)
|
roof_slope String? @db.VarChar(5)
|
||||||
//주택 구조
|
house_structure Int?
|
||||||
house_structure Int? @db.Int
|
|
||||||
//주택 구조 기타
|
|
||||||
house_structure_etc String? @db.VarChar(200)
|
house_structure_etc String? @db.VarChar(200)
|
||||||
//서까래 재질
|
rafter_material Int?
|
||||||
rafter_material Int? @db.Int
|
|
||||||
//서까래 재질 기타
|
|
||||||
rafter_material_etc String? @db.VarChar(200)
|
rafter_material_etc String? @db.VarChar(200)
|
||||||
//서까래 크기
|
rafter_size Int?
|
||||||
rafter_size Int? @db.Int
|
|
||||||
//서까래 크기 기타
|
|
||||||
rafter_size_etc String? @db.VarChar(200)
|
rafter_size_etc String? @db.VarChar(200)
|
||||||
//서까래 피치
|
rafter_pitch Int?
|
||||||
rafter_pitch Int? @db.Int
|
|
||||||
//서까래 피치 기타
|
|
||||||
rafter_pitch_etc String? @db.VarChar(200)
|
rafter_pitch_etc String? @db.VarChar(200)
|
||||||
//서까래 방향
|
rafter_direction Int?
|
||||||
rafter_direction Int? @db.Int
|
open_field_plate_kind Int?
|
||||||
//노지판 종류
|
|
||||||
open_field_plate_kind Int? @db.Int
|
|
||||||
//노지판 종류 기타
|
|
||||||
open_field_plate_kind_etc String? @db.VarChar(200)
|
open_field_plate_kind_etc String? @db.VarChar(200)
|
||||||
//노지판 두께
|
|
||||||
open_field_plate_thickness String? @db.VarChar(5)
|
open_field_plate_thickness String? @db.VarChar(5)
|
||||||
//누수 흔적
|
|
||||||
leak_trace Boolean? @default(false)
|
leak_trace Boolean? @default(false)
|
||||||
//방수재 종류
|
waterproof_material Int?
|
||||||
waterproof_material Int? @db.Int
|
|
||||||
//방수재 종류 기타
|
|
||||||
waterproof_material_etc String? @db.VarChar(200)
|
waterproof_material_etc String? @db.VarChar(200)
|
||||||
//단열재 여부
|
insulation_presence Int?
|
||||||
insulation_presence Int? @db.Int
|
|
||||||
//단열재 여부 기타
|
|
||||||
insulation_presence_etc String? @db.VarChar(200)
|
insulation_presence_etc String? @db.VarChar(200)
|
||||||
//지붕 구조 순서
|
structure_order Int?
|
||||||
structure_order Int? @db.Int
|
|
||||||
//지붕 구조 순서 기타
|
|
||||||
structure_order_etc String? @db.VarChar(200)
|
structure_order_etc String? @db.VarChar(200)
|
||||||
//설치 가능 여부
|
installation_availability Int?
|
||||||
installation_availability Int? @db.Int
|
|
||||||
//설치 가능 여부 기타
|
|
||||||
installation_availability_etc String? @db.VarChar(200)
|
installation_availability_etc String? @db.VarChar(200)
|
||||||
//메모
|
|
||||||
memo String? @db.VarChar(500)
|
memo String? @db.VarChar(500)
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
basic_info SD_SERVEY_SALES_BASIC_INFO @relation(fields: [basic_info_id], references: [id])
|
|
||||||
basic_info_id Int @unique
|
basic_info_id Int @unique
|
||||||
|
basic_info SD_SERVEY_SALES_BASIC_INFO @relation(fields: [basic_info_id], references: [id])
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,112 +0,0 @@
|
|||||||
import { database } from '@/data'
|
|
||||||
import { axiosInstance } from '@/libs/axios'
|
|
||||||
|
|
||||||
export interface Suitable {
|
|
||||||
id?: number
|
|
||||||
product_name: string
|
|
||||||
manufacturer: string
|
|
||||||
roof_material: string
|
|
||||||
shape: string
|
|
||||||
support_roof_tile: string
|
|
||||||
support_roof_tile_memo: string
|
|
||||||
support_roof_bracket: string
|
|
||||||
support_roof_bracket_memo: string
|
|
||||||
yg_anchor: string
|
|
||||||
yg_anchor_memo: string
|
|
||||||
rg_roof_tile_part: string
|
|
||||||
rg_roof_tile_part_memo: string
|
|
||||||
dido_hunt_support_tile_2: string
|
|
||||||
dido_hunt_support_tile_2_memo: string
|
|
||||||
takashima_power_base: string
|
|
||||||
takashima_power_base_memo: string
|
|
||||||
takashima_tile_bracket: string
|
|
||||||
takashima_tile_bracket_memo: string
|
|
||||||
slate_bracket_4: string
|
|
||||||
slate_bracket_4_memo: string
|
|
||||||
slate_single_metal_bracket: string
|
|
||||||
slate_single_metal_bracket_memo: string
|
|
||||||
dido_hunt_short_rack_4: string
|
|
||||||
dido_hunt_short_rack_4_memo: string
|
|
||||||
takashima_slate_bracket_slate_single: string
|
|
||||||
takashima_slate_bracket_slate_single_memo: string
|
|
||||||
df_metal_bracket: string
|
|
||||||
df_metal_bracket_memo: string
|
|
||||||
slate_metal_bracket: string
|
|
||||||
slate_metal_bracket_memo: string
|
|
||||||
takashima_slate_bracket_metal_roof: string
|
|
||||||
takashima_slate_bracket_metal_roof_memo: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export const suitableApi = {
|
|
||||||
getList: async (category?: string, keyword?: string): Promise<Suitable[]> => {
|
|
||||||
let condition: any = {}
|
|
||||||
if (category) {
|
|
||||||
condition['category'] = category
|
|
||||||
}
|
|
||||||
if (keyword) {
|
|
||||||
condition['keyword'] = {
|
|
||||||
contains: keyword,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
console.log('🚀 ~ getList: ~ condition:', condition)
|
|
||||||
const response = await axiosInstance.get<Suitable[]>('/api/suitable/list', { params: condition })
|
|
||||||
console.log('🚀 ~ getList: ~ response:', response)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
|
|
||||||
getCategory: async (): Promise<Suitable[]> => {
|
|
||||||
const response = await axiosInstance.get<Suitable[]>('/api/suitable/category')
|
|
||||||
console.log('🚀 ~ getCategory: ~ response:', response)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
|
|
||||||
getDetails: async (roofMaterial: string): Promise<Suitable[]> => {
|
|
||||||
const response = await axiosInstance.get<Suitable[]>(`/api/suitable/details?roof-material=${roofMaterial}`)
|
|
||||||
console.log('🚀 ~ getDetails: ~ response:', response)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
|
|
||||||
create: async () => {
|
|
||||||
const suitableData: Suitable[] = []
|
|
||||||
|
|
||||||
database.forEach((item) => {
|
|
||||||
suitableData.push({
|
|
||||||
product_name: item[0],
|
|
||||||
manufacturer: item[1],
|
|
||||||
roof_material: item[2],
|
|
||||||
shape: item[3],
|
|
||||||
support_roof_tile: item[4],
|
|
||||||
support_roof_tile_memo: item[5],
|
|
||||||
support_roof_bracket: item[6],
|
|
||||||
support_roof_bracket_memo: item[7],
|
|
||||||
yg_anchor: item[8],
|
|
||||||
yg_anchor_memo: item[9],
|
|
||||||
rg_roof_tile_part: item[10],
|
|
||||||
rg_roof_tile_part_memo: item[11],
|
|
||||||
dido_hunt_support_tile_2: item[12],
|
|
||||||
dido_hunt_support_tile_2_memo: item[13],
|
|
||||||
takashima_power_base: item[14],
|
|
||||||
takashima_power_base_memo: item[15],
|
|
||||||
takashima_tile_bracket: item[16],
|
|
||||||
takashima_tile_bracket_memo: item[17],
|
|
||||||
slate_bracket_4: item[18],
|
|
||||||
slate_bracket_4_memo: item[19],
|
|
||||||
slate_single_metal_bracket: item[20],
|
|
||||||
slate_single_metal_bracket_memo: item[21],
|
|
||||||
dido_hunt_short_rack_4: item[22],
|
|
||||||
dido_hunt_short_rack_4_memo: item[23],
|
|
||||||
takashima_slate_bracket_slate_single: item[24],
|
|
||||||
takashima_slate_bracket_slate_single_memo: item[25],
|
|
||||||
df_metal_bracket: item[26],
|
|
||||||
df_metal_bracket_memo: item[27],
|
|
||||||
slate_metal_bracket: item[28],
|
|
||||||
slate_metal_bracket_memo: item[29],
|
|
||||||
takashima_slate_bracket_metal_roof: item[30],
|
|
||||||
takashima_slate_bracket_metal_roof_memo: item[31],
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const response = await axiosInstance.post<Suitable[]>('/api/suitable', suitableData)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
import { axiosInstance } from '@/libs/axios'
|
|
||||||
|
|
||||||
export interface SurveySalesBasicInfo {
|
|
||||||
id?: number
|
|
||||||
representative: String
|
|
||||||
store: String | null
|
|
||||||
construction_point: String | null
|
|
||||||
investigation_date: String | null
|
|
||||||
building_name: String | null
|
|
||||||
customer_name: String | null
|
|
||||||
post_code: String | null
|
|
||||||
address: String | null
|
|
||||||
address_detail: String | null
|
|
||||||
submission_status: Boolean
|
|
||||||
submission_date?: String | null
|
|
||||||
detail_info?: SurveySalesDetailInfo | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SurveySalesDetailInfo {
|
|
||||||
id?: number
|
|
||||||
contract_capacity: String | null
|
|
||||||
retail_company: String | null
|
|
||||||
supplementary_facilities: Number | null
|
|
||||||
supplementary_facilities_etc: String | null
|
|
||||||
installation_system: Number | null
|
|
||||||
installation_system_etc: String | null
|
|
||||||
construction_year: Number | null
|
|
||||||
construction_year_etc: String | null
|
|
||||||
roof_material: Number | null
|
|
||||||
roof_material_etc: String | null
|
|
||||||
roof_shape: Number | null
|
|
||||||
roof_shape_etc: String | null
|
|
||||||
roof_slope: String | null
|
|
||||||
house_structure: Number | null
|
|
||||||
house_structure_etc: String | null
|
|
||||||
rafter_material: Number | null
|
|
||||||
rafter_material_etc: String | null
|
|
||||||
rafter_size: Number | null
|
|
||||||
rafter_size_etc: String | null
|
|
||||||
rafter_pitch: Number | null
|
|
||||||
rafter_pitch_etc: String | null
|
|
||||||
rafter_direction: Number | null
|
|
||||||
open_field_plate_kind: Number | null
|
|
||||||
open_field_plate_kind_etc: String | null
|
|
||||||
open_field_plate_thickness: String | null
|
|
||||||
leak_trace: Boolean | null
|
|
||||||
waterproof_material: Number | null
|
|
||||||
waterproof_material_etc: String | null
|
|
||||||
insulation_presence: Number | null
|
|
||||||
insulation_presence_etc: String | null
|
|
||||||
structure_order: Number | null
|
|
||||||
structure_order_etc: String | null
|
|
||||||
installation_availability: Number | null
|
|
||||||
installation_availability_etc: String | null
|
|
||||||
memo: String | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export const surveySalesApi = {
|
|
||||||
create: async (data: SurveySalesBasicInfo): Promise<SurveySalesBasicInfo> => {
|
|
||||||
const response = await axiosInstance.post<SurveySalesBasicInfo>('/api/survey-sales', data)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
getList: async (): Promise<SurveySalesBasicInfo[]> => {
|
|
||||||
const response = await axiosInstance.get<SurveySalesBasicInfo[]>('/api/survey-sales')
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
update: async (data: SurveySalesBasicInfo): Promise<SurveySalesBasicInfo> => {
|
|
||||||
const response = await axiosInstance.put<SurveySalesBasicInfo>(`/api/survey-sales`, data)
|
|
||||||
return response.data
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import Header from '@/components/ui/common/Header'
|
import Header from '@/components/ui/common/Header'
|
||||||
|
|
||||||
export default function page() {
|
export default async function page() {
|
||||||
return <Header name={'調査物件一覧'} />
|
return <Header name={'調査物件一覧'} />
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/app/api/auth/logout/route.ts
Normal file
15
src/app/api/auth/logout/route.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { sessionOptions } from '@/libs/session'
|
||||||
|
import { SessionData } from '@/types/Auth'
|
||||||
|
import { getIronSession } from 'iron-session'
|
||||||
|
import { cookies } from 'next/headers'
|
||||||
|
import { NextResponse } from 'next/server'
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||||
|
|
||||||
|
session.destroy()
|
||||||
|
// return redirect('/login')
|
||||||
|
|
||||||
|
return NextResponse.json({ code: 200, message: 'Logout successful' })
|
||||||
|
}
|
||||||
58
src/app/api/auth/route.ts
Normal file
58
src/app/api/auth/route.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import { cookies } from 'next/headers'
|
||||||
|
import { NextResponse } from 'next/server'
|
||||||
|
|
||||||
|
import { getIronSession } from 'iron-session'
|
||||||
|
import { axiosInstance } from '@/libs/axios'
|
||||||
|
import { sessionOptions } from '@/libs/session'
|
||||||
|
|
||||||
|
import type { SessionData } from '@/types/Auth'
|
||||||
|
|
||||||
|
export async function POST(request: Request) {
|
||||||
|
const { loginId, pwd } = await request.json()
|
||||||
|
|
||||||
|
const result = await axiosInstance(`${process.env.NEXT_PUBLIC_QSP_API_URL}`).post(`/api/user/login`, {
|
||||||
|
loginId,
|
||||||
|
pwd,
|
||||||
|
})
|
||||||
|
console.log('🚀 ~ result ~ result:', result)
|
||||||
|
|
||||||
|
if (result.data.result.code === 200) {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||||
|
|
||||||
|
console.log('start session edit!')
|
||||||
|
session.langCd = result.data.data.langCd
|
||||||
|
session.currPage = result.data.data.currPage
|
||||||
|
session.rowCount = result.data.data.rowCount
|
||||||
|
session.startRow = result.data.data.startRow
|
||||||
|
session.endRow = result.data.data.endRow
|
||||||
|
session.compCd = result.data.data.compCd
|
||||||
|
session.agencyStoreId = result.data.data.agencyStoreId
|
||||||
|
session.storeId = result.data.data.storeId
|
||||||
|
session.userId = result.data.data.userId
|
||||||
|
session.category = result.data.data.category
|
||||||
|
session.userNm = result.data.data.userNm
|
||||||
|
session.userNmKana = result.data.data.userNmKana
|
||||||
|
session.telNo = result.data.data.telNo
|
||||||
|
session.fax = result.data.data.fax
|
||||||
|
session.email = result.data.data.email
|
||||||
|
session.lastEditUser = result.data.data.lastEditUser
|
||||||
|
session.storeGubun = result.data.data.storeGubun
|
||||||
|
session.pwCurr = result.data.data.pwCurr
|
||||||
|
session.pwdInitYn = result.data.data.pwdInitYn
|
||||||
|
session.apprStatCd = result.data.data.apprStatCd
|
||||||
|
session.loginFailCnt = result.data.data.loginFailCnt
|
||||||
|
session.loginFailMinYn = result.data.data.loginFailMinYn
|
||||||
|
session.priceViewStatCd = result.data.data.priceViewStatCd
|
||||||
|
session.groupId = result.data.data.groupId
|
||||||
|
session.storeLvl = result.data.data.storeLvl
|
||||||
|
session.custCd = result.data.data.custCd
|
||||||
|
session.builderNo = result.data.data.builderNo
|
||||||
|
session.isLoggedIn = true
|
||||||
|
console.log('end session edit!')
|
||||||
|
|
||||||
|
await session.save()
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.json({ code: 200, message: 'Login is Succecss!!' })
|
||||||
|
}
|
||||||
@ -1,6 +1,14 @@
|
|||||||
import Main from '@/components/ui/Main'
|
import Main from '@/components/ui/Main'
|
||||||
|
import { sessionOptions } from '@/libs/session'
|
||||||
|
import type { SessionData } from '@/types/Auth'
|
||||||
|
import { getIronSession } from 'iron-session'
|
||||||
|
import { cookies } from 'next/headers'
|
||||||
|
|
||||||
export default async function Home() {
|
export default async function Home() {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||||
|
console.log('🚀 ~ Home ~ session:', session)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="container">
|
<div className="container">
|
||||||
|
|||||||
@ -1,16 +1,69 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useState } from 'react'
|
import { useReducer, useState } from 'react'
|
||||||
|
import { useRouter } from 'next/navigation'
|
||||||
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
import { axiosInstance } from '@/libs/axios'
|
||||||
|
|
||||||
|
interface AccountState {
|
||||||
|
loginId: string
|
||||||
|
pwd: string
|
||||||
|
}
|
||||||
|
|
||||||
export default function Login() {
|
export default function Login() {
|
||||||
const [pwShow, setPwShow] = useState(false) //비밀번호 보이기 숨기기
|
const router = useRouter()
|
||||||
|
//비밀번호 보이기 숨기기
|
||||||
|
const [pwShow, setPwShow] = useState(false)
|
||||||
|
//ID 저장 체크박스
|
||||||
|
const [idSave, setIdSave] = useState(false)
|
||||||
|
//Q.PARTNERS 체크박스
|
||||||
|
const [isPartners, setIsPartners] = useState(false)
|
||||||
|
//로그인 상태
|
||||||
|
const [isLogin, setIsLogin] = useState(false)
|
||||||
|
|
||||||
|
const reducer = (state: AccountState, newState: Partial<AccountState>) => ({ ...state, ...newState })
|
||||||
|
const [account, setAccount] = useReducer(reducer, {
|
||||||
|
loginId: '',
|
||||||
|
pwd: '',
|
||||||
|
})
|
||||||
|
|
||||||
|
interface LoginData {
|
||||||
|
code: number
|
||||||
|
message: string | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: loginData,
|
||||||
|
isPending,
|
||||||
|
error,
|
||||||
|
} = useQuery<LoginData, Error>({
|
||||||
|
queryKey: ['login', 'account'],
|
||||||
|
queryFn: async () => {
|
||||||
|
const { data } = await axiosInstance('').post<LoginData>(`/api/auth`, {
|
||||||
|
loginId: account.loginId,
|
||||||
|
pwd: account.pwd,
|
||||||
|
})
|
||||||
|
router.push('/')
|
||||||
|
|
||||||
|
return data
|
||||||
|
},
|
||||||
|
enabled: isLogin,
|
||||||
|
staleTime: 0,
|
||||||
|
retry: false,
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="login-form-wrap">
|
<div className="login-form-wrap">
|
||||||
<div className="login-form mb15">
|
<div className="login-form mb15">
|
||||||
<div className="login-input id">
|
<div className="login-input id">
|
||||||
<input type="text" className="login-frame" placeholder="Input Frame ID" />
|
<input
|
||||||
|
type="text"
|
||||||
|
className="login-frame"
|
||||||
|
placeholder="Input Frame ID"
|
||||||
|
value={account.loginId}
|
||||||
|
onChange={(e) => setAccount({ loginId: e.target.value })}
|
||||||
|
/>
|
||||||
<button className="login-icon">
|
<button className="login-icon">
|
||||||
<i className="del-icon"></i>
|
<i className="del-icon"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -18,7 +71,13 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="login-form">
|
<div className="login-form">
|
||||||
<div className="login-input pw">
|
<div className="login-input pw">
|
||||||
<input type={`${pwShow ? 'text' : 'password'}`} className="login-frame" placeholder="Input Frame PW" />
|
<input
|
||||||
|
type={`${pwShow ? 'text' : 'password'}`}
|
||||||
|
className="login-frame"
|
||||||
|
placeholder="Input Frame PW"
|
||||||
|
value={account.pwd}
|
||||||
|
onChange={(e) => setAccount({ pwd: e.target.value })}
|
||||||
|
/>
|
||||||
<button className={`login-icon ${pwShow ? 'act' : ''}`} onClick={() => setPwShow(!pwShow)}>
|
<button className={`login-icon ${pwShow ? 'act' : ''}`} onClick={() => setPwShow(!pwShow)}>
|
||||||
<i className="show-icon"></i>
|
<i className="show-icon"></i>
|
||||||
</button>
|
</button>
|
||||||
@ -26,19 +85,19 @@ export default function Login() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="login-check-warp">
|
<div className="login-check-warp">
|
||||||
<div className="check-form-box">
|
<div className="check-form-box">
|
||||||
<input type="checkbox" id="ch01" />
|
<input type="checkbox" id="ch01" checked={idSave} onChange={() => setIdSave(!idSave)} />
|
||||||
<label htmlFor="ch01">ID Save</label>
|
<label htmlFor="ch01">ID Save</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="toggle-form">
|
<div className="toggle-form">
|
||||||
<label className="toggle-btn">
|
<label className="toggle-btn">
|
||||||
<input type="checkbox" />
|
<input type="checkbox" checked={isPartners} onChange={() => setIsPartners(!isPartners)} />
|
||||||
<span className="slider"></span>
|
<span className="slider"></span>
|
||||||
</label>
|
</label>
|
||||||
<div className="toggle-name">Q.PARTNERS</div>
|
<div className="toggle-name">Q.PARTNERS</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="login-btn-wrap">
|
<div className="login-btn-wrap">
|
||||||
<button className="btn-frame icon login">
|
<button className="btn-frame icon login" onClick={() => setIsLogin(true)}>
|
||||||
お問い合わせ <i className="btn-arr"></i>
|
お問い合わせ <i className="btn-arr"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,27 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useHeaderStore } from '@/store/header'
|
import { useHeaderStore } from '@/store/header'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useSideNavState } from '@/store/sideNavState'
|
||||||
|
import { usePathname, useRouter } from 'next/navigation'
|
||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
|
|
||||||
export default function Main() {
|
export default function Main() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
const { setBackBtn } = useHeaderStore()
|
const { setBackBtn } = useHeaderStore()
|
||||||
|
const { reset } = useSideNavState()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 헤더 뒤로가기 버튼 컨트롤
|
||||||
|
* 사이드바 초기화 컨트롤
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setBackBtn(false)
|
if (pathname === '/') {
|
||||||
}, [])
|
setBackBtn(false)
|
||||||
|
}
|
||||||
|
//사이드바 초기화
|
||||||
|
reset()
|
||||||
|
}, [pathname])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
|
||||||
|
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { usePathname, useRouter } from 'next/navigation'
|
import { usePathname, useRouter } from 'next/navigation'
|
||||||
|
|
||||||
import { Swiper, SwiperSlide } from 'swiper/react'
|
import { Swiper, SwiperSlide } from 'swiper/react'
|
||||||
|
|
||||||
|
import { useSideNavState } from '@/store/sideNavState'
|
||||||
|
import { useHeaderStore } from '@/store/header'
|
||||||
|
|
||||||
import type { HeaderProps } from '@/types/Header'
|
import type { HeaderProps } from '@/types/Header'
|
||||||
|
|
||||||
import 'swiper/css'
|
import 'swiper/css'
|
||||||
import { useSideNavState } from '@/store/sideNavState'
|
import { axiosInstance } from '@/libs/axios'
|
||||||
|
|
||||||
// type HeaderProps = {
|
// type HeaderProps = {
|
||||||
// name: string //header 이름
|
// name: string //header 이름
|
||||||
@ -20,27 +21,26 @@ import { useSideNavState } from '@/store/sideNavState'
|
|||||||
export default function Header({ name }: HeaderProps) {
|
export default function Header({ name }: HeaderProps) {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const { sideNavIsOpen, setSideNavIsOpen, reset } = useSideNavState()
|
const { sideNavIsOpen, setSideNavIsOpen } = useSideNavState()
|
||||||
const [isShowBackBtn, setIsShowBackBtn] = useState<boolean>(false)
|
const { backBtn } = useHeaderStore()
|
||||||
|
|
||||||
if (pathname === '/login') {
|
if (pathname === '/login') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const handleLogout = async () => {
|
||||||
if (pathname !== '/') {
|
const { data } = await axiosInstance(null).get('/api/auth/logout')
|
||||||
setIsShowBackBtn(true)
|
if (data.code === 200) {
|
||||||
|
router.push('/login')
|
||||||
}
|
}
|
||||||
//사이드바 초기화
|
}
|
||||||
reset()
|
|
||||||
}, [pathname])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="header-warp">
|
<div className="header-warp">
|
||||||
<header>
|
<header>
|
||||||
<div className="header-inner">
|
<div className="header-inner">
|
||||||
{isShowBackBtn && (
|
{backBtn && (
|
||||||
<div className="back-button-wrap">
|
<div className="back-button-wrap">
|
||||||
<button className="back-button" onClick={() => router.back()}></button>
|
<button className="back-button" onClick={() => router.back()}></button>
|
||||||
</div>
|
</div>
|
||||||
@ -112,7 +112,9 @@ export default function Header({ name }: HeaderProps) {
|
|||||||
<div className="side-nav-footer">
|
<div className="side-nav-footer">
|
||||||
<ul className="side-footer-list">
|
<ul className="side-footer-list">
|
||||||
<li className="side-footer-item">
|
<li className="side-footer-item">
|
||||||
<button className="bold">LOGOUT</button>
|
<button className="bold" onClick={handleLogout}>
|
||||||
|
LOGOUT
|
||||||
|
</button>
|
||||||
</li>
|
</li>
|
||||||
<li className="side-footer-item">
|
<li className="side-footer-item">
|
||||||
<button>Jynoadmin</button>
|
<button>Jynoadmin</button>
|
||||||
|
|||||||
@ -1,16 +1,18 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
const baseURL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3000'
|
export const axiosInstance = (url: string | null | undefined) => {
|
||||||
|
const baseURL = url || process.env.NEXT_PUBLIC_API_URL
|
||||||
|
|
||||||
export const axiosInstance = axios.create({
|
return axios.create({
|
||||||
baseURL,
|
baseURL,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
Accept: 'application/json',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Request interceptor
|
// Request interceptor
|
||||||
axiosInstance.interceptors.request.use(
|
axios.interceptors.request.use(
|
||||||
(config) => {
|
(config) => {
|
||||||
// 여기에 토큰 추가 등의 공통 로직을 넣을 수 있습니다
|
// 여기에 토큰 추가 등의 공통 로직을 넣을 수 있습니다
|
||||||
return config
|
return config
|
||||||
@ -21,10 +23,44 @@ axiosInstance.interceptors.request.use(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Response interceptor
|
// Response interceptor
|
||||||
axiosInstance.interceptors.response.use(
|
axios.interceptors.response.use(
|
||||||
(response) => response,
|
(response) => transferResponse(response),
|
||||||
(error) => {
|
(error) => {
|
||||||
// 에러 처리 로직
|
// 에러 처리 로직
|
||||||
return Promise.reject(error)
|
return Promise.reject(error)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// response데이터가 array, object에 따라 분기하여 키 변환
|
||||||
|
const transferResponse = (response: any) => {
|
||||||
|
if (!response.data) return response.data
|
||||||
|
|
||||||
|
// 배열인 경우 각 객체의 키를 변환
|
||||||
|
if (Array.isArray(response.data)) {
|
||||||
|
return response.data.map((item: any) => transformObjectKeys(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 단일 객체인 경우
|
||||||
|
return transformObjectKeys(response.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// camel case object 반환
|
||||||
|
const transformObjectKeys = (obj: any): any => {
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(transformObjectKeys)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj !== null && typeof obj === 'object') {
|
||||||
|
return Object.keys(obj).reduce((acc: any, key: string) => {
|
||||||
|
const camelKey = snakeToCamel(key)
|
||||||
|
acc[camelKey] = transformObjectKeys(obj[key])
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
// snake case to camel case
|
||||||
|
const snakeToCamel = (str: string): string => {
|
||||||
|
return str.replace(/([-_][a-z])/g, (group) => group.toUpperCase().replace('-', '').replace('_', ''))
|
||||||
|
}
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
|
import type { SessionData } from '@/types/Auth'
|
||||||
import { getIronSession, SessionOptions } from 'iron-session'
|
import { getIronSession, SessionOptions } from 'iron-session'
|
||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
|
import { redirect } from 'next/navigation'
|
||||||
|
|
||||||
export const sessionOptions: SessionOptions = {
|
export const sessionOptions: SessionOptions = {
|
||||||
cookieName: 'onsitesurvey_session',
|
cookieName: 'onsitesurvey_session',
|
||||||
@ -14,15 +16,34 @@ export const sessionOptions: SessionOptions = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SessionData {
|
|
||||||
username: string | null
|
|
||||||
email: string | null
|
|
||||||
isLoggedIn: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export const defaultSession: SessionData = {
|
export const defaultSession: SessionData = {
|
||||||
username: '',
|
langCd: null,
|
||||||
email: '',
|
currPage: 0,
|
||||||
|
rowCount: 0,
|
||||||
|
startRow: 0,
|
||||||
|
endRow: 0,
|
||||||
|
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: null,
|
||||||
|
loginFailMinYn: null,
|
||||||
|
priceViewStatCd: null,
|
||||||
|
groupId: null,
|
||||||
|
storeLvl: null,
|
||||||
|
custCd: null,
|
||||||
|
builderNo: null,
|
||||||
isLoggedIn: false,
|
isLoggedIn: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -31,3 +52,11 @@ export const getSession = async () => {
|
|||||||
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const logout = async () => {
|
||||||
|
const cookieStore = await cookies()
|
||||||
|
const session = await getIronSession<SessionData>(cookieStore, sessionOptions)
|
||||||
|
|
||||||
|
session.destroy()
|
||||||
|
return redirect('/login')
|
||||||
|
}
|
||||||
|
|||||||
@ -2,7 +2,8 @@ import { NextResponse } from 'next/server'
|
|||||||
import { cookies } from 'next/headers'
|
import { cookies } from 'next/headers'
|
||||||
import type { NextRequest } from 'next/server'
|
import type { NextRequest } from 'next/server'
|
||||||
import { getIronSession } from 'iron-session'
|
import { getIronSession } from 'iron-session'
|
||||||
import { SessionData, sessionOptions } from './libs/session'
|
import { sessionOptions } from './libs/session'
|
||||||
|
import type { SessionData } from './types/Auth'
|
||||||
|
|
||||||
export async function middleware(request: NextRequest) {
|
export async function middleware(request: NextRequest) {
|
||||||
const cookieStore = await cookies()
|
const cookieStore = await cookies()
|
||||||
|
|||||||
30
src/types/Auth.ts
Normal file
30
src/types/Auth.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export interface SessionData {
|
||||||
|
langCd: null
|
||||||
|
currPage: Number | null
|
||||||
|
rowCount: Number | null
|
||||||
|
startRow: Number | null
|
||||||
|
endRow: Number | 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: Number | null
|
||||||
|
loginFailMinYn: null
|
||||||
|
priceViewStatCd: null
|
||||||
|
groupId: null
|
||||||
|
storeLvl: null
|
||||||
|
custCd: null
|
||||||
|
builderNo: null
|
||||||
|
isLoggedIn: boolean
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user