feat: add AdjustAIButton, EditPlanButton, and AsignaturaPreviewCard components
- Implemented AdjustAIButton for AI-driven plan adjustments with a confetti effect on success. - Created EditPlanButton to allow editing of plan details with form validation and optimistic updates. - Added AsignaturaPreviewCard to display course previews with relevant statistics and details. - Introduced Field component for consistent form field labeling. - Developed GradientMesh for dynamic background effects based on color input. - Added Pulse component for skeleton loading states. - Created SmallStat and StatCard components for displaying statistical information in a card format. - Implemented utility functions in planHelpers for color manipulation and formatting. - Established planQueries for fetching plan and course data from the database. - Updated the plan detail route to utilize new components and queries for improved user experience.
This commit is contained in:
93
src/components/planes/planQueries.ts
Normal file
93
src/components/planes/planQueries.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { supabase } from "@/auth/supabase"
|
||||
|
||||
export const planKeys = {
|
||||
byId: (id: string) => ["plan", id] as const,
|
||||
}
|
||||
export const asignaturaKeys = {
|
||||
count: (planId: string) => ["asignaturas", "count", planId] as const,
|
||||
preview: (planId: string) => ["asignaturas", "preview", planId] as const,
|
||||
extra: (asigId: string) => ["asignatura", "extra", asigId] as const,
|
||||
}
|
||||
|
||||
export type PlanFull = {
|
||||
id: string; nombre: string; nivel: string | null;
|
||||
objetivo_general: string | null; perfil_ingreso: string | null; perfil_egreso: string | null;
|
||||
duracion: string | null; total_creditos: number | null;
|
||||
competencias_genericas: string | null; competencias_especificas: string | null;
|
||||
sistema_evaluacion: string | null; indicadores_desempeno: string | null;
|
||||
pertinencia: string | null; prompt: string | null;
|
||||
estado: string | null; fecha_creacion: string | null;
|
||||
carreras: { id: string; nombre: string; facultades?: { id: string; nombre: string; color?: string | null; icon?: string | null } | null } | null
|
||||
}
|
||||
export type AsignaturaLite = { id: string; nombre: string; semestre: number | null; creditos: number | null }
|
||||
|
||||
export function planByIdOptions(planId: string) {
|
||||
return {
|
||||
queryKey: planKeys.byId(planId),
|
||||
queryFn: async (): Promise<PlanFull> => {
|
||||
const { data, error } = await supabase
|
||||
.from("plan_estudios")
|
||||
.select(`
|
||||
id, nombre, nivel, objetivo_general, perfil_ingreso, perfil_egreso, duracion, total_creditos,
|
||||
competencias_genericas, competencias_especificas, sistema_evaluacion, indicadores_desempeno,
|
||||
pertinencia, prompt, estado, fecha_creacion,
|
||||
carreras ( id, nombre, facultades:facultades ( id, nombre, color, icon ) )
|
||||
`)
|
||||
.eq("id", planId)
|
||||
.maybeSingle()
|
||||
if (error || !data) throw error ?? new Error("Plan no encontrado")
|
||||
return data as unknown as PlanFull
|
||||
},
|
||||
staleTime: 60_000,
|
||||
} as const
|
||||
}
|
||||
export function asignaturasCountOptions(planId: string) {
|
||||
return {
|
||||
queryKey: asignaturaKeys.count(planId),
|
||||
queryFn: async (): Promise<number> => {
|
||||
const { count, error } = await supabase
|
||||
.from("asignaturas")
|
||||
.select("*", { count: "exact", head: true })
|
||||
.eq("plan_id", planId)
|
||||
if (error) throw error
|
||||
return count ?? 0
|
||||
},
|
||||
staleTime: 30_000,
|
||||
} as const
|
||||
}
|
||||
export function asignaturasPreviewOptions(planId: string) {
|
||||
return {
|
||||
queryKey: asignaturaKeys.preview(planId),
|
||||
queryFn: async (): Promise<AsignaturaLite[]> => {
|
||||
const { data, error } = await supabase
|
||||
.from("asignaturas")
|
||||
.select("id, nombre, semestre, creditos")
|
||||
.eq("plan_id", planId)
|
||||
.order("semestre", { ascending: true })
|
||||
.order("nombre", { ascending: true })
|
||||
.limit(8)
|
||||
if (error) throw error
|
||||
return (data ?? []) as unknown as AsignaturaLite[]
|
||||
},
|
||||
staleTime: 30_000,
|
||||
} as const
|
||||
}
|
||||
export function asignaturaExtraOptions(asigId: string) {
|
||||
return {
|
||||
queryKey: asignaturaKeys.extra(asigId),
|
||||
queryFn: async (): Promise<{
|
||||
tipo: string | null
|
||||
horas_teoricas: number | null
|
||||
horas_practicas: number | null
|
||||
contenidos: Record<string, Record<string, string>> | null
|
||||
} | null> => {
|
||||
const { data, error } = await supabase
|
||||
.from("asignaturas")
|
||||
.select("tipo, horas_teoricas, horas_practicas, contenidos")
|
||||
.eq("id", asigId)
|
||||
.maybeSingle()
|
||||
if (error) throw error
|
||||
return (data as any) ?? null
|
||||
},
|
||||
} as const
|
||||
}
|
||||
Reference in New Issue
Block a user