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.