summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-11-26 10:13:54 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-11-26 10:13:54 +0000
commitb807f0e8007d297ab282a4329bfbdeb3afcb63a7 (patch)
tree46610f3260c1b90ff92462f4828e891b1edc3218 /app
parentc775a993930e806f56ea116941574015ee518170 (diff)
(대표님) EDP 작업사항
Diffstat (limited to 'app')
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx95
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/im/[formCode]/page.tsx95
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/page.tsx37
-rw-r--r--app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx18
-rw-r--r--app/api/cron/form-tags-plant/start/route.ts141
-rw-r--r--app/api/cron/form-tags-plant/status/route.ts46
-rw-r--r--app/api/cron/tags-plant/start/route.ts149
-rw-r--r--app/api/cron/tags-plant/status/route.ts46
8 files changed, 613 insertions, 14 deletions
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx
new file mode 100644
index 00000000..351fbca3
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx
@@ -0,0 +1,95 @@
+// app/[lng]/partners/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx
+import DynamicTable from "@/components/form-data-plant/form-data-table";
+import { getFormData, getFormId,getProjectIdByCode } from "@/lib/forms-plant/services";
+import { useTranslation } from "@/i18n";
+import { Skeleton } from "@/components/ui/skeleton";
+
+interface EngineeringFormPageProps {
+ params: {
+ lng: string;
+ projectCode: string;
+ packageCode: string;
+ formCode: string;
+ };
+ searchParams?: {
+ mode?: string;
+ };
+}
+
+export default async function EngineeringFormPage({
+ params,
+ searchParams,
+}: EngineeringFormPageProps) {
+ // 1) 구조 분해 할당
+ const resolvedParams = await params;
+
+ // 2) searchParams도 await 필요
+ const resolvedSearchParams = await searchParams;
+
+ // 3) 구조 분해 할당
+ const { lng, projectCode, packageCode, formCode } = resolvedParams;
+
+ // i18n 설정
+ const { t } = await useTranslation(lng, 'engineering');
+
+ // URL 쿼리 파라미터에서 mode 가져오기 (await 해서 사용)
+ const mode = "ENG"; // 기본값은 IM
+
+ // 4) DB 조회 - projectCode와 packageCode를 전달
+ const { columns, data, editableFieldsMap } = await getFormData(
+ formCode,
+ projectCode,
+ packageCode
+ );
+
+ // 5) formId 조회 - projectCode와 packageCode를 전달
+ const { formId } = await getFormId(projectCode, packageCode, formCode, mode);
+
+ const projectId = await getProjectIdByCode(projectCode)
+
+ // 6) 예외 처리
+ if (!columns) {
+ return (
+ <p className="text-red-500">
+ {t('errors.form_meta_not_found')}
+ </p>
+ );
+ }
+
+ // 7) 렌더링
+ return (
+ <div className="space-y-4">
+ <div className="flex items-center justify-between">
+ <div>
+ <h3 className="text-lg font-semibold">Engineering Form</h3>
+ <p className="text-sm text-muted-foreground">
+ Project: {projectCode} / Package: {packageCode} / Form: {formCode}
+ </p>
+ </div>
+ </div>
+
+ <div className="space-y-6">
+ <DynamicTable
+ projectId={projectId}
+ projectCode={projectCode}
+ packageCode={packageCode}
+ formCode={formCode}
+ formId={formId}
+ columnsJSON={columns}
+ dataJSON={data}
+ editableFieldsMap={editableFieldsMap}
+ mode={"ENG"}
+ />
+ </div>
+ </div>
+ );
+}
+
+function TableSkeleton() {
+ return (
+ <div className="space-y-4">
+ <Skeleton className="h-10 w-full" />
+ <Skeleton className="h-[400px] w-full" />
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/im/[formCode]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/im/[formCode]/page.tsx
new file mode 100644
index 00000000..29188061
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/im/[formCode]/page.tsx
@@ -0,0 +1,95 @@
+// app/[lng]/partners/vendor-data-plant/[projectCode]/[packageCode]/eng/[formCode]/page.tsx
+import DynamicTable from "@/components/form-data-plant/form-data-table";
+import { getFormData, getFormId, getProjectIdByCode } from "@/lib/forms-plant/services";
+import { useTranslation } from "@/i18n";
+import { Skeleton } from "@/components/ui/skeleton";
+
+interface EngineeringFormPageProps {
+ params: {
+ lng: string;
+ projectCode: string;
+ packageCode: string;
+ formCode: string;
+ };
+ searchParams?: {
+ mode?: string;
+ };
+}
+
+export default async function IMFormPage({
+ params,
+ searchParams,
+}: EngineeringFormPageProps) {
+ // 1) 구조 분해 할당
+ const resolvedParams = await params;
+
+ // 2) searchParams도 await 필요
+ const resolvedSearchParams = await searchParams;
+
+ // 3) 구조 분해 할당
+ const { lng, projectCode, packageCode, formCode } = resolvedParams;
+
+ // i18n 설정
+ const { t } = await useTranslation(lng, 'engineering');
+
+ // URL 쿼리 파라미터에서 mode 가져오기 (await 해서 사용)
+ const mode = "IM"; // 기본값은 IM
+
+ // 4) DB 조회 - projectCode와 packageCode를 전달
+ const { columns, data, editableFieldsMap } = await getFormData(
+ formCode,
+ projectCode,
+ packageCode
+ );
+
+ // 5) formId 조회 - projectCode와 packageCode를 전달
+ const { formId } = await getFormId(projectCode, packageCode, formCode, mode);
+
+ const projectId = await getProjectIdByCode(projectCode)
+
+ // 6) 예외 처리
+ if (!columns) {
+ return (
+ <p className="text-red-500">
+ {t('errors.form_meta_not_found')}
+ </p>
+ );
+ }
+
+ // 7) 렌더링
+ return (
+ <div className="space-y-4">
+ <div className="flex items-center justify-between">
+ <div>
+ <h3 className="text-lg font-semibold">Engineering Form</h3>
+ <p className="text-sm text-muted-foreground">
+ Project: {projectCode} / Package: {packageCode} / Form: {formCode}
+ </p>
+ </div>
+ </div>
+
+ <div className="space-y-6">
+ <DynamicTable
+ projectId={projectId}
+ projectCode={projectCode}
+ packageCode={packageCode}
+ formCode={formCode}
+ formId={formId}
+ columnsJSON={columns}
+ dataJSON={data}
+ editableFieldsMap={editableFieldsMap}
+ mode={"IM"}
+ />
+ </div>
+ </div>
+ );
+}
+
+function TableSkeleton() {
+ return (
+ <div className="space-y-4">
+ <Skeleton className="h-10 w-full" />
+ <Skeleton className="h-[400px] w-full" />
+ </div>
+ );
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/page.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/page.tsx
new file mode 100644
index 00000000..4904a8ff
--- /dev/null
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/[projectCode]/[packageCode]/page.tsx
@@ -0,0 +1,37 @@
+// app/[lng]/partners/vendor-data-plant/[projectCode]/[packageCode]/page.tsx
+
+import { TagsTable } from "@/lib/tags-plant/table/tag-table"
+
+interface MasterTagListPageProps {
+ params: Promise<{
+ lng: string
+ projectCode: string
+ packageCode: string
+ }>
+}
+
+export default async function MasterTagListPage({
+ params,
+}: MasterTagListPageProps) {
+ const { projectCode, packageCode } = await params
+
+ return (
+ <div className="space-y-4">
+ <div className="flex items-center justify-between">
+ <div>
+ <h3 className="text-lg font-semibold">Master Tag List</h3>
+ <p className="text-sm text-muted-foreground">
+ Project: {projectCode} / Package: {packageCode}
+ </p>
+ </div>
+ </div>
+
+ {/* 완전 클라이언트 컴포넌트 */}
+ <TagsTable
+ projectCode={projectCode}
+ packageCode={packageCode}
+ formCode="MASTER"
+ />
+ </div>
+ )
+} \ No newline at end of file
diff --git a/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx b/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx
index 8a9c43e9..792a3a6a 100644
--- a/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx
+++ b/app/[lng]/partners/(partners)/vendor-data-plant/layout.tsx
@@ -2,41 +2,35 @@
import * as React from "react"
import { cookies } from "next/headers"
import { Shell } from "@/components/shell"
-import { getVendorProjectsAndContracts } from "@/lib/vendor-data-plant/services"
import { VendorDataContainer } from "@/components/vendor-data-plant/vendor-data-container"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
import { getServerSession } from "next-auth"
import { InformationButton } from "@/components/information/information-button"
import { useTranslation } from "@/i18n"
+import { getVendorProjectsWithPackages } from "@/lib/vendor-data/services"
interface VendorDataLayoutProps {
children: React.ReactNode
params: { lng?: string }
}
-// Layout 컴포넌트는 서버 컴포넌트입니다
export default async function VendorDataLayout({
children,
params,
}: VendorDataLayoutProps) {
- // 기본 언어는 'ko'로 설정, params.locale이 있으면 사용
- const { lng } = await params;
+ const { lng } = await params
const language = lng || 'en'
const { t } = await useTranslation(language, 'engineering')
const session = await getServerSession(authOptions)
const vendorId = session?.user.companyId
- // const vendorId = "17"
const idAsNumber = Number(vendorId)
- // 프로젝트 데이터 가져오기 (type=plant만)
- const projects = await getVendorProjectsAndContracts(idAsNumber, "plant")
+ // 프로젝트 및 패키지 데이터 가져오기
+ const projects = await getVendorProjectsWithPackages(idAsNumber, "plant")
// 레이아웃 설정 쿠키 가져오기
- // Next.js 15에서는 cookies()가 Promise를 반환하므로 await 사용
const cookieStore = await cookies()
-
- // 이제 cookieStore.get() 메서드 사용 가능
const layout = cookieStore.get("react-resizable-panels:layout:mail")
const collapsed = cookieStore.get("react-resizable-panels:collapsed")
@@ -54,9 +48,6 @@ export default async function VendorDataLayout({
</h2>
<InformationButton pagePath="partners/vendor-data-plant" />
</div>
- {/* <p className="text-muted-foreground">
- 각종 Data 입력할 수 있습니다
- </p> */}
</div>
</div>
</div>
@@ -74,7 +65,6 @@ export default async function VendorDataLayout({
defaultCollapsed={defaultCollapsed}
navCollapsedSize={4}
>
- {/* 페이지별 콘텐츠가 여기에 들어갑니다 */}
{children}
</VendorDataContainer>
)}
diff --git a/app/api/cron/form-tags-plant/start/route.ts b/app/api/cron/form-tags-plant/start/route.ts
new file mode 100644
index 00000000..17eb8979
--- /dev/null
+++ b/app/api/cron/form-tags-plant/start/route.ts
@@ -0,0 +1,141 @@
+// app/api/cron/form-tags/start/route.ts
+import { NextRequest } from 'next/server';
+import { v4 as uuidv4 } from 'uuid';
+import { revalidateTag } from 'next/cache';
+
+// 동기화 작업의 상태를 저장할 Map
+// 실제 프로덕션에서는 Redis 또는 DB에 저장하는 것이 좋습니다
+const syncJobs = new Map<string, {
+ status: 'queued' | 'processing' | 'completed' | 'failed';
+ startTime: Date;
+ endTime?: Date;
+ result?: any;
+ error?: string;
+ progress?: number;
+ projectCode?: string;
+ formCode?: string;
+ packageCode?: string;
+ mode?: string
+}>();
+
+export async function POST(request: NextRequest) {
+ try {
+ // 요청 데이터 가져오기
+ let projectCode: string | undefined;
+ let formCode: string | undefined;
+ let packageCode: string | undefined;
+ let mode: string | undefined;
+
+
+ const body = await request.json();
+ projectCode = body.projectCode;
+ formCode = body.formCode;
+ packageCode = body.packageCode;
+ mode = body.mode; // 모드 정보 추출
+
+
+ // 고유 ID 생성
+ const syncId = uuidv4();
+
+ // 작업 상태 초기화
+ syncJobs.set(syncId, {
+ status: 'queued',
+ startTime: new Date(),
+ formCode,
+ projectCode,
+ packageCode,
+ mode
+
+ });
+
+ // 비동기 작업 시작 (백그라운드에서 실행)
+ processTagImport(syncId).catch(error => {
+ console.error('Background tag import job failed:', error);
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred'
+ });
+ });
+
+ // 즉시 응답 반환 (작업 ID 포함)
+ return Response.json({
+ success: true,
+ message: 'Tag import job started',
+ syncId
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Failed to start tag import job:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to start tag import job'
+ }, { status: 500 });
+ }
+}
+
+// 백그라운드에서 실행되는 태그 가져오기 작업
+async function processTagImport(syncId: string) {
+ try {
+ const jobInfo = syncJobs.get(syncId)!;
+ const formCode = jobInfo.formCode;
+ const projectCode = jobInfo.projectCode;
+ const packageCode = jobInfo.packageCode || 0;
+ const mode = jobInfo.mode || 0;
+
+ // 상태 업데이트: 처리 중
+ syncJobs.set(syncId, {
+ ...jobInfo,
+ status: 'processing',
+ progress: 0,
+ });
+
+ if (!formCode || !projectCode ) {
+ throw new Error('formCode,projectCode is required');
+ }
+
+ // 여기서 실제 태그 가져오기 로직 import
+ const { importTagsFromSEDP } = await import('@/lib/sedp/get-form-tags-plant');
+
+ // 진행 상황 업데이트를 위한 콜백 함수
+ const updateProgress = (progress: number) => {
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ progress
+ });
+ };
+
+ // 실제 태그 가져오기 실행
+ const result = await importTagsFromSEDP(formCode, projectCode, packageCode, updateProgress);
+
+ // 명시적으로 캐시 무효화
+ revalidateTag(`forms-${packageCode}-${mode}`);
+
+ // 상태 업데이트: 완료
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'completed',
+ endTime: new Date(),
+ result,
+ progress: 100,
+ });
+
+ return result;
+ } catch (error: any) {
+ // 에러 발생 시 상태 업데이트
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred',
+ });
+
+ throw error; // 에러 다시 던지기
+ }
+}
+
+// 서버 메모리에 저장된 작업 상태 접근 함수 (다른 API에서 사용)
+export function getSyncJobStatus(id: string) {
+ return syncJobs.get(id);
+} \ No newline at end of file
diff --git a/app/api/cron/form-tags-plant/status/route.ts b/app/api/cron/form-tags-plant/status/route.ts
new file mode 100644
index 00000000..9d288f52
--- /dev/null
+++ b/app/api/cron/form-tags-plant/status/route.ts
@@ -0,0 +1,46 @@
+// app/api/cron/tags/status/route.ts
+import { NextRequest } from 'next/server';
+import { getSyncJobStatus } from '../start/route';
+
+export async function GET(request: NextRequest) {
+ try {
+ // URL에서 작업 ID 가져오기
+ const searchParams = request.nextUrl.searchParams;
+ const syncId = searchParams.get('id');
+
+ if (!syncId) {
+ return Response.json({
+ success: false,
+ error: 'Missing sync ID parameter'
+ }, { status: 400 });
+ }
+
+ // 작업 상태 조회
+ const jobStatus = getSyncJobStatus(syncId);
+
+ if (!jobStatus) {
+ return Response.json({
+ success: false,
+ error: 'Sync job not found'
+ }, { status: 404 });
+ }
+
+ // 작업 상태 반환
+ return Response.json({
+ success: true,
+ status: jobStatus.status,
+ startTime: jobStatus.startTime,
+ endTime: jobStatus.endTime,
+ progress: jobStatus.progress,
+ result: jobStatus.result,
+ error: jobStatus.error
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Error retrieving tag import status:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to retrieve tag import status'
+ }, { status: 500 });
+ }
+} \ No newline at end of file
diff --git a/app/api/cron/tags-plant/start/route.ts b/app/api/cron/tags-plant/start/route.ts
new file mode 100644
index 00000000..83e06935
--- /dev/null
+++ b/app/api/cron/tags-plant/start/route.ts
@@ -0,0 +1,149 @@
+// app/api/cron/tags/start/route.ts
+import { NextRequest } from 'next/server';
+import { v4 as uuidv4 } from 'uuid';
+import { revalidateTag } from 'next/cache';
+
+// 동기화 작업의 상태를 저장할 Map
+// 실제 프로덕션에서는 Redis 또는 DB에 저장하는 것이 좋습니다
+const syncJobs = new Map<string, {
+ status: 'queued' | 'processing' | 'completed' | 'failed';
+ startTime: Date;
+ endTime?: Date;
+ result?: any;
+ error?: string;
+ progress?: number;
+ projectCode?: string;
+ packageCode?: string;
+ mode?: string
+}>();
+
+export async function POST(request: NextRequest) {
+ try {
+ // 요청 데이터 가져오기
+ let projectCode: string | undefined;
+ let packageCode: string | undefined;
+ let mode: string | undefined;
+
+ try {
+ const body = await request.json();
+ projectCode = body.projectCode;
+ packageCode = body.packageCode;
+ mode = body.mode; // 모드 정보 추출
+
+ } catch (error) {
+ // 요청 본문이 없거나 JSON이 아닌 경우, URL 파라미터 확인
+ const searchParams = request.nextUrl.searchParams;
+ const projectCodeParam = searchParams.get('projectCode');
+ const packageCodeParam = searchParams.get('packageCode');
+
+ if (projectCodeParam&&packageCodeParam) {
+ projectCode = projectCodeParam;
+ packageCode = packageCodeParam;
+ }
+ mode = searchParams.get('mode') || undefined;
+ }
+
+ // 고유 ID 생성
+ const syncId = uuidv4();
+
+ // 작업 상태 초기화
+ syncJobs.set(syncId, {
+ status: 'queued',
+ startTime: new Date(),
+ projectCode,
+ packageCode,
+ mode
+ });
+
+ // 비동기 작업 시작 (백그라운드에서 실행)
+ processTagImport(syncId).catch(error => {
+ console.error('Background tag import job failed:', error);
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred'
+ });
+ });
+
+ // 즉시 응답 반환 (작업 ID 포함)
+ return Response.json({
+ success: true,
+ message: 'Tag import job started',
+ syncId
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Failed to start tag import job:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to start tag import job'
+ }, { status: 500 });
+ }
+}
+
+// 백그라운드에서 실행되는 태그 가져오기 작업
+async function processTagImport(syncId: string) {
+ try {
+ const jobInfo = syncJobs.get(syncId)!;
+ const projectCode = jobInfo.projectCode;
+ const packageCode = jobInfo.packageCode;
+ const mode = jobInfo.mode; // 모드 정보 추출
+
+
+ // 상태 업데이트: 처리 중
+ syncJobs.set(syncId, {
+ ...jobInfo,
+ status: 'processing',
+ progress: 0,
+ });
+
+ if (!packageCode) {
+ throw new Error('Package is required');
+ }
+
+ // 여기서 실제 태그 가져오기 로직 import
+ const { importTagsFromSEDP } = await import('@/lib/sedp/get-tags-plant');
+
+ // 진행 상황 업데이트를 위한 콜백 함수
+ const updateProgress = (progress: number) => {
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ progress
+ });
+ };
+
+ // 실제 태그 가져오기 실행
+ const result = await importTagsFromSEDP(projectCode, packageCode,updateProgress, mode);
+
+ // 명시적으로 캐시 무효화
+ revalidateTag(`tags-${packageCode}`);
+ revalidateTag(`forms-${packageCode}-${mode}`);
+
+ // 상태 업데이트: 완료
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'completed',
+ endTime: new Date(),
+ result,
+ progress: 100,
+ });
+
+ return result;
+ } catch (error: any) {
+ // 에러 발생 시 상태 업데이트
+ syncJobs.set(syncId, {
+ ...syncJobs.get(syncId)!,
+ status: 'failed',
+ endTime: new Date(),
+ error: error.message || 'Unknown error occurred',
+ });
+
+ throw error; // 에러 다시 던지기
+ }
+}
+
+// 서버 메모리에 저장된 작업 상태 접근 함수 (다른 API에서 사용)
+export function getSyncJobStatus(id: string) {
+ return syncJobs.get(id);
+} \ No newline at end of file
diff --git a/app/api/cron/tags-plant/status/route.ts b/app/api/cron/tags-plant/status/route.ts
new file mode 100644
index 00000000..9d288f52
--- /dev/null
+++ b/app/api/cron/tags-plant/status/route.ts
@@ -0,0 +1,46 @@
+// app/api/cron/tags/status/route.ts
+import { NextRequest } from 'next/server';
+import { getSyncJobStatus } from '../start/route';
+
+export async function GET(request: NextRequest) {
+ try {
+ // URL에서 작업 ID 가져오기
+ const searchParams = request.nextUrl.searchParams;
+ const syncId = searchParams.get('id');
+
+ if (!syncId) {
+ return Response.json({
+ success: false,
+ error: 'Missing sync ID parameter'
+ }, { status: 400 });
+ }
+
+ // 작업 상태 조회
+ const jobStatus = getSyncJobStatus(syncId);
+
+ if (!jobStatus) {
+ return Response.json({
+ success: false,
+ error: 'Sync job not found'
+ }, { status: 404 });
+ }
+
+ // 작업 상태 반환
+ return Response.json({
+ success: true,
+ status: jobStatus.status,
+ startTime: jobStatus.startTime,
+ endTime: jobStatus.endTime,
+ progress: jobStatus.progress,
+ result: jobStatus.result,
+ error: jobStatus.error
+ }, { status: 200 });
+
+ } catch (error: any) {
+ console.error('Error retrieving tag import status:', error);
+ return Response.json({
+ success: false,
+ error: error.message || 'Failed to retrieve tag import status'
+ }, { status: 500 });
+ }
+} \ No newline at end of file