feat: implement file and repository management hooks and APIs
This commit is contained in:
@@ -1,63 +1,37 @@
|
|||||||
import { supabaseBrowser } from "../supabase/client";
|
import { supabaseBrowser } from "../supabase/client";
|
||||||
import { throwIfError, requireData, getUserIdOrThrow } from "./_helpers";
|
import { invokeEdge } from "../supabase/invokeEdge";
|
||||||
import type { Archivo, UUID } from "../types/domain";
|
import { throwIfError } from "./_helpers";
|
||||||
|
import type { AppFile } from "./openaiFiles.api";
|
||||||
|
|
||||||
const DEFAULT_BUCKET = "archivos";
|
const EDGE = {
|
||||||
|
signedUrl: "files_signed_url", // Edge: recibe archivoId o ruta_storage y devuelve URL
|
||||||
|
} as const;
|
||||||
|
|
||||||
export type UploadFileInput = {
|
export async function files_list(params?: {
|
||||||
file: File;
|
|
||||||
bucket?: string;
|
|
||||||
pathPrefix?: string; // ej: "planes/<planId>" o "materias/<id>"
|
|
||||||
temporal?: boolean;
|
temporal?: boolean;
|
||||||
notas?: string | null;
|
search?: string;
|
||||||
};
|
limit?: number;
|
||||||
|
}): Promise<AppFile[]> {
|
||||||
export async function files_upload(input: UploadFileInput): Promise<Archivo> {
|
|
||||||
const supabase = supabaseBrowser();
|
const supabase = supabaseBrowser();
|
||||||
const userId = await getUserIdOrThrow(supabase);
|
|
||||||
|
|
||||||
const bucket = input.bucket ?? DEFAULT_BUCKET;
|
let q = supabase
|
||||||
const safeName = input.file.name.replace(/[^\w.\-() ]+/g, "_");
|
|
||||||
const pathPrefix = (input.pathPrefix ?? `usuarios/${userId}`).replace(/\/+$/g, "");
|
|
||||||
|
|
||||||
const storagePath = `${pathPrefix}/${crypto.randomUUID()}-${safeName}`;
|
|
||||||
|
|
||||||
const { data: upData, error: upErr } = await supabase.storage
|
|
||||||
.from(bucket)
|
|
||||||
.upload(storagePath, input.file, { upsert: false });
|
|
||||||
|
|
||||||
throwIfError(upErr);
|
|
||||||
requireData(upData, "No se pudo subir archivo.");
|
|
||||||
|
|
||||||
const { data: row, error: insErr } = await supabase
|
|
||||||
.from("archivos")
|
.from("archivos")
|
||||||
.insert({
|
.select("id,openai_file_id,nombre,mime_type,bytes,ruta_storage,temporal,notas,subido_en")
|
||||||
ruta_storage: `${bucket}/${storagePath}`,
|
.order("subido_en", { ascending: false });
|
||||||
nombre: input.file.name,
|
|
||||||
mime_type: input.file.type || null,
|
|
||||||
bytes: input.file.size,
|
|
||||||
subido_por: userId as UUID,
|
|
||||||
temporal: Boolean(input.temporal),
|
|
||||||
notas: input.notas ?? null,
|
|
||||||
})
|
|
||||||
.select("id,ruta_storage,nombre,mime_type,bytes,subido_por,subido_en,temporal,openai_file_id,notas")
|
|
||||||
.single();
|
|
||||||
|
|
||||||
throwIfError(insErr);
|
if (typeof params?.temporal === "boolean") q = q.eq("temporal", params.temporal);
|
||||||
return requireData(row, "No se pudo registrar metadata del archivo.");
|
if (params?.search?.trim()) q = q.ilike("nombre", `%${params.search.trim()}%`);
|
||||||
}
|
if (params?.limit) q = q.limit(params.limit);
|
||||||
|
|
||||||
export async function files_signed_url(params: {
|
const { data, error } = await q;
|
||||||
ruta_storage: string; // "bucket/path/to/file"
|
|
||||||
expiresIn?: number; // segundos
|
|
||||||
}): Promise<string> {
|
|
||||||
const supabase = supabaseBrowser();
|
|
||||||
const expires = params.expiresIn ?? 60 * 10;
|
|
||||||
|
|
||||||
const [bucket, ...rest] = params.ruta_storage.split("/");
|
|
||||||
const path = rest.join("/");
|
|
||||||
|
|
||||||
const { data, error } = await supabase.storage.from(bucket).createSignedUrl(path, expires);
|
|
||||||
throwIfError(error);
|
throwIfError(error);
|
||||||
return requireData(data?.signedUrl, "No se pudo generar URL firmada.");
|
return (data ?? []) as AppFile[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Para preview/descarga desde espejo — SIN tocar storage directo en el cliente */
|
||||||
|
export async function files_get_signed_url(payload: {
|
||||||
|
archivoId: string; // id interno (tabla archivos)
|
||||||
|
expiresIn?: number; // segundos
|
||||||
|
}): Promise<{ signedUrl: string }> {
|
||||||
|
return invokeEdge<{ signedUrl: string }>(EDGE.signedUrl, payload);
|
||||||
}
|
}
|
||||||
|
|||||||
64
src/data/api/openaiFiles.api.ts
Normal file
64
src/data/api/openaiFiles.api.ts
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
import { invokeEdge } from "../supabase/invokeEdge";
|
||||||
|
import type { UUID } from "../types/domain";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Metadata “canónica” para UI (archivo OpenAI + espejo en Supabase)
|
||||||
|
* Se apoya en tu tabla `archivos`.
|
||||||
|
*/
|
||||||
|
export type AppFile = {
|
||||||
|
id: UUID; // id interno (tabla archivos)
|
||||||
|
openai_file_id: string; // id OpenAI
|
||||||
|
nombre: string;
|
||||||
|
mime_type: string | null;
|
||||||
|
bytes: number | null;
|
||||||
|
|
||||||
|
// espejo Supabase para preview/descarga
|
||||||
|
ruta_storage: string | null; // "bucket/path"
|
||||||
|
signed_url?: string | null;
|
||||||
|
|
||||||
|
// auditoría/evidencia
|
||||||
|
temporal: boolean;
|
||||||
|
notas?: string | null;
|
||||||
|
|
||||||
|
subido_en: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EDGE = {
|
||||||
|
upload: "openai_files_upload",
|
||||||
|
remove: "openai_files_delete",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sube archivo a OpenAI y (opcional) crea espejo en Storage
|
||||||
|
* - El frontend NO toca Storage.
|
||||||
|
*/
|
||||||
|
export async function openai_files_upload(payload: {
|
||||||
|
/**
|
||||||
|
* Si tu Edge soporta multipart: manda File/Blob directo.
|
||||||
|
* Si no, manda base64/bytes (según tu implementación).
|
||||||
|
*/
|
||||||
|
file: File;
|
||||||
|
|
||||||
|
/** “temporal” = evidencia usada para generar plan/materia */
|
||||||
|
temporal?: boolean;
|
||||||
|
|
||||||
|
/** contexto para auditoría */
|
||||||
|
contexto?: {
|
||||||
|
planId?: UUID;
|
||||||
|
asignaturaId?: UUID;
|
||||||
|
motivo?: "WIZARD_PLAN" | "WIZARD_MATERIA" | "ADHOC";
|
||||||
|
};
|
||||||
|
|
||||||
|
/** si quieres forzar espejo para preview siempre */
|
||||||
|
mirrorToSupabase?: boolean;
|
||||||
|
}): Promise<AppFile> {
|
||||||
|
return invokeEdge<AppFile>(EDGE.upload, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openai_files_delete(payload: {
|
||||||
|
openaiFileId: string;
|
||||||
|
/** si quieres borrar también espejo y registro */
|
||||||
|
hardDelete?: boolean;
|
||||||
|
}): Promise<{ ok: true }> {
|
||||||
|
return invokeEdge<{ ok: true }>(EDGE.remove, payload);
|
||||||
|
}
|
||||||
44
src/data/api/repositories.api.ts
Normal file
44
src/data/api/repositories.api.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { invokeEdge } from "../supabase/invokeEdge";
|
||||||
|
|
||||||
|
export type Repository = {
|
||||||
|
id: string; // id del vector store (OpenAI) o tu id interno
|
||||||
|
nombre: string;
|
||||||
|
creado_en?: string;
|
||||||
|
meta?: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
const EDGE = {
|
||||||
|
create: "repos_create",
|
||||||
|
remove: "repos_delete",
|
||||||
|
add: "repos_add_files",
|
||||||
|
detach: "repos_remove_files",
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
export async function repos_create(payload: {
|
||||||
|
nombre: string;
|
||||||
|
descripcion?: string;
|
||||||
|
/** si tu implementación crea también registro DB */
|
||||||
|
persist?: boolean;
|
||||||
|
}): Promise<Repository> {
|
||||||
|
return invokeEdge<Repository>(EDGE.create, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function repos_delete(payload: { repoId: string }): Promise<{ ok: true }> {
|
||||||
|
return invokeEdge<{ ok: true }>(EDGE.remove, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Agrega archivos (OpenAI file ids) a un repositorio */
|
||||||
|
export async function repos_add_files(payload: {
|
||||||
|
repoId: string;
|
||||||
|
openaiFileIds: string[];
|
||||||
|
}): Promise<{ ok: true }> {
|
||||||
|
return invokeEdge<{ ok: true }>(EDGE.add, payload);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Quita archivos (OpenAI file ids) del repositorio */
|
||||||
|
export async function repos_remove_files(payload: {
|
||||||
|
repoId: string;
|
||||||
|
openaiFileIds: string[];
|
||||||
|
}): Promise<{ ok: true }> {
|
||||||
|
return invokeEdge<{ ok: true }>(EDGE.detach, payload);
|
||||||
|
}
|
||||||
43
src/data/hooks/useFiles.ts
Normal file
43
src/data/hooks/useFiles.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { files_get_signed_url, files_list } from "../api/files.api";
|
||||||
|
import { openai_files_delete, openai_files_upload } from "../api/openaiFiles.api";
|
||||||
|
|
||||||
|
const qkFiles = {
|
||||||
|
list: (filters: any) => ["files", "list", filters] as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useFilesList(filters?: { temporal?: boolean; search?: string; limit?: number }) {
|
||||||
|
return useQuery({
|
||||||
|
queryKey: qkFiles.list(filters ?? {}),
|
||||||
|
queryFn: () => files_list(filters),
|
||||||
|
staleTime: 15_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useUploadOpenAIFile() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: openai_files_upload,
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["files"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDeleteOpenAIFile() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: openai_files_delete,
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["files"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFileSignedUrl() {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: files_get_signed_url,
|
||||||
|
});
|
||||||
|
}
|
||||||
46
src/data/hooks/useRepositories.ts
Normal file
46
src/data/hooks/useRepositories.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
|
import { repos_add_files, repos_create, repos_delete, repos_remove_files } from "../api/repositories.api";
|
||||||
|
|
||||||
|
export function useCreateRepository() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: repos_create,
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["repos"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useDeleteRepository() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: repos_delete,
|
||||||
|
onSuccess: () => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["repos"] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRepoAddFiles() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: repos_add_files,
|
||||||
|
onSuccess: (_ok, vars) => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["repos", vars.repoId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useRepoRemoveFiles() {
|
||||||
|
const qc = useQueryClient();
|
||||||
|
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: repos_remove_files,
|
||||||
|
onSuccess: (_ok, vars) => {
|
||||||
|
qc.invalidateQueries({ queryKey: ["repos", vars.repoId] });
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -12,11 +12,9 @@ import { Route as rootRouteImport } from './routes/__root'
|
|||||||
import { Route as LoginRouteImport } from './routes/login'
|
import { Route as LoginRouteImport } from './routes/login'
|
||||||
import { Route as DashboardRouteImport } from './routes/dashboard'
|
import { Route as DashboardRouteImport } from './routes/dashboard'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
import { Route as MateriasIndexRouteImport } from './routes/materias/index'
|
|
||||||
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
|
import { Route as DemoTanstackQueryRouteImport } from './routes/demo/tanstack-query'
|
||||||
import { Route as PlanesListaRouteRouteImport } from './routes/planes/_lista/route'
|
import { Route as PlanesListaRouteRouteImport } from './routes/planes/_lista/route'
|
||||||
import { Route as PlanesListaNuevoRouteImport } from './routes/planes/_lista/nuevo'
|
import { Route as PlanesListaNuevoRouteImport } from './routes/planes/_lista/nuevo'
|
||||||
import { Route as MateriasMateriaIdMateriaIdRouteImport } from './routes/materias/$materiaId/$materiaId'
|
|
||||||
import { Route as PlanesPlanIdAsignaturasRouteRouteImport } from './routes/planes/$planId/asignaturas/route'
|
import { Route as PlanesPlanIdAsignaturasRouteRouteImport } from './routes/planes/$planId/asignaturas/route'
|
||||||
import { Route as PlanesPlanIdDetalleRouteRouteImport } from './routes/planes/$planId/_detalle/route'
|
import { Route as PlanesPlanIdDetalleRouteRouteImport } from './routes/planes/$planId/_detalle/route'
|
||||||
import { Route as PlanesPlanIdAsignaturasIndexRouteImport } from './routes/planes/$planId/asignaturas/index'
|
import { Route as PlanesPlanIdAsignaturasIndexRouteImport } from './routes/planes/$planId/asignaturas/index'
|
||||||
@@ -46,11 +44,6 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
const MateriasIndexRoute = MateriasIndexRouteImport.update({
|
|
||||||
id: '/materias/',
|
|
||||||
path: '/materias/',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
|
const DemoTanstackQueryRoute = DemoTanstackQueryRouteImport.update({
|
||||||
id: '/demo/tanstack-query',
|
id: '/demo/tanstack-query',
|
||||||
path: '/demo/tanstack-query',
|
path: '/demo/tanstack-query',
|
||||||
@@ -66,12 +59,6 @@ const PlanesListaNuevoRoute = PlanesListaNuevoRouteImport.update({
|
|||||||
path: '/nuevo',
|
path: '/nuevo',
|
||||||
getParentRoute: () => PlanesListaRouteRoute,
|
getParentRoute: () => PlanesListaRouteRoute,
|
||||||
} as any)
|
} as any)
|
||||||
const MateriasMateriaIdMateriaIdRoute =
|
|
||||||
MateriasMateriaIdMateriaIdRouteImport.update({
|
|
||||||
id: '/materias/$materiaId/$materiaId',
|
|
||||||
path: '/materias/$materiaId/$materiaId',
|
|
||||||
getParentRoute: () => rootRouteImport,
|
|
||||||
} as any)
|
|
||||||
const PlanesPlanIdAsignaturasRouteRoute =
|
const PlanesPlanIdAsignaturasRouteRoute =
|
||||||
PlanesPlanIdAsignaturasRouteRouteImport.update({
|
PlanesPlanIdAsignaturasRouteRouteImport.update({
|
||||||
id: '/planes/$planId/asignaturas',
|
id: '/planes/$planId/asignaturas',
|
||||||
@@ -155,10 +142,8 @@ export interface FileRoutesByFullPath {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
'/planes': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/materias': typeof MateriasIndexRoute
|
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
||||||
'/materias/$materiaId/$materiaId': typeof MateriasMateriaIdMateriaIdRoute
|
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
'/planes/$planId/datos': typeof PlanesPlanIdDetalleDatosRoute
|
'/planes/$planId/datos': typeof PlanesPlanIdDetalleDatosRoute
|
||||||
@@ -177,9 +162,7 @@ export interface FileRoutesByTo {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes': typeof PlanesListaRouteRouteWithChildren
|
'/planes': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/materias': typeof MateriasIndexRoute
|
|
||||||
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes/$planId': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
'/materias/$materiaId/$materiaId': typeof MateriasMateriaIdMateriaIdRoute
|
|
||||||
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasIndexRoute
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasIndexRoute
|
||||||
@@ -199,10 +182,8 @@ export interface FileRoutesById {
|
|||||||
'/login': typeof LoginRoute
|
'/login': typeof LoginRoute
|
||||||
'/planes/_lista': typeof PlanesListaRouteRouteWithChildren
|
'/planes/_lista': typeof PlanesListaRouteRouteWithChildren
|
||||||
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
'/demo/tanstack-query': typeof DemoTanstackQueryRoute
|
||||||
'/materias/': typeof MateriasIndexRoute
|
|
||||||
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
'/planes/$planId/_detalle': typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
'/planes/$planId/asignaturas': typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
||||||
'/materias/$materiaId/$materiaId': typeof MateriasMateriaIdMateriaIdRoute
|
|
||||||
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
'/planes/_lista/nuevo': typeof PlanesListaNuevoRoute
|
||||||
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
'/planes/$planId/asignaturas/$asignaturaId': typeof PlanesPlanIdAsignaturasAsignaturaIdRouteRoute
|
||||||
'/planes/$planId/asignaturas/_lista': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
'/planes/$planId/asignaturas/_lista': typeof PlanesPlanIdAsignaturasListaRouteRouteWithChildren
|
||||||
@@ -224,10 +205,8 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
| '/planes'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/materias'
|
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes/$planId/asignaturas'
|
||||||
| '/materias/$materiaId/$materiaId'
|
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/datos'
|
| '/planes/$planId/datos'
|
||||||
@@ -246,9 +225,7 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes'
|
| '/planes'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/materias'
|
|
||||||
| '/planes/$planId'
|
| '/planes/$planId'
|
||||||
| '/materias/$materiaId/$materiaId'
|
|
||||||
| '/planes/nuevo'
|
| '/planes/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes/$planId/asignaturas'
|
||||||
@@ -267,10 +244,8 @@ export interface FileRouteTypes {
|
|||||||
| '/login'
|
| '/login'
|
||||||
| '/planes/_lista'
|
| '/planes/_lista'
|
||||||
| '/demo/tanstack-query'
|
| '/demo/tanstack-query'
|
||||||
| '/materias/'
|
|
||||||
| '/planes/$planId/_detalle'
|
| '/planes/$planId/_detalle'
|
||||||
| '/planes/$planId/asignaturas'
|
| '/planes/$planId/asignaturas'
|
||||||
| '/materias/$materiaId/$materiaId'
|
|
||||||
| '/planes/_lista/nuevo'
|
| '/planes/_lista/nuevo'
|
||||||
| '/planes/$planId/asignaturas/$asignaturaId'
|
| '/planes/$planId/asignaturas/$asignaturaId'
|
||||||
| '/planes/$planId/asignaturas/_lista'
|
| '/planes/$planId/asignaturas/_lista'
|
||||||
@@ -291,10 +266,8 @@ export interface RootRouteChildren {
|
|||||||
LoginRoute: typeof LoginRoute
|
LoginRoute: typeof LoginRoute
|
||||||
PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
|
PlanesListaRouteRoute: typeof PlanesListaRouteRouteWithChildren
|
||||||
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
DemoTanstackQueryRoute: typeof DemoTanstackQueryRoute
|
||||||
MateriasIndexRoute: typeof MateriasIndexRoute
|
|
||||||
PlanesPlanIdDetalleRouteRoute: typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
PlanesPlanIdDetalleRouteRoute: typeof PlanesPlanIdDetalleRouteRouteWithChildren
|
||||||
PlanesPlanIdAsignaturasRouteRoute: typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
PlanesPlanIdAsignaturasRouteRoute: typeof PlanesPlanIdAsignaturasRouteRouteWithChildren
|
||||||
MateriasMateriaIdMateriaIdRoute: typeof MateriasMateriaIdMateriaIdRoute
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -320,13 +293,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexRouteImport
|
preLoaderRoute: typeof IndexRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
'/materias/': {
|
|
||||||
id: '/materias/'
|
|
||||||
path: '/materias'
|
|
||||||
fullPath: '/materias'
|
|
||||||
preLoaderRoute: typeof MateriasIndexRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/demo/tanstack-query': {
|
'/demo/tanstack-query': {
|
||||||
id: '/demo/tanstack-query'
|
id: '/demo/tanstack-query'
|
||||||
path: '/demo/tanstack-query'
|
path: '/demo/tanstack-query'
|
||||||
@@ -348,13 +314,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof PlanesListaNuevoRouteImport
|
preLoaderRoute: typeof PlanesListaNuevoRouteImport
|
||||||
parentRoute: typeof PlanesListaRouteRoute
|
parentRoute: typeof PlanesListaRouteRoute
|
||||||
}
|
}
|
||||||
'/materias/$materiaId/$materiaId': {
|
|
||||||
id: '/materias/$materiaId/$materiaId'
|
|
||||||
path: '/materias/$materiaId/$materiaId'
|
|
||||||
fullPath: '/materias/$materiaId/$materiaId'
|
|
||||||
preLoaderRoute: typeof MateriasMateriaIdMateriaIdRouteImport
|
|
||||||
parentRoute: typeof rootRouteImport
|
|
||||||
}
|
|
||||||
'/planes/$planId/asignaturas': {
|
'/planes/$planId/asignaturas': {
|
||||||
id: '/planes/$planId/asignaturas'
|
id: '/planes/$planId/asignaturas'
|
||||||
path: '/planes/$planId/asignaturas'
|
path: '/planes/$planId/asignaturas'
|
||||||
@@ -527,11 +486,9 @@ const rootRouteChildren: RootRouteChildren = {
|
|||||||
LoginRoute: LoginRoute,
|
LoginRoute: LoginRoute,
|
||||||
PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
|
PlanesListaRouteRoute: PlanesListaRouteRouteWithChildren,
|
||||||
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
DemoTanstackQueryRoute: DemoTanstackQueryRoute,
|
||||||
MateriasIndexRoute: MateriasIndexRoute,
|
|
||||||
PlanesPlanIdDetalleRouteRoute: PlanesPlanIdDetalleRouteRouteWithChildren,
|
PlanesPlanIdDetalleRouteRoute: PlanesPlanIdDetalleRouteRouteWithChildren,
|
||||||
PlanesPlanIdAsignaturasRouteRoute:
|
PlanesPlanIdAsignaturasRouteRoute:
|
||||||
PlanesPlanIdAsignaturasRouteRouteWithChildren,
|
PlanesPlanIdAsignaturasRouteRouteWithChildren,
|
||||||
MateriasMateriaIdMateriaIdRoute: MateriasMateriaIdMateriaIdRoute,
|
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { usePlan } from '@/data';
|
||||||
import { createFileRoute } from '@tanstack/react-router'
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
export const Route = createFileRoute('/planes/$planId/_detalle/datos')({
|
export const Route = createFileRoute('/planes/$planId/_detalle/datos')({
|
||||||
@@ -5,6 +6,10 @@ export const Route = createFileRoute('/planes/$planId/_detalle/datos')({
|
|||||||
})
|
})
|
||||||
|
|
||||||
function DatosGenerales() {
|
function DatosGenerales() {
|
||||||
|
const {data, isFetching} = usePlan('0e0aea4d-b8b4-4e75-8279-6224c3ac769f');
|
||||||
|
if(!isFetching && !data) {
|
||||||
|
return <div>No se encontró el plan de estudios.</div>
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<Card title="Objetivo General">
|
<Card title="Objetivo General">
|
||||||
|
|||||||
Reference in New Issue
Block a user