import type { PoolClient } from "pg"; /** * Valores con los que nace un negocio. * * Existe por la misma razón que su gemelo del backend de demo: un negocio sin * `working_hours` no tiene ninguna franja agendable en ninguna fecha, y uno sin * `slug` no tiene página pública. Poner el default en el `INSERT` —y no en una * migración de relleno— es lo único que cubre a las filas creadas después de que * la migración ya corrió. * * NO se importa desde `server/`: los dos backends conviven sin compartir código, * y cruzarlos ataría la evolución de uno a la del otro. */ export const DEFAULT_WORKING_HOURS = JSON.stringify({ 1: { start: "09:00", end: "20:00" }, 2: { start: "09:00", end: "20:00" }, 3: { start: "09:00", end: "20:00" }, 4: { start: "09:00", end: "20:00" }, 5: { start: "09:00", end: "20:00" }, 6: null, 7: null, }); /** "Lumière Estética & Spa" → "lumiere-estetica-spa". Puro. */ export function slugify(s: string): string { return ( (s || "negocio") .toLowerCase() .normalize("NFD") .replace(/[̀-ͯ]/g, "") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .slice(0, 60) || "negocio" ); } /** * Slug único dentro de la plataforma, con sufijo numérico si ya está tomado. * * Recibe el cliente de la transacción, no el pool: comprobar la unicidad en una * conexión y escribir en otra deja una ventana en la que dos altas simultáneas * eligen el mismo slug. La restricción `UNIQUE` de la columna es la red final, * pero conviene no depender de que salte. */ export async function uniqueSlugPg(tx: PoolClient, nombre: string): Promise { const base = slugify(nombre); let slug = base; for (let n = 2; ; n++) { const { rows } = await tx.query(`SELECT 1 FROM businesses WHERE slug = $1`, [slug]); if (!rows.length) return slug; slug = `${base}-${n}`; } }