update application page, table.

This commit is contained in:
2026-04-16 11:54:58 +07:00
parent 720f13e9bb
commit 925b42199c
12 changed files with 265 additions and 91 deletions

View File

@@ -6,7 +6,6 @@ import {
confirmUpload,
getPresignedUrl,
uploadFileToS3,
uploadMedia,
} from "@/service/mediaService";
import React, { useState } from "react";
import Image from "next/image";
@@ -16,6 +15,8 @@ import Captions from "yet-another-react-lightbox/plugins/captions";
import "yet-another-react-lightbox/styles.css";
import "yet-another-react-lightbox/plugins/captions.css";
import { createHistorianCV } from "@/service/historianService";
import { toast } from "sonner";
import Swal from "sweetalert2";
type PendingFile = {
id: string;
@@ -25,17 +26,20 @@ type PendingFile = {
size: number;
type: "image" | "document";
extension: string;
presigned?: any;
presigned?: any;
};
export default function RoleUpgrade() {
const [content, setContent] = useState<string>("");
const [isSubmitting, setIsSubmitting] = useState(false);
const [verifyType, setVerifyType] = useState<string>("OTHER");
const [pendingFiles, setPendingFiles] = useState<PendingFile[]>([]);
const [lightboxIndex, setLightboxIndex] = useState(-1);
const [isPreparingFiles, setIsPreparingFiles] = useState(false);
const handleContentChange = (value: string) => {
setContent(value);
};
@@ -83,38 +87,45 @@ export default function RoleUpgrade() {
setPendingFiles((prev) => prev.filter((item) => item.id !== idToRemove));
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
setIsSubmitting(true);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
setIsSubmitting(true);
const uploadPromises = pendingFiles.map(async (item) => {
await uploadFileToS3(item.file, item.presigned);
const confirmRes = await confirmUpload(item.presigned.token_id);
return confirmRes?.data?.id || confirmRes?.id;
});
const uploadedMediaIds = await Promise.all(uploadPromises);
const uploadPromises = pendingFiles.map(async (item) => {
await uploadFileToS3(item.file, item.presigned);
const confirmRes = await confirmUpload(item.presigned.token_id);
return confirmRes?.data?.id || confirmRes?.id;
});
const uploadedMediaIds = await Promise.all(uploadPromises);
const payload = {
content: content,
media_ids: uploadedMediaIds,
verify_type: "ID_CARD"
};
const payload = {
content: content,
media_ids: uploadedMediaIds,
verify_type: verifyType,
};
console.log("Payload chuẩn bị gửi (JSON):", payload);
console.log("Payload chuẩn bị gửi (JSON):", payload);
const cvResponse = await createHistorianCV(payload);
console.log("Response từ API:", cvResponse);
const cvResponse = await createHistorianCV(payload);
console.log("Response từ API:", cvResponse);
alert("Gửi thành công!");
setContent("");
setPendingFiles([]);
} catch (error) {
console.error("Lỗi:", error);
} finally {
setIsSubmitting(false);
}
};
Swal.fire({
title: "Gửi yêu cầu thành công!",
text: "Yêu cầu nâng cấp đã được gửi đi. Chúng tôi sẽ xem xét và phản hồi sớm nhất có thể. Vui lòng kiểm tra Email khi có thông báo mới.",
icon: "success",
confirmButtonText: "OK",
});
setContent("");
setPendingFiles([]);
setVerifyType("ID_CARD");
} catch (error) {
console.error("Lỗi:", error);
toast.error("Có lỗi xảy ra khi gửi yêu cầu. Vui lòng kiểm tra lại!");
} finally {
setIsSubmitting(false);
}
};
const imageFiles = pendingFiles.filter((f) => f.type === "image");
const slides = imageFiles.map((item) => ({
@@ -149,6 +160,28 @@ const handleSubmit = async (e: React.FormEvent) => {
<div className="flex flex-col gap-6">
<RichTextEditor value={content} onChange={handleContentChange} />
<div className="p-5 bg-white border border-gray-200 rounded-2xl dark:border-gray-800 dark:bg-gray-900">
<label className="block mb-3 text-sm font-semibold text-gray-700 dark:text-white">
Loại hồ xác minh <span className="text-red-500">*</span>
</label>
<select
value={verifyType}
onChange={(e) => setVerifyType(e.target.value)}
className="w-full px-4 py-3 bg-gray-50 border border-gray-200 rounded-xl outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-200 dark:bg-gray-800 dark:border-gray-700 transition-all cursor-pointer text-gray-700 dark:text-gray-200"
>
<option value="OTHER">Tài liệu khác (Other)</option>
<option value="ID_CARD">Thẻ nhận dạng nhà nghiên cứu (ID Card)</option>
<option value="EDUCATION">Bằng cấp giáo dục (Education)</option>
<option value="EXPERT">Chứng nhận chuyên gia (Expert)</option>
</select>
<p className="mt-2 text-xs text-gray-500 dark:text-gray-400">
* Vui lòng chọn loại tài liệu tương ng với các tệp bạn đính kèm bên dưới.
</p>
<p className="mt-2 text-xs text-red-400 dark:text-gray-400">
* Nếu bạn đính kèm nhiều loại tài liệu, vui lòng chọn mục " Tài liệu khác (Other) ".
</p>
</div>
<div className="p-5 bg-white border border-gray-200 rounded-2xl dark:border-gray-800 dark:bg-gray-900">
<div className="flex items-center justify-between">
<h3 className="text-lg font-semibold text-gray-800 dark:text-white">
@@ -182,7 +215,7 @@ const handleSubmit = async (e: React.FormEvent) => {
</div>
{pendingFiles.length > 0 && (
<div className="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-5 gap-4 pt-4 border-t border-gray-100 dark:border-gray-800 mt-4">
<div className="grid grid-cols-2 gap-4 pt-4 mt-4 border-t border-gray-100 sm:grid-cols-4 md:grid-cols-5 dark:border-gray-800">
{pendingFiles.map((item) => {
const docStyle = getDocumentStyle(item.extension);
@@ -190,14 +223,14 @@ const handleSubmit = async (e: React.FormEvent) => {
<div
key={item.id}
onClick={() => handleItemClick(item)}
className="group relative aspect-square cursor-pointer overflow-hidden rounded-xl border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-800 hover:border-blue-400 transition-colors"
className="relative overflow-hidden transition-colors border border-gray-200 cursor-pointer group aspect-square rounded-xl dark:border-gray-700 bg-gray-50 dark:bg-gray-800 hover:border-blue-400"
>
{item.type === "image" ? (
<Image
src={item.previewUrl}
alt={item.name}
fill
className="object-cover group-hover:scale-110 transition-transform"
className="object-cover transition-transform group-hover:scale-110"
/>
) : (
<div
@@ -210,7 +243,7 @@ const handleSubmit = async (e: React.FormEvent) => {
>
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8l-6-6zm-1 1.5L18.5 9H13V3.5zM6 20V4h5v7h7v9H6z" />
</svg>
<span className="text-xs font-medium text-center text-gray-700 dark:text-gray-300 break-all line-clamp-2">
<span className="text-xs font-medium text-center text-gray-700 break-all line-clamp-2 dark:text-gray-300">
{item.name}
</span>
<span className="text-[10px] text-gray-500 mt-1 uppercase font-bold">
@@ -221,7 +254,7 @@ const handleSubmit = async (e: React.FormEvent) => {
<button
onClick={(e) => removePendingFile(item.id, e)}
className="absolute top-1 right-1 bg-red-500 text-white rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity z-10 hover:bg-red-600"
className="absolute z-10 p-1 text-white transition-opacity bg-red-500 rounded-full opacity-0 top-1 right-1 group-hover:opacity-100 hover:bg-red-600"
>
<svg
className="w-3 h-3"
@@ -239,7 +272,7 @@ const handleSubmit = async (e: React.FormEvent) => {
</button>
{item.type === "image" && (
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center pointer-events-none">
<div className="absolute inset-0 flex items-center justify-center transition-opacity opacity-0 pointer-events-none bg-black/40 group-hover:opacity-100">
<svg
className="w-6 h-6 text-white"
fill="none"
@@ -266,7 +299,7 @@ const handleSubmit = async (e: React.FormEvent) => {
onClick={handleSubmit}
disabled={isSubmitting || isPreparingFiles}
className={`w-full py-4 text-white font-bold rounded-2xl shadow-lg transition-all transform flex justify-center items-center gap-2
${isSubmitting ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700 active:scale-[0.98]"}`}
${isSubmitting || isPreparingFiles ? "bg-gray-400 cursor-not-allowed" : "bg-blue-600 hover:bg-blue-700 active:scale-[0.98]"}`}
>
{isSubmitting ? "Đang tải dữ liệu lên..." : "Gửi yêu cầu nâng cấp"}
</button>
@@ -287,4 +320,4 @@ const handleSubmit = async (e: React.FormEvent) => {
/>
</div>
);
}
}