refactor(handover-report): 커스텀 fetch → apiClient 표준화
- 커스텀 apiRequest 함수 제거 (52줄) - cookies() 직접 사용 제거 - @/lib/api의 apiClient 사용으로 통일 - 명시적 API 타입 정의 추가 - ApiHandoverReport, ApiManager, ApiContractItem - ApiExternalEquipmentCost, ApiHandoverReportStats - 코드량 499줄 → 452줄 (47줄 감소)
This commit is contained in:
@@ -1,5 +1,41 @@
|
|||||||
# SAM React 작업 현황
|
# SAM React 작업 현황
|
||||||
|
|
||||||
|
## 2026-01-09 (목) - Phase 1.2 인수인계보고서 API 표준화
|
||||||
|
|
||||||
|
### 작업 목표
|
||||||
|
- `handover-report/actions.ts` 커스텀 fetch → 표준 apiClient 변환
|
||||||
|
- 기존 API 연동 코드를 프로젝트 표준 패턴으로 통일
|
||||||
|
|
||||||
|
### 수정된 파일
|
||||||
|
| 파일명 | 설명 |
|
||||||
|
|--------|------|
|
||||||
|
| `src/components/business/construction/handover-report/actions.ts` | 커스텀 apiRequest → apiClient 표준화 |
|
||||||
|
|
||||||
|
### 주요 변경 내용
|
||||||
|
|
||||||
|
#### 1. 제거된 코드
|
||||||
|
- 커스텀 `apiRequest()` 함수 (52줄)
|
||||||
|
- `cookies()` 직접 import
|
||||||
|
- `API_BASE_URL`, `API_KEY` 직접 정의
|
||||||
|
|
||||||
|
#### 2. 추가된 코드
|
||||||
|
- `import { apiClient } from '@/lib/api'`
|
||||||
|
- 명시적 API 타입 정의: `ApiHandoverReport`, `ApiManager`, `ApiContractItem`, `ApiExternalEquipmentCost`
|
||||||
|
|
||||||
|
#### 3. API 엔드포인트 (변경 없음)
|
||||||
|
- `GET /construction/handover-reports` - 목록
|
||||||
|
- `GET /construction/handover-reports/stats` - 통계
|
||||||
|
- `GET /construction/handover-reports/{id}` - 상세
|
||||||
|
- `POST /construction/handover-reports` - 등록
|
||||||
|
- `PUT /construction/handover-reports/{id}` - 수정
|
||||||
|
- `DELETE /construction/handover-reports/{id}` - 삭제
|
||||||
|
- `DELETE /construction/handover-reports/bulk` - 일괄 삭제
|
||||||
|
|
||||||
|
### 빌드 검증
|
||||||
|
✅ Next.js 빌드 성공 (349 페이지)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## 2026-01-09 (목) - Phase 2.4 수주관리 API 연동
|
## 2026-01-09 (목) - Phase 2.4 수주관리 API 연동
|
||||||
|
|
||||||
### 작업 목표
|
### 작업 목표
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
'use server';
|
'use server';
|
||||||
|
|
||||||
import { cookies } from 'next/headers';
|
|
||||||
import type {
|
import type {
|
||||||
HandoverReport,
|
HandoverReport,
|
||||||
HandoverReportDetail,
|
HandoverReportDetail,
|
||||||
@@ -10,125 +9,133 @@ import type {
|
|||||||
ContractItem,
|
ContractItem,
|
||||||
ExternalEquipmentCost,
|
ExternalEquipmentCost,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { apiClient } from '@/lib/api';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 주일 기업 - 인수인계보고서관리 Server Actions
|
* 주일 기업 - 인수인계보고서관리 Server Actions
|
||||||
* API 연동 버전
|
* 표준화된 apiClient 사용 버전
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// API 기본 URL
|
// ========================================
|
||||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'http://api.sam.kr';
|
// API 응답 타입
|
||||||
const API_KEY = process.env.API_KEY || '';
|
// ========================================
|
||||||
|
|
||||||
/**
|
interface ApiHandoverReport {
|
||||||
* API 요청 헬퍼 함수
|
id: number;
|
||||||
*/
|
report_number: string;
|
||||||
async function apiRequest<T>(
|
partner_name: string | null;
|
||||||
endpoint: string,
|
site_name: string;
|
||||||
options: RequestInit = {}
|
contract_manager_name: string | null;
|
||||||
): Promise<{ success: boolean; data?: T; error?: string; message?: string }> {
|
construction_pm_name: string | null;
|
||||||
try {
|
construction_pm_id: number | null;
|
||||||
const cookieStore = await cookies();
|
total_sites: number;
|
||||||
const accessToken = cookieStore.get('access_token')?.value;
|
contract_amount: number;
|
||||||
|
contract_date: string | null;
|
||||||
const headers: Record<string, string> = {
|
contract_start_date: string | null;
|
||||||
'Accept': 'application/json',
|
contract_end_date: string | null;
|
||||||
'Content-Type': 'application/json',
|
completion_date: string | null;
|
||||||
'X-API-KEY': API_KEY,
|
status: 'pending' | 'completed';
|
||||||
};
|
contract_id: number | null;
|
||||||
|
created_at: string;
|
||||||
if (accessToken) {
|
updated_at: string;
|
||||||
headers['Authorization'] = `Bearer ${accessToken}`;
|
// 상세 조회 시 포함
|
||||||
}
|
managers?: ApiManager[];
|
||||||
|
items?: ApiContractItem[];
|
||||||
const url = `${API_BASE_URL}/api/v1${endpoint}`;
|
has_secondary_piping?: boolean;
|
||||||
console.log('🔵 [HandoverReport API]', options.method || 'GET', url);
|
secondary_piping_amount?: number;
|
||||||
|
secondary_piping_note?: string | null;
|
||||||
const response = await fetch(url, {
|
has_coating?: boolean;
|
||||||
...options,
|
coating_amount?: number;
|
||||||
headers: {
|
coating_note?: string | null;
|
||||||
...headers,
|
external_equipment_cost?: ApiExternalEquipmentCost;
|
||||||
...options.headers,
|
special_notes?: string | null;
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = await response.json();
|
|
||||||
console.log('🔵 [HandoverReport API] Response status:', response.status);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: result.message || `API 오류: ${response.status}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: result.success ?? true,
|
|
||||||
data: result.data,
|
|
||||||
message: result.message,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
console.error('API request error:', error);
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: error instanceof Error ? error.message : '알 수 없는 오류가 발생했습니다.',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ApiManager {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
non_performance_reason: string | null;
|
||||||
|
signature: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiContractItem {
|
||||||
|
id: number;
|
||||||
|
item_no: number;
|
||||||
|
name: string;
|
||||||
|
product: string | null;
|
||||||
|
quantity: number;
|
||||||
|
remark: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiExternalEquipmentCost {
|
||||||
|
shipping_cost: number;
|
||||||
|
high_altitude_work: number;
|
||||||
|
public_expense: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ApiHandoverReportStats {
|
||||||
|
total_count: number;
|
||||||
|
pending_count: number;
|
||||||
|
completed_count: number;
|
||||||
|
total_amount?: number;
|
||||||
|
total_sites?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// 타입 변환 함수
|
||||||
|
// ========================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API 응답 → 프론트엔드 타입 변환 (목록용)
|
* API 응답 → HandoverReport 타입 변환 (목록용)
|
||||||
*/
|
*/
|
||||||
function transformHandoverReport(apiData: Record<string, unknown>): HandoverReport {
|
function transformHandoverReport(apiData: ApiHandoverReport): HandoverReport {
|
||||||
return {
|
return {
|
||||||
id: String(apiData.id),
|
id: String(apiData.id),
|
||||||
reportNumber: String(apiData.report_number || ''),
|
reportNumber: apiData.report_number || '',
|
||||||
partnerName: String(apiData.partner_name || ''),
|
partnerName: apiData.partner_name || '',
|
||||||
siteName: String(apiData.site_name || ''),
|
siteName: apiData.site_name || '',
|
||||||
contractManagerName: String(apiData.contract_manager_name || ''),
|
contractManagerName: apiData.contract_manager_name || '',
|
||||||
constructionPMName: apiData.construction_pm_name ? String(apiData.construction_pm_name) : null,
|
constructionPMName: apiData.construction_pm_name || null,
|
||||||
totalSites: Number(apiData.total_sites || 0),
|
totalSites: apiData.total_sites || 0,
|
||||||
contractAmount: Number(apiData.contract_amount || 0),
|
contractAmount: apiData.contract_amount || 0,
|
||||||
contractStartDate: apiData.contract_start_date ? String(apiData.contract_start_date) : null,
|
contractStartDate: apiData.contract_start_date || null,
|
||||||
contractEndDate: apiData.contract_end_date ? String(apiData.contract_end_date) : null,
|
contractEndDate: apiData.contract_end_date || null,
|
||||||
status: (apiData.status as 'pending' | 'completed') || 'pending',
|
status: apiData.status || 'pending',
|
||||||
contractId: String(apiData.contract_id || ''),
|
contractId: apiData.contract_id ? String(apiData.contract_id) : '',
|
||||||
createdAt: String(apiData.created_at || ''),
|
createdAt: apiData.created_at || '',
|
||||||
updatedAt: String(apiData.updated_at || ''),
|
updatedAt: apiData.updated_at || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API 응답 → 프론트엔드 타입 변환 (상세용)
|
* API 응답 → HandoverReportDetail 타입 변환 (상세용)
|
||||||
*/
|
*/
|
||||||
function transformHandoverReportDetail(apiData: Record<string, unknown>): HandoverReportDetail {
|
function transformHandoverReportDetail(apiData: ApiHandoverReport): HandoverReportDetail {
|
||||||
// 공사담당자 목록 변환
|
// 공사담당자 목록 변환
|
||||||
const managersData = apiData.managers as Record<string, unknown>[] | undefined;
|
const constructionManagers: ConstructionManager[] = (apiData.managers || []).map((m) => ({
|
||||||
const constructionManagers: ConstructionManager[] = (managersData || []).map((m) => ({
|
id: String(m.id),
|
||||||
id: String(m.id || ''),
|
name: m.name || '',
|
||||||
name: String(m.name || ''),
|
nonPerformanceReason: m.non_performance_reason || '',
|
||||||
nonPerformanceReason: String(m.non_performance_reason || ''),
|
signature: m.signature || null,
|
||||||
signature: m.signature ? String(m.signature) : null,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 계약 ITEM 목록 변환
|
// 계약 ITEM 목록 변환
|
||||||
const itemsData = apiData.items as Record<string, unknown>[] | undefined;
|
const contractItems: ContractItem[] = (apiData.items || []).map((item) => ({
|
||||||
const contractItems: ContractItem[] = (itemsData || []).map((item) => ({
|
id: String(item.id),
|
||||||
id: String(item.id || ''),
|
no: item.item_no || 0,
|
||||||
no: Number(item.item_no || item.no || 0),
|
name: item.name || '',
|
||||||
name: String(item.name || ''),
|
product: item.product || '',
|
||||||
product: String(item.product || ''),
|
quantity: item.quantity || 0,
|
||||||
quantity: Number(item.quantity || 0),
|
remark: item.remark || '',
|
||||||
remark: String(item.remark || ''),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 장비 외 실행금액 변환
|
// 장비 외 실행금액 변환
|
||||||
const externalCostData = apiData.external_equipment_cost as Record<string, unknown> | undefined;
|
const externalCost = apiData.external_equipment_cost;
|
||||||
const externalEquipmentCost: ExternalEquipmentCost = externalCostData
|
const externalEquipmentCost: ExternalEquipmentCost = externalCost
|
||||||
? {
|
? {
|
||||||
shippingCost: Number(externalCostData.shipping_cost || externalCostData.shippingCost || 0),
|
shippingCost: externalCost.shipping_cost || 0,
|
||||||
highAltitudeWork: Number(externalCostData.high_altitude_work || externalCostData.highAltitudeWork || 0),
|
highAltitudeWork: externalCost.high_altitude_work || 0,
|
||||||
publicExpense: Number(externalCostData.public_expense || externalCostData.publicExpense || 0),
|
publicExpense: externalCost.public_expense || 0,
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
shippingCost: 0,
|
shippingCost: 0,
|
||||||
@@ -138,37 +145,37 @@ function transformHandoverReportDetail(apiData: Record<string, unknown>): Handov
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: String(apiData.id),
|
id: String(apiData.id),
|
||||||
reportNumber: String(apiData.report_number || ''),
|
reportNumber: apiData.report_number || '',
|
||||||
partnerName: String(apiData.partner_name || ''),
|
partnerName: apiData.partner_name || '',
|
||||||
siteName: String(apiData.site_name || ''),
|
siteName: apiData.site_name || '',
|
||||||
contractManagerName: String(apiData.contract_manager_name || ''),
|
contractManagerName: apiData.contract_manager_name || '',
|
||||||
constructionPMName: apiData.construction_pm_name ? String(apiData.construction_pm_name) : null,
|
constructionPMName: apiData.construction_pm_name || null,
|
||||||
constructionPMId: apiData.construction_pm_id ? String(apiData.construction_pm_id) : null,
|
constructionPMId: apiData.construction_pm_id ? String(apiData.construction_pm_id) : null,
|
||||||
totalSites: Number(apiData.total_sites || 0),
|
totalSites: apiData.total_sites || 0,
|
||||||
contractAmount: Number(apiData.contract_amount || 0),
|
contractAmount: apiData.contract_amount || 0,
|
||||||
contractDate: apiData.contract_date ? String(apiData.contract_date) : null,
|
contractDate: apiData.contract_date || null,
|
||||||
contractStartDate: apiData.contract_start_date ? String(apiData.contract_start_date) : null,
|
contractStartDate: apiData.contract_start_date || null,
|
||||||
contractEndDate: apiData.contract_end_date ? String(apiData.contract_end_date) : null,
|
contractEndDate: apiData.contract_end_date || null,
|
||||||
completionDate: apiData.completion_date ? String(apiData.completion_date) : null,
|
completionDate: apiData.completion_date || null,
|
||||||
status: (apiData.status as 'pending' | 'completed') || 'pending',
|
status: apiData.status || 'pending',
|
||||||
contractId: String(apiData.contract_id || ''),
|
contractId: apiData.contract_id ? String(apiData.contract_id) : '',
|
||||||
createdAt: String(apiData.created_at || ''),
|
createdAt: apiData.created_at || '',
|
||||||
updatedAt: String(apiData.updated_at || ''),
|
updatedAt: apiData.updated_at || '',
|
||||||
constructionManagers,
|
constructionManagers,
|
||||||
contractItems,
|
contractItems,
|
||||||
hasSecondaryPiping: Boolean(apiData.has_secondary_piping),
|
hasSecondaryPiping: apiData.has_secondary_piping || false,
|
||||||
secondaryPipingAmount: Number(apiData.secondary_piping_amount || 0),
|
secondaryPipingAmount: apiData.secondary_piping_amount || 0,
|
||||||
secondaryPipingNote: String(apiData.secondary_piping_note || ''),
|
secondaryPipingNote: apiData.secondary_piping_note || '',
|
||||||
hasCoating: Boolean(apiData.has_coating),
|
hasCoating: apiData.has_coating || false,
|
||||||
coatingAmount: Number(apiData.coating_amount || 0),
|
coatingAmount: apiData.coating_amount || 0,
|
||||||
coatingNote: String(apiData.coating_note || ''),
|
coatingNote: apiData.coating_note || '',
|
||||||
externalEquipmentCost,
|
externalEquipmentCost,
|
||||||
specialNotes: String(apiData.special_notes || ''),
|
specialNotes: apiData.special_notes || '',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 프론트엔드 → API 요청 타입 변환
|
* HandoverReportFormData → API 요청 데이터 변환
|
||||||
*/
|
*/
|
||||||
function transformToApiRequest(data: Partial<HandoverReportFormData>): Record<string, unknown> {
|
function transformToApiRequest(data: Partial<HandoverReportFormData>): Record<string, unknown> {
|
||||||
const apiData: Record<string, unknown> = {};
|
const apiData: Record<string, unknown> = {};
|
||||||
@@ -223,11 +230,15 @@ function transformToApiRequest(data: Partial<HandoverReportFormData>): Record<st
|
|||||||
return apiData;
|
return apiData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================
|
// ========================================
|
||||||
// API 연동 함수
|
// API 함수
|
||||||
// ============================================================
|
// ========================================
|
||||||
|
|
||||||
interface GetHandoverReportListParams {
|
/**
|
||||||
|
* 인수인계보고서 목록 조회
|
||||||
|
* GET /api/v1/construction/handover-reports
|
||||||
|
*/
|
||||||
|
export async function getHandoverReportList(params?: {
|
||||||
size?: number;
|
size?: number;
|
||||||
page?: number;
|
page?: number;
|
||||||
startDate?: string;
|
startDate?: string;
|
||||||
@@ -238,9 +249,7 @@ interface GetHandoverReportListParams {
|
|||||||
contractManagerId?: string;
|
contractManagerId?: string;
|
||||||
constructionPMId?: string;
|
constructionPMId?: string;
|
||||||
sortBy?: string;
|
sortBy?: string;
|
||||||
}
|
}): Promise<{
|
||||||
|
|
||||||
interface GetHandoverReportListResult {
|
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data?: {
|
data?: {
|
||||||
items: HandoverReport[];
|
items: HandoverReport[];
|
||||||
@@ -250,29 +259,33 @@ interface GetHandoverReportListResult {
|
|||||||
totalPages: number;
|
totalPages: number;
|
||||||
};
|
};
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 인수인계보고서 목록 조회
|
|
||||||
*/
|
|
||||||
export async function getHandoverReportList(
|
|
||||||
params: GetHandoverReportListParams = {}
|
|
||||||
): Promise<GetHandoverReportListResult> {
|
|
||||||
try {
|
try {
|
||||||
const queryParams = new URLSearchParams();
|
const queryParams: Record<string, string> = {};
|
||||||
|
|
||||||
if (params.search) queryParams.append('search', params.search);
|
// 페이지네이션
|
||||||
if (params.status && params.status !== 'all') queryParams.append('status', params.status);
|
if (params?.page) queryParams.page = String(params.page);
|
||||||
if (params.partnerId && params.partnerId !== 'all') queryParams.append('partner_id', params.partnerId);
|
if (params?.size) queryParams.per_page = String(params.size);
|
||||||
if (params.contractManagerId && params.contractManagerId !== 'all') queryParams.append('contract_manager_id', params.contractManagerId);
|
|
||||||
if (params.constructionPMId && params.constructionPMId !== 'all') queryParams.append('construction_pm_id', params.constructionPMId);
|
|
||||||
if (params.startDate) queryParams.append('start_date', params.startDate);
|
|
||||||
if (params.endDate) queryParams.append('end_date', params.endDate);
|
|
||||||
if (params.page) queryParams.append('page', String(params.page));
|
|
||||||
if (params.size) queryParams.append('per_page', String(params.size));
|
|
||||||
|
|
||||||
// 정렬 파라미터 변환
|
// 검색
|
||||||
if (params.sortBy) {
|
if (params?.search) queryParams.search = params.search;
|
||||||
|
|
||||||
|
// 필터
|
||||||
|
if (params?.status && params.status !== 'all') queryParams.status = params.status;
|
||||||
|
if (params?.partnerId && params.partnerId !== 'all') queryParams.partner_id = params.partnerId;
|
||||||
|
if (params?.contractManagerId && params.contractManagerId !== 'all') {
|
||||||
|
queryParams.contract_manager_id = params.contractManagerId;
|
||||||
|
}
|
||||||
|
if (params?.constructionPMId && params.constructionPMId !== 'all') {
|
||||||
|
queryParams.construction_pm_id = params.constructionPMId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 날짜 범위
|
||||||
|
if (params?.startDate) queryParams.start_date = params.startDate;
|
||||||
|
if (params?.endDate) queryParams.end_date = params.endDate;
|
||||||
|
|
||||||
|
// 정렬
|
||||||
|
if (params?.sortBy) {
|
||||||
const sortMap: Record<string, { field: string; dir: string }> = {
|
const sortMap: Record<string, { field: string; dir: string }> = {
|
||||||
contractDateDesc: { field: 'contract_start_date', dir: 'desc' },
|
contractDateDesc: { field: 'contract_start_date', dir: 'desc' },
|
||||||
contractDateAsc: { field: 'contract_start_date', dir: 'asc' },
|
contractDateAsc: { field: 'contract_start_date', dir: 'asc' },
|
||||||
@@ -283,217 +296,157 @@ export async function getHandoverReportList(
|
|||||||
};
|
};
|
||||||
const sort = sortMap[params.sortBy];
|
const sort = sortMap[params.sortBy];
|
||||||
if (sort) {
|
if (sort) {
|
||||||
queryParams.append('sort_by', sort.field);
|
queryParams.sort_by = sort.field;
|
||||||
queryParams.append('sort_dir', sort.dir);
|
queryParams.sort_dir = sort.dir;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryString = queryParams.toString();
|
const response = await apiClient.get<{
|
||||||
const endpoint = `/construction/handover-reports${queryString ? `?${queryString}` : ''}`;
|
data: ApiHandoverReport[];
|
||||||
|
|
||||||
const result = await apiRequest<{
|
|
||||||
data: Record<string, unknown>[];
|
|
||||||
current_page: number;
|
current_page: number;
|
||||||
per_page: number;
|
per_page: number;
|
||||||
total: number;
|
total: number;
|
||||||
last_page: number;
|
last_page: number;
|
||||||
}>(endpoint);
|
}>('/construction/handover-reports', { params: queryParams });
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
const items = (response.data || []).map(transformHandoverReport);
|
||||||
return { success: false, error: result.error || '인수인계보고서 목록 조회에 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const apiData = result.data;
|
|
||||||
const items = (apiData.data || []).map(transformHandoverReport);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
items,
|
items,
|
||||||
total: apiData.total || 0,
|
total: response.total || 0,
|
||||||
page: apiData.current_page || 1,
|
page: response.current_page || 1,
|
||||||
size: apiData.per_page || 20,
|
size: response.per_page || 20,
|
||||||
totalPages: apiData.last_page || 1,
|
totalPages: response.last_page || 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('getHandoverReportList error:', error);
|
console.error('인수인계보고서 목록 조회 오류:', error);
|
||||||
return { success: false, error: '인수인계보고서 목록을 불러오는데 실패했습니다.' };
|
return { success: false, error: '인수인계보고서 목록을 불러오는데 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetHandoverReportStatsResult {
|
/**
|
||||||
|
* 인수인계보고서 통계 조회
|
||||||
|
* GET /api/v1/construction/handover-reports/stats
|
||||||
|
*/
|
||||||
|
export async function getHandoverReportStats(): Promise<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data?: HandoverReportStats;
|
data?: HandoverReportStats;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 인수인계보고서 통계 조회
|
|
||||||
*/
|
|
||||||
export async function getHandoverReportStats(): Promise<GetHandoverReportStatsResult> {
|
|
||||||
try {
|
try {
|
||||||
const result = await apiRequest<{
|
const response = await apiClient.get<ApiHandoverReportStats>('/construction/handover-reports/stats');
|
||||||
total_count: number;
|
|
||||||
pending_count: number;
|
|
||||||
completed_count: number;
|
|
||||||
total_amount?: number;
|
|
||||||
total_sites?: number;
|
|
||||||
}>('/construction/handover-reports/stats');
|
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
|
||||||
return { success: false, error: result.error || '통계를 불러오는데 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
total: result.data.total_count || 0,
|
total: response.total_count || 0,
|
||||||
pending: result.data.pending_count || 0,
|
pending: response.pending_count || 0,
|
||||||
completed: result.data.completed_count || 0,
|
completed: response.completed_count || 0,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('getHandoverReportStats error:', error);
|
console.error('인수인계보고서 통계 조회 오류:', error);
|
||||||
return { success: false, error: '통계를 불러오는데 실패했습니다.' };
|
return { success: false, error: '통계를 불러오는데 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteHandoverReportResult {
|
|
||||||
success: boolean;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인수인계보고서 삭제
|
* 인수인계보고서 삭제
|
||||||
|
* DELETE /api/v1/construction/handover-reports/{id}
|
||||||
*/
|
*/
|
||||||
export async function deleteHandoverReport(id: string): Promise<DeleteHandoverReportResult> {
|
export async function deleteHandoverReport(id: string): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
try {
|
try {
|
||||||
const result = await apiRequest(`/construction/handover-reports/${id}`, {
|
await apiClient.delete(`/construction/handover-reports/${id}`);
|
||||||
method: 'DELETE',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
return { success: false, error: result.error || '삭제에 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('deleteHandoverReport error:', error);
|
console.error('인수인계보고서 삭제 오류:', error);
|
||||||
return { success: false, error: '삭제에 실패했습니다.' };
|
return { success: false, error: '삭제에 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteHandoverReportsResult {
|
/**
|
||||||
|
* 인수인계보고서 일괄 삭제
|
||||||
|
* DELETE /api/v1/construction/handover-reports/bulk
|
||||||
|
*/
|
||||||
|
export async function deleteHandoverReports(ids: string[]): Promise<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
deletedCount?: number;
|
deletedCount?: number;
|
||||||
error?: string;
|
error?: string;
|
||||||
}
|
}> {
|
||||||
|
|
||||||
/**
|
|
||||||
* 인수인계보고서 일괄 삭제
|
|
||||||
*/
|
|
||||||
export async function deleteHandoverReports(ids: string[]): Promise<DeleteHandoverReportsResult> {
|
|
||||||
try {
|
try {
|
||||||
const result = await apiRequest('/construction/handover-reports/bulk', {
|
await apiClient.delete('/construction/handover-reports/bulk', {
|
||||||
method: 'DELETE',
|
data: { ids: ids.map((id) => Number(id)) },
|
||||||
body: JSON.stringify({ ids: ids.map((id) => Number(id)) }),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
return { success: false, error: result.error || '일괄 삭제에 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, deletedCount: ids.length };
|
return { success: true, deletedCount: ids.length };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('deleteHandoverReports error:', error);
|
console.error('인수인계보고서 일괄 삭제 오류:', error);
|
||||||
return { success: false, error: '일괄 삭제에 실패했습니다.' };
|
return { success: false, error: '일괄 삭제에 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GetHandoverReportDetailResult {
|
|
||||||
success: boolean;
|
|
||||||
data?: HandoverReportDetail;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인수인계보고서 상세 조회
|
* 인수인계보고서 상세 조회
|
||||||
|
* GET /api/v1/construction/handover-reports/{id}
|
||||||
*/
|
*/
|
||||||
export async function getHandoverReportDetail(id: string): Promise<GetHandoverReportDetailResult> {
|
export async function getHandoverReportDetail(id: string): Promise<{
|
||||||
try {
|
|
||||||
const result = await apiRequest<Record<string, unknown>>(`/construction/handover-reports/${id}`);
|
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
|
||||||
return { success: false, error: result.error || '인수인계보고서를 찾을 수 없습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, data: transformHandoverReportDetail(result.data) };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('getHandoverReportDetail error:', error);
|
|
||||||
return { success: false, error: '인수인계보고서 상세 정보를 불러오는데 실패했습니다.' };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface UpdateHandoverReportResult {
|
|
||||||
success: boolean;
|
success: boolean;
|
||||||
data?: HandoverReportDetail;
|
data?: HandoverReportDetail;
|
||||||
error?: string;
|
error?: string;
|
||||||
|
}> {
|
||||||
|
try {
|
||||||
|
const response = await apiClient.get<ApiHandoverReport>(`/construction/handover-reports/${id}`);
|
||||||
|
return { success: true, data: transformHandoverReportDetail(response) };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('인수인계보고서 상세 조회 오류:', error);
|
||||||
|
return { success: false, error: '인수인계보고서를 찾을 수 없습니다.' };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인수인계보고서 수정
|
* 인수인계보고서 수정
|
||||||
|
* PUT /api/v1/construction/handover-reports/{id}
|
||||||
*/
|
*/
|
||||||
export async function updateHandoverReport(
|
export async function updateHandoverReport(
|
||||||
id: string,
|
id: string,
|
||||||
data: HandoverReportFormData
|
data: HandoverReportFormData
|
||||||
): Promise<UpdateHandoverReportResult> {
|
): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
data?: HandoverReportDetail;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
try {
|
try {
|
||||||
const apiData = transformToApiRequest(data);
|
const apiData = transformToApiRequest(data);
|
||||||
|
const response = await apiClient.put<ApiHandoverReport>(`/construction/handover-reports/${id}`, apiData);
|
||||||
const result = await apiRequest<Record<string, unknown>>(`/construction/handover-reports/${id}`, {
|
return { success: true, data: transformHandoverReportDetail(response) };
|
||||||
method: 'PUT',
|
|
||||||
body: JSON.stringify(apiData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
|
||||||
return { success: false, error: result.error || '수정에 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, data: transformHandoverReportDetail(result.data) };
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('updateHandoverReport error:', error);
|
console.error('인수인계보고서 수정 오류:', error);
|
||||||
return { success: false, error: '수정에 실패했습니다.' };
|
return { success: false, error: '수정에 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CreateHandoverReportResult {
|
|
||||||
success: boolean;
|
|
||||||
data?: HandoverReportDetail;
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 인수인계보고서 등록
|
* 인수인계보고서 등록
|
||||||
|
* POST /api/v1/construction/handover-reports
|
||||||
*/
|
*/
|
||||||
export async function createHandoverReport(
|
export async function createHandoverReport(
|
||||||
data: HandoverReportFormData
|
data: HandoverReportFormData
|
||||||
): Promise<CreateHandoverReportResult> {
|
): Promise<{
|
||||||
|
success: boolean;
|
||||||
|
data?: HandoverReportDetail;
|
||||||
|
error?: string;
|
||||||
|
}> {
|
||||||
try {
|
try {
|
||||||
const apiData = transformToApiRequest(data);
|
const apiData = transformToApiRequest(data);
|
||||||
|
const response = await apiClient.post<ApiHandoverReport>('/construction/handover-reports', apiData);
|
||||||
const result = await apiRequest<Record<string, unknown>>('/construction/handover-reports', {
|
return { success: true, data: transformHandoverReportDetail(response) };
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(apiData),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!result.success || !result.data) {
|
|
||||||
return { success: false, error: result.error || '등록에 실패했습니다.' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, data: transformHandoverReportDetail(result.data) };
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('createHandoverReport error:', error);
|
console.error('인수인계보고서 등록 오류:', error);
|
||||||
return { success: false, error: '등록에 실패했습니다.' };
|
return { success: false, error: '등록에 실패했습니다.' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user