import { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Plus, Save, Trash2, RotateCcw, ArrowLeft } from 'lucide-react';
import { useUi } from '../../context/ui-context.jsx';
import { useAuth } from '../../context/auth-context.jsx';
import { useAsync, useAction } from '../../hooks/useAsync.js';
import { useDebouncedValue } from '../../hooks/useDebouncedValue.js';
import { listProjects, createProject, deleteProject, restoreProject, permanentlyDeleteProject } from '../../services/projects.js';
import { listUsers } from '../../services/users.js';
import { search as searchApi } from '../../services/search.js';
import { listCatalog } from '../../services/catalogs.js';
import { listDepartments } from '../../services/departments.js';
import { ListView } from '../../components/ListView.jsx';
import { SearchInput } from '../../components/SearchInput.jsx';
import { SearchResultsDropdown } from '../../components/SearchResultsDropdown.jsx';
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';
import { DetailPopup } from '../../components/DetailPopup.jsx';
import { DraftNotice } from '../../components/DraftNotice.jsx';
import { DraftMark, useGuardedClose } from '../../components/NavigationGuard.jsx';
import { useDraftForm, useUserDrafts, hasDraftFor } from '../../hooks/useDraftForm.js';
import { SharedWithIcon } from '../../components/SharedWithIcon.jsx';
const CAN_DELETE_ROLES = ['superadmin', 'admin', 'it_manager', 'project_lead'];
const PROGRESS_FILTER_KEY = 'plc_projects_progress_filter';
const LEGACY_HIDE_COMPLETED_KEY = 'plc_projects_hide_completed';
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' };
const EMPTY_FORM = {
name: '', description: '', objective: '', projectTypeId: '', priorityId: '', statusId: '',
requestingDepartmentId: '', responsibleDepartmentId: '', startDate: '', estimatedEndDate: '',
riskLevel: 'low', notes: '',
};
export default function ProyectosPage() {
const { t, pushToast, openDialog } = useUi();
const { user: currentUser } = useAuth();
const navigate = useNavigate();
const runAction = useAction();
const [showTrash, setShowTrash] = useState(false);
const isSuperadmin = currentUser?.roleCode === 'superadmin';
const canDelete = CAN_DELETE_ROLES.includes(currentUser?.roleCode);
const [filterStatusId, setFilterStatusId] = useState('');
const [filterPriorityId, setFilterPriorityId] = useState('');
const [filterDueSoon, setFilterDueSoon] = useState(false);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
// '' = todos, 'hide' = ocultar 100%, 'only' = solo 100%. Persistido en el navegador (sobrevive
// al cierre de sesion). Respeta el checkbox anterior "Ocultar 100%" (clave vieja) si existia.
const [progressFilter, setProgressFilter] = useState(() => {
try {
const saved = localStorage.getItem(PROGRESS_FILTER_KEY);
if (saved !== null) return saved;
return localStorage.getItem(LEGACY_HIDE_COMPLETED_KEY) === '1' ? 'hide' : '';
} catch { return ''; }
});
function changeProgressFilter(value) {
setProgressFilter(value);
try { localStorage.setItem(PROGRESS_FILTER_KEY, value); } catch { /* sin storage: solo en memoria */ }
}
const [filterOwnerUserId, setFilterOwnerUserId] = useState('');
// Prueba exploratoria: filtro "ver todos / por usuario" solo para roles que ya ven todos los
// proyectos hoy (superadmin/admin/it_manager) -- backend ya soporta ownerUserId, sin cambios.
const canFilterByUser = ['superadmin', 'admin', 'it_manager'].includes(currentUser?.roleCode);
const [search, setSearch] = useState('');
const debouncedSearch = useDebouncedValue(search, 300);
const { data: projectPage, reload } = useAsync(
() => listProjects(showTrash ? { trash: true, page, pageSize, q: debouncedSearch } : {
statusId: filterStatusId, priorityId: filterPriorityId, ownerUserId: filterOwnerUserId || undefined,
dueSoon: filterDueSoon, progress: progressFilter, q: debouncedSearch, page, pageSize,
}),
[showTrash, filterStatusId, filterPriorityId, filterOwnerUserId, filterDueSoon, progressFilter, debouncedSearch, page, pageSize]
);
const projects = projectPage?.items;
const { data: allUsers } = useAsync(listUsers, []);
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, []);
useEffect(() => { setPage(1); }, [showTrash, filterStatusId, filterPriorityId, filterOwnerUserId, filterDueSoon, progressFilter, search, pageSize]);
const [creating, setCreating] = useState(false);
// "Nuevo proyecto" con borrador: si se sale a medio llenar, al volver se reabre el popup.
const draft = useDraftForm({ formId: 'project-new', entityId: null, initial: EMPTY_FORM, label: t('project_new'), enabled: creating });
const form = draft.values;
const setForm = draft.setValues;
const guardedClose = useGuardedClose();
const pendingNew = hasDraftFor(useUserDrafts(), 'project-new', null);
useEffect(() => { if (pendingNew) setCreating(true); }, []); // eslint-disable-line react-hooks/exhaustive-deps
// Busca tambien en actividades y tareas de CUALQUIER proyecto (no solo el nombre del proyecto,
// que ya filtra la tabla mas abajo via getSearchHaystack) — resultados con su ruta completa
// "Proyecto - Actividad - Tarea" en un panel flotante bajo el buscador.
const searchQuery = debouncedSearch.trim();
const { data: searchResults } = useAsync(
() => (searchQuery.length >= 2 ? searchApi(searchQuery) : Promise.resolve(null)),
[searchQuery]
);
const searchItems = searchResults ? [
...searchResults.projects.map((p) => ({
key: `p-${p.id}`,
breadcrumb: [`${p.code} — ${p.name}`],
onClick: () => { setSearch(''); navigate(`/proyectos/${p.id}`); },
})),
...searchResults.activities.map((a) => ({
key: `a-${a.id}`,
breadcrumb: [`${a.project_code} — ${a.project_name}`, `${a.code} — ${a.name}`],
onClick: () => { setSearch(''); navigate(`/proyectos/${a.project_id}?tab=activities&activity=${a.id}`); },
})),
...searchResults.tasks.map((tk) => ({
key: `t-${tk.id}`,
breadcrumb: [`${tk.project_code} — ${tk.project_name}`, `${tk.activity_code} — ${tk.activity_name}`, tk.name],
onClick: () => { setSearch(''); navigate(`/proyectos/${tk.project_id}?tab=activities&activity=${tk.activity_id}&activityTab=tasks`); },
})),
] : null;
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 }));
async function save() {
if (!form.name.trim() || !form.priorityId || !form.statusId) return;
await runAction(async () => {
const project = await createProject({
...form,
projectTypeId: form.projectTypeId || null,
requestingDepartmentId: form.requestingDepartmentId || null,
responsibleDepartmentId: form.responsibleDepartmentId || null,
startDate: form.startDate || null,
estimatedEndDate: form.estimatedEndDate || null,
});
draft.clear();
setCreating(false);
pushToast({ tone: 'success', message: t('common_saved') });
navigate(`/proyectos/${project.id}`);
});
}
function confirmDelete(project) {
openDialog({
tone: 'warning',
title: t('common_confirm_delete_title'),
message: t('project_delete_confirm_msg'),
actions: [
{ label: t('common_cancel'), variant: 'standard' },
{
label: t('common_delete'), variant: 'danger',
onClick: () => runAction(async () => {
await deleteProject(project.id);
await reload();
pushToast({ tone: 'success', message: t('common_saved') });
}),
},
],
});
}
function confirmPurge(project) {
openDialog({
tone: 'error',
title: t('project_delete_permanent'),
message: t('project_delete_permanent_confirm_msg'),
actions: [
{ label: t('common_cancel'), variant: 'standard' },
{
label: t('project_delete_permanent'), variant: 'danger',
onClick: () => runAction(async () => {
await permanentlyDeleteProject(project.id);
await reload();
pushToast({ tone: 'success', message: t('common_saved') });
}),
},
],
});
}
function restore(project) {
runAction(async () => {
await restoreProject(project.id);
await reload();
pushToast({ tone: 'success', message: t('common_saved') });
});
}
const columns = [
{ key: 'code', label: t('project_code') },
{ key: 'name', label: t('common_name') },
{ key: 'status_name', label: t('common_status'), render: (r) =>