fix: restaurar proyecto completo y actualizar dist final

This commit is contained in:
2026-08-13 11:15:00 -04:00
parent 7e41b2f26a
commit f4f6ee23df
102 changed files with 20834 additions and 0 deletions
+219
View File
@@ -0,0 +1,219 @@
import React, {
createContext,
useContext,
useEffect,
useMemo,
useState,
useCallback,
type ReactNode,
} from "react";
import type { User as SupabaseUser } from "@supabase/supabase-js";
import { supabase } from "@/lib/supabase";
import { getActiveTableroCdcAccess, type TableroCdcUserAccess } from "@/lib/accessControl";
function getRedirectTo(): string {
if (typeof window === "undefined") return "/tablero-cdc/";
return new URL(import.meta.env.BASE_URL || "/", window.location.origin).toString();
}
export interface AuthUser {
id: string;
uid: string;
email: string | null;
displayName: string | null;
photoURL: string | null;
raw: SupabaseUser;
}
function normalizeSupabaseUser(user: SupabaseUser): AuthUser {
const metadata = user.user_metadata || {};
const displayName =
metadata.full_name ||
metadata.name ||
metadata.display_name ||
user.email?.split("@")[0] ||
null;
const photoURL = metadata.avatar_url || metadata.picture || null;
return {
id: user.id,
uid: user.id,
email: user.email ?? null,
displayName,
photoURL,
raw: user,
};
}
interface AuthContextValue {
user: AuthUser | null;
loading: boolean;
error: string | null;
isGerardo: boolean;
canDeleteProjects: boolean;
canManageInternalPricing: boolean;
canControlPricingSummary: boolean;
loginWithGoogle: () => Promise<void>;
logout: () => Promise<void>;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<AuthUser | null>(null);
const [access, setAccess] = useState<TableroCdcUserAccess | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const applyUser = useCallback(async (supabaseUser: SupabaseUser | null) => {
if (!supabaseUser) {
setUser(null);
setAccess(null);
return;
}
let nextAccess: TableroCdcUserAccess | null = null;
try {
nextAccess = await getActiveTableroCdcAccess(supabaseUser.email);
} catch (accessError) {
console.error("Error al validar acceso al Tablero CDC:", accessError);
await supabase.auth.signOut();
setUser(null);
setAccess(null);
setError(
"No se pudo validar tu acceso al Tablero CDC. Verifica que el SQL de accesos esté ejecutado o contacta a IT.",
);
return;
}
if (!nextAccess) {
await supabase.auth.signOut();
setUser(null);
setAccess(null);
setError(
"Tu correo no está autorizado para acceder al Tablero CDC. Solicita acceso a IT o al equipo CDC.",
);
return;
}
setUser(normalizeSupabaseUser(supabaseUser));
setAccess(nextAccess);
setError(null);
}, []);
useEffect(() => {
let mounted = true;
supabase.auth
.getSession()
.then(async ({ data, error: sessionError }) => {
if (!mounted) return;
if (sessionError) {
console.error("Error al recuperar sesión de Supabase:", sessionError);
setUser(null);
setError("No se pudo recuperar la sesión. Intenta iniciar sesión de nuevo.");
return;
}
await applyUser(data.session?.user ?? null);
})
.catch((err: unknown) => {
if (!mounted) return;
console.error("Error inesperado al recuperar sesión:", err);
setUser(null);
setError("No se pudo recuperar la sesión. Intenta iniciar sesión de nuevo.");
})
.finally(() => {
if (mounted) setLoading(false);
});
const { data: listener } = supabase.auth.onAuthStateChange((_event, session) => {
setLoading(true);
void applyUser(session?.user ?? null).finally(() => {
if (mounted) setLoading(false);
});
});
return () => {
mounted = false;
listener.subscription.unsubscribe();
};
}, [applyUser]);
const loginWithGoogle = useCallback(async () => {
setError(null);
const { error: loginError } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: getRedirectTo(),
queryParams: {
prompt: "select_account",
},
},
});
if (loginError) {
console.error("Error al iniciar sesión con Google:", loginError);
setError("No se pudo iniciar sesión. Intenta de nuevo.");
}
}, []);
const logout = useCallback(async () => {
const { error: logoutError } = await supabase.auth.signOut();
if (logoutError) {
console.error("Error al cerrar sesión:", logoutError);
setError("No se pudo cerrar sesión. Intenta de nuevo.");
return;
}
setUser(null);
setAccess(null);
setError(null);
}, []);
const isGerardo = useMemo(() => access?.role === "director_creativo", [access?.role]);
const canManageInternalPricing = useMemo(
() => access?.canManageInternalPricing === true,
[access?.canManageInternalPricing],
);
const canDeleteProjects = useMemo(
() => access?.canDeleteProjects === true,
[access?.canDeleteProjects],
);
const canControlPricingSummary = useMemo(
() => access?.canControlPricingSummary === true,
[access?.canControlPricingSummary],
);
return (
<AuthContext.Provider
value={{
user,
loading,
error,
isGerardo,
canDeleteProjects,
canManageInternalPricing,
canControlPricingSummary,
loginWithGoogle,
logout,
}}
>
{children}
</AuthContext.Provider>
);
}
export function useAuth(): AuthContextValue {
const ctx = useContext(AuthContext);
if (!ctx) {
throw new Error("useAuth debe usarse dentro de <AuthProvider>");
}
return ctx;
}