138 lines
4.8 KiB
Vue
138 lines
4.8 KiB
Vue
<!-- src/features/agenda/components/cards/AgendaPulseCardGrid.vue -->
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
|
|
|
|
const props = defineProps({
|
|
stats: { type: Object, default: () => ({}) }
|
|
})
|
|
|
|
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>
|
|
<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> |