Files
crewli/apps/app/src/components-v2/shared/StateBlock.vue

78 lines
2.0 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
/**
* StateBlock — the CLAUDE.md mandatory three-state wrapper (loading /
* error / empty) + success passthrough. Built fresh (no crewli-starter
* source). Per Plan 3 constraint #5 this component intentionally has
* NO @visual baseline yet (self-baseline is tautological); correctness
* is locked by the exhaustive Vitest spec. Visual baseline is a
* Plans 45 follow-up after first real page usage.
*/
import Skeleton from 'primevue/skeleton'
import Message from 'primevue/message'
import Button from 'primevue/button'
import Card from 'primevue/card'
defineProps<{
state: 'loading' | 'error' | 'empty' | 'success'
errorMessage?: string
emptyMessage?: string
actionLabel?: string
retryLabel?: string
}>()
const emit = defineEmits<{ retry: []; action: [] }>()
</script>
<template>
<div
v-if="state === 'loading'"
class="flex flex-col gap-3"
data-state="loading"
>
<Skeleton height="2rem" />
<Skeleton
height="2rem"
width="80%"
/>
<Skeleton
height="2rem"
width="60%"
/>
</div>
<Message
v-else-if="state === 'error'"
severity="error"
:closable="false"
data-state="error"
>
<div class="flex items-center justify-between gap-4 w-full">
<span>{{ errorMessage ?? 'Er ging iets mis.' }}</span>
<Button
:label="retryLabel ?? 'Opnieuw proberen'"
size="small"
@click="emit('retry')"
/>
</div>
</Message>
<Card
v-else-if="state === 'empty'"
data-state="empty"
:pt="{ root: 'border border-dashed border-[var(--p-content-border-color)] shadow-none', content: 'flex flex-col items-center gap-3 py-10 text-center' }"
>
<template #content>
<p class="m-0 text-[var(--p-text-muted-color)]">
{{ emptyMessage ?? 'Nog niets om te tonen.' }}
</p>
<Button
v-if="actionLabel"
:label="actionLabel"
@click="emit('action')"
/>
</template>
</Card>
<slot v-else />
</template>