Files
orion/app/templates/shared/macros/tables.html
Samir Boulahtit 64ab9031d2 feat: add shared Jinja macros for reusable UI components
Add comprehensive macro library in app/templates/shared/macros/:

- pagination.html: pagination(), pagination_simple()
- alerts.html: loading_state(), error_state(), alert(), toast()
- badges.html: badge(), status_badge(), role_badge(), severity_badge()
- buttons.html: btn(), btn_primary(), btn_danger(), action_button()
- forms.html: form_input(), form_select(), form_textarea(), form_toggle()
- tables.html: table_wrapper(), table_header(), table_empty_state()
- cards.html: stat_card(), card(), info_card(), filter_card()
- headers.html: page_header(), section_header(), breadcrumbs()

These macros standardize TailAdmin styling with Alpine.js integration
and dark mode support, reducing code duplication across templates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-06 18:34:59 +01:00

216 lines
6.3 KiB
HTML

{#
Table Macros
============
Reusable table components.
Usage:
{% from 'shared/macros/tables.html' import table_wrapper, table_header, table_empty_state %}
{% call table_wrapper() %}
{{ table_header(['Name', 'Email', 'Status', 'Actions']) }}
<tbody>...</tbody>
{% endcall %}
#}
{#
Table Wrapper
=============
Wraps the table with proper overflow and shadow styling.
#}
{% macro table_wrapper(class_extra='') %}
<div class="w-full overflow-hidden rounded-lg shadow-xs {{ class_extra }}">
<div class="w-full overflow-x-auto">
<table class="w-full whitespace-no-wrap">
{{ caller() }}
</table>
</div>
</div>
{% endmacro %}
{#
Table Header
============
Renders the table header row.
Parameters:
- columns: List of column names
- sortable: Whether columns are sortable (default: false) - future enhancement
#}
{% macro table_header(columns) %}
<thead>
<tr class="text-xs font-semibold tracking-wide text-left text-gray-500 uppercase border-b dark:border-gray-700 bg-gray-50 dark:text-gray-400 dark:bg-gray-800">
{% for column in columns %}
<th class="px-4 py-3">{{ column }}</th>
{% endfor %}
</tr>
</thead>
{% endmacro %}
{#
Table Body Wrapper
==================
Wraps the tbody with proper styling.
#}
{% macro table_body() %}
<tbody class="bg-white divide-y dark:divide-gray-700 dark:bg-gray-800">
{{ caller() }}
</tbody>
{% endmacro %}
{#
Table Empty State
=================
Shows a centered message when the table has no data.
Parameters:
- colspan: Number of columns to span
- icon: Icon name (default: 'inbox')
- title: Empty state title
- message: Empty state message
- show_condition: Alpine.js condition (default: 'true')
- has_filters: Whether to show filter hint (default: true)
#}
{% macro table_empty_state(colspan, icon='inbox', title='No data found', message='', show_condition='true', has_filters=true) %}
<template x-if="{{ show_condition }}">
<tr>
<td colspan="{{ colspan }}" class="px-4 py-8 text-center text-gray-600 dark:text-gray-400">
<div class="flex flex-col items-center">
<span x-html="$icon('{{ icon }}', 'w-12 h-12 mb-2 text-gray-300')"></span>
<p class="font-medium">{{ title }}</p>
{% if message %}
<p class="text-xs mt-1">{{ message }}</p>
{% elif has_filters %}
<p class="text-xs mt-1">Try adjusting your search or filters</p>
{% endif %}
</div>
</td>
</tr>
</template>
{% endmacro %}
{#
Table Row
=========
A standard table row with hover styling.
#}
{% macro table_row(class_extra='') %}
<tr class="text-gray-700 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-700 {{ class_extra }}">
{{ caller() }}
</tr>
{% endmacro %}
{#
Table Cell
==========
A standard table cell.
#}
{% macro table_cell(class_extra='') %}
<td class="px-4 py-3 {{ class_extra }}">
{{ caller() }}
</td>
{% endmacro %}
{#
Table Cell with Text
====================
A simple text cell.
Parameters:
- text: Static text or Alpine.js expression for x-text
- is_dynamic: Whether text is Alpine.js expression (default: false)
- truncate: Whether to truncate with max-width (default: false)
- max_width: Max width class (default: 'max-w-xs')
#}
{% macro table_cell_text(text, is_dynamic=false, truncate=false, max_width='max-w-xs') %}
<td class="px-4 py-3 text-sm">
{% if truncate %}
<p class="truncate {{ max_width }}" {% if is_dynamic %}x-text="{{ text }}" :title="{{ text }}"{% endif %}>
{% if not is_dynamic %}{{ text }}{% endif %}
</p>
{% else %}
{% if is_dynamic %}
<span x-text="{{ text }}"></span>
{% else %}
{{ text }}
{% endif %}
{% endif %}
</td>
{% endmacro %}
{#
Table Cell with Avatar
======================
A cell with avatar image and text.
Parameters:
- image_src: Image source (Alpine.js expression)
- title: Primary text (Alpine.js expression)
- subtitle: Secondary text (Alpine.js expression, optional)
- fallback_icon: Icon to show if no image (default: 'user')
#}
{% macro table_cell_avatar(image_src, title, subtitle=none, fallback_icon='user') %}
<td class="px-4 py-3">
<div class="flex items-center text-sm">
<div class="relative hidden w-8 h-8 mr-3 rounded-full md:block">
<template x-if="{{ image_src }}">
<img class="object-cover w-full h-full rounded-full" :src="{{ image_src }}" :alt="{{ title }}" loading="lazy">
</template>
<template x-if="!{{ image_src }}">
<div class="flex items-center justify-center w-full h-full bg-gray-200 dark:bg-gray-700 rounded-full">
<span x-html="$icon('{{ fallback_icon }}', 'w-4 h-4 text-gray-500')"></span>
</div>
</template>
<div class="absolute inset-0 rounded-full shadow-inner" aria-hidden="true"></div>
</div>
<div>
<p class="font-semibold" x-text="{{ title }}"></p>
{% if subtitle %}
<p class="text-xs text-gray-600 dark:text-gray-400" x-text="{{ subtitle }}"></p>
{% endif %}
</div>
</div>
</td>
{% endmacro %}
{#
Table Cell with Date
====================
A cell that formats a date.
Parameters:
- date_var: Alpine.js variable containing the date
- format_func: JavaScript date formatting function (default: 'formatDate')
#}
{% macro table_cell_date(date_var, format_func='formatDate') %}
<td class="px-4 py-3 text-sm">
<span x-text="{{ format_func }}({{ date_var }})"></span>
</td>
{% endmacro %}
{#
Table Loading Overlay
=====================
An overlay shown while table data is loading.
Parameters:
- show_condition: Alpine.js condition (default: 'loading')
- message: Loading message
#}
{% macro table_loading_overlay(show_condition='loading', message='Loading...') %}
<div x-show="{{ show_condition }}" class="absolute inset-0 bg-white/75 dark:bg-gray-800/75 flex items-center justify-center z-10">
<div class="text-center">
<span x-html="$icon('spinner', 'inline w-8 h-8 text-purple-600')"></span>
<p class="mt-2 text-sm text-gray-600 dark:text-gray-400">{{ message }}</p>
</div>
</div>
{% endmacro %}