diff --git a/src/components/business/construction/structure-review/actions.ts b/src/components/business/construction/structure-review/actions.ts index 5717198a..e5f5f952 100644 --- a/src/components/business/construction/structure-review/actions.ts +++ b/src/components/business/construction/structure-review/actions.ts @@ -1,184 +1,256 @@ 'use server'; -import type { StructureReview, StructureReviewStats } from './types'; +import type { StructureReview, StructureReviewStats, StructureReviewStatus } from './types'; +import { apiClient } from '@/lib/api'; -// 목업 데이터 -const MOCK_STRUCTURE_REVIEWS: StructureReview[] = [ - { - id: '1', - reviewNumber: '123123', - partnerId: '1', - partnerName: '회사명', - siteId: '1', - siteName: '현장명', - requestDate: '2025-12-12', - reviewCompany: '회사명', - reviewerName: '홍길동', - reviewDate: '2025-12-15', - completionDate: '2025-12-15', - status: 'pending', - createdAt: '2025-12-01T00:00:00Z', - updatedAt: '2025-12-01T00:00:00Z', - }, - { - id: '2', - reviewNumber: '123123', - partnerId: '1', - partnerName: '회사명', - siteId: '2', - siteName: '현장명', - requestDate: '2025-12-12', - reviewCompany: '회사명', - reviewerName: '홍길동', - reviewDate: '2025-12-15', - completionDate: null, - status: 'pending', - createdAt: '2025-12-02T00:00:00Z', - updatedAt: '2025-12-02T00:00:00Z', - }, - { - id: '3', - reviewNumber: '123123', - partnerId: '2', - partnerName: '회사명', - siteId: '3', - siteName: '현장명', - requestDate: '2025-12-12', - reviewCompany: '회사명', - reviewerName: '홍길동', - reviewDate: null, - completionDate: null, - status: 'pending', - createdAt: '2025-12-03T00:00:00Z', - updatedAt: '2025-12-03T00:00:00Z', - }, - { - id: '4', - reviewNumber: '123123', - partnerId: '2', - partnerName: '회사명', - siteId: '4', - siteName: '현장명', - requestDate: '2025-12-12', - reviewCompany: '회사명', - reviewerName: '홍길동', - reviewDate: '2025-12-15', - completionDate: '2025-12-15', - status: 'completed', - createdAt: '2025-12-04T00:00:00Z', - updatedAt: '2025-12-04T00:00:00Z', - }, - { - id: '5', - reviewNumber: '123123', - partnerId: '3', - partnerName: '회사명', - siteId: '5', - siteName: '현장명', - requestDate: '2025-12-12', - reviewCompany: '회사명', - reviewerName: '홍길동', - reviewDate: '2025-12-15', - completionDate: '2025-12-15', - status: 'completed', - createdAt: '2025-12-05T00:00:00Z', - updatedAt: '2025-12-05T00:00:00Z', - }, -]; +/** + * 구조검토관리 Server Actions + * 표준화된 apiClient 사용 버전 + */ -// 구조검토 목록 조회 -export async function getStructureReviewList(params?: { - size?: number; - startDate?: string; - endDate?: string; -}): Promise<{ - success: boolean; - data?: { items: StructureReview[]; total: number }; - error?: string; -}> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 500)); +// ======================================== +// API 응답 타입 +// ======================================== +interface ApiStructureReview { + id: number; + review_number: string | null; + partner_id: number | null; + partner_name: string | null; + site_id: number | null; + site_name: string | null; + request_date: string | null; + review_company: string | null; + reviewer_name: string | null; + review_date: string | null; + completion_date: string | null; + status: StructureReviewStatus; + file_url: string | null; + created_at: string; + updated_at: string; +} + +interface ApiStructureReviewStats { + total: number; + pending: number; + completed: number; +} + +// ======================================== +// 타입 변환 함수 +// ======================================== + +/** + * API 응답 → StructureReview 타입 변환 + */ +function transformStructureReview(apiData: ApiStructureReview): StructureReview { return { - success: true, - data: { - items: MOCK_STRUCTURE_REVIEWS, - total: MOCK_STRUCTURE_REVIEWS.length, - }, + id: String(apiData.id), + reviewNumber: apiData.review_number || '', + partnerId: apiData.partner_id ? String(apiData.partner_id) : '', + partnerName: apiData.partner_name || '', + siteId: apiData.site_id ? String(apiData.site_id) : '', + siteName: apiData.site_name || '', + requestDate: apiData.request_date || '', + reviewCompany: apiData.review_company || '', + reviewerName: apiData.reviewer_name || '', + reviewDate: apiData.review_date || null, + completionDate: apiData.completion_date || null, + status: apiData.status || 'pending', + fileUrl: apiData.file_url || undefined, + createdAt: apiData.created_at || '', + updatedAt: apiData.updated_at || '', }; } -// 구조검토 통계 조회 +/** + * StructureReview → API 요청 데이터 변환 + */ +function transformToApiData(data: Partial): Record { + return { + review_number: data.reviewNumber || null, + partner_id: data.partnerId ? Number(data.partnerId) : null, + partner_name: data.partnerName || null, + site_id: data.siteId ? Number(data.siteId) : null, + site_name: data.siteName || null, + request_date: data.requestDate || null, + review_company: data.reviewCompany || null, + reviewer_name: data.reviewerName || null, + review_date: data.reviewDate || null, + completion_date: data.completionDate || null, + status: data.status || 'pending', + file_url: data.fileUrl || null, + }; +} + +// ======================================== +// API 함수 +// ======================================== + +interface GetStructureReviewListParams { + size?: number; + page?: number; + startDate?: string; + endDate?: string; + search?: string; + status?: string; + partnerId?: string; + siteId?: string; + sortBy?: string; +} + +/** + * 구조검토 목록 조회 + * GET /api/v1/construction/structure-reviews + */ +export async function getStructureReviewList(params: GetStructureReviewListParams = {}): Promise<{ + success: boolean; + data?: { + items: StructureReview[]; + total: number; + page: number; + size: number; + totalPages: number; + }; + error?: string; +}> { + try { + const queryParams: Record = {}; + + // 검색 + 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.siteId && params.siteId !== 'all') queryParams.site_id = params.siteId; + + // 날짜 범위 + if (params.startDate) queryParams.start_date = params.startDate; + if (params.endDate) queryParams.end_date = params.endDate; + + // 페이지네이션 + if (params.page) queryParams.page = String(params.page); + if (params.size) queryParams.per_page = String(params.size); + + // 정렬 + if (params.sortBy) { + const sortMap: Record = { + latest: { field: 'created_at', dir: 'desc' }, + oldest: { field: 'created_at', dir: 'asc' }, + partnerNameAsc: { field: 'partner_name', dir: 'asc' }, + partnerNameDesc: { field: 'partner_name', dir: 'desc' }, + siteNameAsc: { field: 'site_name', dir: 'asc' }, + siteNameDesc: { field: 'site_name', dir: 'desc' }, + }; + const sort = sortMap[params.sortBy]; + if (sort) { + queryParams.sort_by = sort.field; + queryParams.sort_dir = sort.dir; + } + } + + const response = await apiClient.get<{ + data: ApiStructureReview[]; + current_page: number; + per_page: number; + total: number; + last_page: number; + }>('/construction/structure-reviews', { params: queryParams }); + + const items = (response.data || []).map(transformStructureReview); + + return { + success: true, + data: { + items, + total: response.total || 0, + page: response.current_page || 1, + size: response.per_page || 20, + totalPages: response.last_page || 1, + }, + }; + } catch (error) { + console.error('구조검토 목록 조회 오류:', error); + return { success: false, error: '구조검토 목록을 불러오는데 실패했습니다.' }; + } +} + +/** + * 구조검토 통계 조회 + * GET /api/v1/construction/structure-reviews/stats + */ export async function getStructureReviewStats(): Promise<{ success: boolean; data?: StructureReviewStats; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 300)); + try { + const response = await apiClient.get('/construction/structure-reviews/stats'); - const pending = MOCK_STRUCTURE_REVIEWS.filter((r) => r.status === 'pending').length; - const completed = MOCK_STRUCTURE_REVIEWS.filter((r) => r.status === 'completed').length; - - return { - success: true, - data: { - total: MOCK_STRUCTURE_REVIEWS.length, - pending, - completed, - }, - }; + return { + success: true, + data: { + total: response.total || 0, + pending: response.pending || 0, + completed: response.completed || 0, + }, + }; + } catch (error) { + console.error('구조검토 통계 조회 오류:', error); + return { success: false, error: '구조검토 통계를 불러오는데 실패했습니다.' }; + } } -// 구조검토 상세 조회 +/** + * 구조검토 상세 조회 + * GET /api/v1/construction/structure-reviews/{id} + */ export async function getStructureReview(id: string): Promise<{ success: boolean; data?: StructureReview; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 300)); + try { + const response = await apiClient.get(`/construction/structure-reviews/${id}`); - const review = MOCK_STRUCTURE_REVIEWS.find((r) => r.id === id); - - if (!review) { + return { + success: true, + data: transformStructureReview(response), + }; + } catch (error) { + console.error('구조검토 상세 조회 오류:', error); return { success: false, error: '구조검토 정보를 찾을 수 없습니다.' }; } - - return { success: true, data: review }; } -// 구조검토 생성 +/** + * 구조검토 생성 + * POST /api/v1/construction/structure-reviews + */ export async function createStructureReview(data: Partial): Promise<{ success: boolean; data?: StructureReview; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 500)); + try { + const apiData = transformToApiData(data); + const response = await apiClient.post('/construction/structure-reviews', apiData); - return { - success: true, - data: { - id: String(Date.now()), - reviewNumber: data.reviewNumber || '', - partnerId: data.partnerId || '', - partnerName: data.partnerName || '', - siteId: data.siteId || '', - siteName: data.siteName || '', - requestDate: data.requestDate || '', - reviewCompany: data.reviewCompany || '', - reviewerName: data.reviewerName || '', - reviewDate: data.reviewDate || null, - completionDate: data.completionDate || null, - status: data.status || 'pending', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - }, - }; + return { + success: true, + data: transformStructureReview(response), + }; + } catch (error) { + console.error('구조검토 생성 오류:', error); + return { success: false, error: '구조검토 등록에 실패했습니다.' }; + } } -// 구조검토 수정 +/** + * 구조검토 수정 + * PUT /api/v1/construction/structure-reviews/{id} + */ export async function updateStructureReview( id: string, data: Partial @@ -187,43 +259,53 @@ export async function updateStructureReview( data?: StructureReview; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 500)); + try { + const apiData = transformToApiData(data); + const response = await apiClient.put(`/construction/structure-reviews/${id}`, apiData); - const existing = MOCK_STRUCTURE_REVIEWS.find((r) => r.id === id); - if (!existing) { - return { success: false, error: '구조검토 정보를 찾을 수 없습니다.' }; + return { + success: true, + data: transformStructureReview(response), + }; + } catch (error) { + console.error('구조검토 수정 오류:', error); + return { success: false, error: '구조검토 수정에 실패했습니다.' }; } - - return { - success: true, - data: { - ...existing, - ...data, - updatedAt: new Date().toISOString(), - }, - }; } -// 구조검토 삭제 +/** + * 구조검토 삭제 + * DELETE /api/v1/construction/structure-reviews/{id} + */ export async function deleteStructureReview(id: string): Promise<{ success: boolean; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 500)); - - return { success: true }; + try { + await apiClient.delete(`/construction/structure-reviews/${id}`); + return { success: true }; + } catch (error) { + console.error('구조검토 삭제 오류:', error); + return { success: false, error: '구조검토 삭제에 실패했습니다.' }; + } } -// 구조검토 일괄 삭제 +/** + * 구조검토 일괄 삭제 + * DELETE /api/v1/construction/structure-reviews/bulk + */ export async function deleteStructureReviews(ids: string[]): Promise<{ success: boolean; deletedCount?: number; error?: string; }> { - // TODO: API 연동 - await new Promise((resolve) => setTimeout(resolve, 500)); - - return { success: true, deletedCount: ids.length }; + try { + await apiClient.delete('/construction/structure-reviews/bulk', { + data: { ids: ids.map((id) => Number(id)) }, + }); + return { success: true, deletedCount: ids.length }; + } catch (error) { + console.error('구조검토 일괄 삭제 오류:', error); + return { success: false, error: '구조검토 일괄 삭제에 실패했습니다.' }; + } } \ No newline at end of file