"use client"; import { useParams, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import Image from "next/image"; import { toast } from "sonner"; import { Project } from "@/interface/project"; import Swal from "sweetalert2"; import PageBreadcrumb from "@/components/common/PageBreadCrumb"; import { apiAddProjectMember, apiChangeProjectOwner, apiDeleteProject, apiGetProjectDetail, apiRemoveProjectMember, apiUpdateProject, apiUpdateProjectMemberRole } from "@/service/projectService"; import Loading from "@/app/loading"; type TabType = "overview" | "members" | "settings"; export default function ProjectDetailsPage() { const params = useParams(); const router = useRouter(); const id = params.id as string; const [project, setProject] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState("overview"); const [editForm, setEditForm] = useState({ title: "", description: "", status: "PRIVATE" as any, }); const [newOwnerId, setNewOwnerId] = useState(""); const [newMember, setNewMember] = useState({ user_id: "", role: "EDITOR" as any, }); const fetchProject = async () => { setLoading(true); try { const res = await apiGetProjectDetail(id); if (res?.status && res.data) { setProject(res.data); setEditForm({ title: res.data.title, description: res.data.description, status: res.data.project_status, }); } } catch (error) { toast.error("Lỗi khi tải dữ liệu dự án"); } finally { setLoading(false); } }; useEffect(() => { if (id) fetchProject(); }, [id]); const handleUpdateInfo = async (e: React.FormEvent) => { e.preventDefault(); try { await apiUpdateProject(id, editForm); toast.success("Cập nhật thông tin thành công!"); fetchProject(); } catch (error) { toast.error("Cập nhật thất bại"); } }; const handleTransferOwnership = async (e: React.FormEvent) => { e.preventDefault(); const memberName = project?.members?.find((m) => m.user_id === newOwnerId)?.display_name || "thành viên này"; const result = await Swal.fire({ title: "Chuyển quyền sở hữu?", html: `Bạn có chắc chắn muốn chuyển dự án này cho ${memberName}?
Hành động này không thể hoàn tác và bạn sẽ không còn là chủ sở hữu nữa.`, icon: "error", showCancelButton: true, confirmButtonColor: "#238636", cancelButtonColor: "#30363d", confirmButtonText: "Tôi hiểu, chuyển quyền sở hữu", cancelButtonText: "Hủy bỏ", color: "#333", customClass: { popup: "border border-[#30363d] rounded-xl", }, }); if (result.isConfirmed) { try { const res = await apiChangeProjectOwner(id, { new_owner_id: newOwnerId, }); if (res?.status) { toast.success("Đã chuyển quyền sở hữu thành công!"); setNewOwnerId(""); fetchProject(); } else { toast.error(res?.message || "Chuyển quyền thất bại"); } } catch (error) { toast.error("Lỗi hệ thống khi chuyển quyền"); } } }; const handleAddMember = async (e: React.FormEvent) => { e.preventDefault(); if (!newMember.user_id) return toast.error("Vui lòng nhập User ID"); try { await apiAddProjectMember(id, newMember); toast.success("Thêm thành viên thành công"); setNewMember({ user_id: "", role: "EDITOR" }); fetchProject(); } catch (error) { toast.error("Lỗi thêm thành viên"); } }; const handleUpdateRole = async (userId: string, newRole: string) => { try { const res = await apiUpdateProjectMemberRole(id, userId, { role: newRole as any, }); if (res?.status) { toast.success("Cập nhật quyền thành công"); fetchProject(); } else { toast.error(res?.message || "Cập nhật quyền thất bại"); } } catch (error: any) { const errorMessage = error.response?.data?.message || "Cập nhật quyền thất bại"; toast.error(errorMessage); } }; const handleRemoveMember = async (userId: string) => { // 1. Hiển thị hộp thoại xác nhận bằng SweetAlert2 const result = await Swal.fire({ title: "Xác nhận xóa?", text: "Bạn có chắc chắn muốn xóa thành viên này khỏi dự án?", icon: "warning", showCancelButton: true, confirmButtonColor: "#3085d6", cancelButtonColor: "#d33", confirmButtonText: "Đồng ý", cancelButtonText: "Hủy", }); if (!result.isConfirmed) return; try { const res = await apiRemoveProjectMember(id, userId); if (res?.status) { toast.success("Đã xóa thành viên"); } else { toast.error(res?.message || "Xóa thành viên thất bại"); } } catch (error: any) { const errorMessage = error.response?.data?.message || "Xóa thành viên thất bại"; toast.error(errorMessage); console.error("Remove Member Error:", error); } finally { fetchProject(); } }; const handleDeleteProject = async () => { const result = await Swal.fire({ title: "Xác nhận xóa dự án?", text: "Hành động này sẽ xóa vĩnh viễn dự án. Bạn không thể hoàn tác sau khi xác nhận!", icon: "warning", showCancelButton: true, confirmButtonColor: "#d33", cancelButtonColor: "#30363d", confirmButtonText: "Tôi hiểu, xóa dự án này", cancelButtonText: "Hủy", color: "#333", customClass: { popup: "border border-[#30363d] rounded-xl", }, }); if (result.isConfirmed) { try { const res = await apiDeleteProject(id); if (res?.status) { toast.success("Đã xóa dự án thành công"); router.push("/user/projects"); } else { toast.error(res?.message || "Xóa dự án thất bại"); } } catch (error : any) { toast.error(error.response?.data?.message || "Xóa dự án thất bại"); console.error(error); } } }; if (loading) return ; if (!project) return (
Không tìm thấy dự án
); // console.log(project) return (
{project.user?.avatar_url ? (
avatar
) : (
{project.user?.display_name?.charAt(0)?.toUpperCase() || "U"}
)}
{project.user?.display_name} / {project.title} {project.project_status}
{[ { id: "overview", label: "Overview", icon: "M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z", }, { id: "members", label: `Members`, count: project.members?.length || 0, icon: "M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z", }, { id: "settings", label: "Settings", icon: "M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z", }, ].map((tab) => ( ))}
{activeTab === "overview" && (
About
{project.description || ( Không có mô tả cho dự án này. )}

Owner

{project.user?.avatar_url ? (
avatar
) : (
{project.user?.display_name ?.charAt(0) ?.toUpperCase() || "U"}
)}
{project.user?.display_name}
{project.user?.email || "No email"}
)} {activeTab === "members" && (

Manage access

setNewMember({ ...newMember, user_id: e.target.value }) } className="flex-1 px-3 py-2 text-sm rounded-md border border-gray-300 dark:border-[#30363d] bg-white dark:bg-[#0d1117] outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500 transition-shadow" />
{project.members && project.members.length > 0 ? ( project.members.map((member) => (
{member.display_name}
{member.display_name}
ID: {member.user_id}
)) ) : (
Chưa có thành viên nào.
)}
)} {activeTab === "settings" && (

General

setEditForm({ ...editForm, title: e.target.value }) } className="w-full max-w-md px-3 py-2 text-sm rounded-md border border-gray-300 dark:border-[#30363d] bg-white dark:bg-[#0d1117] outline-none focus:ring-2 focus:ring-blue-500/30 focus:border-blue-500 transition-shadow" />