From 196aea5df9ab526605d46739f91bd3d3c32a494c Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 26 Aug 2025 15:58:30 -0600 Subject: [PATCH] feat: add canvas-confetti integration and AuroraButton component; enhance UI with animations and improve button interactions --- bun.lock | 6 ++ package.json | 2 + src/components/effect/aurora-button.tsx | 23 ++++++ .../asignatura/$asignaturaId.tsx | 26 +++++- src/routes/_authenticated/asignaturas.tsx | 79 +++++++++++-------- .../_authenticated/facultad/$facultadId.tsx | 8 +- src/routes/_authenticated/plan/$planId.tsx | 16 +++- src/styles.css | 24 ++++++ 8 files changed, 143 insertions(+), 41 deletions(-) create mode 100644 src/components/effect/aurora-button.tsx diff --git a/bun.lock b/bun.lock index bca3ce2..2a4dd8a 100644 --- a/bun.lock +++ b/bun.lock @@ -22,6 +22,8 @@ "@tanstack/react-router": "^1.130.2", "@tanstack/react-router-devtools": "^1.131.5", "@tanstack/router-plugin": "^1.121.2", + "@types/canvas-confetti": "^1.9.0", + "canvas-confetti": "^1.9.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -426,6 +428,8 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/canvas-confetti": ["@types/canvas-confetti@1.9.0", "", {}, "sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg=="], + "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], "@types/d3-array": ["@types/d3-array@3.2.1", "", {}, "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg=="], @@ -510,6 +514,8 @@ "caniuse-lite": ["caniuse-lite@1.0.30001735", "", {}, "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w=="], + "canvas-confetti": ["canvas-confetti@1.9.3", "", {}, "sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g=="], + "chai": ["chai@5.3.1", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-48af6xm9gQK8rhIcOxWwdGzIervm8BVTin+yRp9HEvU20BtVZ2lBywlIJBzwaDtvo0FvjeL7QdCADoUoqIbV3A=="], "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], diff --git a/package.json b/package.json index 2005e95..38a26b0 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,8 @@ "@tanstack/react-router": "^1.130.2", "@tanstack/react-router-devtools": "^1.131.5", "@tanstack/router-plugin": "^1.121.2", + "@types/canvas-confetti": "^1.9.0", + "canvas-confetti": "^1.9.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/src/components/effect/aurora-button.tsx b/src/components/effect/aurora-button.tsx new file mode 100644 index 0000000..79dc2c6 --- /dev/null +++ b/src/components/effect/aurora-button.tsx @@ -0,0 +1,23 @@ +// components/ui/aurora-button.tsx +import { Button } from "@/components/ui/button" + +export function AuroraButton({ + loading, + children, + ...props +}: React.ComponentProps & { loading?: boolean }) { + return ( + + ) +} diff --git a/src/routes/_authenticated/asignatura/$asignaturaId.tsx b/src/routes/_authenticated/asignatura/$asignaturaId.tsx index db405ad..b21b313 100644 --- a/src/routes/_authenticated/asignatura/$asignaturaId.tsx +++ b/src/routes/_authenticated/asignatura/$asignaturaId.tsx @@ -6,6 +6,8 @@ import { supabase } from "@/auth/supabase" import { Button } from "@/components/ui/button" import { Badge } from "@/components/ui/badge" import { Input } from "@/components/ui/input" +import confetti from "canvas-confetti" + import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion" @@ -514,6 +516,11 @@ function MejorarAIButton({ asignaturaId, onApply }: { } const nuevo = await res.json() onApply(nuevo as Asignatura) + confetti({ + particleCount: 120, + spread: 80, + origin: { y: 0.6 }, + }) setOpen(false) } catch (e: any) { alert(e?.message ?? "Error al mejorar la asignatura") @@ -547,7 +554,24 @@ function MejorarAIButton({ asignaturaId, onApply }: { - + diff --git a/src/routes/_authenticated/asignaturas.tsx b/src/routes/_authenticated/asignaturas.tsx index 0a80f82..ebd114b 100644 --- a/src/routes/_authenticated/asignaturas.tsx +++ b/src/routes/_authenticated/asignaturas.tsx @@ -291,7 +291,7 @@ function RouteComponent() { // NEW: acciones carrito function addToCart(a: Asignatura) { setCart(prev => prev.find(x => x.id === a.id) ? prev : [...prev, a]) - toast.success('Asignatura añadida al carrito') + toast.success('Asignatura añadida al carrito de asignaturas') } function removeFromCart(id: string) { setCart(prev => prev.filter(x => x.id !== id)) @@ -355,7 +355,7 @@ function RouteComponent() { className="relative" > - Carrito + Carrito de asignaturas {cart.length > 0 && ( {cart.length} @@ -369,36 +369,51 @@ function RouteComponent() { {/* Filtros */} -
- setQ(e.target.value)} - placeholder="Buscar por nombre, clave, plan, carrera, facultad…" - className="w-full" - /> - - - +
+
+ + setQ(e.target.value)} + placeholder="Nombre, clave, plan, carrera, facultad…" + /> +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ {/* Chips de salud */}
- +
@@ -686,7 +701,7 @@ function AsignaturaCard({ a, onClone, onAddToCart }: { a: Asignatura; onClone: ( Clonar… - Añadir al carrito + Añadir al carrito de asignaturas diff --git a/src/routes/_authenticated/facultad/$facultadId.tsx b/src/routes/_authenticated/facultad/$facultadId.tsx index 6cf3f6f..bed94d9 100644 --- a/src/routes/_authenticated/facultad/$facultadId.tsx +++ b/src/routes/_authenticated/facultad/$facultadId.tsx @@ -171,10 +171,10 @@ function RouteComponent() { {/* Métricas principales */}
- - - - + + + +
{/* Calidad + Salud */} diff --git a/src/routes/_authenticated/plan/$planId.tsx b/src/routes/_authenticated/plan/$planId.tsx index 9614473..304153e 100644 --- a/src/routes/_authenticated/plan/$planId.tsx +++ b/src/routes/_authenticated/plan/$planId.tsx @@ -13,6 +13,8 @@ import gsap from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' import { AcademicSections } from '@/components/planes/academic-sections' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@radix-ui/react-tabs' +import confetti from 'canvas-confetti' +import { AuroraButton } from '@/components/effect/aurora-button' gsap.registerPlugin(ScrollTrigger) @@ -407,6 +409,8 @@ function AdjustAIButton({ plan }: { plan: PlanFull }) { body: JSON.stringify({ prompt, plan_id: plan.id }), }).catch(() => { }) setLoading(false) + confetti({ particleCount: 120, spread: 80, origin: { y: 0.6 } }) + setOpen(false) } @@ -423,9 +427,9 @@ function AdjustAIButton({ plan }: { plan: PlanFull }) {