import { useEffect, useState } from 'react'; import { Pencil, Save, X, UserPlus, Trash2 } from 'lucide-react'; import { useUi } from '../../context/ui-context.jsx'; import { useAsync, useAction } from '../../hooks/useAsync.js'; import { updateProject, addProjectMember, removeProjectMember } from '../../services/projects.js'; import { listCatalog } from '../../services/catalogs.js'; import { listDepartments } from '../../services/departments.js'; import { listUsers } from '../../services/users.js'; import { Button } from '../../components/Button.jsx'; import { Field, FieldRow } from '../../components/Field.jsx'; import { ComboBox } from '../../components/ComboBox.jsx'; import { StatusBadge, ProgressBar } from '../../components/StatusBadge.jsx'; import { Badge } from '../../components/Badge.jsx'; function flatten(nodes, acc = []) { for (const n of nodes) { acc.push(n); if (n.children?.length) flatten(n.children, acc); } return acc; } const RISK_COLOR = { low: 'success', medium: 'warning', high: 'danger' }; function toForm(p) { return { name: p.name, description: p.description || '', objective: p.objective || '', projectTypeId: p.project_type_id || '', priorityId: p.priority_id, statusId: p.status_id, ownerUserId: p.owner_user_id, requestingDepartmentId: p.requesting_department_id || '', responsibleDepartmentId: p.responsible_department_id || '', startDate: p.start_date || '', estimatedEndDate: p.estimated_end_date || '', actualEndDate: p.actual_end_date || '', riskLevel: p.risk_level, notes: p.notes || '', }; } export default function ProjectInfoTab({ project, onChanged }) { const { t, pushToast, openDialog } = useUi(); const runAction = useAction(); const { data: priorities } = useAsync(() => listCatalog('priorities'), []); const { data: statuses } = useAsync(() => listCatalog('project-statuses'), []); const { data: types } = useAsync(() => listCatalog('project-types'), []); const { data: deptTree } = useAsync(listDepartments, []); const { data: users } = useAsync(listUsers, []); const [editing, setEditing] = useState(false); const [form, setForm] = useState(() => toForm(project)); const [addingMember, setAddingMember] = useState(''); useEffect(() => { setForm(toForm(project)); }, [project]); const deptOptions = deptTree ? flatten(deptTree).map((d) => ({ value: d.id, label: d.name })) : []; const priorityOptions = (priorities || []).map((p) => ({ value: p.id, label: p.name })); const statusOptions = (statuses || []).map((s) => ({ value: s.id, label: s.name })); const typeOptions = (types || []).map((tp) => ({ value: tp.id, label: tp.name })); const userOptions = (users || []).filter((u) => !project.members.some((m) => m.user_id === u.id)).map((u) => ({ value: u.id, label: u.name })); async function save() { const changed = JSON.stringify(form) !== JSON.stringify(toForm(project)); if (!changed) { setEditing(false); return; } await runAction(async () => { await updateProject(project.id, { ...form, projectTypeId: form.projectTypeId || null, requestingDepartmentId: form.requestingDepartmentId || null, responsibleDepartmentId: form.responsibleDepartmentId || null, startDate: form.startDate || null, estimatedEndDate: form.estimatedEndDate || null, actualEndDate: form.actualEndDate || null, }); setEditing(false); pushToast({ tone: 'success', message: t('common_saved') }); onChanged(); }); } async function addMember() { if (!addingMember) return; await runAction(async () => { await addProjectMember(project.id, Number(addingMember)); setAddingMember(''); onChanged(); }); } function confirmRemoveMember(member) { openDialog({ tone: 'warning', title: t('common_confirm_delete_title'), message: t('common_confirm_delete_msg'), actions: [ { label: t('common_cancel'), variant: 'standard' }, { label: t('common_delete'), variant: 'danger', onClick: () => runAction(async () => { await removeProjectMember(project.id, member.user_id); onChanged(); }) }, ], }); } return (