adding a table component

This commit is contained in:
2026-06-01 17:25:47 -06:00
parent 9dfe4a9a01
commit d34d61458b
+139
View File
@@ -0,0 +1,139 @@
<script setup>
defineProps({
headers: {
type: Array,
default: () => ['Author', 'Function', 'Status', 'Employed', 'Action'],
},
/**
* Each row: {
* image?: string — avatar URL (falls back to initials)
* initials?: string — shown when no image
* color?: string — gradient color for initials avatar
* name: string
* email: string
* position: [string, string] — [label, sublabel]
* status: boolean — true = Online, false = Offline
* date: string
* action: { label, route }
* }
*/
rows: {
type: Array,
required: true,
},
})
const avatarGradients = {
primary: 'bg-gradient-primary shadow-primary',
secondary: 'bg-gradient-secondary shadow-secondary',
success: 'bg-gradient-success shadow-success',
warning: 'bg-gradient-warning shadow-warning',
danger: 'bg-gradient-danger shadow-danger',
info: 'bg-gradient-info shadow-info',
dark: 'bg-gradient-dark shadow-dark',
}
</script>
<template>
<div class="overflow-hidden rounded-2xl bg-white shadow-soft-md">
<!-- Optional slot for a card header (title, actions, etc.) -->
<div v-if="$slots.header" class="border-b border-gray-100 px-6 py-4">
<slot name="header" />
</div>
<div class="overflow-x-auto">
<table class="w-full text-left text-sm">
<thead>
<tr class="border-b border-gray-100">
<th
v-for="(header, i) in headers"
:key="header"
class="px-6 py-3 text-xs font-bold uppercase tracking-wide text-secondary"
:class="i > 1 ? 'text-center' : ''"
>
{{ header }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(row, i) in rows"
:key="i"
:class="i < rows.length - 1 ? 'border-b border-gray-100' : ''"
>
<!-- Author: avatar + name + email -->
<td class="px-6 py-3">
<div class="flex items-center gap-3">
<!-- Photo avatar -->
<img
v-if="row.image"
:src="row.image"
:alt="row.name"
class="h-9 w-9 shrink-0 rounded-xl object-cover"
/>
<!-- Initials avatar -->
<div
v-else
class="flex h-9 w-9 shrink-0 items-center justify-center rounded-xl text-xs font-bold text-white"
:class="avatarGradients[row.color] ?? avatarGradients.dark"
>
{{ row.initials ?? row.name?.slice(0, 2).toUpperCase() }}
</div>
<div>
<p class="font-medium text-dark">{{ row.name }}</p>
<p class="text-xs text-secondary">{{ row.email }}</p>
</div>
</div>
</td>
<!-- Position: two-line label -->
<td class="px-6 py-3">
<p class="font-medium text-dark">{{ row.position?.[0] ?? '—' }}</p>
<p class="text-xs text-secondary">{{ row.position?.[1] ?? '' }}</p>
</td>
<!-- Status badge -->
<td class="px-6 py-3 text-center">
<span
class="inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium"
:class="row.status
? 'bg-success/10 text-success'
: 'bg-gray-100 text-secondary'"
>
<span
class="h-1.5 w-1.5 rounded-full"
:class="row.status ? 'bg-success' : 'bg-gray-400'"
/>
{{ row.status ? 'Online' : 'Offline' }}
</span>
</td>
<!-- Date -->
<td class="px-6 py-3 text-center text-xs text-secondary">
{{ row.date }}
</td>
<!-- Action -->
<td class="px-6 py-3 text-center">
<a
:href="row.action?.route ?? '#'"
class="text-xs font-medium text-secondary no-underline transition-colors hover:text-primary"
>
{{ row.action?.label ?? 'Edit' }}
</a>
</td>
</tr>
</tbody>
</table>
</div>
<!-- Optional slot for pagination or footer -->
<div v-if="$slots.footer" class="border-t border-gray-100 px-6 py-4">
<slot name="footer" />
</div>
</div>
</template>