35 Commits

Author SHA1 Message Date
Leonardo 676042268b first commit 2026-02-18 22:36:45 -03:00
Cagatay Civici ec6b6ef53a set version as 5 2026-02-02 21:49:03 +03:00
tugcekucukoglu 76a3b60333 cchore: update PrimeVue version 2026-01-30 17:51:18 +03:00
tugcekucukoglu 410c08d693 chore: layout config updates 2025-12-25 10:03:32 +03:00
tugcekucukoglu a4b2c96b0d submodule added 2025-12-25 09:09:55 +03:00
tugcekucukoglu a47200fdf7 remove assets 2025-12-25 09:07:45 +03:00
tugcekucukoglu 7c32ae1f6f chore: remove sass warnings 2025-12-09 14:05:01 +03:00
Atakan db99863fac update transitions & dependencies 2025-12-08 14:36:22 +03:00
Atakan c2ef85fcab update for tw v4 2025-12-04 12:26:25 +03:00
Atakan deea8861f8 update 2025-11-18 05:16:32 +03:00
Atakan 319f976d2b add blocks 2025-11-08 17:37:47 +03:00
Atakan 13a50a3af3 update tw 2025-11-08 17:35:43 +03:00
Cagatay Civici 23bcf922ab Set version 2025-02-26 17:40:28 +03:00
tugcekucukoglu e1ecd23050 Update package-lock.json 2025-02-25 15:31:36 +03:00
tugcekucukoglu 2f5b71a3eb Use @primeuix/themes instead of @primevue/themes 2025-02-25 15:28:20 +03:00
tugcekucukoglu 22ba8601f3 Nora added 2025-02-18 16:01:41 +03:00
tugcekucukoglu 4a8745b497 Update FooterWidget.vue 2025-02-18 16:01:32 +03:00
tugcekucukoglu d5ec7dba67 Update package-lock.json 2025-02-18 16:01:27 +03:00
tugcekucukoglu 7c54176132 Update OverlayDoc.vue 2025-02-18 16:01:24 +03:00
tugcekucukoglu 03ef1236f0 Fixed #62 2025-02-18 16:01:19 +03:00
tugcekucukoglu 7ac2ba9013 Fixed #55 2025-02-18 15:51:13 +03:00
tugcekucukoglu 5f951584c7 Add Sakai-Vue to a Nuxt Project section added 2025-01-20 14:52:48 +03:00
tugcekucukoglu 817ffa0d62 Flag fixes 2025-01-16 13:51:47 +03:00
tugcekucukoglu 9585f62298 Update MiscDoc.vue 2025-01-16 13:03:27 +03:00
tugcekucukoglu aad48fca63 Set new version 2024-12-09 10:49:37 +03:00
tugcekucukoglu 59f3ebffe7 Update CHANGELOG.md 2024-12-09 10:49:27 +03:00
tugcekucukoglu 6fd2e4d96e Layout composable changes 2024-12-06 16:04:53 +03:00
tugcekucukoglu 1c65a74541 Update CHANGELOG.md 2024-12-05 13:28:00 +03:00
tugcekucukoglu a5aafc1d34 Laanding re-implementation 2024-12-05 13:26:47 +03:00
tugcekucukoglu 0f42b3760d Dashboard re-implementation 2024-12-05 13:16:36 +03:00
tugcekucukoglu c4dec65f2a Sass warning fixes 2024-12-05 13:04:39 +03:00
tugcekucukoglu 6f85c751de Version updates 2024-12-05 13:04:19 +03:00
Cagatay Civici 411fecb517 Fixed warning 2024-09-02 15:59:14 +03:00
tugcekucukoglu 4c7b0c0f5d Refactor 2024-08-05 09:27:58 +03:00
Cagatay Civici 3ba6d75db2 Update README.md 2024-08-03 11:36:25 +03:00
261 changed files with 29742 additions and 3887 deletions
+2
View File
@@ -0,0 +1,2 @@
VITE_SUPABASE_URL=http://127.0.0.1:54321
VITE_SUPABASE_ANON_KEY=sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH
+2
View File
@@ -0,0 +1,2 @@
VITE_SUPABASE_URL=http://127.0.0.1:54321
VITE_SUPABASE_ANON_KEY=sb_publishable_ACJWlzQHlZjBrEguHvfOxg_3BJgxAaH
+3 -1
View File
@@ -5,10 +5,12 @@ coverage
.nitro
.cache
.output
.env
# .env
dist
.DS_Store
.idea
.eslintcache
api-generator/typedoc.json
**/.DS_Store
Dev-documentacao/
supabase/
+3
View File
@@ -0,0 +1,3 @@
[submodule "src/assets"]
path = src/assets
url = https://github.com/primefaces/sakai-assets.git
+31
View File
@@ -0,0 +1,31 @@
### Observação sobre `tenant_admin` com UUID coincidente
Foi identificado que o registro de `tenant_members` possui:
- `tenant_id = 816b24fe-a0c3-4409-b79b-c6c0a6935d03`
- `user_id = 816b24fe-a0c3-4409-b79b-c6c0a6935d03`
- `role = tenant_admin`
À primeira vista pode parecer inconsistência, mas não é.
Verificação realizada:
O UUID `816b24fe-a0c3-4409-b79b-c6c0a6935d03` existe em `auth.users`
(email: admin@agenciapsi.com.br).
Portanto:
- `tenant_members.user_id` referencia corretamente `auth.users.id`
- Não há violação de integridade referencial
- O registro é válido
Trata-se de um caso em que:
- O usuário administrador principal possui um UUID específico
- O tenant foi criado com o mesmo UUID
- O administrador é `tenant_admin` desse próprio tenant
Esse padrão não quebra a arquitetura multi-tenant e é funcionalmente válido.
A coincidência entre `tenant_id` e `user_id` é apenas estrutural, não conceitual.
Conclusão:
Nenhuma correção estrutural é necessária.
+13
View File
@@ -1,5 +1,18 @@
# Changelog
## 4.3.0 (2025-02-26)
**Implemented New Features and Enhancements**
- Update PrimeVue version
## 4.2.0 (2024-12-09)
**Implemented New Features and Enhancements**
- Refactored dashboard sections to components
- Migrate sass from @import to @use
## 4.1.0 (2024-07-29)
- Changed menu button location at topbar
+101
View File
@@ -0,0 +1,101 @@
O que foi feito (até agora)
Usuários de teste criados
admin@agenciapsi.com.br
— senha: 123Mudar@
patient@agenciapsi.com.br
— senha: 123Mudar@
therapist@agenciapsi.com.br
— senha: 123Mudar@
Base funcionando
✅ Auth (Supabase) está funcionando
✅ Tabela profiles criada e ok
✅ Trigger automático cria profile após signup
✅ Campo role definido (admin | therapist | patient)
✅ RLS básico ativo
✅ Login funcionando
✅ Logout funcionando
✅ Guard de rota implementado e ativo
✅ RBAC básico operando via meta.role + redirect para painel correto
✅ Home pública / com 3 cards (Admin | Therapist | Patient) levando ao login
✅ Pós-login: busca profiles.role e redireciona para:
/admin
/therapist
/patient
Estrutura implementada agora (menus e sessão para o Sakai)
Sessão central (evita menu errado e if(role) espalhado)
✅ Criado src/app/session.js com:
sessionUser, sessionRole, sessionReady (refs globais)
initSession() (carrega user + role antes de renderizar o layout)
listenAuthChanges() (atualiza sessão ao logar/deslogar)
✅ Ajustado src/main.js para usar bootstrap async:
chama await initSession() antes de app.mount()
liga listenAuthChanges()
mantém PrimeVue, tema Aura, ToastService e ConfirmationService
mantém imports de CSS existentes
Menu dinâmico por role no Sakai
✅ Menus foram estruturados no formato do Sakai (sections com label + items) e separados por role:
src/navigation/menus/admin.menu.js
src/navigation/menus/therapist.menu.js
src/navigation/menus/patient.menu.js
✅ Criado src/navigation/index.js com getMenuByRole(role) para centralizar a escolha do menu (sem if(role) em componentes).
✅ Ajustado o AppMenu.vue (menu do Sakai) para:
usar computed() com sessionRole/sessionReady
carregar dinamicamente getMenuByRole(sessionRole.value)
evitar “piscar” menu errado antes de carregar (sessionReady)
Menu demo do Sakai mantido sem quebrar o produto
✅ Mantivemos o menu demo (UIKit/Blocks/Start etc.) em arquivo separado para não perder as páginas do template:
src/navigation/menus/sakai.demo.menu.js (conteúdo original do template)
✅ Estratégia adotada:
Admin pode ver o menu demo (idealmente só em DEV)
Therapist/Patient ficam com menu limpo (clínico)
Rotas demo do Sakai corrigidas (arquivos com sufixo Doc)
✅ Problema resolvido: itens do menu demo davam 404 porque as rotas/imports não existiam com os nomes esperados (Input.vue etc.).
✅ Ajuste aplicado: rotas demo apontam para arquivos *Doc.vue (ex.: ButtonDoc.vue, InputDoc.vue).
📌 Criado/ajustado src/router/routes.demo.js para mapear:
/uikit/* → @/views/uikit/*Doc.vue
e demais demos conforme existirem
✅ Incluído demoRoutes no router principal para o menu demo funcionar.
Testes
✅ Confirmado que localStorage.clear() limpa sessão para testar outros usuários/roles rapidamente.
+2 -28
View File
@@ -1,29 +1,3 @@
This template should help get you started developing with Vue 3 in Vite.
Sakai is an application template for Vue based on the [create-vue](https://github.com/vuejs/create-vue), the recommended way to start a Vite-powered Vue projects.
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
npm install
```
### Compile and Hot-Reload for Development
```sh
npm run dev
```
### Compile and Minify for Production
```sh
npm run build
```
### Lint with [ESLint](https://eslint.org/)
```sh
npm run lint
```
Visit the [documentation](https://sakai.primevue.org/documentation) to get started.
+47
View File
@@ -0,0 +1,47 @@
🔁 CONTEXTO DO PROJETO (SaaS multi-tenant)
Stack:
- Supabase
- Multi-tenant por clinic/tenant
- Assinaturas por tenant (subscriptions.tenant_id)
- Controle de features: features, plan_features, subscription_intents, entitlementsStore, view v_tenant_entitlements
- Ativação manual: activate_subscription_from_intent()
- Merge concluído: agenda_online → online_scheduling.manage
- Entitlements e bloqueio PRO no menu funcionando
- Signup + intent funcionando; ativação cria subscription ativa; view retorna feature correta
Modelo de “Contas” decidido:
- Auth user (login) ≠ Clínica (tenant)
- Clínica = tenant; Usuário pode ser dono/admin de clínica e também profissional
- Clínica convida usuários (tenant_members). Usuário pode aceitar/recusar.
- Profissional pode trabalhar anos e depois sair: clínica mantém registros; profissional mantém histórico (audit trail), sem acesso após saída.
Regras de offboarding:
- Profissional só pode sair se NÃO houver agenda futura atribuída a ele.
- Se houver, cria “pedido de saída” e admin precisa realocar/cancelar; depois finaliza saída.
Tabelas existentes:
- tenant_members: (id uuid pk, tenant_id uuid, user_id uuid, role text, status text, created_at timestamptz)
- UNIQUE (tenant_id, user_id) atualmente
- Agenda: agenda_eventos, agenda_excecoes, agenda_configuracoes, agenda_regras_semanais
- Outros: subscriptions, subscription_intents, plan_features, features, subscription_events
O que estamos fazendo agora:
- Ajustar modelo de membership lifecycle e offboarding (exit_requests)
- Garantir integridade: histórico de vínculos + auditoria + bloqueio de saída com agenda futura
- Implementar SQL + RPC + RLS + UI (passo a passo)
✔ subscriptions
Representa o plano da clínica (tenant)
✔ tenant_members
Define quais usuários pertencem à clínica
✔ entitlements
Define o que aquela clínica pode usar
Dados que faltam confirmar:
1) Estrutura de agenda_eventos (colunas e como relaciona com profissional)
2) Valores usados em tenant_members.status (active/invited/etc)
3) Estratégia de reentrada: remover UNIQUE (tenant_id,user_id) e usar unique parcial por status ativo/convite
4) Se existe tabela public.users como espelho do auth.users
+4532 -1272
View File
File diff suppressed because it is too large Load Diff
+12 -9
View File
@@ -1,6 +1,6 @@
{
"name": "sakai-vue",
"version": "4.1.0",
"version": "5.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
@@ -8,26 +8,29 @@
"lint": "eslint --fix . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore"
},
"dependencies": {
"@primevue/themes": "^4.0.0",
"@primeuix/themes": "^2.0.0",
"@supabase/supabase-js": "^2.95.3",
"chart.js": "3.3.2",
"primeicons": "^6.0.1",
"primevue": "^4.0.0",
"pinia": "^3.0.4",
"primeicons": "^7.0.0",
"primevue": "^4.5.4",
"tailwindcss-primeui": "^0.6.0",
"vue": "^3.4.34",
"vue-router": "^4.4.0"
},
"devDependencies": {
"@primevue/auto-import-resolver": "^4.0.1",
"@primevue/auto-import-resolver": "^4.3.1",
"@rushstack/eslint-patch": "^1.8.0",
"@tailwindcss/vite": "^4.1.17",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/eslint-config-prettier": "^9.0.0",
"autoprefixer": "^10.4.19",
"autoprefixer": "^10.4.24",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"postcss": "^8.4.40",
"postcss": "^8.5.6",
"prettier": "^3.2.5",
"sass": "^1.55.0",
"tailwindcss": "^3.4.6",
"tailwindcss-primeui": "^0.3.2",
"tailwindcss": "^4.1.18",
"unplugin-vue-components": "^0.27.3",
"vite": "^5.3.1"
}
-6
View File
@@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="300px" height="200px" viewBox="0 0 300 200" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>Artboard</title>
<g id="Artboard" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="Rectangle" fill="#F8F9FA" x="0" y="0" width="300" height="200"></rect>
<g id="image" transform="translate(110.000000, 70.000000)" fill="#BABABC" fill-rule="nonzero">
<path d="M75,0 L5,0 C2.23857625,0 0,2.23857625 0,5 L0,55 C0,57.7614237 2.23857625,60 5,60 L75,60 C77.7614237,60 80,57.7614237 80,55 L80,5 C80,2.23857625 77.7614237,0 75,0 Z M20,10 C25.5228475,10 30,14.4771525 30,20 C30,25.5228475 25.5228475,30 20,30 C14.4771525,30 10,25.5228475 10,20 C10,14.4771525 14.4771525,10 20,10 Z M70,40 L70,50 L10,50 L10,40 L18.55,35.7 C19.4648753,35.2524957 20.5351247,35.2524957 21.45,35.7 L30,40 L53.65,21.1 C54.4866298,20.4991452 55.6133702,20.4991452 56.45,21.1 L70,30 L70,40 Z" id="Shape"></path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

+22 -4
View File
@@ -1,7 +1,25 @@
<script setup></script>
<script setup>
import { onMounted } from 'vue'
import { useRoute } from 'vue-router'
import { useTenantStore } from '@/stores/tenantStore'
import { useEntitlementsStore } from '@/stores/entitlementsStore'
const route = useRoute()
const tenant = useTenantStore()
const ent = useEntitlementsStore()
onMounted(async () => {
await tenant.loadSessionAndTenant()
await ent.loadForTenant(tenant.activeTenantId)
// pode remover esses logs depois
console.log('tenant.activeTenantId', tenant.activeTenantId)
console.log('role', tenant.activeRole)
console.log('can online_scheduling.manage?', ent.can('online_scheduling.manage'))
})
</script>
<template>
<router-view />
<router-view />
</template>
<style scoped></style>
+112
View File
@@ -0,0 +1,112 @@
import { supabase } from '@/lib/supabase/client'
import { useLayout } from '@/layout/composables/layout'
import { $t, updatePreset, updateSurfacePalette } from '@primeuix/themes'
import Aura from '@primeuix/themes/aura'
import Lara from '@primeuix/themes/lara'
import Nora from '@primeuix/themes/nora'
const presets = { Aura, Lara, Nora }
function safeEq (a, b) {
return String(a || '').trim() === String(b || '').trim()
}
// copia do seu getPresetExt (ou exporta ele do Perfil pra reutilizar)
function getPresetExt(primaryColors, layoutConfig) {
const color = primaryColors.find((c) => c.name === layoutConfig.primary) || { name: 'noir', palette: {} }
if (color.name === 'noir') {
return {
semantic: {
primary: {
50: '{surface.50}', 100: '{surface.100}', 200: '{surface.200}', 300: '{surface.300}',
400: '{surface.400}', 500: '{surface.500}', 600: '{surface.600}', 700: '{surface.700}',
800: '{surface.800}', 900: '{surface.900}', 950: '{surface.950}'
},
colorScheme: {
light: {
primary: { color: '{primary.950}', contrastColor: '#ffffff', hoverColor: '{primary.800}', activeColor: '{primary.700}' },
highlight: { background: '{primary.950}', focusBackground: '{primary.700}', color: '#ffffff', focusColor: '#ffffff' }
},
dark: {
primary: { color: '{primary.50}', contrastColor: '{primary.950}', hoverColor: '{primary.200}', activeColor: '{primary.300}' },
highlight: { background: '{primary.50}', focusBackground: '{primary.300}', color: '{primary.950}', focusColor: '{primary.950}' }
}
}
}
}
}
return {
semantic: {
primary: color.palette,
colorScheme: {
light: {
primary: { color: '{primary.500}', contrastColor: '#ffffff', hoverColor: '{primary.600}', activeColor: '{primary.700}' },
highlight: { background: '{primary.50}', focusBackground: '{primary.100}', color: '{primary.700}', focusColor: '{primary.800}' }
},
dark: {
primary: { color: '{primary.400}', contrastColor: '{surface.900}', hoverColor: '{primary.300}', activeColor: '{primary.200}' },
highlight: {
background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
color: 'rgba(255,255,255,.87)',
focusColor: 'rgba(255,255,255,.87)'
}
}
}
}
}
}
export async function bootstrapUserSettings({
primaryColors = [], // passe a lista do seu Perfil (ou uma versão reduzida)
surfaces = [] // idem
} = {}) {
const { layoutConfig, isDarkTheme, toggleDarkMode, changeMenuMode } = useLayout()
const { data: uRes, error: uErr } = await supabase.auth.getUser()
if (uErr) return
const user = uRes?.user
if (!user) return
const { data: settings, error } = await supabase
.from('user_settings')
.select('theme_mode, preset, primary_color, surface_color, menu_mode')
.eq('user_id', user.id)
.maybeSingle()
if (error || !settings) return
// menu mode
if (settings.menu_mode && settings.menu_mode !== layoutConfig.menuMode) {
layoutConfig.menuMode = settings.menu_mode
changeMenuMode()
}
// preset
if (settings.preset && settings.preset !== layoutConfig.preset) {
layoutConfig.preset = settings.preset
const presetValue = presets[settings.preset] || presets.Aura
const surfacePalette = surfaces.find(s => s.name === layoutConfig.surface)?.palette
$t().preset(presetValue).preset(getPresetExt(primaryColors, layoutConfig)).surfacePalette(surfacePalette).use({ useDefaultOptions: true })
}
// colors
if (settings.primary_color && !safeEq(settings.primary_color, layoutConfig.primary)) {
layoutConfig.primary = settings.primary_color
updatePreset(getPresetExt(primaryColors, layoutConfig))
}
if (settings.surface_color && !safeEq(settings.surface_color, layoutConfig.surface)) {
layoutConfig.surface = settings.surface_color
const surface = surfaces.find(s => s.name === settings.surface_color)
if (surface) updateSurfacePalette(surface.palette)
}
// dark/light
if (settings.theme_mode) {
const shouldBeDark = settings.theme_mode === 'dark'
if (shouldBeDark !== isDarkTheme) toggleDarkMode()
}
}

Some files were not shown because too many files have changed in this diff Show More