159 lines
4.2 KiB
TypeScript
159 lines
4.2 KiB
TypeScript
|
|
import React, { useState, ChangeEvent } from "react";
|
||
|
|
import * as XLSX from "xlsx";
|
||
|
|
import apiClient from "@/api/client";
|
||
|
|
|
||
|
|
interface ImportPayload {
|
||
|
|
departments: any[];
|
||
|
|
doctors: any[];
|
||
|
|
timings: any[];
|
||
|
|
careers: any[];
|
||
|
|
inquiries: any[];
|
||
|
|
academics: any[];
|
||
|
|
appointments: any[];
|
||
|
|
candidates: any[];
|
||
|
|
news: any[];
|
||
|
|
}
|
||
|
|
|
||
|
|
const ImportData: React.FC = () => {
|
||
|
|
const [loading, setLoading] = useState<boolean>(false);
|
||
|
|
const [status, setStatus] = useState<string>("");
|
||
|
|
|
||
|
|
const handleFileUpload = (e: ChangeEvent<HTMLInputElement>) => {
|
||
|
|
const file = e.target.files?.[0];
|
||
|
|
if (!file) return;
|
||
|
|
|
||
|
|
setLoading(true);
|
||
|
|
setStatus("Reading Excel file...");
|
||
|
|
|
||
|
|
const reader = new FileReader();
|
||
|
|
reader.onload = async (evt: ProgressEvent<FileReader>) => {
|
||
|
|
try {
|
||
|
|
const bstr = evt.target?.result;
|
||
|
|
if (!bstr) throw new Error("Failed to read file content.");
|
||
|
|
|
||
|
|
const wb = XLSX.read(bstr, { type: "binary" });
|
||
|
|
|
||
|
|
const payload: ImportPayload = {
|
||
|
|
departments: XLSX.utils.sheet_to_json(wb.Sheets["Departments"]) || [],
|
||
|
|
doctors: XLSX.utils.sheet_to_json(wb.Sheets["Doctors"]) || [],
|
||
|
|
timings: XLSX.utils.sheet_to_json(wb.Sheets["Doctor Timings"]) || [],
|
||
|
|
careers: XLSX.utils.sheet_to_json(wb.Sheets["Careers"]) || [],
|
||
|
|
inquiries: XLSX.utils.sheet_to_json(wb.Sheets["Inquiry"]) || [],
|
||
|
|
academics:
|
||
|
|
XLSX.utils.sheet_to_json(wb.Sheets["Academics & Research"]) || [],
|
||
|
|
appointments:
|
||
|
|
XLSX.utils.sheet_to_json(wb.Sheets["Appointment"]) || [],
|
||
|
|
candidates: XLSX.utils.sheet_to_json(wb.Sheets["Candidate"]) || [],
|
||
|
|
news: XLSX.utils.sheet_to_json(wb.Sheets["News & Media"]) || [],
|
||
|
|
};
|
||
|
|
|
||
|
|
setStatus("Uploading data to server (this may take a moment)...");
|
||
|
|
|
||
|
|
const response = await apiClient.post("/import/bulk", payload);
|
||
|
|
|
||
|
|
if (response.status === 200) {
|
||
|
|
setStatus("✅ ALL DATA IMPORT COMPLETED SUCCESSFULLY!");
|
||
|
|
} else {
|
||
|
|
setStatus("❌ Server responded with an error.");
|
||
|
|
}
|
||
|
|
} catch (err: any) {
|
||
|
|
console.error("Import Error:", err);
|
||
|
|
const errorMsg = err.response?.data?.error || "Error processing file.";
|
||
|
|
setStatus(`❌ ${errorMsg}`);
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
if (e.target) e.target.value = "";
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
reader.onerror = () => {
|
||
|
|
setStatus("❌ Failed to read the file.");
|
||
|
|
setLoading(false);
|
||
|
|
};
|
||
|
|
|
||
|
|
reader.readAsBinaryString(file);
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div style={containerStyle}>
|
||
|
|
<div style={cardStyle}>
|
||
|
|
<h2 style={{ color: "#333", marginBottom: "10px" }}>
|
||
|
|
Database Bulk Import
|
||
|
|
</h2>
|
||
|
|
<p style={{ color: "#666", marginBottom: "30px" }}>
|
||
|
|
Select the <b>gg_hospital.xlsx</b> file. This will update all tables.
|
||
|
|
</p>
|
||
|
|
|
||
|
|
<div style={{ marginBottom: "20px" }}>
|
||
|
|
<input
|
||
|
|
type="file"
|
||
|
|
accept=".xlsx, .xls"
|
||
|
|
onChange={handleFileUpload}
|
||
|
|
id="excel-upload"
|
||
|
|
style={{ display: "none" }}
|
||
|
|
disabled={loading}
|
||
|
|
/>
|
||
|
|
<label
|
||
|
|
htmlFor="excel-upload"
|
||
|
|
style={{
|
||
|
|
...buttonStyle,
|
||
|
|
backgroundColor: loading ? "#a0aec0" : "#3182ce",
|
||
|
|
cursor: loading ? "not-allowed" : "pointer",
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{loading ? "⌛ Processing..." : "📂 Choose Excel File"}
|
||
|
|
</label>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{status && (
|
||
|
|
<div
|
||
|
|
style={{
|
||
|
|
marginTop: "25px",
|
||
|
|
padding: "15px",
|
||
|
|
borderRadius: "8px",
|
||
|
|
backgroundColor: status.includes("✅") ? "#f0fff4" : "#fff5f5",
|
||
|
|
color: status.includes("✅") ? "#2f855a" : "#c53030",
|
||
|
|
border: `1px solid ${status.includes("✅") ? "#c6f6d5" : "#fed7d7"}`,
|
||
|
|
fontWeight: "500",
|
||
|
|
whiteSpace: "pre-wrap",
|
||
|
|
}}
|
||
|
|
>
|
||
|
|
{status}
|
||
|
|
</div>
|
||
|
|
)}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
};
|
||
|
|
|
||
|
|
const containerStyle: React.CSSProperties = {
|
||
|
|
display: "flex",
|
||
|
|
justifyContent: "center",
|
||
|
|
alignItems: "center",
|
||
|
|
minHeight: "80vh",
|
||
|
|
backgroundColor: "#f7fafc",
|
||
|
|
fontFamily: "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif",
|
||
|
|
};
|
||
|
|
|
||
|
|
const cardStyle: React.CSSProperties = {
|
||
|
|
backgroundColor: "white",
|
||
|
|
padding: "40px",
|
||
|
|
borderRadius: "12px",
|
||
|
|
boxShadow: "0 4px 6px rgba(0,0,0,0.1)",
|
||
|
|
maxWidth: "500px",
|
||
|
|
width: "100%",
|
||
|
|
textAlign: "center",
|
||
|
|
};
|
||
|
|
|
||
|
|
const buttonStyle: React.CSSProperties = {
|
||
|
|
padding: "12px 24px",
|
||
|
|
color: "white",
|
||
|
|
borderRadius: "6px",
|
||
|
|
fontSize: "16px",
|
||
|
|
fontWeight: "bold",
|
||
|
|
transition: "all 0.2s ease",
|
||
|
|
display: "inline-block",
|
||
|
|
};
|
||
|
|
|
||
|
|
export default ImportData;
|