feat: Implement initial Vue 3 application structure with SSR, routing, authentication, and core dashboard components.
This commit is contained in:
79
src/components/dashboard/PageHeader.vue
Normal file
79
src/components/dashboard/PageHeader.vue
Normal file
@@ -0,0 +1,79 @@
|
||||
<script setup lang="ts">
|
||||
interface Breadcrumb {
|
||||
label: string;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
interface Action {
|
||||
label: string;
|
||||
icon?: string;
|
||||
variant?: 'primary' | 'secondary' | 'danger';
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
title: string;
|
||||
description?: string;
|
||||
breadcrumbs?: Breadcrumb[];
|
||||
actions?: Action[];
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
|
||||
const getButtonClass = (variant?: string) => {
|
||||
const baseClass = 'px-4 py-2.5 rounded-lg font-medium transition-all press-animated flex items-center gap-2';
|
||||
|
||||
switch (variant) {
|
||||
case 'primary':
|
||||
return `${baseClass} bg-primary hover:bg-primary-600 text-white shadow-sm`;
|
||||
case 'danger':
|
||||
return `${baseClass} bg-danger hover:bg-danger-600 text-white shadow-sm`;
|
||||
case 'secondary':
|
||||
default:
|
||||
return `${baseClass} bg-white hover:bg-gray-50 text-gray-700 border border-gray-300`;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-header mb-6">
|
||||
<!-- Breadcrumb -->
|
||||
<nav v-if="breadcrumbs && breadcrumbs.length" class="flex items-center gap-2 text-sm mb-2">
|
||||
<template v-for="(crumb, index) in breadcrumbs" :key="index">
|
||||
<router-link
|
||||
v-if="crumb.to"
|
||||
:to="crumb.to"
|
||||
class="text-gray-500 hover:text-primary transition-colors"
|
||||
>
|
||||
{{ crumb.label }}
|
||||
</router-link>
|
||||
<span v-else class="text-gray-700 font-medium">{{ crumb.label }}</span>
|
||||
|
||||
<span
|
||||
v-if="index < breadcrumbs.length - 1"
|
||||
class="i-heroicons-chevron-right w-4 h-4 text-gray-400"
|
||||
/>
|
||||
</template>
|
||||
</nav>
|
||||
|
||||
<!-- Title & Actions -->
|
||||
<div class="flex items-start justify-between gap-4 flex-wrap">
|
||||
<div class="flex-1 min-w-0">
|
||||
<h1 class="text-3xl font-bold text-gray-900 mb-1">{{ title }}</h1>
|
||||
<p v-if="description" class="text-gray-600">{{ description }}</p>
|
||||
</div>
|
||||
|
||||
<div v-if="actions && actions.length" class="flex items-center gap-2 flex-shrink-0">
|
||||
<button
|
||||
v-for="(action, index) in actions"
|
||||
:key="index"
|
||||
@click="action.onClick"
|
||||
:class="getButtonClass(action.variant)"
|
||||
>
|
||||
<span v-if="action.icon" :class="[action.icon, 'w-5 h-5']" />
|
||||
{{ action.label }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user