diff --git a/frontend/src/app/import/page.tsx b/frontend/src/app/import/page.tsx new file mode 100644 index 0000000..552f50b --- /dev/null +++ b/frontend/src/app/import/page.tsx @@ -0,0 +1,280 @@ +'use client'; + +import { useState, useRef } from 'react'; +import { api } from '@/lib/api'; +import Navbar from '@/components/Navbar'; +import { Upload, Loader2, CheckCircle, XCircle } from 'lucide-react'; + +interface ImportResult { + success: boolean; + message: string; + created?: number; + updated?: number; +} + +export default function ImportPage() { + const [kyosaiFile, setKyosaiFile] = useState(null); + const [yoshidaFile, setYoshidaFile] = useState(null); + const [uploading, setUploading] = useState(false); + const [kyosaiResult, setKyosaiResult] = useState(null); + const [yoshidaResult, setYoshidaResult] = useState(null); + const kyosaiInputRef = useRef(null); + const yoshidaInputRef = useRef(null); + + const handleKyosaiUpload = async () => { + if (!kyosaiFile) { + alert('ファイルを選択してください'); + return; + } + + setUploading(true); + setKyosaiResult(null); + + try { + const formData = new FormData(); + formData.append('file', kyosaiFile); + + const response = await api.post('/fields/import/kyosai/', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + const data = response.data; + setKyosaiResult({ + success: true, + message: data.message || 'インポートが完了しました', + created: data.created, + updated: data.updated, + }); + } catch (error: unknown) { + console.error('Upload failed:', error); + let errorMessage = 'アップロードに失敗しました'; + if (error && typeof error === 'object' && 'response' in error) { + const axiosError = error as { response?: { data?: { error?: string } } }; + if (axiosError.response?.data?.error) { + errorMessage = axiosError.response.data.error; + } + } + setKyosaiResult({ + success: false, + message: errorMessage, + }); + } finally { + setUploading(false); + } + }; + + const handleYoshidaUpload = async () => { + if (!yoshidaFile) { + alert('ファイルを選択してください'); + return; + } + + setUploading(true); + setYoshidaResult(null); + + try { + const formData = new FormData(); + formData.append('file', yoshidaFile); + + const response = await api.post('/fields/import/yoshida/', formData, { + headers: { + 'Content-Type': 'multipart/form-data', + }, + }); + + const data = response.data; + setYoshidaResult({ + success: true, + message: data.message || 'インポートが完了しました', + created: data.created, + updated: data.updated, + }); + } catch (error: unknown) { + console.error('Upload failed:', error); + let errorMessage = 'アップロードに失敗しました'; + if (error && typeof error === 'object' && 'response' in error) { + const axiosError = error as { response?: { data?: { error?: string } } }; + if (axiosError.response?.data?.error) { + errorMessage = axiosError.response.data.error; + } + } + setYoshidaResult({ + success: false, + message: errorMessage, + }); + } finally { + setUploading(false); + } + }; + + return ( +
+ +
+

データインポート

+ +
+ {/* 共済マスタ取込 */} +
+

+ 共済マスタ取込 +

+

+ 共済細目データをインポートします(k_num, s_num, address...) +

+ +
+ setKyosaiFile(e.target.files?.[0] || null)} + className="hidden" + /> + + {kyosaiFile && ( + + )} +
+ + + + {kyosaiResult && ( +
+ {kyosaiResult.success ? ( + + ) : ( + + )} +
+

{kyosaiResult.message}

+ {kyosaiResult.success && kyosaiResult.created !== undefined && ( +

+ 作成: {kyosaiResult.created}件 / 更新: {kyosaiResult.updated}件 +

+ )} +
+
+ )} +
+ + {/* 実圃場データ取込 */} +
+

+ 実圃場データ取込 +

+

+ 吉田農地台帳データをインポートします +

+ +
+ setYoshidaFile(e.target.files?.[0] || null)} + className="hidden" + /> + + {yoshidaFile && ( + + )} +
+ + + + {yoshidaResult && ( +
+ {yoshidaResult.success ? ( + + ) : ( + + )} +
+

{yoshidaResult.message}

+ {yoshidaResult.success && yoshidaResult.created !== undefined && ( +

+ 作成: {yoshidaResult.created}件 / 更新: {yoshidaResult.updated}件 +

+ )} +
+
+ )} +
+
+
+
+ ); +} diff --git a/frontend/src/components/Navbar.tsx b/frontend/src/components/Navbar.tsx index ae6ba99..d6da2d2 100644 --- a/frontend/src/components/Navbar.tsx +++ b/frontend/src/components/Navbar.tsx @@ -1,7 +1,7 @@ 'use client'; import { useRouter, usePathname } from 'next/navigation'; -import { LogOut, Wheat, MapPin, FileText } from 'lucide-react'; +import { LogOut, Wheat, MapPin, FileText, Upload } from 'lucide-react'; import { logout } from '@/lib/api'; export default function Navbar() { @@ -54,6 +54,17 @@ export default function Navbar() { 帳票出力 +