Ajuste usuarios - Inicio agenda

This commit is contained in:
Leonardo
2026-02-23 18:57:40 -03:00
parent 89b4ecaba1
commit b1c0cb47c0
11 changed files with 2244 additions and 576 deletions
@@ -1,12 +1,157 @@
<!-- src/features/agenda/components/AgendaToolbar.vue -->
<script setup>
defineProps({
items: { type: Array, default: () => [] }
import { computed, ref, watch } from 'vue'
import Card from 'primevue/card'
import Button from 'primevue/button'
import Divider from 'primevue/divider'
import SelectButton from 'primevue/selectbutton'
import InputText from 'primevue/inputtext'
import FloatLabel from 'primevue/floatlabel'
import Tag from 'primevue/tag'
const props = defineProps({
title: { type: String, default: 'Agenda' },
view: { type: String, default: 'day' }, // 'day' | 'week'
mode: { type: String, default: 'work_hours' } // 'full_24h' | 'work_hours'
})
defineEmits(['open', 'confirm', 'reschedule'])
const emit = defineEmits([
'today',
'prev',
'next',
'changeView',
'toggleMode',
'createSession',
'createBlock',
'search'
])
// UX: busca com debounce simples
const searchLocal = ref('')
let t = null
watch(searchLocal, (v) => {
clearTimeout(t)
t = setTimeout(() => emit('search', v), 220)
})
// SelectButtons (executivo, direto)
const viewOptions = [
{ label: 'Dia', value: 'day' },
{ label: 'Semana', value: 'week' }
]
const modeOptions = [
{ label: 'Horário de funcionamento', value: 'work_hours' },
{ label: 'Dia completo (24h)', value: 'full_24h' }
]
const modeTag = computed(() => {
return props.mode === 'full_24h'
? { severity: 'info', text: '24h' }
: { severity: 'success', text: 'Funcionamento' }
})
function onChangeView(val) {
emit('changeView', val)
}
function onToggleMode(val) {
emit('toggleMode', val)
}
</script>
<template>
<div class="p-3 border rounded-lg">
<b>AgendaNextSessionsCardList (placeholder)</b>
</div>
<Card class="mb-3 md:mb-4">
<template #content>
<div class="flex flex-column gap-3">
<!-- Top row -->
<div class="flex align-items-start justify-content-between gap-3 flex-wrap">
<div class="min-w-0">
<div class="flex align-items-center gap-2 flex-wrap">
<div class="text-xl md:text-2xl font-semibold leading-tight">
{{ title }}
</div>
<Tag :severity="modeTag.severity" :value="modeTag.text" />
</div>
<div class="text-sm mt-1" style="color: var(--text-color-secondary);">
Navegue, filtre e crie compromissos com velocidade sem perder controle clínico.
</div>
</div>
<div class="flex align-items-center gap-2 flex-wrap">
<Button
size="small"
icon="pi pi-plus"
label="Sessão"
@click="emit('createSession')"
/>
<Button
size="small"
icon="pi pi-lock"
severity="secondary"
label="Bloqueio"
@click="emit('createBlock')"
/>
</div>
</div>
<Divider class="my-0" />
<!-- Controls row -->
<div class="grid gap-3 md:gap-4" style="grid-template-columns: 1fr;">
<div class="grid gap-3 md:gap-4 items-center" style="grid-template-columns: 1fr;">
<div class="flex align-items-center justify-content-between gap-3 flex-wrap">
<!-- Nav -->
<div class="flex align-items-center gap-2 flex-wrap">
<Button size="small" text icon="pi pi-angle-left" @click="emit('prev')" />
<Button size="small" text icon="pi pi-angle-right" @click="emit('next')" />
<Button size="small" icon="pi pi-calendar" label="Hoje" @click="emit('today')" />
</div>
<!-- View + Mode -->
<div class="flex align-items-center gap-2 flex-wrap">
<SelectButton
:modelValue="view"
:options="viewOptions"
optionLabel="label"
optionValue="value"
@update:modelValue="onChangeView"
/>
<SelectButton
:modelValue="mode"
:options="modeOptions"
optionLabel="label"
optionValue="value"
@update:modelValue="onToggleMode"
/>
</div>
</div>
<!-- Search -->
<div class="flex align-items-center gap-2 flex-wrap">
<div class="flex-1 min-w-[260px]">
<FloatLabel>
<InputText id="agenda-search" v-model="searchLocal" class="w-full" />
<label for="agenda-search">Buscar por título / observação</label>
</FloatLabel>
</div>
<Button
size="small"
text
icon="pi pi-times"
:disabled="!searchLocal"
@click="searchLocal = ''"
/>
</div>
</div>
</div>
</div>
</template>
</Card>
</template>
@@ -1,12 +1,142 @@
<!-- src/features/agenda/components/cards/AgendaPulseCardGrid.vue -->
<script setup>
defineProps({
import { computed } from 'vue'
import Card from 'primevue/card'
import Button from 'primevue/button'
import Divider from 'primevue/divider'
import Tag from 'primevue/tag'
const props = defineProps({
stats: { type: Object, default: () => ({}) }
})
defineEmits(['quickBlock', 'quickCreate'])
const emit = defineEmits(['quickBlock', 'quickCreate'])
const dados_de_exemplo = {
totalSessions: 6,
totalMinutes: 300,
biggestFreeWindow: '2h 10m',
pending: 0,
reschedules: 1,
attentions: 1,
suggested1: '14:00',
suggested2: '16:30',
nextBreak: '12:00'
}
const s = computed(() => {
const base = props.stats && Object.keys(props.stats).length ? props.stats : dados_de_exemplo
return {
totalSessions: base.totalSessions ?? 0,
totalMinutes: base.totalMinutes ?? 0,
biggestFreeWindow: base.biggestFreeWindow ?? '—',
pending: base.pending ?? 0,
reschedules: base.reschedules ?? 0,
attentions: base.attentions ?? 0,
suggested1: base.suggested1 ?? '—',
suggested2: base.suggested2 ?? '—',
nextBreak: base.nextBreak ?? '—'
}
})
function minutesToHuman(min) {
const n = Number(min || 0)
const h = Math.floor(n / 60)
const m = n % 60
if (h <= 0) return `${m}m`
if (m <= 0) return `${h}h`
return `${h}h ${m}m`
}
const totalTimeHuman = computed(() => minutesToHuman(s.value.totalMinutes))
const attentionSeverity = computed(() => {
if (s.value.attentions >= 3) return 'danger'
if (s.value.attentions >= 1) return 'warning'
return 'success'
})
</script>
<template>
<div class="p-3 border rounded-lg">
<b>AgendaPulseCardGrid (placeholder)</b>
</div>
<Card>
<template #title>
<div class="flex align-items-center justify-content-between gap-2 flex-wrap">
<div class="flex flex-column">
<span>Pulso da agenda</span>
<span class="text-xs" style="color: var(--text-color-secondary);">
Indicadores rápidos para decisão imediata.
</span>
</div>
<div class="flex align-items-center gap-2">
<Tag :severity="attentionSeverity" :value="`Atenções: ${s.attentions}`" />
</div>
</div>
</template>
<template #content>
<div class="grid gap-2">
<!-- Linha 1: métricas -->
<div class="grid gap-2" style="grid-template-columns: 1fr 1fr;">
<div class="p-3 border-round-xl" style="border: 1px solid var(--surface-border); background: var(--surface-card);">
<div class="text-xs" style="color: var(--text-color-secondary);">Sessões (no filtro)</div>
<div class="text-xl font-semibold mt-1">{{ s.totalSessions }}</div>
</div>
<div class="p-3 border-round-xl" style="border: 1px solid var(--surface-border); background: var(--surface-card);">
<div class="text-xs" style="color: var(--text-color-secondary);">Tempo total</div>
<div class="text-xl font-semibold mt-1">{{ totalTimeHuman }}</div>
</div>
</div>
<!-- Linha 2: janelas e atenção -->
<div class="grid gap-2" style="grid-template-columns: 1fr 1fr;">
<div class="p-3 border-round-xl" style="border: 1px solid var(--surface-border); background: var(--surface-card);">
<div class="text-xs" style="color: var(--text-color-secondary);">Maior janela livre</div>
<div class="text-base font-semibold mt-1">{{ s.biggestFreeWindow }}</div>
<div class="text-xs mt-2" style="color: var(--text-color-secondary);">
Sugestões: <b>{{ s.suggested1 }}</b> e <b>{{ s.suggested2 }}</b>
</div>
</div>
<div class="p-3 border-round-xl" style="border: 1px solid var(--surface-border); background: var(--surface-card);">
<div class="text-xs" style="color: var(--text-color-secondary);">Pontos de atenção</div>
<div class="flex align-items-center gap-2 mt-2 flex-wrap">
<Tag severity="warning" :value="`Pendências: ${s.pending}`" />
<Tag severity="info" :value="`Remarcar: ${s.reschedules}`" />
</div>
<div class="text-xs mt-2" style="color: var(--text-color-secondary);">
Próxima pausa: <b>{{ s.nextBreak }}</b>
</div>
</div>
</div>
<Divider class="my-2" />
<!-- Ações rápidas -->
<div class="flex align-items-center justify-content-between gap-2 flex-wrap">
<div class="text-xs" style="color: var(--text-color-secondary);">
Ações rápidas (criação sempre abre modal nada nasce direto).
</div>
<div class="flex align-items-center gap-2">
<Button
size="small"
icon="pi pi-plus"
label="Nova sessão"
@click="emit('quickCreate')"
/>
<Button
size="small"
icon="pi pi-lock"
severity="secondary"
label="Bloquear horário"
@click="emit('quickBlock')"
/>
</div>
</div>
</div>
</template>
</Card>
</template>