diff --git a/prisma/migrations/20260905010000_add_general_bug_feedback_kind/migration.sql b/prisma/migrations/20260905010000_add_general_bug_feedback_kind/migration.sql new file mode 100644 index 0000000..209f120 --- /dev/null +++ b/prisma/migrations/20260905010000_add_general_bug_feedback_kind/migration.sql @@ -0,0 +1,7 @@ +-- "Reportar un bug" (rail y perfil) dejó de apuntar a GitHub y ahora abre el +-- mismo widget de Feedback que discrepancia/confuso/idea/elogio. Necesita su +-- propio valor de kind para no mezclarse con "other". +-- +-- ALTER TYPE ... ADD VALUE es seguro (idempotente con IF NOT EXISTS) y el +-- valor nuevo no se usa en esta misma migración. +ALTER TYPE "FeedbackKind" ADD VALUE IF NOT EXISTS 'bug'; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index ad13f3c..c522cfa 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -844,6 +844,13 @@ enum FeedbackKind { * contenido y la clase real, o "esta fue una mala unidad/lección". */ discrepancy + /** + * Error técnico GENERAL, no atado a un paso/ejercicio concreto (para eso + * está `BugReport`): "no puedo iniciar sesión", "esta página no carga". + * Es el destino del link "Reportar un bug" del rail y del perfil — antes + * apuntaba a GitHub, que la mayoría de los alumnos no sabe usar. + */ + bug /** * "no entendí esto" */ diff --git a/src/app/app/(global)/perfil/page.tsx b/src/app/app/(global)/perfil/page.tsx index 47d28cc..c0bfb40 100644 --- a/src/app/app/(global)/perfil/page.tsx +++ b/src/app/app/(global)/perfil/page.tsx @@ -21,6 +21,7 @@ import { AchievementsSection } from "@/features/profile/components/achievements- import { ChangePasswordDialog } from "@/features/profile/components/change-password-dialog"; import { DeleteAccountDialog } from "@/features/profile/components/delete-account-dialog"; import { SignOutButton } from "@/features/profile/components/sign-out-button"; +import { ReportBugButton } from "@/features/feedback/components/report-bug-button"; export const metadata = { title: "Mi perfil", @@ -198,16 +199,11 @@ export default async function PerfilPage() {

- ¿Encontraste un bug?{" "} - - Repórtalo en GitHub - - . + ¿Encontraste un bug o algo que no corresponde con tu clase?{" "} + + Repórtalo aquí + {" "} + — no hace falta GitHub.

); diff --git a/src/components/layout/sidebar.tsx b/src/components/layout/sidebar.tsx index 514068d..1a10d44 100644 --- a/src/components/layout/sidebar.tsx +++ b/src/components/layout/sidebar.tsx @@ -1,11 +1,11 @@ import Link from "next/link"; -import { Bug } from "lucide-react"; import { Logo } from "@/components/shared/logo"; import { UNOFFICIAL_NOTICE } from "@/lib/branding"; import { SidebarNav } from "@/components/layout/sidebar-nav"; import type { CourseSwitcherItem } from "@/features/courses/components/course-switcher"; import { FeedbackRailButton } from "@/features/feedback/components/feedback-rail-button"; +import { ReportBugRailRow } from "@/features/feedback/components/report-bug-button"; import type { RoadmapUnit } from "@/features/roadmap/types"; /** @@ -52,18 +52,11 @@ export function Sidebar({
{/* Feedback general de la experiencia. Para contenido roto está el - botón de reporte dentro de la propia lección/ejercicio. */} + botón de reporte dentro de la propia lección/ejercicio. Ninguno + de los dos manda a GitHub: la mayoría de los alumnos no sabe + qué es un issue y no lo abriría. */} - - - Reportar un bug - v0.1 - +

{UNOFFICIAL_NOTICE}

diff --git a/src/components/layout/user-menu.tsx b/src/components/layout/user-menu.tsx index 9b45aa1..77c1524 100644 --- a/src/components/layout/user-menu.tsx +++ b/src/components/layout/user-menu.tsx @@ -3,6 +3,7 @@ import * as React from "react"; import { useRouter } from "next/navigation"; import { + Bug, Dumbbell, LogOut, MessageSquarePlus, @@ -56,6 +57,9 @@ export function UserMenu({ }: UserMenuProps) { const router = useRouter(); const [feedbackOpen, setFeedbackOpen] = React.useState(false); + const [feedbackKind, setFeedbackKind] = React.useState<"confusing" | "bug">( + "confusing", + ); const initials = user.name .split(" ") .map((n) => n[0]) @@ -154,12 +158,23 @@ export function UserMenu({ // El menú se cierra al elegir; abrimos el diálogo aparte para que // no se desmonte con él. event.preventDefault(); + setFeedbackKind("confusing"); setFeedbackOpen(true); }} > Enviar comentario + { + event.preventDefault(); + setFeedbackKind("bug"); + setFeedbackOpen(true); + }} + > + + Reportar un bug + {isAdmin ? ( router.push("/app/admin")}> @@ -172,7 +187,11 @@ export function UserMenu({ Cerrar sesión - + ); } diff --git a/src/features/admin/components/triage-row.tsx b/src/features/admin/components/triage-row.tsx index 0440a80..17dce97 100644 --- a/src/features/admin/components/triage-row.tsx +++ b/src/features/admin/components/triage-row.tsx @@ -25,6 +25,7 @@ const STATUSES: { value: ReportStatusInput; label: string }[] = [ const FEEDBACK_KIND_LABEL: Record = { discrepancy: "Discrepancia con clase", + bug: "Error técnico (general)", confusing: "Confuso", idea: "Idea", praise: "Elogio", @@ -79,7 +80,9 @@ export function TriageRow({ item }: { item: TriageItem }) {
=> { diff --git a/src/features/feedback/components/feedback-dialog.tsx b/src/features/feedback/components/feedback-dialog.tsx index 6b34416..4d8540a 100644 --- a/src/features/feedback/components/feedback-dialog.tsx +++ b/src/features/feedback/components/feedback-dialog.tsx @@ -4,6 +4,7 @@ import * as React from "react"; import { usePathname } from "next/navigation"; import { AlertTriangle, + Bug, Lightbulb, MessageSquarePlus, Send, @@ -27,7 +28,7 @@ import { submitFeedback } from "@/features/feedback/actions"; import { FEEDBACK_MAX_LENGTH } from "@/features/feedback/context"; import { cn } from "@/lib/utils"; -type Kind = "discrepancy" | "confusing" | "idea" | "praise"; +type Kind = "discrepancy" | "bug" | "confusing" | "idea" | "praise"; const KINDS: { value: Kind; label: string; icon: typeof HelpCircle }[] = [ { @@ -35,6 +36,7 @@ const KINDS: { value: Kind; label: string; icon: typeof HelpCircle }[] = [ label: "No corresponde con mi clase", icon: AlertTriangle, }, + { value: "bug", label: "Encontré un error técnico", icon: Bug }, { value: "confusing", label: "Algo me confundió", icon: HelpCircle }, { value: "idea", label: "Tengo una idea", icon: Lightbulb }, { value: "praise", label: "Algo me gustó", icon: Sparkles }, @@ -45,11 +47,18 @@ const COPY: Record< { title: string; description: string; placeholder: string } > = { discrepancy: { - title: "Reportar una discrepancia", + title: "Reportar una discrepancia con tu clase", description: - '¿Esta lección o unidad no corresponde con lo que viste en tu clase, o te pareció una mala sesión? Cuéntanos qué no cuadra — ya sabemos en qué pantalla estás, no hace falta que lo expliques.', + "¿Esta lección o unidad no corresponde con lo que tu profe está dando en clase, con el orden de tu plantel, o con el temario oficial? Es especialmente útil en semestres avanzados, donde casi no hay referencia práctica más allá del temario — y no todos los planteles lo siguen igual. Cuéntanos qué no cuadra; ya sabemos en qué pantalla estás.", placeholder: - 'Ej: "En mi clase vimos primero los arreglos y aquí aparecen hasta después de POO."', + 'Ej: "En mi plantel ya vimos punteros antes que arreglos, y aquí van después."', + }, + bug: { + title: "Reportar un error técnico", + description: + "¿Algo no carga, se traba, o no funciona como debería? No hace falta GitHub ni saber qué es un issue: cuéntanos qué pasó y lo revisamos. Ya sabemos en qué pantalla estás.", + placeholder: + 'Ej: "No puedo iniciar sesión con Google, se queda cargando."', }, confusing: { title: "¿Cómo te está yendo?", @@ -73,14 +82,16 @@ const COPY: Record< /** * Feedback general sobre la experiencia — incluye reportar que el contenido - * NO corresponde con la clase real, o que una lección/unidad fue mala. El - * contexto (dónde estaba el alumno) viaja solo: mandamos la ruta y el + * NO corresponde con la clase real (o que una lección/unidad fue mala) y + * reportar un error técnico general (login, una página que no carga, etc.). + * El contexto (dónde estaba el alumno) viaja solo: mandamos la ruta y el * servidor la interpreta. No le pedimos que explique en qué pantalla estaba. * - * Para contenido roto (typo, test mal configurado) existe `ReportBugDialog`, - * que sí apunta a un paso/ejercicio concreto. Ninguno de los dos crea un - * issue de GitHub: caen en la cola de triage interna (`/app/admin/reportes`) - * y un admin decide si vale la pena abrir uno. + * Para contenido roto atado a un paso/ejercicio concreto (typo, test mal + * configurado) existe `ReportBugDialog`. Ninguno de los dos crea un issue de + * GitHub — la mayoría de los alumnos no sabe qué es eso y no lo usaría: caen + * en la cola de triage interna (`/app/admin/reportes`) y un admin decide si + * vale la pena normalizarlo como issue real. */ export function FeedbackDialog({ children, diff --git a/src/features/feedback/components/report-bug-button.tsx b/src/features/feedback/components/report-bug-button.tsx new file mode 100644 index 0000000..ea31051 --- /dev/null +++ b/src/features/feedback/components/report-bug-button.tsx @@ -0,0 +1,43 @@ +"use client"; + +import * as React from "react"; +import { Bug } from "lucide-react"; + +import { FeedbackDialog } from "@/features/feedback/components/feedback-dialog"; +import { cn } from "@/lib/utils"; + +/** + * "Reportar un bug" GENERAL (login, una página que no carga, etc.), sin + * pasar por GitHub — la mayoría de los alumnos no sabe qué es un issue y no + * lo abriría. Reemplaza el link que antes salía a + * `github.com/.../issues` desde el rail y desde el perfil. + * + * Para contenido roto atado a un paso/ejercicio concreto está + * `ReportBugDialog`, que vive dentro de la propia lección. + */ +export function ReportBugButton({ + className, + children, +}: { + className?: string; + children: React.ReactNode; +}) { + return ( + + + + ); +} + +/** Fila lista para usar en el rail: ícono + texto, mismo look que antes. */ +export function ReportBugRailRow() { + return ( + + + Reportar un bug + v0.1 + + ); +} diff --git a/src/features/feedback/components/report-discrepancy-button.tsx b/src/features/feedback/components/report-discrepancy-button.tsx index 8d7e0d2..dad6646 100644 --- a/src/features/feedback/components/report-discrepancy-button.tsx +++ b/src/features/feedback/components/report-discrepancy-button.tsx @@ -13,7 +13,11 @@ import { FeedbackDialog } from "@/features/feedback/components/feedback-dialog"; * * Vive junto al `ReportBugDialog` en la cabecera de la lección y en la * página de unidad: el alumno lo ve exactamente donde nota el problema, sin - * tener que buscarlo en un menú aparte. + * tener que buscarlo en un menú aparte. Estilo llamativo (no `ghost`) a + * propósito — un ícono apagado entre otros íconos apagados nunca se nota, y + * esto es justo lo que más nos importa saber en semestres avanzados, donde + * el temario oficial casi no tiene referencia práctica y cada plantel lo + * sigue distinto. */ export function ReportDiscrepancyButton() { return ( @@ -21,11 +25,13 @@ export function ReportDiscrepancyButton() { );