All files / src/components/ui PageHeader.vue

100% Statements 10/10
50% Branches 1/2
100% Functions 0/0
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41                                                1x 1x 1x 1x 1x 1x           1x 43x 1x 1x    
<script setup lang="ts">
/**
 * Unified page header for all top-level "main entity" views
 * (Deployments, Apps, Kurse, Approvals, User-Settings, ...).
 *
 * Lifts the exact pattern from ``CoursesView.vue`` into a reusable
 * component: a flex row with title + optional subtitle on the left
 * and an actions slot on the right. ``mb-8`` separator below so the
 * page body always sits the same distance from the title.
 *
 * Why a component and not a Tailwind preset: the actions slot is
 * page-specific (create button, filter toggle, refresh, ...). A
 * slot beats a prop here because each page wants different
 * combinations of buttons, sometimes none.
 */
defineProps<{
  title: string
  /** Optional one-line subtitle in muted gray. Omitting it just
   *  removes the line — title sizing stays the same. */
  subtitle?: string
}>()
</script>
 
<template>
  <div class="flex items-center justify-between mb-8 gap-4 flex-wrap">
    <div class="min-w-0">
      <h1 class="text-3xl font-bold text-gray-900 mb-1">{{ title }}</h1>
      <p v-if="subtitle" class="text-gray-500">{{ subtitle }}</p>
    </div>
    <!--
      Actions go right of the title row. Multiple buttons stack
      horizontally; the parent decides the order. When the slot is
      empty Vue renders nothing here and the title row uses the full
      width — no empty placeholder div.
    -->
    <div v-if="$slots.actions" class="flex items-center gap-2 shrink-0">
      <slot name="actions" />
    </div>
  </div>
</template>