diff --git a/app/templates/shared/macros/alerts.html b/app/templates/shared/macros/alerts.html new file mode 100644 index 00000000..aa70c8ed --- /dev/null +++ b/app/templates/shared/macros/alerts.html @@ -0,0 +1,178 @@ +{# + Alert & Loading State Macros + ============================ + Reusable components for alerts, loading states, and notifications. + + Usage: + {% from 'shared/macros/alerts.html' import loading_state, error_state, alert %} + {{ loading_state('Loading vendors...') }} + {{ error_state('Error loading data', 'error') }} + {{ alert('success', 'Success!', 'Your changes have been saved.') }} +#} + + +{# + Loading State + ============= + Shows a centered loading spinner with message. + + Parameters: + - message: Text to display below the spinner (default: 'Loading...') + - show_condition: Alpine.js condition for x-show (default: 'loading') +#} +{% macro loading_state(message='Loading...', show_condition='loading') %} +
+ +

{{ message }}

+
+{% endmacro %} + + +{# + Error State + =========== + Shows an error message box with icon. + + Parameters: + - title: Error title (default: 'Error') + - error_var: Alpine.js variable containing error message (default: 'error') + - show_condition: Alpine.js condition for x-show (default: 'error && !loading') +#} +{% macro error_state(title='Error', error_var='error', show_condition='error && !loading') %} +
+ +
+

{{ title }}

+

+
+
+{% endmacro %} + + +{# + Alert + ===== + A versatile alert component for different message types. + + Parameters: + - type: 'success' | 'warning' | 'error' | 'info' (default: 'info') + - title: Alert title + - message: Alert message (can be Alpine.js expression with x-text) + - dismissible: Whether the alert can be dismissed (default: false) + - show_condition: Alpine.js condition for x-show (default: 'true') + - icon: Override the default icon (optional) +#} +{% macro alert(type='info', title='', message='', dismissible=false, show_condition='true', icon=none) %} +{% set colors = { + 'success': 'bg-green-100 border-green-400 text-green-700 dark:bg-green-900 dark:border-green-700 dark:text-green-200', + 'warning': 'bg-yellow-100 border-yellow-400 text-yellow-700 dark:bg-yellow-900 dark:border-yellow-700 dark:text-yellow-200', + 'error': 'bg-red-100 border-red-400 text-red-700 dark:bg-red-900 dark:border-red-700 dark:text-red-200', + 'info': 'bg-blue-100 border-blue-400 text-blue-700 dark:bg-blue-900 dark:border-blue-700 dark:text-blue-200' +} %} +{% set icons = { + 'success': 'check-circle', + 'warning': 'exclamation', + 'error': 'exclamation-circle', + 'info': 'information-circle' +} %} +{% set alert_icon = icon if icon else icons[type] %} + +{% endmacro %} + + +{# + Alert with Alpine.js binding + ============================ + Alert where the message comes from an Alpine.js variable. + + Parameters: + - type: 'success' | 'warning' | 'error' | 'info' (default: 'info') + - title: Alert title + - message_var: Alpine.js variable for the message + - show_condition: Alpine.js condition for x-show (default: 'true') +#} +{% macro alert_dynamic(type='info', title='', message_var='message', show_condition='true') %} +{% set colors = { + 'success': 'bg-green-100 border-green-400 text-green-700 dark:bg-green-900 dark:border-green-700 dark:text-green-200', + 'warning': 'bg-yellow-100 border-yellow-400 text-yellow-700 dark:bg-yellow-900 dark:border-yellow-700 dark:text-yellow-200', + 'error': 'bg-red-100 border-red-400 text-red-700 dark:bg-red-900 dark:border-red-700 dark:text-red-200', + 'info': 'bg-blue-100 border-blue-400 text-blue-700 dark:bg-blue-900 dark:border-blue-700 dark:text-blue-200' +} %} +{% set icons = { + 'success': 'check-circle', + 'warning': 'exclamation', + 'error': 'exclamation-circle', + 'info': 'information-circle' +} %} + +{% endmacro %} + + +{# + Toast Notification + ================== + A toast notification that appears and auto-dismisses. + + Parameters: + - type: 'success' | 'warning' | 'error' | 'info' (default: 'success') + - message_var: Alpine.js variable for the message + - show_var: Alpine.js variable to control visibility + - duration: Auto-dismiss duration in ms (default: 3000, 0 to disable) +#} +{% macro toast(type='success', message_var='toastMessage', show_var='showToast', duration=3000) %} +{% set colors = { + 'success': 'bg-green-500', + 'warning': 'bg-yellow-500', + 'error': 'bg-red-500', + 'info': 'bg-blue-500' +} %} +{% set icons = { + 'success': 'check-circle', + 'warning': 'exclamation', + 'error': 'exclamation-circle', + 'info': 'information-circle' +} %} +
0 %} + x-init="$watch('{{ show_var }}', value => { if (value) setTimeout(() => {{ show_var }} = false, {{ duration }}) })" + {% endif %} + class="fixed bottom-4 right-4 z-50 flex items-center px-4 py-3 text-white rounded-lg shadow-lg {{ colors[type] }}" + role="alert" +> + + + +
+{% endmacro %} diff --git a/app/templates/shared/macros/badges.html b/app/templates/shared/macros/badges.html new file mode 100644 index 00000000..435297fe --- /dev/null +++ b/app/templates/shared/macros/badges.html @@ -0,0 +1,211 @@ +{# + Badge Macros + ============ + Reusable badge components for status indicators, labels, and tags. + + Usage: + {% from 'shared/macros/badges.html' import badge, status_badge, verification_badge, role_badge %} + {{ badge('New', 'purple') }} + {{ status_badge('is_active', 'Active', 'Inactive') }} + {{ verification_badge('item.is_verified') }} + {{ role_badge('user.role') }} +#} + + +{# + Basic Badge + =========== + A simple colored badge. + + Parameters: + - text: Badge text (static) + - color: 'gray' | 'red' | 'green' | 'blue' | 'yellow' | 'purple' | 'orange' | 'pink' (default: 'gray') + - size: 'sm' | 'md' (default: 'sm') +#} +{% macro badge(text, color='gray', size='sm') %} +{% set colors = { + 'gray': 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700', + 'red': 'text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700', + 'green': 'text-green-700 bg-green-100 dark:text-green-100 dark:bg-green-700', + 'blue': 'text-blue-700 bg-blue-100 dark:text-blue-100 dark:bg-blue-700', + 'yellow': 'text-yellow-700 bg-yellow-100 dark:text-yellow-100 dark:bg-yellow-700', + 'purple': 'text-purple-700 bg-purple-100 dark:text-purple-100 dark:bg-purple-700', + 'orange': 'text-orange-700 bg-orange-100 dark:text-orange-100 dark:bg-orange-700', + 'pink': 'text-pink-700 bg-pink-100 dark:text-pink-100 dark:bg-pink-700' +} %} +{% set sizes = { + 'sm': 'px-2 py-1 text-xs', + 'md': 'px-3 py-1 text-sm' +} %} + + {{ text }} + +{% endmacro %} + + +{# + Dynamic Badge + ============= + A badge with text from Alpine.js variable. + + Parameters: + - text_var: Alpine.js variable for badge text + - color: Badge color (default: 'gray') + - size: 'sm' | 'md' (default: 'sm') +#} +{% macro badge_dynamic(text_var, color='gray', size='sm') %} +{% set colors = { + 'gray': 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700', + 'red': 'text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700', + 'green': 'text-green-700 bg-green-100 dark:text-green-100 dark:bg-green-700', + 'blue': 'text-blue-700 bg-blue-100 dark:text-blue-100 dark:bg-blue-700', + 'yellow': 'text-yellow-700 bg-yellow-100 dark:text-yellow-100 dark:bg-yellow-700', + 'purple': 'text-purple-700 bg-purple-100 dark:text-purple-100 dark:bg-purple-700', + 'orange': 'text-orange-700 bg-orange-100 dark:text-orange-100 dark:bg-orange-700', + 'pink': 'text-pink-700 bg-pink-100 dark:text-pink-100 dark:bg-pink-700' +} %} +{% set sizes = { + 'sm': 'px-2 py-1 text-xs', + 'md': 'px-3 py-1 text-sm' +} %} + +{% endmacro %} + + +{# + Status Badge (Boolean) + ====================== + A badge that changes based on a boolean condition. + + Parameters: + - condition: Alpine.js boolean expression + - true_label: Text when true (default: 'Active') + - false_label: Text when false (default: 'Inactive') + - true_color: Color when true (default: 'green') + - false_color: Color when false (default: 'gray') +#} +{% macro status_badge(condition, true_label='Active', false_label='Inactive', true_color='green', false_color='gray') %} +{% set colors = { + 'gray': 'text-gray-700 bg-gray-100 dark:text-gray-100 dark:bg-gray-700', + 'red': 'text-red-700 bg-red-100 dark:text-red-100 dark:bg-red-700', + 'green': 'text-green-700 bg-green-100 dark:text-green-100 dark:bg-green-700', + 'blue': 'text-blue-700 bg-blue-100 dark:text-blue-100 dark:bg-blue-700', + 'yellow': 'text-yellow-700 bg-yellow-100 dark:text-yellow-100 dark:bg-yellow-700', + 'purple': 'text-purple-700 bg-purple-100 dark:text-purple-100 dark:bg-purple-700', + 'orange': 'text-orange-700 bg-orange-100 dark:text-orange-100 dark:bg-orange-700' +} %} + + + +{% endmacro %} + + +{# + Verification Badge + ================== + A specialized badge for verified/pending status with icon. + + Parameters: + - condition: Alpine.js boolean expression (e.g., 'item.is_verified') + - verified_label: Text when verified (default: 'Verified') + - pending_label: Text when not verified (default: 'Pending') +#} +{% macro verification_badge(condition, verified_label='Verified', pending_label='Pending') %} + + + + +{% endmacro %} + + +{# + Role Badge + ========== + A badge for user roles with role-specific colors. + + Parameters: + - role_var: Alpine.js variable containing the role string + - capitalize: Whether to capitalize the role text (default: true) +#} +{% macro role_badge(role_var, capitalize=true) %} + + +{% endmacro %} + + +{# + Order Status Badge + ================== + A specialized badge for order statuses. + + Parameters: + - status_var: Alpine.js variable containing the status string +#} +{% macro order_status_badge(status_var) %} + + +{% endmacro %} + + +{# + Severity Badge + ============== + A badge for severity levels (error, warning, info). + + Parameters: + - severity_var: Alpine.js variable containing 'error' | 'warning' | 'info' +#} +{% macro severity_badge(severity_var) %} + + +{% endmacro %} + + +{# + Count Badge + =========== + A small badge typically used for counts (notifications, items, etc.). + + Parameters: + - count_var: Alpine.js variable or expression for the count + - color: Badge color (default: 'red') + - show_zero: Whether to show when count is 0 (default: false) +#} +{% macro count_badge(count_var, color='red', show_zero=false) %} +{% set colors = { + 'red': 'bg-red-500 text-white', + 'green': 'bg-green-500 text-white', + 'blue': 'bg-blue-500 text-white', + 'purple': 'bg-purple-500 text-white', + 'gray': 'bg-gray-500 text-white' +} %} + + +{% endmacro %} diff --git a/app/templates/shared/macros/buttons.html b/app/templates/shared/macros/buttons.html new file mode 100644 index 00000000..5b7e788a --- /dev/null +++ b/app/templates/shared/macros/buttons.html @@ -0,0 +1,224 @@ +{# + Button Macros + ============= + Reusable button components for actions, navigation, and forms. + + Usage: + {% from 'shared/macros/buttons.html' import btn, btn_primary, btn_secondary, btn_danger, action_button %} + {{ btn_primary('Save Changes', icon='save', type='submit') }} + {{ btn_secondary('Cancel', href='/back') }} + {{ btn_danger('Delete', onclick='deleteItem()') }} + {{ action_button('eye', 'viewItem()', 'blue', 'View details') }} +#} + + +{# + Base Button + =========== + A flexible button component that can be styled as primary, secondary, etc. + + Parameters: + - text: Button text + - variant: 'primary' | 'secondary' | 'danger' | 'success' | 'warning' | 'ghost' (default: 'primary') + - size: 'sm' | 'md' | 'lg' (default: 'md') + - icon: Icon name (optional, uses $icon helper) + - icon_position: 'left' | 'right' (default: 'left') + - type: 'button' | 'submit' | 'reset' (default: 'button') + - href: If provided, renders as tag instead of +{% endif %} +{% endmacro %} + + +{# Convenience macros for common button types #} + +{% macro btn_primary(text, icon=none, icon_position='left', type='button', href=none, onclick=none, disabled=none, loading=none, size='md') %} +{{ btn(text, 'primary', size, icon, icon_position, type, href, onclick, disabled, loading) }} +{% endmacro %} + +{% macro btn_secondary(text, icon=none, icon_position='left', type='button', href=none, onclick=none, disabled=none, loading=none, size='md') %} +{{ btn(text, 'secondary', size, icon, icon_position, type, href, onclick, disabled, loading) }} +{% endmacro %} + +{% macro btn_danger(text, icon=none, icon_position='left', type='button', href=none, onclick=none, disabled=none, loading=none, size='md') %} +{{ btn(text, 'danger', size, icon, icon_position, type, href, onclick, disabled, loading) }} +{% endmacro %} + +{% macro btn_success(text, icon=none, icon_position='left', type='button', href=none, onclick=none, disabled=none, loading=none, size='md') %} +{{ btn(text, 'success', size, icon, icon_position, type, href, onclick, disabled, loading) }} +{% endmacro %} + +{% macro btn_ghost(text, icon=none, icon_position='left', type='button', href=none, onclick=none, disabled=none, loading=none, size='md') %} +{{ btn(text, 'ghost', size, icon, icon_position, type, href, onclick, disabled, loading) }} +{% endmacro %} + + +{# + Action Button (Icon Only) + ========================= + A small icon button for table actions (view, edit, delete). + + Parameters: + - icon: Icon name + - onclick: JavaScript onclick handler + - color: 'blue' | 'purple' | 'red' | 'green' | 'gray' (default: 'gray') + - title: Tooltip text + - disabled: Alpine.js expression for disabled state + - href: If provided, renders as tag +#} +{% macro action_button(icon, onclick=none, color='gray', title='', disabled=none, href=none) %} +{% set colors = { + 'blue': 'text-blue-600 hover:bg-blue-50 dark:text-blue-400 dark:hover:bg-gray-700', + 'purple': 'text-purple-600 hover:bg-purple-50 dark:text-purple-400 dark:hover:bg-gray-700', + 'red': 'text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-gray-700', + 'green': 'text-green-600 hover:bg-green-50 dark:text-green-400 dark:hover:bg-gray-700', + 'gray': 'text-gray-600 hover:bg-gray-50 dark:text-gray-400 dark:hover:bg-gray-700', + 'orange': 'text-orange-600 hover:bg-orange-50 dark:text-orange-400 dark:hover:bg-gray-700' +} %} +{% set base_classes = 'flex items-center justify-center p-2 rounded-lg focus:outline-none transition-colors disabled:opacity-50 disabled:cursor-not-allowed' %} + +{% if href %} + + + +{% else %} + +{% endif %} +{% endmacro %} + + +{# + Action Button Group + =================== + A group of action buttons (typically for table rows). + + Usage: + {{ action_button_group([ + {'icon': 'eye', 'onclick': 'view(item)', 'color': 'blue', 'title': 'View'}, + {'icon': 'edit', 'onclick': 'edit(item)', 'color': 'purple', 'title': 'Edit'}, + {'icon': 'delete', 'onclick': 'delete(item)', 'color': 'red', 'title': 'Delete'} + ]) }} +#} +{% macro action_button_group(buttons) %} +
+ {% for button in buttons %} + {{ action_button( + button.icon, + button.get('onclick'), + button.get('color', 'gray'), + button.get('title', ''), + button.get('disabled'), + button.get('href') + ) }} + {% endfor %} +
+{% endmacro %} + + +{# + Button Group + ============ + A group of buttons with proper spacing. +#} +{% macro button_group(class_extra='') %} +
+ {{ caller() }} +
+{% endmacro %} + + +{# + Back Button + =========== + A standardized back navigation button. + + Parameters: + - href: URL to navigate back to + - text: Button text (default: 'Back') +#} +{% macro back_button(href, text='Back') %} + + + {{ text }} + +{% endmacro %} + + +{# + Submit Button with Loading + ========================== + A form submit button with built-in loading state. + + Parameters: + - text: Button text + - loading_text: Text shown while loading (default: 'Saving...') + - loading_var: Alpine.js variable for loading state (default: 'saving') + - icon: Icon name (default: 'save') +#} +{% macro submit_button(text='Save', loading_text='Saving...', loading_var='saving', icon='save') %} + +{% endmacro %} diff --git a/app/templates/shared/macros/cards.html b/app/templates/shared/macros/cards.html new file mode 100644 index 00000000..758f0b90 --- /dev/null +++ b/app/templates/shared/macros/cards.html @@ -0,0 +1,206 @@ +{# + Card Macros + =========== + Reusable card components including stats cards, info cards, and action cards. + + Usage: + {% from 'shared/macros/cards.html' import stat_card, info_card, card %} + {{ stat_card('users', 'Total Users', 'stats.total', 'blue') }} + {{ info_card('User Details', 'View and edit user information') }} +#} + + +{# + Stat Card + ========= + A statistics card with icon and value. + + Parameters: + - icon: Icon name + - label: Card label/title + - value: Alpine.js expression for the value + - color: 'orange' | 'green' | 'blue' | 'purple' | 'red' | 'yellow' (default: 'orange') + - format: Optional format function to apply to value +#} +{% macro stat_card(icon, label, value, color='orange', format=none) %} +{% set colors = { + 'orange': 'text-orange-500 bg-orange-100 dark:text-orange-100 dark:bg-orange-500', + 'green': 'text-green-500 bg-green-100 dark:text-green-100 dark:bg-green-500', + 'blue': 'text-blue-500 bg-blue-100 dark:text-blue-100 dark:bg-blue-500', + 'purple': 'text-purple-500 bg-purple-100 dark:text-purple-100 dark:bg-purple-500', + 'red': 'text-red-500 bg-red-100 dark:text-red-100 dark:bg-red-500', + 'yellow': 'text-yellow-500 bg-yellow-100 dark:text-yellow-100 dark:bg-yellow-500', + 'teal': 'text-teal-500 bg-teal-100 dark:text-teal-100 dark:bg-teal-500' +} %} +
+
+ +
+
+

+ {{ label }} +

+

+ 0 +

+
+
+{% endmacro %} + + +{# + Stats Grid + ========== + A grid container for stat cards. + + Parameters: + - columns: Number of columns (default: 4) +#} +{% macro stats_grid(columns=4) %} +{% set col_classes = { + 2: 'md:grid-cols-2', + 3: 'md:grid-cols-3', + 4: 'md:grid-cols-2 xl:grid-cols-4' +} %} +
+ {{ caller() }} +
+{% endmacro %} + + +{# + Card + ==== + A basic card container. + + Parameters: + - title: Card title (optional) + - subtitle: Card subtitle (optional) + - class_extra: Additional CSS classes + - padding: Whether to add padding (default: true) +#} +{% macro card(title=none, subtitle=none, class_extra='', padding=true) %} +
+ {% if title %} +
+

{{ title }}

+ {% if subtitle %} +

{{ subtitle }}

+ {% endif %} +
+ {% endif %} +
+ {{ caller() }} +
+
+{% endmacro %} + + +{# + Info Card + ========= + An informational card with icon and description. + + Parameters: + - icon: Icon name + - title: Card title + - description: Card description + - color: Icon color (default: 'purple') + - href: Optional link URL +#} +{% macro info_card(icon, title, description, color='purple', href=none) %} +{% set colors = { + 'purple': 'text-purple-500 bg-purple-100 dark:text-purple-100 dark:bg-purple-500', + 'blue': 'text-blue-500 bg-blue-100 dark:text-blue-100 dark:bg-blue-500', + 'green': 'text-green-500 bg-green-100 dark:text-green-100 dark:bg-green-500', + 'orange': 'text-orange-500 bg-orange-100 dark:text-orange-100 dark:bg-orange-500' +} %} +{% if href %} + +{% else %} +
+{% endif %} +
+
+ +
+
+

{{ title }}

+

{{ description }}

+
+
+{% if href %} +
+{% else %} +
+{% endif %} +{% endmacro %} + + +{# + Quick Actions Card + ================== + A card with quick action buttons and status badges. + + Parameters: + - title: Card title (default: 'Quick Actions') +#} +{% macro quick_actions_card(title='Quick Actions') %} +
+

+ {{ title }} +

+
+ {{ caller() }} +
+
+{% endmacro %} + + +{# + Coming Soon Card + ================ + A placeholder card for features under development. + + Parameters: + - emoji: Emoji to display + - title: Feature title + - description: Feature description + - back_url: URL for back button + - back_label: Back button text (default: 'Go Back') +#} +{% macro coming_soon_card(emoji='📦', title='Coming Soon', description='This feature is under development.', back_url='/', back_label='Go Back') %} +
+
+
{{ emoji }}
+

+ {{ title }} +

+

+ {{ description }} +

+ + {{ back_label }} + +
+
+{% endmacro %} + + +{# + Filter Card + =========== + A card for search and filter controls. + + Parameters: + - show_condition: Alpine.js condition (default: '!loading') +#} +{% macro filter_card(show_condition='!loading') %} +
+
+ {{ caller() }} +
+
+{% endmacro %} diff --git a/app/templates/shared/macros/forms.html b/app/templates/shared/macros/forms.html new file mode 100644 index 00000000..92352fe7 --- /dev/null +++ b/app/templates/shared/macros/forms.html @@ -0,0 +1,332 @@ +{# + Form Macros + =========== + Reusable form input components. + + Usage: + {% from 'shared/macros/forms.html' import form_input, form_select, form_textarea, form_checkbox %} + {{ form_input('Email', 'email', 'formData.email', type='email', required=true) }} + {{ form_select('Status', 'formData.status', [{'value': 'active', 'label': 'Active'}]) }} + {{ form_textarea('Description', 'formData.description', rows=4) }} +#} + + +{# + Form Input + ========== + A text input field with label, validation, and error handling. + + Parameters: + - label: Field label + - name: Input name attribute + - x_model: Alpine.js x-model binding + - type: Input type (default: 'text') + - placeholder: Placeholder text + - required: Whether the field is required (default: false) + - disabled: Alpine.js expression for disabled state + - error: Alpine.js expression for error message + - help: Help text shown below the input + - maxlength: Maximum length + - min: Minimum value (for number inputs) + - max: Maximum value (for number inputs) + - step: Step value (for number inputs) + - autocomplete: Autocomplete attribute + - class_extra: Additional CSS classes for the input +#} +{% macro form_input(label, name, x_model, type='text', placeholder='', required=false, disabled=none, error=none, help=none, maxlength=none, min=none, max=none, step=none, autocomplete=none, class_extra='') %} + +{% endmacro %} + + +{# + Form Select + =========== + A select dropdown with label and error handling. + + Parameters: + - label: Field label + - x_model: Alpine.js x-model binding + - options: List of options [{'value': '', 'label': ''}] or Alpine.js expression + - name: Input name attribute + - required: Whether the field is required (default: false) + - disabled: Alpine.js expression for disabled state + - error: Alpine.js expression for error message + - placeholder: First empty option text (default: 'Select...') + - on_change: Alpine.js @change handler +#} +{% macro form_select(label, x_model, options=[], name='', required=false, disabled=none, error=none, placeholder='Select...', on_change=none) %} + +{% endmacro %} + + +{# + Form Select (Dynamic Options) + ============================= + A select dropdown with options from Alpine.js. + + Parameters: + - label: Field label + - x_model: Alpine.js x-model binding + - options_var: Alpine.js variable containing options array + - value_key: Key for option value (default: 'value') + - label_key: Key for option label (default: 'label') + - ... (other params same as form_select) +#} +{% macro form_select_dynamic(label, x_model, options_var, value_key='value', label_key='label', name='', required=false, disabled=none, error=none, placeholder='Select...', on_change=none) %} + +{% endmacro %} + + +{# + Form Textarea + ============= + A textarea field with label and error handling. + + Parameters: + - label: Field label + - x_model: Alpine.js x-model binding + - name: Input name attribute + - rows: Number of rows (default: 3) + - placeholder: Placeholder text + - required: Whether the field is required (default: false) + - disabled: Alpine.js expression for disabled state + - error: Alpine.js expression for error message + - maxlength: Maximum length +#} +{% macro form_textarea(label, x_model, name='', rows=3, placeholder='', required=false, disabled=none, error=none, maxlength=none) %} + +{% endmacro %} + + +{# + Form Checkbox + ============= + A checkbox input with label. + + Parameters: + - label: Checkbox label + - x_model: Alpine.js x-model binding + - name: Input name attribute + - disabled: Alpine.js expression for disabled state + - help: Help text shown below +#} +{% macro form_checkbox(label, x_model, name='', disabled=none, help=none) %} + +{% if help %} +{{ help }} +{% endif %} +{% endmacro %} + + +{# + Form Toggle Switch + ================== + A toggle switch (styled checkbox). + + Parameters: + - label: Toggle label + - x_model: Alpine.js x-model binding + - disabled: Alpine.js expression for disabled state +#} +{% macro form_toggle(label, x_model, disabled=none) %} +
+ + {{ label }} +
+{% endmacro %} + + +{# + Form Radio Group + ================ + A group of radio buttons. + + Parameters: + - label: Group label + - name: Radio group name + - x_model: Alpine.js x-model binding + - options: List of options [{'value': '', 'label': ''}] + - inline: Whether to display inline (default: false) +#} +{% macro form_radio_group(label, name, x_model, options=[], inline=false) %} +
+ {{ label }} +
+ {% for option in options %} + + {% endfor %} +
+
+{% endmacro %} + + +{# + Search Input + ============ + A search input with icon. + + Parameters: + - x_model: Alpine.js x-model binding + - placeholder: Placeholder text (default: 'Search...') + - on_input: Alpine.js @input handler (e.g., 'debouncedSearch()') + - class_extra: Additional CSS classes +#} +{% macro search_input(x_model, placeholder='Search...', on_input=none, class_extra='') %} +
+ + + + +
+{% endmacro %} + + +{# + Filter Select + ============= + A compact select for filtering (no label, inline style). + + Parameters: + - x_model: Alpine.js x-model binding + - options: List of options [{'value': '', 'label': ''}] + - on_change: Alpine.js @change handler + - placeholder: First option text (default: 'All') +#} +{% macro filter_select(x_model, options=[], on_change=none, placeholder='All') %} + +{% endmacro %} diff --git a/app/templates/shared/macros/headers.html b/app/templates/shared/macros/headers.html new file mode 100644 index 00000000..ee197eea --- /dev/null +++ b/app/templates/shared/macros/headers.html @@ -0,0 +1,222 @@ +{# + Header Macros + ============= + Reusable page header and navigation components. + + Usage: + {% from 'shared/macros/headers.html' import page_header, section_header, breadcrumbs %} + {{ page_header('User Management', action_label='Create User', action_url='/users/create') }} + {{ section_header('Account Settings') }} + {{ breadcrumbs([{'label': 'Home', 'url': '/'}, {'label': 'Users'}]) }} +#} + + +{# + Page Header + =========== + A page header with title, optional subtitle, and action button. + + Parameters: + - title: Page title + - subtitle: Page subtitle (optional) + - action_label: Action button text (optional) + - action_url: Action button URL (optional) + - action_icon: Action button icon (default: 'plus') + - action_onclick: JavaScript onclick handler (optional, instead of URL) + - back_url: Back button URL (optional) + - back_label: Back button text (default: 'Back') +#} +{% macro page_header(title, subtitle=none, action_label=none, action_url=none, action_icon='plus', action_onclick=none, back_url=none, back_label='Back') %} +
+
+

+ {{ title }} +

+ {% if subtitle %} +

+ {{ subtitle }} +

+ {% endif %} +
+
+ {% if back_url %} + + + {{ back_label }} + + {% endif %} + {% if action_label and (action_url or action_onclick) %} + {% if action_url %} + + + {{ action_label }} + + {% else %} + + {% endif %} + {% endif %} +
+
+{% endmacro %} + + +{# + Page Header (Dynamic) + ===================== + A page header where the title comes from Alpine.js. + + Parameters: + - title_var: Alpine.js variable for the title + - subtitle_var: Alpine.js variable for subtitle (optional) + - ... other params same as page_header +#} +{% macro page_header_dynamic(title_var, subtitle_var=none, action_label=none, action_url=none, action_icon='plus', back_url=none, back_label='Back') %} +
+
+

+ {% if subtitle_var %} +

+ {% endif %} +
+
+ {% if back_url %} + + + {{ back_label }} + + {% endif %} + {% if action_label and action_url %} + + + {{ action_label }} + + {% endif %} +
+
+{% endmacro %} + + +{# + Section Header + ============== + A section header within a page. + + Parameters: + - title: Section title + - subtitle: Section subtitle (optional) + - action_label: Action button text (optional) + - action_onclick: Action button onclick (optional) + - icon: Section icon (optional) +#} +{% macro section_header(title, subtitle=none, action_label=none, action_onclick=none, icon=none) %} +
+
+ {% if icon %} + + {% endif %} +
+

{{ title }}

+ {% if subtitle %} +

{{ subtitle }}

+ {% endif %} +
+
+ {% if action_label and action_onclick %} + + {% endif %} +
+{% endmacro %} + + +{# + Breadcrumbs + =========== + Navigation breadcrumbs. + + Parameters: + - items: List of breadcrumb items [{'label': '', 'url': ''}] + Last item (without url) is treated as current page + - separator: Separator character (default: '/') +#} +{% macro breadcrumbs(items, separator='/') %} + +{% endmacro %} + + +{# + Card Header + =========== + A header for card sections. + + Parameters: + - title: Card header title + - subtitle: Card header subtitle (optional) + - class_extra: Additional CSS classes +#} +{% macro card_header(title, subtitle=none, class_extra='') %} +
+

{{ title }}

+ {% if subtitle %} +

{{ subtitle }}

+ {% endif %} +
+{% endmacro %} + + +{# + Tab Header + ========== + A tab navigation header. + + Parameters: + - tabs: List of tab items [{'id': '', 'label': '', 'icon': ''}] + - active_var: Alpine.js variable for active tab +#} +{% macro tab_header(tabs, active_var='activeTab') %} +
+ +
+{% endmacro %} diff --git a/app/templates/shared/macros/pagination.html b/app/templates/shared/macros/pagination.html new file mode 100644 index 00000000..2a44ae4d --- /dev/null +++ b/app/templates/shared/macros/pagination.html @@ -0,0 +1,128 @@ +{# + Pagination Macro + ================ + A reusable pagination component for tables and lists. + + Usage: + {% from 'shared/macros/pagination.html' import pagination %} + {{ pagination() }} + + Required Alpine.js data properties: + - pagination.page: Current page number + - pagination.total: Total number of items + - startIndex: First item index on current page + - endIndex: Last item index on current page + - totalPages: Total number of pages + - pageNumbers: Array of page numbers with '...' for ellipsis + + Required Alpine.js methods: + - previousPage(): Go to previous page + - nextPage(): Go to next page + - goToPage(pageNum): Go to specific page +#} + +{% macro pagination(show_condition="true") %} +
+ {# Results Info #} + + Showing - of + + + + {# Pagination Controls #} + + + +
+{% endmacro %} + + +{# + Simple Pagination Macro (Prev/Next only) + ======================================== + A simpler pagination with just previous/next buttons. + + Usage: + {% from 'shared/macros/pagination.html' import pagination_simple %} + {{ pagination_simple() }} +#} + +{% macro pagination_simple(show_condition="true") %} +
+
+ + Showing - + of results + +
+
+ + + Page of + + +
+
+{% endmacro %} diff --git a/app/templates/shared/macros/tables.html b/app/templates/shared/macros/tables.html new file mode 100644 index 00000000..fdba39a6 --- /dev/null +++ b/app/templates/shared/macros/tables.html @@ -0,0 +1,215 @@ +{# + 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']) }} + ... + {% endcall %} +#} + + +{# + Table Wrapper + ============= + Wraps the table with proper overflow and shadow styling. +#} +{% macro table_wrapper(class_extra='') %} +
+
+ + {{ caller() }} +
+
+
+{% 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) %} + + + {% for column in columns %} + {{ column }} + {% endfor %} + + +{% endmacro %} + + +{# + Table Body Wrapper + ================== + Wraps the tbody with proper styling. +#} +{% macro table_body() %} + + {{ caller() }} + +{% 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) %} + +{% endmacro %} + + +{# + Table Row + ========= + A standard table row with hover styling. +#} +{% macro table_row(class_extra='') %} + + {{ caller() }} + +{% endmacro %} + + +{# + Table Cell + ========== + A standard table cell. +#} +{% macro table_cell(class_extra='') %} + + {{ caller() }} + +{% 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') %} + + {% if truncate %} +

+ {% if not is_dynamic %}{{ text }}{% endif %} +

+ {% else %} + {% if is_dynamic %} + + {% else %} + {{ text }} + {% endif %} + {% endif %} + +{% 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') %} + +
+ +
+

+ {% if subtitle %} +

+ {% endif %} +
+
+ +{% 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') %} + + + +{% 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...') %} +
+
+ +

{{ message }}

+
+
+{% endmacro %}