feat(ui) : clickable entity links, site→machines links, DataTable fixedLayout

- Add NuxtLink on component/piece/product names in machine hierarchy
  (using composantId, pieceId, product.id)
- Make site machine count badges clickable → /machines?sites={id}
- Add opt-in fixedLayout prop and minWidth to DataTable columns

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 16:39:21 +02:00
parent 6716d31126
commit 4f13f7d301
7 changed files with 43 additions and 13 deletions

View File

@@ -30,7 +30,15 @@
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 flex-wrap">
<h3 class="text-sm font-semibold truncate" :class="component.pendingEntity ? 'text-error' : 'text-base-content'">
{{ component.name }}
<NuxtLink
v-if="!isEditMode && !component.pendingEntity && component.composantId"
:to="`/component/${component.composantId}`"
class="hover:underline hover:text-primary transition-colors"
@click.stop
>
{{ component.name }}
</NuxtLink>
<span v-else>{{ component.name }}</span>
</h3>
<button
v-if="component.pendingEntity"

View File

@@ -29,7 +29,15 @@
</button>
<div class="flex-1 min-w-0">
<h3 class="text-lg font-semibold" :class="{ 'text-error': piece._emptySlot || piece.pendingEntity }">
{{ pieceData.name }}
<NuxtLink
v-if="!isEditMode && !piece.pendingEntity && !piece._emptySlot && piece.pieceId"
:to="`/piece/${piece.pieceId}`"
class="hover:underline hover:text-primary transition-colors"
@click.stop
>
{{ pieceData.name }}
</NuxtLink>
<template v-else>{{ pieceData.name }}</template>
<span v-if="piece._emptySlot" class="text-sm font-semibold text-error ml-1"> manquant</span>
<button
v-if="piece.pendingEntity"

View File

@@ -71,7 +71,7 @@
>
<span class="loading loading-spinner text-primary" aria-hidden="true" />
</div>
<table :class="['table table-sm md:table-md', tableClass]">
<table :class="['table table-sm md:table-md', tableClass, { 'table-fixed': fixedLayout }]">
<thead>
<!-- Header labels + sort -->
<tr>
@@ -85,6 +85,7 @@
alignClass(col),
{ 'hidden sm:table-cell': col.hiddenMobile },
]"
:style="col.minWidth ? { minWidth: col.minWidth } : undefined"
>
<slot :name="`header-${col.key}`" :column="col">
<span
@@ -221,6 +222,8 @@ const props = withDefaults(defineProps<{
tableClass?: string
showCounter?: boolean
showPerPage?: boolean
/** Use table-layout: fixed for stable column widths. Only enable on tables where columns define width/minWidth. */
fixedLayout?: boolean
}>(), {
rowKey: 'id',
loading: false,

View File

@@ -29,7 +29,14 @@
>
<div class="flex items-center justify-between flex-wrap gap-2">
<p class="font-semibold" :class="product.pendingEntity ? 'text-error' : 'text-base-content'">
{{ product.name }}
<NuxtLink
v-if="!product.pendingEntity && product.id"
:to="`/product/${product.id}`"
class="hover:underline hover:text-primary transition-colors"
>
{{ product.name }}
</NuxtLink>
<span v-else>{{ product.name }}</span>
</p>
<div class="flex items-center gap-2">
<button

View File

@@ -11,13 +11,14 @@
<h3 class="card-title text-lg text-base-content">
{{ site.name }}
</h3>
<div
class="badge font-bold"
<NuxtLink
:to="`/machines?sites=${site.id}`"
class="badge font-bold hover:opacity-80 transition-opacity"
:style="site.color ? { backgroundColor: site.color + '30', color: site.color, borderColor: site.color + '50' } : {}"
:class="!site.color ? 'badge-primary' : ''"
>
{{ machineCount }} machines
</div>
</NuxtLink>
</div>
<div class="space-y-3 text-sm">
@@ -39,10 +40,10 @@
</span>
</div>
<div class="flex items-center gap-2 text-base-content/60">
<NuxtLink :to="`/machines?sites=${site.id}`" class="flex items-center gap-2 text-base-content/60 hover:text-primary transition-colors">
<IconLucideFactory class="w-4 h-4 text-blue-500" aria-hidden="true" />
<span>{{ machineCount }} machine(s)</span>
</div>
</NuxtLink>
</div>
<div class="card-actions justify-end mt-4">

View File

@@ -141,13 +141,14 @@
</div>
</div>
<div class="flex items-center gap-2 shrink-0">
<span
class="badge font-bold"
<NuxtLink
:to="`/machines?sites=${site.id}`"
class="badge font-bold hover:opacity-80 transition-opacity"
:style="site.color ? { backgroundColor: site.color + '30', color: site.color, borderColor: site.color + '50' } : {}"
:class="!site.color ? 'badge-primary' : ''"
>
{{ site.machines?.length || 0 }}
</span>
{{ site.machines?.length || 0 }} machine{{ (site.machines?.length || 0) > 1 ? 's' : '' }}
</NuxtLink>
<button
class="btn btn-ghost btn-xs btn-circle"
@click="toggleSiteCollapse(site.id)"

View File

@@ -15,6 +15,8 @@ export interface DataTableColumn {
headerClass?: string
/** Width hint (e.g. 'w-24', 'min-w-[10rem]') */
width?: string
/** Inline min-width style (e.g. '120px', '8rem'). Only effective with fixedLayout. */
minWidth?: string
/** Text alignment: 'left' (default), 'center', 'right' */
align?: 'left' | 'center' | 'right'
/** Hide on mobile (adds 'hidden sm:table-cell') */