feat: actualizar carpeta dist probada en XAMPP y modulos de edicion de tiempo y persistencia UI

This commit is contained in:
2026-08-16 19:08:48 -04:00
parent b79a2fa6ad
commit 7e5c955044
20 changed files with 1714 additions and 607 deletions
+127 -14
View File
@@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useEffect, useRef, useState } from "react";
import {
ChevronDown,
ChevronLeft,
@@ -69,6 +69,73 @@ const EMPTY_ADVANCED_FILTERS: AdvancedFilters = {
cm: "",
};
const DEFAULT_PAGE_BY_FILTER: Record<BoardView, number> = {
nuevos: 1,
todos: 1,
abiertos: 1,
cerrados: 1,
no_aprobados: 1,
};
const BOARD_UI_STATE_KEY = "tablero-cdc:ui-state:v1";
type PersistedBoardUiState = {
query: string;
filter: BoardView;
advancedFilters: AdvancedFilters;
pageByFilter: Record<BoardView, number>;
tariffManagerOpen: boolean;
listManagerOpen: boolean;
scrollY: number;
};
function loadBoardUiState(): PersistedBoardUiState {
const fallback: PersistedBoardUiState = {
query: "",
filter: "todos",
advancedFilters: EMPTY_ADVANCED_FILTERS,
pageByFilter: DEFAULT_PAGE_BY_FILTER,
tariffManagerOpen: false,
listManagerOpen: false,
scrollY: 0,
};
if (typeof window === "undefined") return fallback;
try {
const raw = window.localStorage.getItem(BOARD_UI_STATE_KEY);
if (!raw) return fallback;
const parsed = JSON.parse(raw) as Partial<PersistedBoardUiState>;
const validFilters: BoardView[] = ["nuevos", "todos", "abiertos", "cerrados", "no_aprobados"];
const nextFilter = validFilters.includes(parsed.filter as BoardView)
? (parsed.filter as BoardView)
: fallback.filter;
return {
query: typeof parsed.query === "string" ? parsed.query : fallback.query,
filter: nextFilter,
advancedFilters: {
country: typeof parsed.advancedFilters?.country === "string" ? parsed.advancedFilters.country : "",
brand: typeof parsed.advancedFilters?.brand === "string" ? parsed.advancedFilters.brand : "",
client: typeof parsed.advancedFilters?.client === "string" ? parsed.advancedFilters.client : "",
cm: typeof parsed.advancedFilters?.cm === "string" ? parsed.advancedFilters.cm : "",
},
pageByFilter: Object.fromEntries(
Object.entries({ ...DEFAULT_PAGE_BY_FILTER, ...(parsed.pageByFilter || {}) }).map(([key, value]) => [
key,
Math.max(1, Math.floor(Number(value) || 1)),
]),
) as Record<BoardView, number>,
tariffManagerOpen: parsed.tariffManagerOpen === true,
listManagerOpen: parsed.listManagerOpen === true,
scrollY: Number.isFinite(Number(parsed.scrollY)) ? Math.max(0, Number(parsed.scrollY)) : 0,
};
} catch (error) {
console.warn("No se pudo restaurar la vista anterior del Tablero CDC:", error);
return fallback;
}
}
function initials(name: string | null | undefined, email: string | null | undefined): string {
if (name) {
const parts = name.trim().split(/\s+/);
@@ -80,21 +147,18 @@ function initials(name: string | null | undefined, email: string | null | undefi
}
export default function BoardPage() {
const [initialUiState] = useState(loadBoardUiState);
const hasMountedFilterReset = useRef(false);
const hasRestoredScroll = useRef(initialUiState.scrollY <= 0);
const [open, setOpen] = useState(false);
const [editing, setEditing] = useState<Project | null>(null);
const [query, setQuery] = useState("");
const [filter, setFilter] = useState<BoardView>("todos");
const [advancedFilters, setAdvancedFilters] = useState<AdvancedFilters>(EMPTY_ADVANCED_FILTERS);
const [pageByFilter, setPageByFilter] = useState<Record<BoardView, number>>({
nuevos: 1,
todos: 1,
abiertos: 1,
cerrados: 1,
no_aprobados: 1,
});
const [query, setQuery] = useState(initialUiState.query);
const [filter, setFilter] = useState<BoardView>(initialUiState.filter);
const [advancedFilters, setAdvancedFilters] = useState<AdvancedFilters>(initialUiState.advancedFilters);
const [pageByFilter, setPageByFilter] = useState<Record<BoardView, number>>(initialUiState.pageByFilter);
const [deletingProjectId, setDeletingProjectId] = useState<string | null>(null);
const [tariffManagerOpen, setTariffManagerOpen] = useState(false);
const [listManagerOpen, setListManagerOpen] = useState(false);
const [tariffManagerOpen, setTariffManagerOpen] = useState(initialUiState.tariffManagerOpen);
const [listManagerOpen, setListManagerOpen] = useState(initialUiState.listManagerOpen);
const [timeProject, setTimeProject] = useState<Project | null>(null);
const [reviewingBriefId, setReviewingBriefId] = useState<string | null>(null);
@@ -185,16 +249,65 @@ export default function BoardPage() {
const visibleEnd = Math.min(pageStart + PROJECTS_PER_PAGE, currentTotalItems);
useEffect(() => {
if (!hasMountedFilterReset.current) {
hasMountedFilterReset.current = true;
return;
}
setPageByFilter((prev) => ({ ...prev, [filter]: 1 }));
}, [
query,
filter,
advancedFilters.country,
advancedFilters.brand,
advancedFilters.client,
advancedFilters.cm,
]);
useEffect(() => {
const saveUiState = () => {
try {
const nextState: PersistedBoardUiState = {
query,
filter,
advancedFilters,
pageByFilter,
tariffManagerOpen,
listManagerOpen,
scrollY: hasRestoredScroll.current ? window.scrollY : initialUiState.scrollY,
};
window.localStorage.setItem(BOARD_UI_STATE_KEY, JSON.stringify(nextState));
} catch (error) {
console.warn("No se pudo guardar la vista del Tablero CDC:", error);
}
};
saveUiState();
const handlePageHide = () => saveUiState();
const handleVisibilityChange = () => {
if (document.visibilityState === "hidden") saveUiState();
};
window.addEventListener("pagehide", handlePageHide);
document.addEventListener("visibilitychange", handleVisibilityChange);
return () => {
window.removeEventListener("pagehide", handlePageHide);
document.removeEventListener("visibilitychange", handleVisibilityChange);
};
}, [query, filter, advancedFilters, pageByFilter, tariffManagerOpen, listManagerOpen, initialUiState.scrollY]);
useEffect(() => {
const savedScrollY = initialUiState.scrollY;
const currentViewLoading = isBriefReviewView ? briefInbox.loading : loading;
if (savedScrollY <= 0 || currentViewLoading || hasRestoredScroll.current) return;
const timer = window.setTimeout(() => {
window.scrollTo({ top: savedScrollY, behavior: "auto" });
hasRestoredScroll.current = true;
}, 120);
return () => window.clearTimeout(timer);
}, [initialUiState.scrollY, isBriefReviewView, briefInbox.loading, loading]);
useEffect(() => {
if (currentPage > totalPages) {
setPageByFilter((prev) => ({ ...prev, [filter]: totalPages }));