Template Partials Guide¶
This guide documents all reusable template partials (fragments) used throughout Arctyk ITSM.
What Are Partials?¶
Partials are small, reusable template fragments that encapsulate specific UI components. They help maintain the DRY (Don't Repeat Yourself) principle and ensure consistency across the application.
Benefits of Using Partials¶
✅ Consistency: Same component looks and behaves identically everywhere
✅ Maintainability: Update in one place, changes reflect everywhere
✅ Readability: Keep parent templates clean and focused
✅ Reusability: Use the same component in multiple pages
✅ Testing: Easier to test isolated components
How to Include Partials¶
Use the {% include %} template tag:
Passing Context to Partials¶
{# Pass specific variables #}
{% include "partials/breadcrumbs.html" with page_title="Tickets" %}
{# Pass multiple variables #}
{% include "partials/pagination.html" with page_obj=tickets page_name="ticket_list" %}
{# Use only passed context (isolate from parent) #}
{% include "partials/card.html" with title="Summary" only %}
Available Partials¶
Navigation Components¶
sidebar.html- Main navigation sidebartopbar.html- Top navigation barbreadcrumbs.html- Breadcrumb navigation
Layout Components¶
_layout_wrapper.html- Page layout wrapper
Data Display Components¶
pagination.html- Pagination controlssort_header.html- Sortable table headers
Filter Components¶
log_filters.html- Change log filters
Sidebar.html¶
Location: templates/partials/sidebar.html
Purpose: Main navigation sidebar with app sections and menu items
Usage¶
Features¶
- Branding: Arctyk logo and site name
- Navigation sections: Tickets, Projects, Assets, Reports
- Active state highlighting: Current page is highlighted
- Collapsible sections: Expandable menu groups
- User preferences: Density settings (compact/comfortable)
Code Example¶
<aside class="sidebar" id="sidebar">
{# Logo and branding #}
<div class="sidebar-header">
<img src="{% static 'images/arctyk-logo.svg' %}" alt="Arctyk ITSM" class="sidebar-logo">
<h1 class="sidebar-title">Arctyk ITSM</h1>
</div>
{# Navigation menu #}
<nav class="sidebar-nav">
{# Tickets section #}
<div class="nav-section">
<h2 class="nav-section-title">Tickets</h2>
<ul class="nav-list">
<li class="nav-item {% if request.resolver_match.url_name == 'ticket_list' %}active{% endif %}">
<a href="{% url 'ticket_list' %}" class="nav-link">
<i class="icon icon-list"></i>
<span>All Tickets</span>
</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name == 'ticket_create' %}active{% endif %}">
<a href="{% url 'ticket_create' %}" class="nav-link">
<i class="icon icon-plus"></i>
<span>Create Ticket</span>
</a>
</li>
</ul>
</div>
{# Projects section #}
<div class="nav-section">
<h2 class="nav-section-title">Projects</h2>
<ul class="nav-list">
<li class="nav-item {% if request.resolver_match.url_name == 'project_list' %}active{% endif %}">
<a href="{% url 'project_list' %}" class="nav-link">
<i class="icon icon-folder"></i>
<span>All Projects</span>
</a>
</li>
</ul>
</div>
{# Assets section #}
<div class="nav-section">
<h2 class="nav-section-title">Assets</h2>
<ul class="nav-list">
<li class="nav-item {% if request.resolver_match.url_name == 'asset_list' %}active{% endif %}">
<a href="{% url 'asset_list' %}" class="nav-link">
<i class="icon icon-server"></i>
<span>Inventory</span>
</a>
</li>
</ul>
</div>
</nav>
{# User section #}
<div class="sidebar-footer">
<div class="user-info">
<span class="user-avatar">{{ user.username|slice:":1"|upper }}</span>
<span class="user-name">{{ user.username }}</span>
</div>
</div>
</aside>
Styling¶
Styled in static/scss/components/_sidebar.scss: - Fixed left position - Scrollable content - Hover states - Active link highlighting
JavaScript¶
Interactive features in static/js/components/sidebar.js: - Toggle sidebar collapse - Persist user density preference - Highlight active section
Topbar.html¶
Location: templates/partials/topbar.html
Purpose: Top navigation bar with global actions and user menu
Usage¶
Features¶
- Quick create: Fast ticket creation button
- Search: Global search across tickets, projects, assets
- Notifications: Notification bell with count badge
- User menu: Account settings, profile, logout
Code Example¶
<header class="topbar" id="topbar">
<div class="topbar-content">
{# Left side: Create button #}
<div class="topbar-left">
<button class="btn btn-primary" data-action="create-ticket">
<i class="icon icon-plus"></i>
<span>Create</span>
</button>
</div>
{# Center: Search #}
<div class="topbar-center">
<form class="search-form" method="get" action="{% url 'search' %}">
<input
type="search"
name="q"
placeholder="Search tickets, projects, assets..."
class="search-input"
autocomplete="off"
>
<button type="submit" class="search-submit">
<i class="icon icon-search"></i>
</button>
</form>
</div>
{# Right side: Notifications and user menu #}
<div class="topbar-right">
{# Notifications #}
<div class="topbar-item">
<button class="notifications-trigger" data-toggle="dropdown">
<i class="icon icon-bell"></i>
{% if unread_notifications_count > 0 %}
<span class="badge badge-count">{{ unread_notifications_count }}</span>
{% endif %}
</button>
<div class="dropdown-menu notifications-menu">
<h3>Notifications</h3>
{% for notification in recent_notifications %}
<div class="notification-item">
<p>{{ notification.message }}</p>
<span class="notification-time">{{ notification.created_at|timesince }} ago</span>
</div>
{% empty %}
<p class="empty-state">No new notifications</p>
{% endfor %}
</div>
</div>
{# User menu #}
<div class="topbar-item">
<button class="user-menu-trigger" data-toggle="dropdown">
<span class="user-avatar">{{ user.username|slice:":1"|upper }}</span>
<span class="user-name">{{ user.username }}</span>
<i class="icon icon-chevron-down"></i>
</button>
<div class="dropdown-menu user-menu">
<a href="{% url 'profile' %}" class="dropdown-item">
<i class="icon icon-user"></i> Profile
</a>
<a href="{% url 'settings' %}" class="dropdown-item">
<i class="icon icon-settings"></i> Settings
</a>
<hr class="dropdown-divider">
<a href="{% url 'logout' %}" class="dropdown-item">
<i class="icon icon-logout"></i> Logout
</a>
</div>
</div>
</div>
</div>
</header>
Styling¶
Styled in static/scss/components/_topbar.scss: - Fixed top position - Flexbox layout - Dropdown menus - Responsive breakpoints
Breadcrumbs.html¶
Location: templates/partials/breadcrumbs.html
Purpose: Hierarchical navigation showing current page location
Usage¶
Features¶
- Hierarchical navigation: Shows path to current page
- Clickable ancestors: Navigate back to parent pages
- Current page indicator: Last item is not clickable
- Automatic generation: Built from URL structure
Code Example¶
<nav class="breadcrumbs" aria-label="Breadcrumb">
<ol class="breadcrumb-list">
<li class="breadcrumb-item">
<a href="{% url 'dashboard' %}">
<i class="icon icon-home"></i>
Home
</a>
</li>
{% block breadcrumbs %}
{# Override this block in child templates #}
{% endblock %}
</ol>
</nav>
Usage in Page Template¶
{% extends "layouts/_layout_wrapper.html" %}
{% block breadcrumbs %}
<li class="breadcrumb-item">
<a href="{% url 'ticket_list' %}">Tickets</a>
</li>
<li class="breadcrumb-item active" aria-current="page">
{{ ticket.title }}
</li>
{% endblock %}
Result¶
Styling¶
Styled in static/scss/components/_breadcrumbs.scss: - Separator arrows - Hover states - Active item styling
Pagination.html¶
Location: templates/partials/pagination.html
Purpose: Pagination controls for paginated lists
Usage¶
Features¶
- Page numbers: Direct page navigation
- Previous/Next: Navigate sequentially
- First/Last: Jump to endpoints
- Page info: "Showing X to Y of Z results"
- Responsive: Adapts to screen size
Code Example¶
{% if page_obj.has_other_pages %}
<nav class="pagination" aria-label="Pagination">
<div class="pagination-info">
Showing {{ page_obj.start_index }} to {{ page_obj.end_index }} of {{ page_obj.paginator.count }} results
</div>
<ul class="pagination-list">
{# Previous button #}
{% if page_obj.has_previous %}
<li class="pagination-item">
<a href="?page={{ page_obj.previous_page_number }}" class="pagination-link">
<i class="icon icon-chevron-left"></i>
Previous
</a>
</li>
{% else %}
<li class="pagination-item disabled">
<span class="pagination-link">
<i class="icon icon-chevron-left"></i>
Previous
</span>
</li>
{% endif %}
{# Page numbers #}
{% for num in page_obj.paginator.page_range %}
{% if page_obj.number == num %}
<li class="pagination-item active">
<span class="pagination-link">{{ num }}</span>
</li>
{% elif num > page_obj.number|add:'-3' and num < page_obj.number|add:'3' %}
<li class="pagination-item">
<a href="?page={{ num }}" class="pagination-link">{{ num }}</a>
</li>
{% endif %}
{% endfor %}
{# Next button #}
{% if page_obj.has_next %}
<li class="pagination-item">
<a href="?page={{ page_obj.next_page_number }}" class="pagination-link">
Next
<i class="icon icon-chevron-right"></i>
</a>
</li>
{% else %}
<li class="pagination-item disabled">
<span class="pagination-link">
Next
<i class="icon icon-chevron-right"></i>
</span>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
Required Context¶
page_obj: Django Paginator page object
View Setup¶
from django.core.paginator import Paginator
def ticket_list(request):
tickets = Ticket.objects.all()
paginator = Paginator(tickets, 25) # 25 per page
page_number = request.GET.get('page')
page_obj = paginator.get_page(page_number)
return render(request, 'tickets/ticket_list.html', {
'tickets': page_obj,
'page_obj': page_obj,
})
Sort_header.html¶
Location: templates/partials/sort_header.html
Purpose: Sortable table column headers
Usage¶
<thead>
<tr>
{% include "partials/sort_header.html" with field="id" label="ID" %}
{% include "partials/sort_header.html" with field="title" label="Title" %}
{% include "partials/sort_header.html" with field="created_at" label="Created" %}
</tr>
</thead>
Features¶
- Click to sort: Toggle ascending/descending
- Visual indicators: Arrows show sort direction
- URL preservation: Maintains other query parameters
- Accessible: ARIA labels for screen readers
Code Example¶
<th class="sortable {% if request.GET.sort == field %}sorted{% endif %}">
<a href="?sort={{ field }}{% if request.GET.sort == field and request.GET.order != 'desc' %}&order=desc{% endif %}"
class="sort-link">
{{ label }}
{% if request.GET.sort == field %}
{% if request.GET.order == 'desc' %}
<i class="icon icon-arrow-down"></i>
{% else %}
<i class="icon icon-arrow-up"></i>
{% endif %}
{% else %}
<i class="icon icon-arrow-updown"></i>
{% endif %}
</a>
</th>
Required Context¶
field: Database field name to sort bylabel: Display label for the column
View Implementation¶
def ticket_list(request):
tickets = Ticket.objects.all()
# Handle sorting
sort_field = request.GET.get('sort', 'created_at')
sort_order = request.GET.get('order', 'asc')
if sort_order == 'desc':
sort_field = '-' + sort_field
tickets = tickets.order_by(sort_field)
return render(request, 'tickets/ticket_list.html', {
'tickets': tickets,
})
Log_filters.html¶
Location: templates/partials/log_filters.html
Purpose: Filters for change logs and audit trails
Usage¶
Features¶
- Date range: Filter by date
- User filter: Filter by who made changes
- Action filter: Filter by change type (create, update, delete)
- Reset filters: Clear all filters
Code Example¶
<div class="log-filters">
<form method="get" class="filter-form">
{# Date range #}
<div class="filter-group">
<label for="date_from">From:</label>
<input type="date" id="date_from" name="date_from" value="{{ request.GET.date_from }}">
</div>
<div class="filter-group">
<label for="date_to">To:</label>
<input type="date" id="date_to" name="date_to" value="{{ request.GET.date_to }}">
</div>
{# User filter #}
<div class="filter-group">
<label for="user">User:</label>
<select id="user" name="user">
<option value="">All Users</option>
{% for user in users %}
<option value="{{ user.id }}" {% if request.GET.user == user.id|stringformat:"s" %}selected{% endif %}>
{{ user.username }}
</option>
{% endfor %}
</select>
</div>
{# Action filter #}
<div class="filter-group">
<label for="action">Action:</label>
<select id="action" name="action">
<option value="">All Actions</option>
<option value="create" {% if request.GET.action == 'create' %}selected{% endif %}>Created</option>
<option value="update" {% if request.GET.action == 'update' %}selected{% endif %}>Updated</option>
<option value="delete" {% if request.GET.action == 'delete' %}selected{% endif %}>Deleted</option>
</select>
</div>
{# Apply and reset #}
<div class="filter-actions">
<button type="submit" class="btn btn-primary">Apply Filters</button>
<a href="{% url 'changelog' %}" class="btn btn-secondary">Reset</a>
</div>
</form>
</div>
_layout_wrapper.html¶
Location: templates/layouts/_layout_wrapper.html
Purpose: Standard page layout with sidebar and topbar
Usage¶
{% extends "layouts/_layout_wrapper.html" %}
{% block page_content %}
<h1>My Page Content</h1>
{% endblock %}
Features¶
- Consistent layout: All authenticated pages use this
- Includes partials: Sidebar, topbar, breadcrumbs
- Responsive: Adapts to mobile/tablet/desktop
- Content block: Override with page-specific content
Code Example¶
{% extends "base.html" %}
{% block body %}
<div class="app-layout">
{# Sidebar #}
{% include "partials/sidebar.html" %}
{# Main content area #}
<div class="main-content">
{# Top navigation #}
{% include "partials/topbar.html" %}
{# Content wrapper #}
<div class="content-area">
{# Breadcrumbs #}
{% include "partials/breadcrumbs.html" %}
{# Main page content (override this) #}
<main class="page-content">
{% block page_content %}{% endblock %}
</main>
</div>
</div>
</div>
{% endblock body %}
Creating New Partials¶
When to Create a Partial¶
Create a partial when:
✅ Component is used in multiple places
✅ Component has complex structure
✅ Component needs to be testable in isolation
✅ Helps keep parent templates readable
Naming Conventions¶
- Use descriptive names:
sidebar.html, notnav.html - Prefix layout partials with underscore:
_layout_wrapper.html - Group by category:
partials/,layouts/,components/
Template Structure¶
{# partials/my_component.html #}
{# Description of what this component does #}
<div class="my-component">
{# Component markup #}
{{ content }}
</div>
Best Practices¶
- Document the partial: Add comments explaining purpose and required context
- Keep it focused: One responsibility per partial
- Make it flexible: Accept context variables for customization
- Style consistently: Follow existing patterns
- Test thoroughly: Ensure it works in all use cases
Related Documentation¶
- Template Architecture - Django template system
- UI Patterns - Common UI components
- SCSS Structure - Styling partials
- Components Library - Reusable components
Summary¶
Partials are essential for maintaining a clean, DRY codebase in Arctyk ITSM. They enable:
- Code reuse across multiple pages
- Consistent UI throughout the application
- Easier maintenance with single source of truth
- Better organization of template code
All partials follow Django's {% include %} pattern and can accept context variables for flexibility.