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:
@@ -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>
|
||||
Reference in New Issue
Block a user