Synaplan
开源 AI 软件平台,面向企业:自托管 AI 代理,符合 GDPR 的聊天机器人小部件,RAG 知识库,WhatsApp 与 Outlook AI,MCP,多模型路由。支持 Docker、Kubernetes 或在 Germ 托管。
AI 翻译
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Sistema de gestión para una clínica dental. Incluye:
Stack: Next.js 16 (App Router), TypeScript, Tailwind CSS 4, Supabase (Postgres, Auth, Storage y RLS), Motion, Recharts, Zod, @react-pdf/renderer y ExcelJS.
El prototipo original y las instrucciones del bot están en referencia/, solo como referencia de reglas de negocio.
Requisito: Node 22 o superior.
npm install
npm run dev # http://localhost:3000
Mientras .env.local no tenga las claves de Supabase, la app arranca en vista previa:
/login eliges un rol (Administrador, Odontólogo o Recepción).npm run dev vuelven los datos originales. Ajustes → Datos de ejemplo → Reiniciar hace lo mismo.| Comando | Qué hace |
|---|---|
npm run dev | Servidor de desarrollo |
npm run build && npm start | Compilación y servidor de producción |
npm test | Pruebas de las reglas críticas (saldo, recordatorios, citas repetidas, choque de horario, odontograma, horarios libres) |
npm run typecheck | Revisión de tipos |
npm run seed:generar | Regenera supabase/seed.sql desde src/datos/ejemplo.ts |
npm run superadmin -- correo "Nombre" | Crea la cuenta de superadministrador de Rido |
npm run probar:rls | Prueba las políticas RLS contra tu proyecto de Supabase |
Crear el proyecto en supabase.com → New project.
Copiar las claves en .env.local. Están en Project Settings → API Keys:
| Variable | Clave |
|---|---|
NEXT_PUBLIC_SUPABASE_URL | URL del proyecto |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | publishable (o anon public) |
SUPABASE_SECRET_KEY | secret (o service_role). Solo servidor, nunca en git |
Crear las tablas. En SQL Editor ejecuta en orden:
supabase/migrations/20261021000001_base.sql (clínica, equipo, roles, auditoría)supabase/migrations/20261021000002_operacion.sql (pacientes, citas, recordatorios, caja, cotizaciones)supabase/migrations/20261021000003_clinico.sql (panorámicas, odontograma, IA y buckets privados)supabase/migrations/20261022000004_inventario.sql (inventario y sus movimientos)supabase/seed.sql (opcional: datos de ejemplo con es_ejemplo = true)Con la CLI también se puede: npx supabase login, npx supabase link --project-ref <ref> y npx supabase db push.
Configurar Auth:
Sign In / Providers → Email: desactiva "Allow new users to sign up". Solo entra quien fue invitado.
URL Configuration:
http://localhost:3000 para probar.<tu dominio>/** a Redirect URLs.Emails → Templates: en Invite user y Reset password, cambia el enlace por:
{{ .SiteURL }}/auth/confirmar?token_hash={{ .TokenHash }}&type=invite&siguiente=/cuenta/contrasena
En Reset password usa .
| Rol | Puede |
|---|---|
| Administrador | Todo, incluidos el equipo, el catálogo, los ajustes y la actividad (auditoría) |
| Odontólogo | Agenda, todos los pacientes, cotizaciones, odontograma y panorámicas. Sin caja |
| Recepción | Agenda, pacientes, recordatorios, caja, cotizaciones e inventario. No ve radiografías, solo sabe si el paciente tiene |
En el inventario, todo el equipo puede poner y sacar existencias (queda en el historial quién lo hizo). Solo administración y recepción crean, editan o borran artículos.
/api/panoramicas/[id]: revisa el rol y redirige a una URL firmada que vence en 2 minutos. Nunca hay URLs públicas.Flujo existente: "Clínica Dental · Bot de recordatorios WhatsApp" (ID tA98h2EQOxFvT49K).
Todos piden Authorization: Bearer <N8N_TOKEN>. Pon un valor largo y aleatorio en N8N_TOKEN. En la vista previa el token es vista-previa.
| Método y ruta | Para qué |
|---|---|
GET /api/n8n/recordatorios | Recordatorios que deben salir hoy: avisos del mismo día, avisos de 3 días antes y fallidos para reintentar |
POST /api/n8n/recordatorios/resultado | Marca cada uno como enviado o fallido |
POST /api/n8n/respuestas | Preparado y apagado. Recibe «Confirmo» o «Aplazar» (N8N_RESPUESTAS_ACTIVAS=true) |
Respuesta de GET /api/n8n/recordatorios:
{ "fecha": "2026-10-21", "total": 2, "recordatorios": [
{ "recordatorioId": "…", "citaId": "…", "tipo": "previo", "telefono": "18095550111", "nombre": "Ana",
"fecha": "sábado, 24 de octubre", "hora": "9:00 a. m.", "doctor": "Dra. Laura Peña",
"clinica": "Clínica Dental Sonrisa", "direccion": "Calle del Sol #45, Santiago", "mensaje": "…" } ],
"sinTelefono": [] }
Cuerpo de POST /api/n8n/recordatorios/resultado:
{ "resultados": [ { "recordatorioId": "…", "estado": "enviado" },
{ "recordatorioId": "…", "estado": "fallido", "error": "Número sin WhatsApp" } ] }
45 7 * * *.America/Santo_Domingo (Settings del flujo → Timezone).https://<tu-dominio>/api/n8n/recordatorios.Authorization: Bearer <N8N_TOKEN>.recordatorios.Plantilla: recordatorio_cita_3dias si tipo es previo, recordatorio_cita_hoy si es hoy. Usa un nodo If o una expresión.
Variables:
| Variable | Campo |
|---|---|
{{1}} | nombre |
{{2}} | fecha |
{{3}} | hora |
{{4}} | doctor |
{{5}} | clinica |
{{6}} | direccion |
Destinatario: telefono.
En Settings del nodo, activa Continue On Fail para que un error no detenga a los demás.
{ recordatorioId, estado: $json.error ? "fallido" : "enviado", error: $json.error?.message }.resultados.https://<tu-dominio>/api/n8n/recordatorios/resultado, con la misma credencial y el cuerpo { "resultados": … }.Lo que el bot no pueda enviar aparece en Recordatorios → Fallidos para enviarlo a mano.
POST /api/n8n/respuestas:
{ "citaId", "respuesta": "confirmo" } → la cita pasa a confirmada.{ "citaId", "respuesta": "aplazar" } → devuelve 3 horarios libres del mismo odontólogo, dentro del horario de la clínica.{ "citaId", "respuesta": "aplazar", "eleccion": { "fecha", "hora" } } → mueve la cita y reprograma los recordatorios.N8N_RESPUESTAS_ACTIVAS=true.| Función | En la vista previa |
|---|---|
| Cotizaciones | El asistente responde con preguntas y respuestas fijas, como un FAQ. Arma la cotización solo con servicios del catálogo y sus precios exactos |
| Panorámicas | Apagadas por defecto (Ajustes → IA). Al activarlas, «Pedir sugerencia» marca zonas fijas para revisar |
Las sugerencias de panorámicas siempre llevan la etiqueta «Sugerencia, no es diagnóstico. Confirmar por el odontólogo». Nunca cambian el odontograma solas: el odontólogo acepta o descarta cada una.
Para usar un modelo real, solo se cambia src/lib/ia/asistente.ts y la llamada se hace desde el servidor. La clave va en .env.local y nunca en el navegador.
.env.local en Settings → Environment Variables. NEXT_PUBLIC_SITE_URL debe ser el dominio real.https://<tu-dominio>/demo.La vista previa en memoria es solo para demostrar en local. En Vercel, conecta Supabase.
src/
app/
(auth)/ login, recuperar, crear contraseña
(app)/ pantallas con barra lateral
resumen/ agenda/ recordatorios/ caja/ cotizaciones/ ajustes/
pacientes/[id]/clinico/ visor, comparador, odontograma, panel de pieza
demo/ demostración pública del bot
api/ panorámicas, comprobantes, Excel, PDF y endpoints de n8n
design/tokens.css ÚNICO archivo del sistema de diseño (oscuro y claro)
components/bona/ tarjetas, calendario, anillos, chips, mini gráficas…
components/ui/ formularios, diálogos, pestañas, menús y avisos
datos/ repo.ts (Supabase o memoria) y datos de ejemplo
lib/reglas/ reglas de negocio puras y probadas
lib/ia/ asistente (modo de prueba)
proxy.ts protege rutas y refresca la sesión
supabase/migrations/ SQL versionado con RLS
scripts/ superadmin, prueba de RLS y generador del seed
tests/ Vitest
npm run probar:rls (al cerrar la venta).type=recoveryCrear el superadministrador:
npm run superadmin -- [email protected] "Tu Nombre"
Imprime una contraseña temporal. Cámbiala desde «¿Olvidaste tu contraseña?».
Invitar al equipo desde Ajustes → Equipo.
Verificar la seguridad con npm run probar:rls. Crea usuarios temporales por rol, comprueba qué ve cada uno y los borra.