177 lines
5.4 KiB
JavaScript
177 lines
5.4 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI — main.js
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
(function applyDarkModeImmediate() {
|
|
try {
|
|
const saved = localStorage.getItem('ui_theme_mode');
|
|
if (saved === 'dark' || saved === 'light') {
|
|
document.documentElement.classList.toggle('app-dark', saved === 'dark');
|
|
}
|
|
} catch { }
|
|
})();
|
|
|
|
import { pinia } from '@/plugins/pinia';
|
|
import router from '@/router';
|
|
import { createApp } from 'vue';
|
|
import App from './App.vue';
|
|
|
|
import { initSession, listenAuthChanges, refreshSession, setOnSignedOut } from '@/app/session';
|
|
|
|
import PrimeVue from 'primevue/config';
|
|
import { applyThemeEngine } from '@/theme/theme.options';
|
|
import { useLayout } from '@/layout/composables/layout';
|
|
|
|
import ConfirmationService from 'primevue/confirmationservice';
|
|
import ToastService from 'primevue/toastservice';
|
|
|
|
import Button from 'primevue/button';
|
|
import InputText from 'primevue/inputtext';
|
|
import Tag from 'primevue/tag';
|
|
|
|
import AppLoadingPhrases from '@/components/ui/AppLoadingPhrases.vue';
|
|
import LoadedPhraseBlock from '@/components/ui/LoadedPhraseBlock.vue';
|
|
|
|
import '@/assets/styles.scss';
|
|
import '@/assets/tailwind.css';
|
|
|
|
import { supabase } from '@/lib/supabase/client';
|
|
|
|
const ptBR = {
|
|
firstDayOfWeek: 1,
|
|
dayNames: ['domingo', 'segunda-feira', 'terça-feira', 'quarta-feira', 'quinta-feira', 'sexta-feira', 'sábado'],
|
|
dayNamesShort: ['dom', 'seg', 'ter', 'qua', 'qui', 'sex', 'sáb'],
|
|
dayNamesMin: ['D', 'S', 'T', 'Q', 'Q', 'S', 'S'],
|
|
monthNames: ['janeiro', 'fevereiro', 'março', 'abril', 'maio', 'junho', 'julho', 'agosto', 'setembro', 'outubro', 'novembro', 'dezembro'],
|
|
monthNamesShort: ['jan', 'fev', 'mar', 'abr', 'mai', 'jun', 'jul', 'ago', 'set', 'out', 'nov', 'dez'],
|
|
today: 'Hoje',
|
|
clear: 'Limpar',
|
|
weekHeader: 'Sm',
|
|
dateFormat: 'dd/mm/yy'
|
|
};
|
|
|
|
function syncThemeFromDB() {
|
|
const run = async () => {
|
|
try {
|
|
const { data } = await supabase.auth.getUser();
|
|
if (!data?.user) return;
|
|
|
|
const { data: settings } = await supabase
|
|
.from('user_settings')
|
|
.select('theme_mode, preset, primary_color, surface_color, menu_mode')
|
|
.eq('user_id', data.user.id)
|
|
.maybeSingle();
|
|
|
|
if (!settings) return;
|
|
|
|
if (settings.theme_mode) {
|
|
document.documentElement.classList.toggle('app-dark', settings.theme_mode === 'dark');
|
|
localStorage.setItem('ui_theme_mode', settings.theme_mode);
|
|
}
|
|
|
|
const cfg = {};
|
|
if (settings.preset) cfg.preset = settings.preset;
|
|
if (settings.primary_color) cfg.primary = settings.primary_color;
|
|
if (settings.surface_color) cfg.surface = settings.surface_color;
|
|
if (settings.menu_mode) cfg.menuMode = settings.menu_mode;
|
|
|
|
if (Object.keys(cfg).length) {
|
|
try {
|
|
const prev = JSON.parse(localStorage.getItem('ui_theme_config') || '{}');
|
|
localStorage.setItem('ui_theme_config', JSON.stringify({ ...prev, ...cfg }));
|
|
} catch { }
|
|
}
|
|
} catch { }
|
|
};
|
|
|
|
if ('requestIdleCallback' in window) {
|
|
requestIdleCallback(run, { timeout: 4000 });
|
|
} else {
|
|
setTimeout(run, 300);
|
|
}
|
|
}
|
|
|
|
setOnSignedOut(() => router.replace('/auth/login'));
|
|
|
|
window.__sessionRefreshing = false;
|
|
window.__fromVisibilityRefresh = false;
|
|
window.__appBootstrapped = false;
|
|
|
|
let lastVisibilityRefreshAt = 0;
|
|
|
|
document.addEventListener('visibilitychange', async () => {
|
|
if (document.visibilityState !== 'visible') return;
|
|
if (!window.__appBootstrapped) return;
|
|
|
|
const now = Date.now();
|
|
if (now - lastVisibilityRefreshAt < 10_000) return;
|
|
if (window.__sessionRefreshing) return;
|
|
|
|
try {
|
|
const { data } = await supabase.auth.getUser();
|
|
if (!data?.user) return;
|
|
} catch {
|
|
return;
|
|
}
|
|
|
|
lastVisibilityRefreshAt = now;
|
|
|
|
try {
|
|
window.__sessionRefreshing = true;
|
|
window.__fromVisibilityRefresh = true;
|
|
|
|
await refreshSession();
|
|
|
|
const path = router.currentRoute.value?.path ?? '';
|
|
const isTenantArea =
|
|
path.startsWith('/admin') ||
|
|
path.startsWith('/therapist') ||
|
|
path.startsWith('/saas');
|
|
|
|
if (isTenantArea) {
|
|
window.dispatchEvent(new CustomEvent('app:session-refreshed', { detail: { source: 'visibility' } }));
|
|
}
|
|
} finally {
|
|
window.__fromVisibilityRefresh = false;
|
|
window.__sessionRefreshing = false;
|
|
}
|
|
});
|
|
|
|
async function bootstrap() {
|
|
await initSession({ initial: true });
|
|
|
|
const app = createApp(App);
|
|
app.use(pinia);
|
|
app.use(router);
|
|
|
|
await router.isReady();
|
|
|
|
listenAuthChanges();
|
|
syncThemeFromDB();
|
|
|
|
const { layoutConfig } = useLayout();
|
|
|
|
app.use(PrimeVue, {
|
|
locale: ptBR,
|
|
theme: { options: { darkModeSelector: '.app-dark' } }
|
|
});
|
|
|
|
applyThemeEngine(layoutConfig);
|
|
|
|
app.use(ToastService);
|
|
app.use(ConfirmationService);
|
|
|
|
app.component('Button', Button);
|
|
app.component('InputText', InputText);
|
|
app.component('Tag', Tag);
|
|
app.component('AppLoadingPhrases', AppLoadingPhrases);
|
|
app.component('LoadedPhraseBlock', LoadedPhraseBlock);
|
|
|
|
app.mount('#app');
|
|
window.__appBootstrapped = true;
|
|
}
|
|
|
|
bootstrap();
|