Lalabase
티켓·문서·근무시간·고객대화를 한 곳에 모아 KI로 검색, 소프트웨어 기업 고객 프로젝트용, 오픈소스로 자체 서버·독일 클라우드에 배포
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
Sistema de turnos y gestión comercial para una peluquería de operación unipersonal: un profesional, una estación de trabajo. • Portal público (/, /reservar, /turno/[token]): el cliente elige servicio, día y horario, deja sus datos y recibe un link propio para seguir su turno. Sin pago ni seña previa. • Panel privado (/admin): agenda, bandeja de solicitudes, catálogo de servicios y configuración de disponibilidad.README 전문 보기 ↗
Sistema de turnos y gestión comercial para una peluquería de operación unipersonal: un profesional, una estación de trabajo.
/, /reservar, /turno/[token], /baja/[token]): el
cliente elige servicio, día y horario, deja sus datos y recibe un link propio
para seguir su turno. Sin pago ni seña previa. Un botón flotante abre
WhatsApp con el local./admin): Hoy, agenda, bandeja de solicitudes, caja por
categoría y, desde Más, servicios, productos, disponibilidad y alta de
clientes.South America (São Paulo)).anon y la clave service_role.cp .env.local.example .env.local
Completar NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY y
SUPABASE_SERVICE_ROLE_KEY.
Generar también MANAGE_TOKEN_SECRET, con el que se derivan los links de
autogestión (/turno/[token]) a partir del id de cada turno:
node -e "console.log(require('crypto').randomBytes(32).toString('base64url'))"
MANAGE_TOKEN_SECRETtiene que tener el mismo valor en.env.localy en Vercel: los dos entornos usan la misma base, y un turno reservado en uno puede recibir emails armados en el otro. Si cambia, los links de los turnos ya reservados dejan de coincidir.
Para los emails y la tarea diaria completar también:
NEXT_PUBLIC_SITE_URL: la URL pública (https://houssestudio.com.ar en
producción), base de los links de los mails.RESEND_API_KEY, EMAIL_FROM (remitente verificado en Resend) y
ADMIN_EMAIL (la casilla del peluquero, que recibe los avisos).CRON_SECRET: lo exige /api/cron/reminders, que Vercel Cron llama todos
los días a las 10 de Buenos Aires (vercel.json). Se genera con el mismo
comando que MANAGE_TOKEN_SECRET y se carga igual en Vercel.
SUPABASE_SERVICE_ROLE_KEYsaltea todas las políticas de seguridad de la base. Nunca se commitea ni se expone al navegador: los módulos que la usan importanserver-only, así que el build falla si alguna vez terminan alcanzados por código de cliente.
En el SQL Editor de Supabase, ejecutar en orden los archivos de
supabase/migrations/:
0001_schema.sql — tablas, tipos y la restricción anti-solapamiento.0002_rls.sql — Row Level Security.0003_seed.sql — servicios y horario comercial iniciales.0004_dni.sql — DNI como identidad del cliente.0005_ventana_de_reserva.sql — ventana de reserva configurable.0006_horario_partido.sql — segundo tramo horario (reemplazado por la 0009).0007_ventas_sueltas.sql — ventas sueltas y cobro atómico de turnos.0008_ventas_productos.sql — venta de productos además de servicios.0009_horario_bloques.sql — horario con N bloques por día.0010_retira_columnas_horario.sql — retira las columnas de horario
anteriores a la 0009.0011_email_aceptacion.sql — tipo de email para el turno confirmado.0012_productos_y_categorias.sql — categorías y productos, ventas
sueltas con varios ítems y la función que las registra.0013_email_vuelta.sql — tipo de email del recordatorio para volver.
Va sola: Postgres no deja usar un valor de enum recién agregado en la
misma ejecución.0014_recordatorio_vuelta.sql — baja de ese recordatorio
(customers.comeback_opt_out_at) y el índice que impide repetirlo.Cada archivo depende de los anteriores: la 0009 copia a bloques el horario que dejan cargado la 0003 y la 0006, y la 0010 recién después borra esas columnas.
No hay registro público: la cuenta se crea a mano.
User UID y habilitarlo como administrador desde el SQL Editor:insert into public.admins (user_id) values ('<pegar-el-user-uid>');
npm run dev
El portal público queda en http://localhost:3000 y el panel en
http://localhost:3000/admin.
npm run dev
npm test
npm run typecheck
npm run build
| Capa | Ubicación |
|---|---|
| Motor de disponibilidad | lib/availability.ts — función pura, cubierta por tests |
| Lecturas del portal público | lib/data/availability.ts, lib/data/public.ts |
| Lecturas del panel | lib/data/appointments.ts |
| Escrituras | lib/actions/ — una Server Action por dominio |
| Emails | lib/email/ — plantillas puras (con tests) y envío por Resend |
| Tarea diaria | app/api/cron/reminders/route.ts — los dos recordatorios, aislados |
| Navegación por fecha (Agenda y Caja) | components/admin/period-navigation.tsx, lib/period-nav.ts |
| Esquema | supabase/migrations/ |
Tres decisiones que conviene conocer antes de tocar el código:
Los horarios se encadenan según la duración del servicio. Dentro de cada hueco libre los slots arrancan en el borde del hueco y avanzan de a la duración del servicio elegido. Aprovecha mejor la agenda de una estación única, y trae como consecuencia que los horarios ofrecidos cambien según el servicio — por eso el flujo de reserva pide el servicio antes que la hora.
El portal público nunca habla directo con Postgres. RLS está activo en todas
las tablas y anon no tiene ninguna política. Todo el tráfico público pasa por
Server Actions que usan la service_role del lado del servidor, así que hay una
sola superficie de entrada que auditar.
Contra el doble booking manda la base, no el frontend. Una restricción de exclusión GiST sobre el rango de cada turno impide dos turnos activos solapados. La validación previa mejora el mensaje de error, pero entre leer la disponibilidad y escribir el turno siempre hay una ventana: la que cierra la base.
Todo se guarda en UTC (timestamptz) y se muestra en
America/Argentina/Buenos_Aires. Las fechas de agenda se identifican por su
clave local yyyy-MM-dd y no por un Date, porque un Date arrastra una hora
que al cruzar zonas corre los turnos de día.
Fase 1 completa: reservas públicas, agenda, solicitudes, servicios y disponibilidad.
Fase 2 completa (ver SPEC.md): emails transaccionales con el diseño de
la web, autogestión de cancelaciones, registro de cobros, venta suelta con
carrito de catálogo, productos con categorías y cierre de caja por categoría.
Fase 3 completa: recordatorio automático el día anterior al turno, con
dominio propio para los emails ([email protected]).
Mejoras de octubre completas (ver SPEC.md §9 y
tasks/plan-mejoras-octubre.md): botón de WhatsApp, alta de clientes de otra
zona, estados de carga del panel y recordatorio "Ya pasó un mes" con baja.
Pendiente (ver tasks/todo.md): pasar ADMIN_EMAIL a la casilla del
peluquero, Vercel Pro y copias de seguridad de Supabase antes de usarla con
clientes reales. CRM y reportes quedan para más adelante.