mirror of
https://github.com/marcogll/AnchorOS.git
synced 2026-03-15 19:24:32 +00:00
Compare commits
4 Commits
66e20d25a7
...
2c19c49f14
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2c19c49f14 | ||
|
|
1ca7a2cfbc | ||
|
|
d1735878ef | ||
|
|
bedf1c028a |
@@ -34,8 +34,9 @@ deploy.sh
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
API_TESTING_GUIDE.md
|
||||
DEPLOYMENT_README.md
|
||||
# Keep deployment guides in production image
|
||||
!DEPLOYMENT_README.md
|
||||
!API_TESTING_GUIDE.md
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
|
||||
@@ -27,6 +27,10 @@ RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
# App
|
||||
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
||||
|
||||
# Formbricks (Surveys - Optional)
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=your-environment-id
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
|
||||
|
||||
# Optional: Redis para caching
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
||||
|
||||
@@ -83,6 +83,74 @@
|
||||
- `POST /api/cron/reset-invitations` - Reset diario
|
||||
- Buscar: Invitaciones expiradas reseteadas
|
||||
|
||||
### **📧 Webhooks (Formularios Públicos)**
|
||||
- `POST https://flows.soul23.cloud/webhook-test/4YZ7RPfo1GT` - Webhook test
|
||||
- Body: Payload completo con form type
|
||||
- Buscar: 200 OK + acknowledgment
|
||||
- `POST https://flows.soul23.cloud/webhook/4YZ7RPfo1GT` - Webhook prod
|
||||
- Body: Payload completo con form type
|
||||
- Buscar: 200 OK + acknowledgment
|
||||
|
||||
**Form Types disponibles:**
|
||||
- `contact` - Formulario de contacto
|
||||
- `franchise` - Solicitud de franquicia
|
||||
- `membership` - Solicitud de membresía
|
||||
|
||||
**Payload Base:**
|
||||
```json
|
||||
{
|
||||
"form": "contact|franchise|membership",
|
||||
"timestamp_utc": "2026-01-18T04:26:30.187Z",
|
||||
"device_type": "mobile|desktop|unknown"
|
||||
}
|
||||
```
|
||||
|
||||
**Contact Payload:**
|
||||
```json
|
||||
{
|
||||
"form": "contact",
|
||||
"nombre": "Nombre Completo",
|
||||
"email": "email@example.com",
|
||||
"telefono": "+52 844 123 4567",
|
||||
"motivo": "cita|membresia|franquicia|servicios|pago|resena|otro",
|
||||
"mensaje": "Texto del mensaje",
|
||||
"timestamp_utc": "2026-01-18T04:26:30.187Z",
|
||||
"device_type": "mobile"
|
||||
}
|
||||
```
|
||||
|
||||
**Franchise Payload:**
|
||||
```json
|
||||
{
|
||||
"form": "franchise",
|
||||
"nombre": "Nombre Completo",
|
||||
"email": "email@example.com",
|
||||
"telefono": "+52 844 123 4567",
|
||||
"ciudad": "Monterrey",
|
||||
"estado": "Nuevo León",
|
||||
"socios": 2,
|
||||
"experiencia_sector": "1-3-anos",
|
||||
"experiencia_belleza": true,
|
||||
"mensaje": "Mensaje adicional",
|
||||
"timestamp_utc": "2026-01-18T04:26:30.187Z",
|
||||
"device_type": "desktop"
|
||||
}
|
||||
```
|
||||
|
||||
**Membership Payload:**
|
||||
```json
|
||||
{
|
||||
"form": "membership",
|
||||
"membership_id": "vip",
|
||||
"nombre": "Nombre Completo",
|
||||
"email": "email@example.com",
|
||||
"telefono": "+52 844 123 4567",
|
||||
"mensaje": "Pregunta específica",
|
||||
"timestamp_utc": "2026-01-18T04:26:30.187Z",
|
||||
"device_type": "mobile"
|
||||
}
|
||||
```
|
||||
|
||||
## 🔍 **Qué Buscar en Cada Respuesta**
|
||||
|
||||
### **✅ Éxito**
|
||||
|
||||
@@ -154,7 +154,30 @@ public/images/gallery/
|
||||
|
||||
---
|
||||
|
||||
## 8. Logo SVG Original (@src/logo.svg)
|
||||
## 8. Nuevos Componentes (@src/components/)
|
||||
|
||||
**Ubicación sugerida:** `components/`
|
||||
|
||||
**Componentes agregados:**
|
||||
- `animated-logo.tsx` - Logo SVG animado con fade-in
|
||||
- `rolling-phrases.tsx` - Frases rotativas para hero sections
|
||||
- `formbricks-provider.tsx` - Provider para encuestas Formbricks
|
||||
- `webhook-form.tsx` - Formulario unificado para webhooks
|
||||
- `app-wrapper.tsx` - Wrapper de aplicación con contexto
|
||||
- `loading-screen.tsx` - Pantalla de carga con animación
|
||||
- `pattern-overlay.tsx` - Overlay de patrones decorativos
|
||||
- `responsive-nav.tsx` - Navegación responsiva con menú móvil
|
||||
|
||||
**Iconos adicionales:**
|
||||
- Diamond (check, success states)
|
||||
- Crown (VIP tier)
|
||||
|
||||
**Colores actualizados:**
|
||||
- `--charcoal-brown`: #3f362e (marrón oscuro elegante)
|
||||
- `--deep-earth`: #6f5e4f (marrón medio)
|
||||
- `--mocha-taupe`: #b8a89a (beige cálido)
|
||||
|
||||
## 9. Logo SVG Original (@src/logo.svg)
|
||||
|
||||
**Ruta:** `src/logo.svg`
|
||||
|
||||
@@ -332,6 +355,30 @@ public/images/gallery/
|
||||
|
||||
---
|
||||
|
||||
## 📋 21. Formbricks Integration
|
||||
|
||||
**Ubicación:** `components/formbricks-provider.tsx`
|
||||
|
||||
**Configuración:**
|
||||
- Environment ID para surveys
|
||||
- API Host URL
|
||||
- Device detection (mobile/desktop)
|
||||
- Route change tracking
|
||||
|
||||
**Variables de entorno:**
|
||||
```bash
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=your-id
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
|
||||
```
|
||||
|
||||
**Uso previsto:**
|
||||
- Encuestas post-experiencia
|
||||
- Feedback de clientes
|
||||
- NPS (Net Promoter Score)
|
||||
- Estudios de satisfacción
|
||||
|
||||
---
|
||||
|
||||
## 📋 Checklist de Implementación
|
||||
|
||||
| Tarea | Estado | Prioridad |
|
||||
@@ -340,6 +387,17 @@ public/images/gallery/
|
||||
| Optimizar imágenes A23_VIA_* | pending | alta |
|
||||
| Implementar logo SVG en Hero sin animación | completed | alta |
|
||||
| Implementar logo SVG en Loading sin fade-in| completed | alta |
|
||||
| Crear componente animated-logo.tsx | completed | alta |
|
||||
| Crear componente rolling-phrases.tsx | completed | alta |
|
||||
| Crear componente webhook-form.tsx | completed | alta |
|
||||
| Crear componente formbricks-provider.tsx | completed | media |
|
||||
| Crear componente responsive-nav.tsx | completed | alta |
|
||||
| Actualizar colores a #3E352E | completed | alta |
|
||||
| Agregar campo motivo en contacto | completed | alta |
|
||||
| Agregar campos estado/ciudad/socios en franchise | pending | alta |
|
||||
| Agregar check experiencia belleza en franchise | pending | alta |
|
||||
| Actualizar info franchise a $100k | completed | alta |
|
||||
| Agregar link Contacto en nav/footer | completed | alta |
|
||||
| Agregar imágenes Hero/Fundamento | pending | media |
|
||||
| Agregar imágenes Historia | pending | media |
|
||||
| Agregar testimonios | pending | media |
|
||||
@@ -360,6 +418,12 @@ public/images/gallery/
|
||||
- **Background Loading:** #3F362E (Marrón oscuro elegante)
|
||||
- **Gradient (alternativo):** #6f5e4f → #8B4513 → #5a4a3a
|
||||
|
||||
### Colores de Botones
|
||||
- **Botón primario:** #3E352E (Marrón elegante) - reemplaza --deep-earth
|
||||
- **Botón secundario:** Gradiente --bone-white → --soft-cream
|
||||
- **Tarjetas featured:** #3E352E (Marrón elegante)
|
||||
- **Hover effects:** #3E352E/90 (90% opacidad)
|
||||
|
||||
### Fondos de Secciones
|
||||
- **Hero:** #F5F5DC (Bone White)
|
||||
- **Services:** #F5F5DC
|
||||
|
||||
@@ -25,6 +25,16 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJxxxxx
|
||||
SUPABASE_SERVICE_ROLE_KEY=eyJxxxxx
|
||||
RESEND_API_KEY=re_xxxxx
|
||||
NEXT_PUBLIC_APP_URL=https://tu-dominio.com
|
||||
|
||||
# Formbricks (opcional - encuestas)
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=your-environment-id
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
|
||||
|
||||
# Optional: Redis para caching
|
||||
REDIS_URL=redis://redis:6379
|
||||
|
||||
# Optional: Analytics
|
||||
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX
|
||||
```
|
||||
|
||||
### 3. **SSL Certificates**
|
||||
@@ -165,6 +175,83 @@ docker-compose -f docker-compose.prod.yml restart
|
||||
- Query optimization
|
||||
- Redis caching (opcional)
|
||||
|
||||
## 📝 **Formbricks Integration**
|
||||
|
||||
### **Configuración de Encuestas**
|
||||
```bash
|
||||
# Activar Formbricks para recolección de feedback
|
||||
NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=clxxxxxxxx
|
||||
NEXT_PUBLIC_FORMBRICKS_API_HOST=https://app.formbricks.com
|
||||
```
|
||||
|
||||
### **Webhooks**
|
||||
```bash
|
||||
# Endpoints de webhook para formularios
|
||||
# Test: https://flows.soul23.cloud/webhook-test/4YZ7RPfo1GT
|
||||
# Prod: https://flows.soul23.cloud/webhook/4YZ7RPfo1GT
|
||||
|
||||
# Formularios que envían a webhooks:
|
||||
# - contact (Contacto)
|
||||
# - franchise (Franquicias)
|
||||
# - membership (Membresías)
|
||||
|
||||
# Payload structure:
|
||||
{
|
||||
"form": "contact|franchise|membership",
|
||||
"timestamp_utc": "ISO-8601",
|
||||
"device_type": "mobile|desktop|unknown",
|
||||
"...": "campos específicos del formulario"
|
||||
}
|
||||
```
|
||||
|
||||
### **Form Types y Campos**
|
||||
|
||||
**Contact (contacto)**
|
||||
```json
|
||||
{
|
||||
"form": "contact",
|
||||
"nombre": "string",
|
||||
"email": "string",
|
||||
"telefono": "string",
|
||||
"motivo": "cita|membresia|franquicia|servicios|pago|resena|otro",
|
||||
"mensaje": "string",
|
||||
"timestamp_utc": "string",
|
||||
"device_type": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Franchise (franquicias)**
|
||||
```json
|
||||
{
|
||||
"form": "franchise",
|
||||
"nombre": "string",
|
||||
"email": "string",
|
||||
"telefono": "string",
|
||||
"ciudad": "string",
|
||||
"estado": "string",
|
||||
"socios": "number",
|
||||
"experiencia_sector": "string",
|
||||
"experiencia_belleza": "boolean",
|
||||
"mensaje": "string",
|
||||
"timestamp_utc": "string",
|
||||
"device_type": "string"
|
||||
}
|
||||
```
|
||||
|
||||
**Membership (membresías)**
|
||||
```json
|
||||
{
|
||||
"form": "membership",
|
||||
"membership_id": "gold|black|vip",
|
||||
"nombre": "string",
|
||||
"email": "string",
|
||||
"telefono": "string",
|
||||
"mensaje": "string",
|
||||
"timestamp_utc": "string",
|
||||
"device_type": "string"
|
||||
}
|
||||
```
|
||||
|
||||
## 🔒 **Seguridad**
|
||||
|
||||
- SSL/TLS 1.2+
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# Dockerfile optimizado para Next.js production
|
||||
FROM node:18-alpine AS base
|
||||
|
||||
# Instalar dependencias solo para producción
|
||||
# Instalar dependencias para build
|
||||
FROM base AS deps
|
||||
RUN apk add --no-cache libc6-compat
|
||||
WORKDIR /app
|
||||
|
||||
# Copiar archivos de dependencias
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci --only=production --ignore-scripts && npm cache clean --force
|
||||
RUN npm ci --ignore-scripts && npm cache clean --force
|
||||
|
||||
# Build stage
|
||||
FROM base AS builder
|
||||
@@ -19,6 +19,11 @@ COPY . .
|
||||
# Variables de entorno para build
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_PUBLIC_SUPABASE_URL=https://placeholder.supabase.co
|
||||
ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=placeholder-anon-key
|
||||
ENV SUPABASE_SERVICE_ROLE_KEY=placeholder-service-role-key
|
||||
ENV STRIPE_SECRET_KEY=sk_test_placeholder_key
|
||||
ENV RESEND_API_KEY=re_placeholder_key
|
||||
|
||||
# Build optimizado
|
||||
RUN npm run build
|
||||
|
||||
@@ -6,8 +6,8 @@ import { createClient } from '@supabase/supabase-js';
|
||||
* @returns Statistics for dashboard display
|
||||
*/
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
|
||||
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://your-project.supabase.co'
|
||||
const supabaseServiceKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'your-service-role-key-here'
|
||||
|
||||
if (!supabaseUrl || !supabaseServiceKey) {
|
||||
throw new Error('Missing Supabase environment variables');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { supabaseAdmin } from '@/lib/supabase/admin'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
|
||||
/**
|
||||
* @description Retrieves all active locations
|
||||
*/
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { data: locations, error } = await supabaseAdmin
|
||||
const { data: locations, error } = await supabase
|
||||
.from('locations')
|
||||
.select('*')
|
||||
.eq('is_active', true)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextRequest, NextResponse } from 'next/server'
|
||||
import { supabaseAdmin } from '@/lib/supabase/admin'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
|
||||
/**
|
||||
* @description Retrieves active services, optionally filtered by location
|
||||
@@ -9,7 +9,7 @@ export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url)
|
||||
const locationId = searchParams.get('location_id')
|
||||
|
||||
let query = supabaseAdmin
|
||||
let query = supabase
|
||||
.from('services')
|
||||
.select('id, name, description, duration_minutes, base_price, requires_dual_artist, premium_fee_enabled, category, is_active, created_at, updated_at')
|
||||
.eq('is_active', true)
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import { AnimatedLogo } from '@/components/animated-logo'
|
||||
import { RollingPhrases } from '@/components/rolling-phrases'
|
||||
|
||||
/** @description Services page with home page style structure */
|
||||
'use client'
|
||||
|
||||
import { useState, useEffect } from 'react'
|
||||
import { AnimatedLogo } from '@/components/animated-logo'
|
||||
import { RollingPhrases } from '@/components/rolling-phrases'
|
||||
|
||||
/** @description Services page with home page style structure */
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
interface Service {
|
||||
id: string
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -247,7 +247,6 @@ export default function PayrollManagement() {
|
||||
<SelectValue placeholder="Todos los empleados" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Todos los empleados</SelectItem>
|
||||
{/* This would need to be populated with actual staff data */}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
@@ -310,7 +310,6 @@ export default function POSSystem() {
|
||||
<SelectValue placeholder="Seleccionar cliente (opcional)" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="">Sin cliente especificado</SelectItem>
|
||||
{customers.slice(0, 10).map(customer => (
|
||||
<SelectItem key={customer.id} value={customer.id}>
|
||||
{customer.first_name} {customer.last_name}
|
||||
|
||||
@@ -14,6 +14,8 @@ services:
|
||||
- SUPABASE_SERVICE_ROLE_KEY=${SUPABASE_SERVICE_ROLE_KEY}
|
||||
- RESEND_API_KEY=${RESEND_API_KEY}
|
||||
- NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
|
||||
- NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID=${NEXT_PUBLIC_FORMBRICKS_ENVIRONMENT_ID}
|
||||
- NEXT_PUBLIC_FORMBRICKS_API_HOST=${NEXT_PUBLIC_FORMBRICKS_API_HOST}
|
||||
ports:
|
||||
- "3000:3000"
|
||||
networks:
|
||||
@@ -23,7 +25,6 @@ services:
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
# Recursos optimizados
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
@@ -48,10 +49,6 @@ services:
|
||||
- anchoros
|
||||
networks:
|
||||
- anchoros_network
|
||||
# SSL termination y caching
|
||||
environment:
|
||||
- NGINX_ENVSUBST_TEMPLATE_DIR=/etc/nginx/templates
|
||||
- NGINX_ENVSUBST_OUTPUT_DIR=/etc/nginx/conf.d
|
||||
|
||||
# Opcional: Redis para caching adicional
|
||||
redis:
|
||||
@@ -70,4 +67,4 @@ volumes:
|
||||
|
||||
networks:
|
||||
anchoros_network:
|
||||
driver: bridge
|
||||
driver: bridge
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createClient } from '@supabase/supabase-js'
|
||||
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
|
||||
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY!
|
||||
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL || 'https://your-project.supabase.co'
|
||||
const supabaseServiceRoleKey = process.env.SUPABASE_SERVICE_ROLE_KEY || 'your-service-role-key-here'
|
||||
|
||||
// Admin Supabase client for server-side operations with service role
|
||||
export const supabaseAdmin = createClient(
|
||||
|
||||
42
scripts/load-admin-users.sh
Executable file
42
scripts/load-admin-users.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script para cargar usuarios admin en Supabase
|
||||
# Uso: ./load-admin-users.sh
|
||||
|
||||
echo "Cargando usuarios admin en Supabase..."
|
||||
echo ""
|
||||
|
||||
# Verificar que las variables de entorno están definidas
|
||||
if [ -z "$NEXT_PUBLIC_SUPABASE_URL" ] || [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
|
||||
echo "ERROR: Variables de entorno no definidas."
|
||||
echo "Asegúrate de tener NEXT_PUBLIC_SUPABASE_URL y SUPABASE_SERVICE_ROLE_KEY definidas."
|
||||
echo ""
|
||||
echo "Ejemplo de uso:"
|
||||
echo " export NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co"
|
||||
echo " export SUPABASE_SERVICE_ROLE_KEY=your-service-role-key"
|
||||
echo " ./load-admin-users.sh"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ejecutar el script SQL
|
||||
echo "Ejecutando scripts/seed-admin-users.sql..."
|
||||
echo ""
|
||||
|
||||
psql "$SUPABASE_URL?options=project%3Ddefault" <<EOF
|
||||
$(cat scripts/seed-admin-users.sql)
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
echo ""
|
||||
echo "✓ Usuarios admin cargados exitosamente:"
|
||||
echo " - frida.lara@example.com (Frida Lara) - Admin"
|
||||
echo " - america.cruz@example.com (América de la Cruz) - Admin"
|
||||
echo " - alejandra.ponce@example.com (Alejandra Ponce) - Admin"
|
||||
echo ""
|
||||
echo "Contraseña predeterminada: admin123"
|
||||
echo "IMPORTANTE: Cambiar contraseñas en primer inicio de sesión."
|
||||
else
|
||||
echo ""
|
||||
echo "✗ Error al cargar usuarios admin"
|
||||
exit 1
|
||||
fi
|
||||
111
scripts/seed-admin-users.sql
Normal file
111
scripts/seed-admin-users.sql
Normal file
@@ -0,0 +1,111 @@
|
||||
-- Agregar usuarios admin específicos
|
||||
-- Script para insertar administradores iniciales
|
||||
|
||||
-- Primero, verificar que las tablas existen
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'users'
|
||||
) THEN
|
||||
RAISE EXCEPTION 'Tabla users no existe';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Insertar Frida Lara como admin
|
||||
INSERT INTO users (email, password_hash, role, created_at, updated_at, email_verified)
|
||||
VALUES (
|
||||
'frida.lara@example.com',
|
||||
crypt('admin123', gen_salt('bf')),
|
||||
'admin',
|
||||
NOW(),
|
||||
NOW(),
|
||||
true
|
||||
)
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- Insertar América de la Cruz como admin
|
||||
INSERT INTO users (email, password_hash, role, created_at, updated_at, email_verified)
|
||||
VALUES (
|
||||
'america.cruz@example.com',
|
||||
crypt('admin123', gen_salt('bf')),
|
||||
'admin',
|
||||
NOW(),
|
||||
NOW(),
|
||||
true
|
||||
)
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- Insertar Alejandra Ponce como admin
|
||||
INSERT INTO users (email, password_hash, role, created_at, updated_at, email_verified)
|
||||
VALUES (
|
||||
'alejandra.ponce@example.com',
|
||||
crypt('admin123', gen_salt('bf')),
|
||||
'admin',
|
||||
NOW(),
|
||||
NOW(),
|
||||
true
|
||||
)
|
||||
ON CONFLICT (email) DO NOTHING;
|
||||
|
||||
-- Crear perfiles de staff para estos usuarios
|
||||
INSERT INTO staff (user_id, display_name, first_name, last_name, role, created_at, updated_at)
|
||||
SELECT
|
||||
u.id,
|
||||
u.email,
|
||||
CASE
|
||||
WHEN u.email = 'frida.lara@example.com' THEN 'Frida'
|
||||
WHEN u.email = 'america.cruz@example.com' THEN 'América'
|
||||
WHEN u.email = 'alejandra.ponce@example.com' THEN 'Alejandra'
|
||||
END,
|
||||
CASE
|
||||
WHEN u.email = 'frida.lara@example.com' THEN 'Lara'
|
||||
WHEN u.email = 'america.cruz@example.com' THEN 'de la Cruz'
|
||||
WHEN u.email = 'alejandra.ponce@example.com' THEN 'Ponce'
|
||||
END,
|
||||
'admin',
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM users u
|
||||
WHERE u.email IN (
|
||||
'frida.lara@example.com',
|
||||
'america.cruz@example.com',
|
||||
'alejandra.ponce@example.com'
|
||||
)
|
||||
AND u.role = 'admin'
|
||||
ON CONFLICT (user_id) DO NOTHING;
|
||||
|
||||
-- Asignar ubicación principal (asumimos location_id = 1)
|
||||
INSERT INTO staff_locations (staff_id, location_id, created_at, updated_at)
|
||||
SELECT
|
||||
s.id,
|
||||
1,
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM staff s
|
||||
JOIN users u ON s.user_id = u.id
|
||||
WHERE u.email IN (
|
||||
'frida.lara@example.com',
|
||||
'america.cruz@example.com',
|
||||
'alejandra.ponce@example.com'
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM staff_locations sl
|
||||
WHERE sl.staff_id = s.id
|
||||
);
|
||||
|
||||
-- Confirmación
|
||||
SELECT
|
||||
u.email,
|
||||
u.role,
|
||||
s.display_name,
|
||||
sl.location_id
|
||||
FROM users u
|
||||
LEFT JOIN staff s ON s.user_id = u.id
|
||||
LEFT JOIN staff_locations sl ON sl.staff_id = s.id
|
||||
WHERE u.email IN (
|
||||
'frida.lara@example.com',
|
||||
'america.cruz@example.com',
|
||||
'alejandra.ponce@example.com'
|
||||
);
|
||||
Reference in New Issue
Block a user