fix: restaurar proyecto completo y actualizar dist final
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user