feat: update API endpoints to use production URL and enhance UI components in authenticated routes
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
// api.ts
|
// api.ts
|
||||||
const API_BASE =
|
const API_BASE =
|
||||||
(import.meta.env.VITE_API_BASE?.replace(/\/$/, "")) ||
|
(import.meta.env.VITE_API_BASE?.replace(/\/$/, "")) ||
|
||||||
"http://localhost:3001"; // 👈 tu Bun.serve real
|
"https://genesis-engine.apps.lci.ulsa.mx"; // 👈 tu Bun.serve real
|
||||||
|
|
||||||
export async function postAPI<T=any>(path: string, body: any): Promise<T> {
|
export async function postAPI<T=any>(path: string, body: any): Promise<T> {
|
||||||
const url = `${API_BASE}${path}`;
|
const url = `${API_BASE}${path}`;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, SheetTrigger } from "@/components/ui/sheet"
|
||||||
import { ModeToggle } from "@/components/mode-toggle"
|
import { ModeToggle } from "@/components/mode-toggle"
|
||||||
import {
|
import {
|
||||||
Menu,
|
Menu,
|
||||||
@@ -94,17 +94,17 @@ function Layout() {
|
|||||||
</Button>
|
</Button>
|
||||||
</SheetTrigger>
|
</SheetTrigger>
|
||||||
<SheetContent side="left" className="p-0">
|
<SheetContent side="left" className="p-0">
|
||||||
<Sidebar onNavigate={() => { }} />
|
<SheetHeader>
|
||||||
|
<SheetTitle>Menú de Navegación</SheetTitle>
|
||||||
|
</SheetHeader> <Sidebar onNavigate={() => { }} />
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
</Sheet>
|
</Sheet>
|
||||||
|
|
||||||
{/* Brand */}
|
{/* Brand */}
|
||||||
<Link to={user.isAdmin ? "/dashboard" : "/planes"} className="hidden items-center gap-2 md:flex">
|
<Link to={user.isAdmin ? "/dashboard" : "/planes"} className="hidden items-center gap-2 md:flex">
|
||||||
<span className="inline-flex h-8 w-8 items-center justify-center rounded-xl bg-primary/10 text-primary font-bold">U</span>
|
<span className="inline-flex h-8 w-25 items-center justify-center rounded-xl bg-primary/10 text-primary font-bold">
|
||||||
<div className="flex flex-col leading-tight">
|
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcS8C2SvZe281wTNc9werkudO9aJiX3dOZm9T3s5DAfS0OUOvDbgc_WC61U_esY8GE8bZoI&usqp=CAU" alt="" />
|
||||||
<span className="font-semibold tracking-tight">La Salle · Ingeniería</span>
|
</span>
|
||||||
<span className="text-[10px] text-muted-foreground font-mono uppercase tracking-wider">Génesis</span>
|
|
||||||
</div>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
{/* Search */}
|
{/* Search */}
|
||||||
@@ -154,13 +154,7 @@ function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full">
|
<div className="h-full">
|
||||||
<div className="flex items-center gap-2 p-4 md:hidden">
|
|
||||||
<span className="inline-flex h-8 w-8 items-center justify-center rounded-xl bg-primary/10 text-primary font-bold">U</span>
|
|
||||||
<div className="leading-tight">
|
|
||||||
<span className="font-semibold">La Salle · Ingeniería</span>
|
|
||||||
<div className="text-[10px] text-muted-foreground font-mono uppercase tracking-wider">Génesis</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Separator />
|
<Separator />
|
||||||
<ScrollArea className="h-[calc(100%-4rem)] p-2">
|
<ScrollArea className="h-[calc(100%-4rem)] p-2">
|
||||||
<nav className="grid gap-1 p-2">
|
<nav className="grid gap-1 p-2">
|
||||||
@@ -181,6 +175,7 @@ function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
|||||||
{canSeeCarreras && (
|
{canSeeCarreras && (
|
||||||
<Link
|
<Link
|
||||||
to="/carreras"
|
to="/carreras"
|
||||||
|
key='/carreras'
|
||||||
activeOptions={{ exact: true }}
|
activeOptions={{ exact: true }}
|
||||||
activeProps={{ className: "bg-primary/10 text-foreground" }}
|
activeProps={{ className: "bg-primary/10 text-foreground" }}
|
||||||
className="group inline-flex items-center gap-3 rounded-xl px-3 py-2 text-sm text-muted-foreground hover:bg-primary/10 hover:text-foreground"
|
className="group inline-flex items-center gap-3 rounded-xl px-3 py-2 text-sm text-muted-foreground hover:bg-primary/10 hover:text-foreground"
|
||||||
@@ -193,6 +188,7 @@ function Sidebar({ onNavigate }: { onNavigate?: () => void }) {
|
|||||||
{isAdmin && (
|
{isAdmin && (
|
||||||
<Link
|
<Link
|
||||||
to="/facultades"
|
to="/facultades"
|
||||||
|
key='facultades'
|
||||||
activeOptions={{ exact: true }}
|
activeOptions={{ exact: true }}
|
||||||
activeProps={{ className: "bg-primary/10 text-foreground" }}
|
activeProps={{ className: "bg-primary/10 text-foreground" }}
|
||||||
className="group inline-flex items-center gap-3 rounded-xl px-3 py-2 text-sm text-muted-foreground hover:bg-primary/10 hover:text-foreground"
|
className="group inline-flex items-center gap-3 rounded-xl px-3 py-2 text-sm text-muted-foreground hover:bg-primary/10 hover:text-foreground"
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ function UploadDialog({
|
|||||||
try {
|
try {
|
||||||
const fileBase64 = await toBase64(file)
|
const fileBase64 = await toBase64(file)
|
||||||
// Enviamos al motor (inserta en la tabla si insert=true)
|
// Enviamos al motor (inserta en la tabla si insert=true)
|
||||||
const res = await fetch("http://localhost:3001/api/upload/documento", {
|
const res = await fetch("https://genesis-engine.apps.lci.ulsa.mx/api/upload/documento", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
|
|||||||
@@ -497,7 +497,7 @@ function MejorarAIButton({ asignaturaId, onApply }: {
|
|||||||
async function apply() {
|
async function apply() {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch("http://localhost:3001/api/mejorar/asignatura", {
|
const res = await fetch("https://genesis-engine.apps.lci.ulsa.mx/api/mejorar/asignatura", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ asignatura_id: asignaturaId, prompt, insert }),
|
body: JSON.stringify({ asignatura_id: asignaturaId, prompt, insert }),
|
||||||
|
|||||||
@@ -510,7 +510,7 @@ function AddAsignaturaButton({
|
|||||||
if (!canIA) return
|
if (!canIA) return
|
||||||
setSaving(true)
|
setSaving(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch("http://localhost:3001/api/generar/asignatura", {
|
const res = await fetch("https://genesis-engine.apps.lci.ulsa.mx/api/generar/asignatura", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -522,7 +522,7 @@ function AddAsignaturaButton({
|
|||||||
})
|
})
|
||||||
if (!res.ok) throw new Error(await res.text())
|
if (!res.ok) throw new Error(await res.text())
|
||||||
setOpen(false); onAdded?.()
|
setOpen(false); onAdded?.()
|
||||||
} catch (e:any) {
|
} catch (e: any) {
|
||||||
alert(e?.message ?? "Error al generar la asignatura")
|
alert(e?.message ?? "Error al generar la asignatura")
|
||||||
} finally {
|
} finally {
|
||||||
setSaving(false)
|
setSaving(false)
|
||||||
@@ -568,29 +568,29 @@ function AddAsignaturaButton({
|
|||||||
<TabsContent value="manual" className="mt-4">
|
<TabsContent value="manual" className="mt-4">
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
<Field label="Nombre">
|
<Field label="Nombre">
|
||||||
<Input value={f.nombre} onChange={e=>setF(s=>({...s, nombre:e.target.value}))} />
|
<Input value={f.nombre} onChange={e => setF(s => ({ ...s, nombre: e.target.value }))} />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Clave">
|
<Field label="Clave">
|
||||||
<Input value={f.clave} onChange={e=>setF(s=>({...s, clave:e.target.value}))} />
|
<Input value={f.clave} onChange={e => setF(s => ({ ...s, clave: e.target.value }))} />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Tipo">
|
<Field label="Tipo">
|
||||||
<Input value={f.tipo} onChange={e=>setF(s=>({...s, tipo:e.target.value}))} placeholder="Obligatoria / Optativa / Taller…" />
|
<Input value={f.tipo} onChange={e => setF(s => ({ ...s, tipo: e.target.value }))} placeholder="Obligatoria / Optativa / Taller…" />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Semestre">
|
<Field label="Semestre">
|
||||||
<Input value={f.semestre} onChange={e=>setF(s=>({...s, semestre:e.target.value}))} placeholder="1–10" />
|
<Input value={f.semestre} onChange={e => setF(s => ({ ...s, semestre: e.target.value }))} placeholder="1–10" />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Créditos">
|
<Field label="Créditos">
|
||||||
<Input value={f.creditos} onChange={e=>setF(s=>({...s, creditos:e.target.value}))} />
|
<Input value={f.creditos} onChange={e => setF(s => ({ ...s, creditos: e.target.value }))} />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Horas teóricas">
|
<Field label="Horas teóricas">
|
||||||
<Input value={f.horas_teoricas} onChange={e=>setF(s=>({...s, horas_teoricas:e.target.value}))} />
|
<Input value={f.horas_teoricas} onChange={e => setF(s => ({ ...s, horas_teoricas: e.target.value }))} />
|
||||||
</Field>
|
</Field>
|
||||||
<Field label="Horas prácticas">
|
<Field label="Horas prácticas">
|
||||||
<Input value={f.horas_practicas} onChange={e=>setF(s=>({...s, horas_practicas:e.target.value}))} />
|
<Input value={f.horas_practicas} onChange={e => setF(s => ({ ...s, horas_practicas: e.target.value }))} />
|
||||||
</Field>
|
</Field>
|
||||||
<div className="sm:col-span-2">
|
<div className="sm:col-span-2">
|
||||||
<Field label="Objetivo (opcional)">
|
<Field label="Objetivo (opcional)">
|
||||||
<Textarea value={f.objetivos} onChange={e=>setF(s=>({...s, objetivos:e.target.value}))} className="min-h-[90px]" />
|
<Textarea value={f.objetivos} onChange={e => setF(s => ({ ...s, objetivos: e.target.value }))} className="min-h-[90px]" />
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -603,14 +603,14 @@ function AddAsignaturaButton({
|
|||||||
<Field label="Indica el enfoque / requisitos">
|
<Field label="Indica el enfoque / requisitos">
|
||||||
<Textarea
|
<Textarea
|
||||||
value={iaPrompt}
|
value={iaPrompt}
|
||||||
onChange={e=>setIaPrompt(e.target.value)}
|
onChange={e => setIaPrompt(e.target.value)}
|
||||||
className="min-h-[120px]"
|
className="min-h-[120px]"
|
||||||
placeholder="Ej.: Diseña una materia de Programación Web con proyectos, evaluación por rúbricas y bibliografía actual…"
|
placeholder="Ej.: Diseña una materia de Programación Web con proyectos, evaluación por rúbricas y bibliografía actual…"
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
<Field label="Periodo (opcional)">
|
<Field label="Periodo (opcional)">
|
||||||
<Input value={iaSemestre} onChange={e=>setIaSemestre(e.target.value)} placeholder="1–10" />
|
<Input value={iaSemestre} onChange={e => setIaSemestre(e.target.value)} placeholder="1–10" />
|
||||||
</Field>
|
</Field>
|
||||||
</div>
|
</div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|||||||
Reference in New Issue
Block a user