diff --git a/.gitignore b/.gitignore index 5ef6a52..f390d12 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +/src/generated/prisma diff --git a/docs/MANUAL_MODELOS.md b/docs/MANUAL_MODELOS.md new file mode 100644 index 0000000..0c85716 --- /dev/null +++ b/docs/MANUAL_MODELOS.md @@ -0,0 +1,336 @@ +# IrisTEA - Manual de Modelos de Negócio + +## Visão Geral + +A IrisTEA pode operar em **3 modelos diferentes**, cada um com suas vantagens: + +| Modelo | Descrição | Custo Operacional | Escalabilidade | +|--------|-----------|-------------------|----------------| +| **100% IA** | Terapeuta virtual IA | Muito baixo | Infinita | +| **Híbrido** | IA + Supervisão humana | Médio | Alta | +| **100% Humano** | Terapeutas BCBA via teleterapia | Alto | Limitada | + +--- + +## MODELO 1: 100% IA (Atual) + +### Como Funciona +``` +Criança ←→ Plataforma IA ←→ Análise Automática + ↓ + Relatórios para Pais +``` + +### Tecnologias +- **GPT-4 / Claude** para conversação e orientação +- **Visão Computacional** para análise de comportamentos +- **Speech-to-Text** para análise de fala +- **Machine Learning** para personalização + +### Preços Sugeridos +| Plano | Preço | Margem | +|-------|-------|--------| +| Básico | R$ 97/mês | ~90% | +| Completo | R$ 197/mês | ~92% | +| Premium | R$ 397/mês | ~85% (inclui BCBA) | + +### Vantagens +- ✅ Disponível 24/7 +- ✅ Sem lista de espera +- ✅ Custo muito baixo para o cliente +- ✅ Escalabilidade infinita +- ✅ Consistência total +- ✅ Análise de dados em tempo real + +### Desvantagens +- ❌ Casos complexos precisam de humano +- ❌ Resistência de alguns pais +- ❌ Regulamentação incerta +- ❌ Não substitui diagnóstico + +### Custos Operacionais (por usuário/mês) +- API OpenAI/Anthropic: ~R$ 15-30 +- Infraestrutura: ~R$ 5 +- **Total: ~R$ 20-35/usuário** + +--- + +## MODELO 2: HÍBRIDO (Recomendado para começar) + +### Como Funciona +``` +Criança ←→ Plataforma IA (80% do tempo) + ↓ + BCBA Supervisor (20% do tempo) + ↓ + Plano Terapêutico Validado +``` + +### Estrutura +1. **IA faz o dia a dia:** + - Sessões interativas + - Atividades gamificadas + - Coleta de dados + - Relatórios automáticos + +2. **BCBA humano faz:** + - Avaliação inicial (1 sessão) + - Supervisão mensal (1-2 sessões) + - Ajustes no plano terapêutico + - Casos de crise + +### Preços Sugeridos +| Plano | Preço | Sessões BCBA | +|-------|-------|--------------| +| Essencial | R$ 297/mês | 1/mês | +| Completo | R$ 497/mês | 2/mês | +| Intensivo | R$ 897/mês | 4/mês | + +### Vantagens +- ✅ Credibilidade (tem humano) +- ✅ Atende casos complexos +- ✅ Mais fácil de regulamentar +- ✅ IA reduz custo do BCBA +- ✅ Escalável (menos humanos necessários) + +### Desvantagens +- ❌ Precisa contratar BCBAs +- ❌ Gestão de agenda +- ❌ Custo maior que 100% IA + +### Custos Operacionais (por usuário/mês) +- API IA: ~R$ 20 +- BCBA (1h/mês): ~R$ 100-150 +- Infraestrutura: ~R$ 5 +- **Total: ~R$ 125-175/usuário** + +### Como Contratar BCBAs +1. **CLT**: Salário R$ 8.000-15.000 + benefícios + - 1 BCBA atende ~40 famílias/mês + - Custo/família: ~R$ 200-375 + +2. **PJ/Freelancer**: R$ 150-300/hora + - Mais flexível + - Sem vínculo + - Custo/família (1h/mês): R$ 150-300 + +3. **Parceria com Clínicas**: Comissão 30-40% + - Eles fornecem os BCBAs + - Você fornece a plataforma e clientes + +--- + +## MODELO 3: 100% HUMANO (Estilo AnswersNow) + +### Como Funciona +``` +Criança ←→ BCBA via Vídeo ←→ Plataforma + ↓ + Sessões Semanais (2-8x) + ↓ + Relatórios Manuais +``` + +### Estrutura da Equipe +``` +CEO + ├── Diretor Clínico (BCBA-D) + │ ├── BCBA Supervisores (5-10) + │ │ └── Cada BCBA atende 15-20 famílias + │ └── Coordenador de Casos + ├── Diretor Comercial + │ ├── Vendas/Intake + │ └── Marketing + └── Diretor de Tecnologia + ├── Desenvolvedores + └── Suporte +``` + +### Preços Sugeridos (Mercado Brasil) +| Plano | Sessões/mês | Preço | +|-------|-------------|-------| +| Básico | 4 (1x/semana) | R$ 800/mês | +| Padrão | 8 (2x/semana) | R$ 1.400/mês | +| Intensivo | 16 (4x/semana) | R$ 2.400/mês | + +### Preços AnswersNow (EUA - referência) +- ~$200-400/semana = $800-1.600/mês +- Convertido: R$ 4.000-8.000/mês + +### Vantagens +- ✅ Modelo comprovado +- ✅ Regulamentação clara +- ✅ Confiança dos pais +- ✅ Atende qualquer complexidade + +### Desvantagens +- ❌ Alto custo operacional +- ❌ Escala limitada (precisa contratar) +- ❌ Lista de espera inevitável +- ❌ Dependência de profissionais + +### Custos Operacionais +- BCBA (4h/mês por família): R$ 400-600 +- Plataforma: R$ 20 +- Overhead (admin, comercial): R$ 100 +- **Total: ~R$ 520-720/usuário** +- **Margem**: 30-50% nos planos + +--- + +## COMPARATIVO FINANCEIRO + +### Cenário: 500 famílias ativas + +| Métrica | 100% IA | Híbrido | 100% Humano | +|---------|---------|---------|-------------| +| **Receita/mês** | R$ 98.500 | R$ 248.500 | R$ 700.000 | +| **Custo/mês** | R$ 17.500 | R$ 87.500 | R$ 350.000 | +| **Lucro/mês** | R$ 81.000 | R$ 161.000 | R$ 350.000 | +| **Margem** | 82% | 65% | 50% | +| **Equipe necessária** | 3-5 | 10-15 | 30-50 | +| **Tempo p/ escalar** | Dias | Meses | Anos | + +### Cenário: 5.000 famílias + +| Métrica | 100% IA | Híbrido | 100% Humano | +|---------|---------|---------|-------------| +| **Receita/mês** | R$ 985.000 | R$ 2.485.000 | R$ 7.000.000 | +| **Custo/mês** | R$ 175.000 | R$ 875.000 | R$ 3.500.000 | +| **Lucro/mês** | R$ 810.000 | R$ 1.610.000 | R$ 3.500.000 | +| **Equipe** | 10-15 | 50-80 | 200-300 | + +--- + +## RECOMENDAÇÃO + +### Fase 1 (0-6 meses): Modelo Híbrido Light +- Lançar com IA + 1 sessão BCBA/mês +- Validar produto e mercado +- Contratar 2-3 BCBAs PJ +- Meta: 100 famílias + +### Fase 2 (6-18 meses): Escalar Híbrido +- Aumentar base de BCBAs +- Melhorar IA com dados reais +- Parcerias com clínicas +- Meta: 1.000 famílias + +### Fase 3 (18+ meses): Migrar para IA +- IA já treinada com dados suficientes +- Oferecer planos 100% IA (mais baratos) +- Manter híbrido como premium +- Meta: 10.000+ famílias + +--- + +## REQUISITOS LEGAIS + +### Para operar no Brasil: + +1. **Empresa** + - CNPJ (LTDA ou SA) + - CNAE: 8650-0/03 (atividades de psicologia) + +2. **Responsável Técnico** + - BCBA ou Psicólogo com CRP + - Assina como responsável clínico + +3. **Regulamentação** + - CFP (se psicólogo) / CRP estadual + - BACB (para BCBAs internacionais) + - LGPD compliance + +4. **Seguro** + - Responsabilidade Civil Profissional + - ~R$ 3.000-5.000/ano + +### Considerações sobre IA +- Não existe regulamentação específica para "terapeuta IA" +- Recomendação: posicionar como "ferramenta de apoio" +- Ter sempre supervisão humana disponível +- Deixar claro que não substitui diagnóstico médico + +--- + +## STACK TECNOLÓGICA + +### MVP (o que já temos) +- Next.js + TypeScript +- Tailwind CSS +- Autenticação JWT + +### Próximos Passos +``` +1. Backend + - Node.js/Go API + - PostgreSQL + - Redis (cache/sessões) + +2. IA/ML + - OpenAI GPT-4 API + - Azure Cognitive Services (visão) + - Whisper (transcrição) + +3. Vídeo + - Azure Communication Services + - ou Daily.co / Twilio + +4. Pagamentos + - Stripe ou Pagar.me + - Assinaturas recorrentes + +5. Infraestrutura + - DigitalOcean / AWS + - CDN para assets +``` + +--- + +## ROADMAP SUGERIDO + +### Mês 1-2 +- [ ] Finalizar landing page +- [ ] Implementar autenticação +- [ ] Integrar pagamentos +- [ ] Chat com IA básico + +### Mês 3-4 +- [ ] Dashboard para pais +- [ ] Sistema de sessões +- [ ] Relatórios automáticos +- [ ] Contratar 2 BCBAs PJ + +### Mês 5-6 +- [ ] Lançamento beta (50 famílias) +- [ ] Coletar feedback +- [ ] Iterar produto +- [ ] Preparar lançamento oficial + +### Mês 7-12 +- [ ] Lançamento oficial +- [ ] Marketing digital +- [ ] Parcerias com clínicas +- [ ] Meta: 500 famílias + +--- + +## CONTATOS ÚTEIS + +### Associações +- ABPMC (Análise do Comportamento): abpmc.org.br +- SBNp (Neuropsicologia): sbnp.org.br + +### Certificação BCBA +- BACB: bacb.com +- Cursos no Brasil: várias universidades + +### Referências +- AnswersNow: getanswersnow.com +- Rethink: rethinkfirst.com +- Catalyst: catalyst.care + +--- + +*Documento criado em Fevereiro/2026* +*IrisTEA - Terapia ABA Inteligente* diff --git a/docs/MODELO_NEGOCIO.md b/docs/MODELO_NEGOCIO.md new file mode 100644 index 0000000..a039b69 --- /dev/null +++ b/docs/MODELO_NEGOCIO.md @@ -0,0 +1,266 @@ +# IrisTEA - Modelo de Negócio + +## 🎯 O que é + +**Plataforma de apoio ao desenvolvimento infantil para crianças com TEA**, combinando: +- Atividades interativas guiadas por IA +- Supervisão de profissionais certificados (BCBAs) +- Treinamento de pais/cuidadores + +--- + +## 🔄 Como Funciona + +``` +┌─────────────────────────────────────────────────────────────┐ +│ FAMÍLIA │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────┐ │ +│ │ PLATAFORMA IRISTEA │ │ +│ │ │ │ +│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ IA │ │ BCBA │ │ DADOS │ │ │ +│ │ │ 80% │ + │ 20% │ = │ANALYTICS │ │ │ +│ │ │ do tempo │ │ do tempo │ │ │ │ │ +│ │ └──────────┘ └──────────┘ └──────────┘ │ │ +│ │ │ │ │ │ │ +│ │ ▼ ▼ ▼ │ │ +│ │ • Atividades • Avaliação • Relatórios │ │ +│ │ • Chat 24/7 • Supervisão • Progresso │ │ +│ │ • Exercícios • Plano • Métricas │ │ +│ │ • Gamificação • Ajustes • Insights │ │ +│ └─────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ CRIANÇA EVOLUI │ +└─────────────────────────────────────────────────────────────┘ +``` + +--- + +## 💰 Modelo de Receita + +### Planos de Assinatura (B2C) + +| Plano | Preço | O que inclui | +|-------|-------|--------------| +| **Essencial** | R$ 297/mês | IA ilimitada + 1 sessão BCBA/mês | +| **Completo** | R$ 497/mês | IA ilimitada + 2 sessões BCBA/mês + grupo pais | +| **Intensivo** | R$ 897/mês | IA ilimitada + 4 sessões BCBA/mês + multidisciplinar | + +### Receita Adicional (Futuro) + +| Fonte | Modelo | +|-------|--------| +| **B2B Clínicas** | Licenciamento da plataforma (R$ 2.000-5.000/mês) | +| **B2B Escolas** | Pacotes institucionais | +| **B2B Planos de Saúde** | Convênio/parceria | +| **Cursos para Pais** | Venda avulsa (R$ 197-497) | +| **Certificação ABA** | Parceria com instituições | + +--- + +## 📊 Unit Economics + +### Por Família (Plano Completo - R$ 497/mês) + +**Receita:** R$ 497 + +**Custos Variáveis:** +| Item | Custo | +|------|-------| +| API IA (OpenAI/Azure) | R$ 25 | +| BCBA (2h × R$ 80/h) | R$ 160 | +| Infraestrutura | R$ 10 | +| Pagamento (3.5%) | R$ 17 | +| **Total Variável** | **R$ 212** | + +**Margem Bruta:** R$ 285 (57%) + +### Projeção Escala + +| Famílias | Receita/mês | Custo Var. | Margem Bruta | +|----------|-------------|------------|--------------| +| 100 | R$ 49.700 | R$ 21.200 | R$ 28.500 | +| 500 | R$ 248.500 | R$ 106.000 | R$ 142.500 | +| 1.000 | R$ 497.000 | R$ 212.000 | R$ 285.000 | +| 5.000 | R$ 2.485.000 | R$ 1.060.000 | R$ 1.425.000 | + +--- + +## 👥 Estrutura Operacional + +### Equipe Mínima (Fase 1: 0-200 famílias) + +| Cargo | Qtd | Salário | Total/mês | +|-------|-----|---------|-----------| +| CEO/Fundador | 1 | Pró-labore | R$ 5.000 | +| Desenvolvedor Full Stack | 1 | R$ 12.000 | R$ 12.000 | +| BCBA Responsável Técnico | 1 | R$ 10.000 | R$ 10.000 | +| BCBAs PJ (atendimento) | 3 | R$ 80/h | ~R$ 15.000 | +| Marketing/Comercial | 1 | R$ 6.000 | R$ 6.000 | +| **Total Folha** | | | **R$ 48.000** | + +### Equipe Escala (Fase 2: 500-1000 famílias) + +| Cargo | Qtd | Total/mês | +|-------|-----|-----------| +| Diretoria | 2 | R$ 25.000 | +| Desenvolvimento | 3 | R$ 40.000 | +| Clínico (BCBAs) | 10 | R$ 80.000 | +| Comercial/Marketing | 3 | R$ 25.000 | +| Suporte/CS | 2 | R$ 10.000 | +| Administrativo | 1 | R$ 5.000 | +| **Total** | **21** | **R$ 185.000** | + +--- + +## 🎯 Funil de Vendas + +``` +TOPO (Awareness) +│ • Anúncios Facebook/Instagram +│ • Google Ads (palavras: autismo, TEA, ABA) +│ • Conteúdo blog/YouTube +│ • Parcerias com influenciadores +│ +▼ 10.000 visitantes/mês + +MEIO (Consideração) +│ • Landing page +│ • Lead magnet (e-book gratuito) +│ • Webinars gratuitos +│ • Avaliação gratuita +│ +▼ 500 leads/mês (5% conversão) + +FUNDO (Decisão) +│ • Trial 7 dias grátis +│ • Ligação de vendas +│ • Demonstração personalizada +│ +▼ 50 clientes/mês (10% conversão) +``` + +### Métricas Alvo + +| Métrica | Meta | +|---------|------| +| CAC (Custo Aquisição Cliente) | < R$ 300 | +| LTV (Lifetime Value) | > R$ 3.000 | +| LTV/CAC | > 10x | +| Churn mensal | < 5% | +| NPS | > 60 | + +--- + +## 🏆 Diferenciais Competitivos + +### vs Clínicas Tradicionais +| Tradicional | IrisTEA | +|-------------|---------| +| R$ 2.000-5.000/mês | R$ 297-897/mês | +| 3-12 meses de espera | Início em 48h | +| Horário comercial | 24/7 com IA | +| 1 cidade | Brasil todo | +| Relatórios manuais | Automáticos em tempo real | + +### vs Apps Educativos +| Apps genéricos | IrisTEA | +|----------------|---------| +| Atividades genéricas | Baseado em ABA | +| Sem profissional | BCBA supervisionando | +| Sem personalização | IA adaptativa | +| Sem relatórios | Dashboard completo | + +--- + +## 📅 Roadmap + +### Q1 2026 (Jan-Mar) +- [x] MVP landing page +- [x] Logo e identidade +- [ ] Sistema de autenticação +- [ ] Integração pagamentos +- [ ] Chat com IA básico +- [ ] Contratar 2 BCBAs + +### Q2 2026 (Abr-Jun) +- [ ] Dashboard completo +- [ ] Sistema de sessões vídeo +- [ ] Relatórios automáticos +- [ ] Beta fechado (50 famílias) +- [ ] Validar product-market fit + +### Q3 2026 (Jul-Set) +- [ ] Lançamento público +- [ ] Marketing digital +- [ ] Meta: 200 famílias +- [ ] App mobile + +### Q4 2026 (Out-Dez) +- [ ] Parcerias clínicas +- [ ] B2B para escolas +- [ ] Meta: 500 famílias +- [ ] Preparar para investimento + +--- + +## 💵 Projeção Financeira (Ano 1) + +| Mês | Famílias | Receita | Custos | Lucro | +|-----|----------|---------|--------|-------| +| 1 | 10 | R$ 4.970 | R$ 50.000 | -R$ 45.030 | +| 2 | 25 | R$ 12.425 | R$ 52.000 | -R$ 39.575 | +| 3 | 50 | R$ 24.850 | R$ 55.000 | -R$ 30.150 | +| 4 | 80 | R$ 39.760 | R$ 58.000 | -R$ 18.240 | +| 5 | 120 | R$ 59.640 | R$ 65.000 | -R$ 5.360 | +| 6 | 170 | R$ 84.490 | R$ 75.000 | **R$ 9.490** | +| 7 | 220 | R$ 109.340 | R$ 85.000 | R$ 24.340 | +| 8 | 280 | R$ 139.160 | R$ 95.000 | R$ 44.160 | +| 9 | 350 | R$ 173.950 | R$ 110.000 | R$ 63.950 | +| 10 | 420 | R$ 208.740 | R$ 125.000 | R$ 83.740 | +| 11 | 480 | R$ 238.560 | R$ 140.000 | R$ 98.560 | +| 12 | 550 | R$ 273.350 | R$ 155.000 | R$ 118.350 | +| **ANO** | | **R$ 1.369.235** | **R$ 1.065.000** | **R$ 304.235** | + +**Break-even:** Mês 6 (~150 famílias) + +--- + +## 🚀 Necessidade de Investimento + +### Para chegar ao break-even (6 meses) + +| Item | Valor | +|------|-------| +| Desenvolvimento plataforma | R$ 100.000 | +| Marketing inicial | R$ 80.000 | +| Equipe (6 meses) | R$ 288.000 | +| Infraestrutura | R$ 20.000 | +| Jurídico/Contábil | R$ 15.000 | +| Reserva | R$ 47.000 | +| **Total** | **R$ 550.000** | + +### Opções de Funding + +1. **Bootstrapping:** Começar menor, crescer orgânico +2. **Investidor Anjo:** R$ 300-500k por 10-15% +3. **Aceleradoras:** Inovativa, Startup Chile, Y Combinator +4. **Venture Capital:** Série Seed após tração + +--- + +## 📞 Próximos Passos + +1. **Validar demanda:** Criar lista de espera, medir interesse +2. **Prototipar IA:** Testar chat com 10 famílias +3. **Contratar BCBA:** Responsável técnico +4. **Registrar domínio:** iristea.com.br +5. **MVP funcional:** 60 dias + +--- + +*Documento: Modelo de Negócio IrisTEA* +*Versão: 1.0 - Fevereiro 2026* diff --git a/package-lock.json b/package-lock.json index 993a23c..19cee25 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,12 +8,20 @@ "name": "iris", "version": "0.1.0", "dependencies": { + "@auth/prisma-adapter": "^2.11.1", + "@prisma/client": "^6.19.2", + "bcrypt": "^6.0.0", + "framer-motion": "^12.33.0", + "lucide-react": "^0.563.0", "next": "16.1.6", + "next-auth": "^5.0.0-beta.30", + "prisma": "^6.19.2", "react": "19.2.3", "react-dom": "19.2.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/bcrypt": "^6.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", @@ -36,6 +44,47 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@auth/core": { + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.41.1.tgz", + "integrity": "sha512-t9cJ2zNYAdWMacGRMT6+r4xr1uybIdmYa49calBPeTqwgAFPV/88ac9TEvCR85pvATiSPt8VaNf+Gt24JIT/uw==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^7.0.7" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@auth/prisma-adapter": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@auth/prisma-adapter/-/prisma-adapter-2.11.1.tgz", + "integrity": "sha512-Ke7DXP0Fy0Mlmjz/ZJLXwQash2UkA4621xCM0rMtEczr1kppLc/njCbUkHkIQ/PnmILjqSPEKeTjDPsYruvkug==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.41.1" + }, + "peerDependencies": { + "@prisma/client": ">=2.26.0 || >=3 || >=4 || >=5 || >=6" + } + }, "node_modules/@babel/code-frame": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", @@ -1226,6 +1275,94 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@prisma/client": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", + "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", + "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.18.4", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", + "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", + "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.2", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", + "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.2" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", + "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.2" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1233,6 +1370,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1524,6 +1667,16 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-/oJGukuH3D2+D+3H4JWLaAsJ/ji86dhRidzZ/Od7H/i8g+aCmvkeCc6Ni/f9uxGLSQVCRZkX2/lqEFG2BvWtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2415,6 +2568,20 @@ "baseline-browser-mapping": "dist/cli.js" } }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -2473,6 +2640,34 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2570,6 +2765,30 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -2603,6 +2822,21 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -2718,6 +2952,15 @@ "dev": true, "license": "MIT" }, + "node_modules/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -2754,6 +2997,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "license": "MIT" + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -2777,6 +3032,18 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2792,6 +3059,16 @@ "node": ">= 0.4" } }, + "node_modules/effect": { + "version": "3.18.4", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", + "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.286", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", @@ -2806,6 +3083,15 @@ "dev": true, "license": "MIT" }, + "node_modules/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/enhanced-resolve": { "version": "5.19.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", @@ -3444,6 +3730,34 @@ "node": ">=0.10.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3585,6 +3899,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/framer-motion": { + "version": "12.33.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.33.0.tgz", + "integrity": "sha512-ca8d+rRPcDP5iIF+MoT3WNc0KHJMjIyFAbtVLvM9eA7joGSpeqDfiNH/kCs1t4CHi04njYvWyj0jS4QlEK/rJQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.33.0", + "motion-utils": "^12.29.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3716,6 +4057,23 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -4393,12 +4751,20 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "dev": true, "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4833,6 +5199,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.563.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz", + "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4900,6 +5275,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/motion-dom": { + "version": "12.33.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.33.0.tgz", + "integrity": "sha512-XRPebVypsl0UM+7v0Hr8o9UAj0S2djsQWRdHBd5iVouVpMrQqAI0C/rDAT3QaYnXnHuC5hMcwDHCboNeyYjPoQ==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.29.2" + } + }, + "node_modules/motion-utils": { + "version": "12.29.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz", + "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5001,6 +5391,62 @@ } } }, + "node_modules/next-auth": { + "version": "5.0.0-beta.30", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.30.tgz", + "integrity": "sha512-+c51gquM3F6nMVmoAusRJ7RIoY0K4Ts9HCCwyy/BRoe4mp3msZpOzYMyb5LAYc1wSo74PMQkGDcaghIO7W6Xjg==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.41.0" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0 || ^16.0.0", + "nodemailer": "^7.0.7", + "react": "^18.2.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-auth/node_modules/@auth/core": { + "version": "0.41.0", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.41.0.tgz", + "integrity": "sha512-Wd7mHPQ/8zy6Qj7f4T46vg3aoor8fskJm6g2Zyj064oQ3+p0xNZXAV60ww0hY+MbTesfu29kK14Zk5d5JTazXQ==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^6.8.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, "node_modules/next/node_modules/postcss": { "version": "8.4.31", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", @@ -5029,6 +5475,32 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "license": "MIT" + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/node-releases": { "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", @@ -5036,6 +5508,38 @@ "dev": true, "license": "MIT" }, + "node_modules/nypm": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", + "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", + "license": "MIT", + "dependencies": { + "citty": "^0.2.0", + "pathe": "^2.0.3", + "tinyexec": "^1.0.2" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.0.tgz", + "integrity": "sha512-8csy5IBFI2ex2hTVpaHN2j+LNE199AgiI7y4dMintrr8i0lQiFn+0AWMZrWdHKIgMOer65f8IThysYhoReqjWA==", + "license": "MIT" + }, + "node_modules/oauth4webapi": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.3.tgz", + "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5159,6 +5663,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "license": "MIT" + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5267,6 +5777,18 @@ "dev": true, "license": "MIT" }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "license": "MIT" + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5286,6 +5808,17 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -5325,6 +5858,25 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", + "license": "MIT", + "peerDependencies": { + "preact": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -5335,6 +5887,31 @@ "node": ">= 0.8.0" } }, + "node_modules/prisma": { + "version": "6.19.2", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", + "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.2", + "@prisma/engines": "6.19.2" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5357,6 +5934,22 @@ "node": ">=6" } }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5378,6 +5971,16 @@ ], "license": "MIT" }, + "node_modules/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, "node_modules/react": { "version": "19.2.3", "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", @@ -5406,6 +6009,19 @@ "dev": true, "license": "MIT" }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -6039,6 +6655,15 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -6240,7 +6865,7 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/package.json b/package.json index a917206..bce6bdb 100644 --- a/package.json +++ b/package.json @@ -9,12 +9,20 @@ "lint": "eslint" }, "dependencies": { + "@auth/prisma-adapter": "^2.11.1", + "@prisma/client": "^6.19.2", + "bcrypt": "^6.0.0", + "framer-motion": "^12.33.0", + "lucide-react": "^0.563.0", "next": "16.1.6", + "next-auth": "^5.0.0-beta.30", + "prisma": "^6.19.2", "react": "19.2.3", "react-dom": "19.2.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", + "@types/bcrypt": "^6.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", diff --git a/prisma/dev.db b/prisma/dev.db new file mode 100644 index 0000000..fabe727 Binary files /dev/null and b/prisma/dev.db differ diff --git a/prisma/migrations/20260207031740_init/migration.sql b/prisma/migrations/20260207031740_init/migration.sql new file mode 100644 index 0000000..e5d65d4 --- /dev/null +++ b/prisma/migrations/20260207031740_init/migration.sql @@ -0,0 +1,158 @@ +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL PRIMARY KEY, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "name" TEXT NOT NULL, + "phone" TEXT, + "city" TEXT, + "state" TEXT, + "role" TEXT NOT NULL DEFAULT 'parent', + "emailVerified" DATETIME, + "image" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "Account" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "type" TEXT NOT NULL, + "provider" TEXT NOT NULL, + "providerAccountId" TEXT NOT NULL, + "refresh_token" TEXT, + "access_token" TEXT, + "expires_at" INTEGER, + "token_type" TEXT, + "scope" TEXT, + "id_token" TEXT, + "session_state" TEXT, + CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Session" ( + "id" TEXT NOT NULL PRIMARY KEY, + "sessionToken" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "expires" DATETIME NOT NULL, + CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "VerificationToken" ( + "identifier" TEXT NOT NULL, + "token" TEXT NOT NULL, + "expires" DATETIME NOT NULL +); + +-- CreateTable +CREATE TABLE "Subscription" ( + "id" TEXT NOT NULL PRIMARY KEY, + "userId" TEXT NOT NULL, + "plan" TEXT NOT NULL, + "status" TEXT NOT NULL DEFAULT 'pending', + "stripeCustomerId" TEXT, + "stripeSubId" TEXT, + "currentPeriodEnd" DATETIME, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Subscription_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Child" ( + "id" TEXT NOT NULL PRIMARY KEY, + "parentId" TEXT NOT NULL, + "name" TEXT NOT NULL, + "birthDate" DATETIME NOT NULL, + "diagnosis" TEXT NOT NULL, + "diagnosisDate" TEXT, + "currentTherapies" TEXT, + "priorities" TEXT, + "challenges" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Child_parentId_fkey" FOREIGN KEY ("parentId") REFERENCES "User" ("id") ON DELETE CASCADE ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "TherapistPatient" ( + "id" TEXT NOT NULL PRIMARY KEY, + "therapistId" TEXT NOT NULL, + "patientId" TEXT NOT NULL, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "TherapistPatient_therapistId_fkey" FOREIGN KEY ("therapistId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "TherapistPatient_patientId_fkey" FOREIGN KEY ("patientId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Appointment" ( + "id" TEXT NOT NULL PRIMARY KEY, + "childId" TEXT NOT NULL, + "therapistId" TEXT NOT NULL, + "scheduledAt" DATETIME NOT NULL, + "duration" INTEGER NOT NULL DEFAULT 50, + "status" TEXT NOT NULL DEFAULT 'scheduled', + "roomUrl" TEXT, + "roomName" TEXT, + "notes" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "Appointment_childId_fkey" FOREIGN KEY ("childId") REFERENCES "Child" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "Appointment_therapistId_fkey" FOREIGN KEY ("therapistId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "SessionNote" ( + "id" TEXT NOT NULL PRIMARY KEY, + "appointmentId" TEXT NOT NULL, + "childId" TEXT NOT NULL, + "therapistId" TEXT NOT NULL, + "content" TEXT NOT NULL, + "objectives" TEXT, + "progress" TEXT, + "nextSteps" TEXT, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" DATETIME NOT NULL, + CONSTRAINT "SessionNote_appointmentId_fkey" FOREIGN KEY ("appointmentId") REFERENCES "Appointment" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "SessionNote_childId_fkey" FOREIGN KEY ("childId") REFERENCES "Child" ("id") ON DELETE RESTRICT ON UPDATE CASCADE, + CONSTRAINT "SessionNote_therapistId_fkey" FOREIGN KEY ("therapistId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateTable +CREATE TABLE "Progress" ( + "id" TEXT NOT NULL PRIMARY KEY, + "childId" TEXT NOT NULL, + "category" TEXT NOT NULL, + "value" INTEGER NOT NULL, + "notes" TEXT, + "date" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + "createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT "Progress_childId_fkey" FOREIGN KEY ("childId") REFERENCES "Child" ("id") ON DELETE RESTRICT ON UPDATE CASCADE +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId"); + +-- CreateIndex +CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token"); + +-- CreateIndex +CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token"); + +-- CreateIndex +CREATE UNIQUE INDEX "Subscription_userId_key" ON "Subscription"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "TherapistPatient_therapistId_patientId_key" ON "TherapistPatient"("therapistId", "patientId"); + +-- CreateIndex +CREATE UNIQUE INDEX "SessionNote_appointmentId_key" ON "SessionNote"("appointmentId"); diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..2a5a444 --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (e.g., Git) +provider = "sqlite" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..0dacac9 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,179 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "sqlite" + url = env("DATABASE_URL") +} + +// ============ AUTENTICAÇÃO ============ + +model User { + id String @id @default(cuid()) + email String @unique + password String + name String + phone String? + city String? + state String? + role String @default("parent") // parent, therapist, admin + emailVerified DateTime? + image String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + // Relações + accounts Account[] + sessions Session[] + subscription Subscription? + children Child[] + + // Para terapeutas + patients TherapistPatient[] @relation("TherapistPatients") + assignedTo TherapistPatient[] @relation("PatientAssignments") + appointments Appointment[] @relation("TherapistAppointments") + sessionsNotes SessionNote[] +} + +model Account { + id String @id @default(cuid()) + userId String + type String + provider String + providerAccountId String + refresh_token String? + access_token String? + expires_at Int? + token_type String? + scope String? + id_token String? + session_state String? + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@unique([provider, providerAccountId]) +} + +model Session { + id String @id @default(cuid()) + sessionToken String @unique + userId String + expires DateTime + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +model VerificationToken { + identifier String + token String @unique + expires DateTime + + @@unique([identifier, token]) +} + +// ============ PAGAMENTOS ============ + +model Subscription { + id String @id @default(cuid()) + userId String @unique + plan String // essencial, completo, intensivo + status String @default("pending") // pending, active, cancelled, expired + stripeCustomerId String? + stripeSubId String? + currentPeriodEnd DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +} + +// ============ CRIANÇAS ============ + +model Child { + id String @id @default(cuid()) + parentId String + name String + birthDate DateTime + diagnosis String + diagnosisDate String? + currentTherapies String? // JSON array + priorities String? // JSON array + challenges String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + parent User @relation(fields: [parentId], references: [id], onDelete: Cascade) + progress Progress[] + appointments Appointment[] + sessionNotes SessionNote[] +} + +// ============ TERAPEUTAS ============ + +model TherapistPatient { + id String @id @default(cuid()) + therapistId String + patientId String + createdAt DateTime @default(now()) + + therapist User @relation("TherapistPatients", fields: [therapistId], references: [id]) + patient User @relation("PatientAssignments", fields: [patientId], references: [id]) + + @@unique([therapistId, patientId]) +} + +// ============ AGENDAMENTOS ============ + +model Appointment { + id String @id @default(cuid()) + childId String + therapistId String + scheduledAt DateTime + duration Int @default(50) // minutos + status String @default("scheduled") // scheduled, completed, cancelled, no-show + roomUrl String? // URL Daily.co + roomName String? + notes String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + child Child @relation(fields: [childId], references: [id]) + therapist User @relation("TherapistAppointments", fields: [therapistId], references: [id]) + sessionNote SessionNote? +} + +// ============ NOTAS DE SESSÃO ============ + +model SessionNote { + id String @id @default(cuid()) + appointmentId String @unique + childId String + therapistId String + content String // Markdown ou texto + objectives String? // JSON array + progress String? // JSON object com métricas + nextSteps String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + appointment Appointment @relation(fields: [appointmentId], references: [id]) + child Child @relation(fields: [childId], references: [id]) + therapist User @relation(fields: [therapistId], references: [id]) +} + +// ============ PROGRESSO ============ + +model Progress { + id String @id @default(cuid()) + childId String + category String // comunicacao, habilidades_sociais, autonomia, regulacao_emocional + value Int // 0-100 + notes String? + date DateTime @default(now()) + createdAt DateTime @default(now()) + + child Child @relation(fields: [childId], references: [id]) +} diff --git a/public/icon-iristea.png b/public/icon-iristea.png new file mode 100644 index 0000000..3579700 Binary files /dev/null and b/public/icon-iristea.png differ diff --git a/public/icon-iristea.svg b/public/icon-iristea.svg new file mode 100644 index 0000000..0992ed4 --- /dev/null +++ b/public/icon-iristea.svg @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/logo-iristea.png b/public/logo-iristea.png new file mode 100644 index 0000000..7ffeab9 Binary files /dev/null and b/public/logo-iristea.png differ diff --git a/public/logo-iristea.svg b/public/logo-iristea.svg new file mode 100644 index 0000000..ce7850a --- /dev/null +++ b/public/logo-iristea.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IrisTEA + + + + + Teleterapia especializada em autismo + + diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..5ef28c1 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,3 @@ +import { handlers } from '@/lib/auth'; + +export const { GET, POST } = handlers; diff --git a/src/app/api/register/route.ts b/src/app/api/register/route.ts new file mode 100644 index 0000000..55b8ef3 --- /dev/null +++ b/src/app/api/register/route.ts @@ -0,0 +1,112 @@ +import { NextRequest, NextResponse } from 'next/server'; +import bcrypt from 'bcrypt'; +import { prisma } from '@/lib/prisma'; + +export async function POST(request: NextRequest) { + try { + const body = await request.json(); + + const { + // Step 1 - Responsável + nomeResponsavel, + email, + telefone, + cidade, + estado, + + // Step 2 - Criança + nomeCrianca, + dataNascimento, + diagnostico, + quandoDiagnostico, + quaisTerapias, + + // Step 3 - Prioridades + prioridades, + desafios, + disponibilidade, + planoInteresse, + + // Senha + password, + } = body; + + // Validações básicas + if (!email || !password || !nomeResponsavel) { + return NextResponse.json( + { error: 'Campos obrigatórios faltando' }, + { status: 400 } + ); + } + + // Verificar se usuário já existe + const existingUser = await prisma.user.findUnique({ + where: { email }, + }); + + if (existingUser) { + return NextResponse.json( + { error: 'Este e-mail já está cadastrado' }, + { status: 400 } + ); + } + + // Hash da senha + const hashedPassword = await bcrypt.hash(password, 10); + + // Criar usuário e criança em uma transação + const result = await prisma.$transaction(async (tx) => { + // Criar usuário + const user = await tx.user.create({ + data: { + email, + password: hashedPassword, + name: nomeResponsavel, + phone: telefone, + city: cidade, + state: estado, + role: 'parent', + }, + }); + + // Criar assinatura pendente + await tx.subscription.create({ + data: { + userId: user.id, + plan: planoInteresse || 'completo', + status: 'pending', + }, + }); + + // Criar perfil da criança + if (nomeCrianca && dataNascimento) { + await tx.child.create({ + data: { + parentId: user.id, + name: nomeCrianca, + birthDate: new Date(dataNascimento), + diagnosis: diagnostico || '', + diagnosisDate: quandoDiagnostico, + currentTherapies: JSON.stringify(quaisTerapias || []), + priorities: JSON.stringify(prioridades || []), + challenges: desafios, + }, + }); + } + + return user; + }); + + return NextResponse.json({ + success: true, + message: 'Cadastro realizado com sucesso!', + userId: result.id, + }); + } catch (error) { + console.error('Erro no cadastro:', error); + return NextResponse.json( + { error: 'Erro interno do servidor' }, + { status: 500 } + ); + } +} diff --git a/src/app/cadastro/page.tsx b/src/app/cadastro/page.tsx new file mode 100644 index 0000000..42a9183 --- /dev/null +++ b/src/app/cadastro/page.tsx @@ -0,0 +1,618 @@ +'use client'; + +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { + Sparkles, ArrowLeft, ArrowRight, Check, User, Baby, Target, + Lock, Eye, EyeOff +} from 'lucide-react'; + +type Step = 1 | 2 | 3; + +interface FormData { + // Step 1 - Responsável + nomeResponsavel: string; + email: string; + password: string; + confirmPassword: string; + telefone: string; + cidade: string; + estado: string; + comoConheceu: string; + + // Step 2 - Criança + nomeCrianca: string; + dataNascimento: string; + diagnostico: string; + quandoDiagnostico: string; + fazTerapia: string; + quaisTerapias: string[]; + + // Step 3 - Prioridades + prioridades: string[]; + desafios: string; + expectativas: string; + disponibilidade: string[]; + planoInteresse: string; +} + +export default function CadastroPage() { + const router = useRouter(); + const [step, setStep] = useState(1); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + const [showPassword, setShowPassword] = useState(false); + const [formData, setFormData] = useState({ + nomeResponsavel: '', + email: '', + password: '', + confirmPassword: '', + telefone: '', + cidade: '', + estado: '', + comoConheceu: '', + nomeCrianca: '', + dataNascimento: '', + diagnostico: '', + quandoDiagnostico: '', + fazTerapia: '', + quaisTerapias: [], + prioridades: [], + desafios: '', + expectativas: '', + disponibilidade: [], + planoInteresse: 'completo', + }); + + const estados = ['AC','AL','AP','AM','BA','CE','DF','ES','GO','MA','MT','MS','MG','PA','PB','PR','PE','PI','RJ','RN','RS','RO','RR','SC','SP','SE','TO']; + + const terapias = ['ABA', 'Fonoaudiologia', 'Terapia Ocupacional', 'Psicologia', 'Psicopedagogia', 'Musicoterapia', 'Equoterapia', 'Nenhuma']; + + const prioridadesOpcoes = [ + 'Comunicação / Fala', + 'Comportamentos desafiadores', + 'Habilidades sociais', + 'Autonomia / Vida diária', + 'Sono', + 'Alimentação seletiva', + 'Treino de desfralde', + 'Rotina escolar', + 'Regulação emocional', + 'Estereotipias', + ]; + + const disponibilidadeOpcoes = [ + 'Manhã (8h-12h)', + 'Tarde (13h-18h)', + 'Noite (19h-21h)', + 'Sábados', + ]; + + const handleChange = (e: React.ChangeEvent) => { + setFormData({ ...formData, [e.target.name]: e.target.value }); + setError(''); + }; + + const handleCheckbox = (field: 'quaisTerapias' | 'prioridades' | 'disponibilidade', value: string) => { + const current = formData[field]; + if (current.includes(value)) { + setFormData({ ...formData, [field]: current.filter(v => v !== value) }); + } else { + setFormData({ ...formData, [field]: [...current, value] }); + } + }; + + const nextStep = () => { + // Validações por step + if (step === 1) { + if (!formData.nomeResponsavel || !formData.email || !formData.password) { + setError('Preencha todos os campos obrigatórios'); + return; + } + if (formData.password !== formData.confirmPassword) { + setError('As senhas não conferem'); + return; + } + if (formData.password.length < 6) { + setError('A senha deve ter no mínimo 6 caracteres'); + return; + } + } + + if (step < 3) setStep((step + 1) as Step); + }; + + const prevStep = () => { + if (step > 1) setStep((step - 1) as Step); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + setError(''); + + try { + const response = await fetch('/api/register', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(formData), + }); + + const data = await response.json(); + + if (!response.ok) { + throw new Error(data.error || 'Erro ao cadastrar'); + } + + router.push('/cadastro/sucesso'); + } catch (err: any) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + return ( +
+ {/* Header */} +
+
+ +
+ +
+ Íris + +
+
+ + {/* Progress */} +
+
+ {[1, 2, 3].map((s) => ( +
+
+ {s < step ? : s} +
+ + {s < 3 &&
} +
+ ))} +
+ + {/* Form Card */} +
+ {error && ( +
+ {error} +
+ )} + +
+ + {/* Step 1 - Responsável */} + {step === 1 && ( +
+
+
+ +
+
+

Seus Dados

+

Informações do responsável

+
+
+ +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ )} + + {/* Step 2 - Criança */} + {step === 2 && ( +
+
+
+ +
+
+

Sobre a Criança

+

Informações do seu filho(a)

+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ +
+ + +
+
+ + {formData.fazTerapia === 'sim' && ( +
+ +
+ {terapias.map(t => ( + + ))} +
+
+ )} +
+ )} + + {/* Step 3 - Prioridades */} + {step === 3 && ( +
+
+
+ +
+
+

Prioridades Terapêuticas

+

O que você gostaria de trabalhar?

+
+
+ +
+ +
+ {prioridadesOpcoes.map(p => ( + + ))} +
+
+ +
+ +