Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras

This commit is contained in:
Leonardo
2026-03-24 21:26:58 -03:00
parent a89d1f5560
commit 53a4980396
453 changed files with 121427 additions and 174407 deletions
+323
View File
@@ -0,0 +1,323 @@
<!--
|--------------------------------------------------------------------------
| Agência PSI
|--------------------------------------------------------------------------
| Criado e desenvolvido por Leonardo Nohama
|
| Tecnologia aplicada à escuta.
| Estrutura para o cuidado.
|
| Arquivo: src/layout/AppThemeBar.vue
| Data: 2026
| Local: São Carlos/SP Brasil
|--------------------------------------------------------------------------
| © 2026 Todos os direitos reservados
|--------------------------------------------------------------------------
-->
<script setup>
import { computed, onMounted } from 'vue';
import { useLayout } from '@/layout/composables/layout';
import { useConfiguratorBar } from '@/layout/composables/useConfiguratorBar';
import { primaryColors, surfaces, presetOptions, applyThemeEngine } from '@/theme/theme.options';
import { useUserSettingsPersistence } from '@/composables/useUserSettingsPersistence';
const { layoutConfig, isDarkTheme, changeMenuMode, setVariant, layoutState, isMobile, effectiveVariant, effectiveMenuMode } = useLayout();
const { close } = useConfiguratorBar();
const { init: initSettings, queuePatch } = useUserSettingsPersistence();
onMounted(() => initSettings());
/* ── Offset esquerdo dinâmico ──────────────────────────────
Rail desktop : 60px (rail) + 260px (painel, se aberto)
Clássico static ativo: 20rem (sidebar)
Demais casos : 0px
───────────────────────────────────────────────────────────── */
const leftOffset = computed(() => {
if (isMobile.value) return '0px';
if (effectiveVariant.value === 'rail') {
const panelW = layoutState.railPanelOpen ? 260 : 0;
return `${60 + panelW}px`;
}
// Clássico
if (effectiveMenuMode.value === 'overlay' || layoutState.staticMenuInactive) return '0px';
return '20rem';
});
const barStyle = computed(() => ({ left: leftOffset.value }));
/* ── Cores e presets ────────────────────────────────────── */
const menuModeOptions = [
{ label: 'Static', value: 'static' },
{ label: 'Overlay', value: 'overlay' }
];
const presetModel = computed({
get: () => layoutConfig.preset,
set: (v) => {
layoutConfig.preset = v;
applyThemeEngine(layoutConfig);
queuePatch?.({ preset: v });
}
});
const menuModeModel = computed({
get: () => layoutConfig.menuMode,
set: (v) => {
changeMenuMode(v);
queuePatch?.({ menu_mode: v });
}
});
function updateColors(type, item) {
if (type === 'primary') {
layoutConfig.primary = item.name;
applyThemeEngine(layoutConfig);
queuePatch?.({ primary_color: item.name });
}
if (type === 'surface') {
layoutConfig.surface = item.name;
applyThemeEngine(layoutConfig);
queuePatch?.({ surface_color: item.name });
}
}
function handleSetVariant(v) {
setVariant(v);
queuePatch?.({ layout_variant: v });
}
</script>
<template>
<div class="theme-bar" :style="barStyle">
<div class="theme-bar__inner">
<!-- Cor principal -->
<div class="theme-bar__section">
<span class="theme-bar__label">Cor principal</span>
<div class="theme-bar__swatches">
<button
v-for="c of primaryColors"
:key="c.name"
type="button"
:title="c.name"
@click="updateColors('primary', c)"
:class="['swatch', { 'swatch--active': layoutConfig.primary === c.name }]"
:style="{ backgroundColor: c.name === 'noir' ? 'var(--text-color)' : c.palette['500'] }"
/>
</div>
</div>
<div class="theme-bar__divider" />
<!-- Surface -->
<div class="theme-bar__section">
<span class="theme-bar__label">Surface</span>
<div class="theme-bar__swatches">
<button
v-for="s of surfaces"
:key="s.name"
type="button"
:title="s.name"
@click="updateColors('surface', s)"
:class="[
'swatch',
{
'swatch--active': layoutConfig.surface
? layoutConfig.surface === s.name
: isDarkTheme
? s.name === 'zinc'
: s.name === 'slate'
}
]"
:style="{ backgroundColor: s.palette['500'] }"
/>
</div>
</div>
<div class="theme-bar__divider" />
<!-- Preset -->
<div class="theme-bar__section">
<span class="theme-bar__label">Preset</span>
<SelectButton v-model="presetModel" :options="presetOptions" :allowEmpty="false" size="small" />
</div>
<!-- Menu mode (somente layout clássico) -->
<template v-if="effectiveVariant === 'classic'">
<div class="theme-bar__divider" />
<div class="theme-bar__section">
<span class="theme-bar__label">Menu</span>
<SelectButton v-model="menuModeModel" :options="menuModeOptions" :allowEmpty="false" optionLabel="label" optionValue="value" size="small" />
</div>
</template>
<div class="theme-bar__divider" />
<!-- Layout variant -->
<div class="theme-bar__section">
<span class="theme-bar__label">Layout</span>
<div class="flex gap-2">
<button
type="button"
class="variant-btn"
:class="{ 'variant-btn--active': layoutConfig.variant === 'rail' }"
@click="handleSetVariant('rail')"
>
<i :class="layoutConfig.variant === 'rail' ? 'pi pi-check-circle' : 'pi pi-circle'" />
Rail
</button>
<button
type="button"
class="variant-btn"
:class="{ 'variant-btn--active': layoutConfig.variant === 'classic' }"
@click="handleSetVariant('classic')"
>
<i :class="layoutConfig.variant === 'classic' ? 'pi pi-check-circle' : 'pi pi-circle'" />
Clássico
</button>
</div>
</div>
<!-- Fechar -->
<button type="button" class="theme-bar__close" title="Fechar painel de tema" @click="close">
<i class="pi pi-times" />
</button>
</div>
</div>
</template>
<style scoped>
.theme-bar {
position: fixed;
bottom: 0;
right: 0;
background: var(--surface-card);
border-top: 1px solid var(--surface-border);
box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.08);
z-index: 200;
padding: 0.6rem 1rem;
/* left acompanha o painel rail; transform é o slide-up/down de abrir/fechar */
transition:
left 0.22s cubic-bezier(0.22, 1, 0.36, 1),
transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}
.theme-bar__inner {
display: flex;
align-items: center;
gap: 0;
flex-wrap: wrap;
row-gap: 0.5rem;
min-height: 52px;
}
.theme-bar__section {
display: flex;
flex-direction: column;
gap: 0.3rem;
padding: 0 0.85rem;
}
.theme-bar__label {
font-size: 0.68rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--text-color-secondary);
white-space: nowrap;
}
.theme-bar__swatches {
display: flex;
gap: 0.28rem;
flex-wrap: wrap;
max-width: 220px;
}
.theme-bar__divider {
width: 1px;
height: 44px;
background: var(--surface-border);
flex-shrink: 0;
align-self: center;
}
/* ── Swatches ─────────────────────────────────────────── */
.swatch {
width: 1.05rem;
height: 1.05rem;
border-radius: 50%;
border: none;
padding: 0;
cursor: pointer;
outline: none;
outline-offset: 2px;
transition: transform 0.12s;
flex-shrink: 0;
}
.swatch:hover {
transform: scale(1.2);
}
.swatch--active {
outline: 2px solid var(--primary-color);
}
/* ── Variant buttons ──────────────────────────────────── */
.variant-btn {
display: inline-flex;
align-items: center;
gap: 0.3rem;
padding: 0.28rem 0.6rem;
border-radius: 6px;
border: 1px solid var(--surface-border);
background: var(--surface-ground);
color: var(--text-color-secondary);
font-size: 0.78rem;
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
}
.variant-btn:hover {
color: var(--text-color);
border-color: color-mix(in srgb, var(--primary-color) 60%, transparent);
}
.variant-btn--active {
background: color-mix(in srgb, var(--primary-color) 10%, transparent);
color: var(--primary-color);
border-color: var(--primary-color);
}
/* ── Fechar ───────────────────────────────────────────── */
.theme-bar__close {
margin-left: auto;
padding-left: 0.85rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
border: none;
background: transparent;
color: var(--text-color-secondary);
display: grid;
place-items: center;
cursor: pointer;
transition: all 0.15s;
flex-shrink: 0;
}
.theme-bar__close:hover {
background: var(--surface-ground);
color: var(--text-color);
}
/* ── Mobile: wrap, sem dividers verticais ─────────────── */
@media (max-width: 768px) {
.theme-bar__divider {
display: none;
}
.theme-bar__section {
padding: 0 0.5rem;
}
.theme-bar__swatches {
max-width: 100%;
}
}
</style>