### Example Taskiq Dashboard with Scheduler Setup Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_scheduler.md This Python code demonstrates the setup for running Taskiq Dashboard with a scheduler. It requires importing necessary components and configuring the dashboard with a scheduler instance. ```python from taskiq import TaskiqBroker from taskiq_dashboard import TaskiqDashboard broker = TaskiqBroker() scheduler = broker.scheduler app = TaskiqDashboard(broker=broker, scheduler=scheduler) @broker.task def example_task(x: int, y: int) -> int: return x + y ``` -------------------------------- ### Start Dashboard Application Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Start the taskiq-dashboard application locally. ```bash make run ``` -------------------------------- ### Install taskiq-dashboard with uv Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Use this command to install the taskiq-dashboard package using uv. ```bash uv pip install taskiq-dashboard ``` -------------------------------- ### Initialize Development Environment Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Initialize the development environment by creating a virtual environment, installing dependencies, and setting up pre-commit hooks. ```bash make init ``` -------------------------------- ### Install taskiq-dashboard with pip Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md Use this command to install the taskiq-dashboard package. ```bash pip install taskiq-dashboard ``` -------------------------------- ### Install taskiq-dashboard with poetry Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Use this command to add the taskiq-dashboard package as a dependency using poetry. ```bash poetry add taskiq-dashboard ``` -------------------------------- ### Run Local PostgreSQL Instance Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Start a local PostgreSQL database instance using Docker for development purposes. ```bash make run_infra ``` -------------------------------- ### Run Taskiq Scheduler Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_scheduler.md This command starts the Taskiq scheduler, which is optional but necessary for sending scheduled tasks. Specify the path to your scheduler instance. ```bash uv run taskiq scheduler docs.examples.example_with_scheduler:scheduler ``` -------------------------------- ### Run Taskiq Admin Panel Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_scheduler.md Run this command to start the Taskiq admin panel. This command assumes your application is structured within a Python module. ```bash uv run python -m docs.examples.example_with_scheduler ``` -------------------------------- ### Run Taskiq Worker Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_scheduler.md Execute this command in a terminal to start the Taskiq worker. Ensure the path to your broker is correctly specified. ```bash uv run taskiq worker docs.examples.example_with_scheduler:broker --workers 1 ``` -------------------------------- ### Run Taskiq Admin Panel Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_broker.md Run this command to start the Taskiq admin panel. This command assumes your broker configuration is accessible via the specified Python module. ```bash uv run python -m docs.examples.example_with_broker admin_panel ``` -------------------------------- ### Taskiq Dashboard with PostgreSQL Broker Example Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_broker.md This Python code sets up Taskiq Dashboard with a PostgreSQL broker and a task scheduler. It requires the `taskiq` and `uvicorn` libraries. ```python from taskiq import AsyncBroker from taskiq_dashboard import TaskiqDashboard broker = AsyncBroker("postgres://user:password@host:port/database") TaskiqDashboard(broker=broker).startup() ``` -------------------------------- ### Run Taskiq Worker Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_broker.md Execute this command in a terminal to start the Taskiq worker. Ensure the broker is configured correctly in your Python file. ```bash uv run taskiq worker docs.examples.example_with_broker:broker --workers 1 ``` -------------------------------- ### Disable Startup Cleanup Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/cleanup.md Set this environment variable to false to prevent cleanup from running when the application starts. Cleanup will still run periodically based on the configured interval. ```bash export TASKIQ_DASHBOARD__CLEANUP__IS_CLEANUP_ON_STARTUP_ENABLED=false ``` -------------------------------- ### Calculate Task Duration in Jinja2 Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html Jinja2 template snippet to calculate and display the duration of a task in seconds. It handles cases where start or finish times might be missing. ```html {{ (task.finished_at - task.started_at).total_seconds() | round(3) }} seconds ``` -------------------------------- ### View Make Help Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Display available commands and their descriptions by running the 'make help' command. ```bash make help ``` -------------------------------- ### Clone Repository and Navigate Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Clone the project repository and change into the project directory. ```bash git clone https://github.com/danfimov/taskiq-dashboard.git cd taskiq-dashboard ``` -------------------------------- ### Run TaskiqDashboard with Code Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md Instantiate and run the TaskiqDashboard application. Ensure the api_token and database_dsn are correctly set. The broker instance is optional but enables additional features. ```python from taskiq_dashboard import TaskiqDashboard from your_project.broker import broker # your Taskiq broker instance def run_admin_panel() -> None: app = TaskiqDashboard( api_token='supersecret', # the same secret as in middleware storage_type='postgresql', # or 'sqlite' database_dsn="postgresql://taskiq-dashboard:look_in_vault@postgres:5432/taskiq-dashboard", broker=broker, # pass your broker instance here to enable additional features (optional) host='0.0.0.0', port=8000, ) app.run() if __name__ == '__main__': run_admin_panel() ``` -------------------------------- ### Configure Taskiq-Dashboard with PostgreSQL using Constructor Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Use this when initializing TaskiqDashboard directly in your Python code and connecting to a PostgreSQL database. Uvicorn parameters can also be passed. ```python app = TaskiqDashboard( api_token='supersecret', storage_type='postgres', database_dsn="postgresql://taskiq-dashboard:look_in_vault@postgres:5432/taskiq-dashboard", # all this keywords will be passed to uvicorn host='localhost', port=8000, log_level='info', access_log=False, ) ``` -------------------------------- ### Configure Taskiq-Dashboard with SQLite using Constructor Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Use this when initializing TaskiqDashboard directly in your Python code and connecting to a SQLite database. Uvicorn parameters can also be passed. ```python app = TaskiqDashboard( api_token='supersecret', storage_type='sqlite', database_dsn="sqlite+aiosqlite:///taskiq_dashboard.db", # all this keywords will be passed to uvicorn host='localhost', port=8000, log_level='info', access_log=False, ) ``` -------------------------------- ### Run Taskiq Dashboard with SQLite Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Instantiate and run the TaskiqDashboard with SQLite as the storage type. Ensure the API token and database DSN are correctly configured. ```python from taskiq_dashboard import TaskiqDashboard from your_project.broker import broker # your Taskiq broker instance def run_admin_panel() -> None: app = TaskiqDashboard( api_token='supersecret', # the same secret as in middleware storage_type='sqlite', database_dsn="sqlite+aiosqlite:///taskiq_dashboard.db", broker=broker, # pass your broker instance here to enable additional features host='0.0.0.0', port=8000, ) app.run() if __name__ == '__main__': run_admin_panel() ``` -------------------------------- ### Connect TaskiqAdminMiddleware to Taskiq Broker Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Import and connect the TaskiqAdminMiddleware to your Taskiq broker instance. Ensure the URL, API token, and broker name match your dashboard configuration. ```python from taskiq.middlewares.taskiq_admin_middleware import TaskiqAdminMiddleware broker = ( RedisStreamBroker( url=redis_url, queue_name="my_lovely_queue", ) .with_result_backend(result_backend) .with_middlewares( TaskiqAdminMiddleware( url="http://localhost:8000", # the url to your taskiq-dashboard instance api_token="supersecret", # secret for accessing the dashboard API taskiq_broker_name="my_worker", # it will be worker name in the dashboard ) ) ) ``` -------------------------------- ### Run Taskiq Dashboard with PostgreSQL Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Instantiate and run the TaskiqDashboard with PostgreSQL as the storage type. Ensure the API token and database DSN are correctly configured. ```python from taskiq_dashboard import TaskiqDashboard from your_project.broker import broker # your Taskiq broker instance def run_admin_panel() -> None: app = TaskiqDashboard( api_token='supersecret', # the same secret as in middleware storage_type='postgres', database_dsn="postgresql://taskiq-dashboard:look_in_vault@postgres:5432/taskiq-dashboard", broker=broker, # pass your broker instance here to enable additional features host='0.0.0.0', port=8000, ) app.run() if __name__ == '__main__': run_admin_panel() ``` -------------------------------- ### Connect DashboardMiddleware to Taskiq Broker Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md Import and connect the DashboardMiddleware to your Taskiq broker. Ensure the URL, api_token, and broker_name match your configuration. ```python from taskiq_dashboard import DashboardMiddleware broker = ( RedisStreamBroker( url=redis_url, queue_name="my_lovely_queue", ) .with_result_backend(result_backend) .with_middlewares( DashboardMiddleware( url="http://localhost:8000", # the url to your taskiq-dashboard instance api_token="supersecret", # secret for accessing the dashboard API broker_name="my_worker", # it will be worker name in the dashboard ) ) ) ``` -------------------------------- ### Docker Compose for PostgreSQL and Taskiq-Dashboard Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md This docker-compose.yml file sets up PostgreSQL and taskiq-dashboard services. Configure environment variables for database connection and API token. ```yaml services: postgres: image: postgres:18 environment: POSTGRES_USER: taskiq-dashboard POSTGRES_PASSWORD: look_in_vault POSTGRES_DB: taskiq-dashboard volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" dashboard: image: ghcr.io/danfimov/taskiq-dashboard:latest depends_on: - postgres environment: TASKIQ_DASHBOARD__STORAGE_TYPE: postgres TASKIQ_DASHBOARD__POSTGRES__HOST: postgres TASKIQ_DASHBOARD__API__TOKEN: supersecret ports: - "8000:8000" volumes: postgres_data: ``` -------------------------------- ### Initialize Theme Switching Logic Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/partial/head.html This JavaScript code initializes theme switching for the dashboard. It reads theme preferences from local storage or uses system preferences if no preference is found. The theme is then applied to the document. ```javascript (function () { var THEMES = { latte: 1, frappe: 1, macchiato: 1, mocha: 1, auto: 1, }; try { var isAuto = localStorage.getItem("theme-auto") === "true"; var t = localStorage.getItem("theme"); if (!t || !THEMES[t] || isAuto) { t = matchMedia("(prefers-color-scheme: dark)").matches ? "mocha" : "latte"; } document.documentElement.className = t; document.documentElement.style.colorScheme = t === "latte" ? "light" : "dark"; document.documentElement.setAttribute("data-theme-ready", "true"); window.__theme = t; } catch (_err) {} })(); ``` -------------------------------- ### Docker Compose for SQLite and Taskiq-Dashboard Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md This docker-compose.yml file sets up taskiq-dashboard to use SQLite. Configure environment variables for storage type, DSN, and API token. ```yaml services: dashboard: image: ghcr.io/danfimov/taskiq-dashboard:latest environment: TASKIQ_DASHBOARD__STORAGE_TYPE: sqlite TASKIQ_DASHBOARD__SQLITE__DSN: sqlite+aiosqlite:///taskiq_dashboard.db TASKIQ_DASHBOARD__API__TOKEN: supersecret volumes: - taskiq_dashboard_sqlite:/app/taskiq-dashboard.db ports: - "8000:8000" volumes: taskiq_dashboard_sqlite: ``` -------------------------------- ### Manage Task Selection and Bulk Actions Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/home.html Handles the logic for selecting individual tasks, a 'select all' option, and updating the UI for bulk actions. It manages the state of selected tasks and enables/disables the apply button based on selection and chosen action. ```javascript (function() { const selectedTasks = new Set(); const selectAllCheckbox = document.getElementById('select-all-checkbox'); const applyButton = document.getElementById('apply-bulk-action'); const bulkActionSelect = document.getElementById('bulk-action-select'); const selectedCountSpan = document.getElementById('selected-count'); function updateUI() { const count = selectedTasks.size; selectedCountSpan.textContent = `${count} selected`; applyButton.disabled = count === 0 || !bulkActionSelect.value; // Update select all checkbox state const allCheckboxes = document.querySelectorAll('.task-checkbox'); if (allCheckboxes.length > 0) { const checkedCount = Array.from(allCheckboxes).filter(cb => cb.checked).length; selectAllCheckbox.checked = checkedCount === allCheckboxes.length && count > 0; selectAllCheckbox.indeterminate = checkedCount > 0 && checkedCount < allCheckboxes.length; } else { selectAllCheckbox.checked = false; selectAllCheckbox.indeterminate = false; } } function handleCheckboxChange(checkbox) { const taskId = checkbox.value; if (checkbox.checked) { selectedTasks.add(taskId); } else { selectedTasks.delete(taskId); } updateUI(); } // Handle individual task checkboxes document.addEventListener('change', (e) => { if (e.target.classList.contains('task-checkbox')) { handleCheckboxChange(e.target); } }); // Handle select all checkbox selectAllCheckbox.addEventListener('change', (e) => { const allCheckboxes = document.querySelectorAll('.task-checkbox'); allCheckboxes.forEach(checkbox => { checkbox.checked = e.target.checked; const taskId = checkbox.value; if (e.target.checked) { selectedTasks.add(taskId); } else { selectedTasks.delete(taskId); } }); updateUI(); }); // Handle bulk action select change bulkActionSelect.addEventListener('change', () => { updateUI(); }); // Handle apply button click applyButton.addEventListener('click', () => { const action = bulkActionSelect.value; if (!action || selectedTasks.size === 0) { return; } const taskIds = Array.from(selectedTasks); const url = action === 'rerun' ? '{{ url_for("Bulk rerun tasks") }}' : '{{ url_for("Bulk delete tasks") }}'; // Disable button during request applyButton.disabled = true; applyButton.textContent = 'Processing...'; fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'HX-Request': 'true' }, body: JSON.stringify({ task_ids: taskIds }) }) .then(response => { if (action === 'delete') { // For delete, reload the page to refresh the task list window.location.reload(); return; } else { // For rerun, show notification return response.text(); } }) .then(html => { if (html) { // Add notification to notification container c ``` -------------------------------- ### Initialize Search Input Behavior Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/home.html Adds event listeners to a search input to manage a hint's display based on focus and input value. Ensures the hint is hidden when the input is active or has text. ```javascript (function() { const input = document.getElementById('search'); const hint = document.getElementById('search-slash-hint'); if (!input || !hint) return; function update() { hint.style.display = (document.activeElement === input || input.value !== '') ? 'none' : ''; } input.addEventListener('focus', update); input.addEventListener('blur', update); input.addEventListener('input', update); update(); })(); ``` -------------------------------- ### Configure Taskiq-Dashboard PostgreSQL via Environment Variables Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Set these environment variables to configure Taskiq-dashboard for PostgreSQL connection and API settings, especially when running in a Docker container. ```dotenv TASKIQ_DASHBOARD__POSTGRES__DRIVER=postgresql+asyncpg TASKIQ_DASHBOARD__POSTGRES__HOST=localhost TASKIQ_DASHBOARD__POSTGRES__PORT=5432 TASKIQ_DASHBOARD__POSTGRES__USER=taskiq-dashboard TASKIQ_DASHBOARD__POSTGRES__PASSWORD=look_in_vault TASKIQ_DASHBOARD__POSTGRES__DATABASE=taskiq-dashboard TASKIQ_DASHBOARD__POSTGRES__MIN_POOL_SIZE=1 TASKIQ_DASHBOARD__POSTGRES__MAX_POOL_SIZE=5 # or just use DSN: TASKIQ_DASHBOARD__POSTGRES__DSN=postgresql+asyncpg://taskiq-dashboard:look_in_vault@localhost:5432/taskiq-dashboard TASKIQ_DASHBOARD__API__HOST=localhost TASKIQ_DASHBOARD__API__PORT=8000 TASKIQ_DASHBOARD__API__TOKEN=supersecret TASKIQ_DASHBOARD__API__TRUSTED_HOSTS=* ``` -------------------------------- ### Handle Task Selection and Bulk Actions Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/home.html Manages task selection, UI updates for bulk actions, and submission of selected tasks for processing. Includes error handling for the request. ```javascript const notificationContainer = document.getElementById('notification-list'); const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; notificationContainer.appendChild(tempDiv.firstElementChild); } // Clear selection and reset UI selectedTasks.clear(); bulkActionSelect.value = ''; document.querySelectorAll('.task-checkbox').forEach(cb => cb.checked = false); selectAllCheckbox.checked = false; selectAllCheckbox.indeterminate = false; applyButton.textContent = 'Apply'; updateUI(); }) .catch(error => { console.error('Error:', error); alert('An error occurred while processing the request'); applyButton.disabled = false; applyButton.textContent = 'Apply'; ``` -------------------------------- ### Send Task to Taskiq Broker Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_with_broker.md Use this command to send a task to the Taskiq broker. This is useful for testing task execution and observing it in the dashboard. ```bash uv run python -m docs.examples.example_with_broker send_task ``` -------------------------------- ### Configure Taskiq-Dashboard SQLite via Environment Variables Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/index.md Set these environment variables to configure Taskiq-dashboard for SQLite connection and API settings, especially when running in a Docker container. ```dotenv TASKIQ_DASHBOARD__SQLITE__DRIVER=sqlite+aiosqlite TASKIQ_DASHBOARD__SQLITE__FILE_PATH=taskiq-dashboard.db # or just use DSN: TASKIQ_DASHBOARD__SQLITE__DSN=sqlite+aiosqlite:///taskiq_dashboard.db TASKIQ_DASHBOARD__API__HOST=localhost TASKIQ_DASHBOARD__API__PORT=8000 TASKIQ_DASHBOARD__API__TOKEN=supersecret TASKIQ_DASHBOARD__API__TRUSTED_HOSTS=* ``` -------------------------------- ### Run Tailwind CSS Watch Mode Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/contributing.md Compile CSS using Tailwind CSS in watch mode to automatically update styles during development. ```bash bun run dev ``` -------------------------------- ### Pull taskiq-dashboard Docker image Source: https://github.com/danfimov/taskiq-dashboard/blob/main/README.md Use this command to pull the latest Docker image for taskiq-dashboard. ```bash docker pull ghcr.io/danfimov/taskiq-dashboard:latest ``` -------------------------------- ### Mount Taskiq Dashboard in FastAPI Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_as_mounted_app.md Mount the Taskiq Dashboard application under a specific URL path in your existing FastAPI app. This is suitable for simple integrations. ```python from taskiq_dashboard import TaskiqDashboard import fastapi app = fastapi.FastAPI(...) admin_dashboard = TaskiqDashboard(...) app.mount('/admin', admin_dashboard.application) ``` -------------------------------- ### Display Task Arguments using tojson Filter Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html Jinja2 template snippet to display task arguments. It uses the `tojson` filter to format the arguments as a JSON string with indentation. ```html {{ task.args | tojson(indent=2) }} ``` ```html {{ task.kwargs | tojson(indent=2) }} ``` ```html {{ task.labels | tojson(indent=2) }} ``` -------------------------------- ### Display Task Status in Jinja2 Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html Jinja2 template snippet to display the human-readable status of a task based on its status code. ```html {% if task.status == 0 %}In progress {% elif task.status == 1 %}Completed {% elif task.status == 2 %}Failure {% elif task.status == 3 %}Queued {% elif task.status == 4 %}Abandoned {% else %}Unknown{% endif %} ``` -------------------------------- ### Update UI on HTMX Swap Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/home.html Listens for HTMX swap events to update the UI, specifically when the task list is reloaded. It ensures that deselected tasks are removed from the selection and the UI reflects the current state. ```javascript // Update UI when new tasks are loaded via HTMX document.body.addEventListener('htmx:afterSwap', (e) => { if (e.detail.target.id === 'task-list-body') { // Remove task IDs from selection if their checkboxes no longer exist const currentCheckboxes = Array.from(document.querySelectorAll('.task-checkbox')); const currentTaskIds = new Set(currentCheckboxes.map(cb => cb.value)); const tasksToRemove = Array.from(selectedTasks).filter(id => !currentTaskIds.has(id)); tasksToRemove.forEach(id => selectedTasks.delete(id)); // Always update UI to reflect current state updateUI(); } }); // Initial UI update updateUI(); })(); ``` -------------------------------- ### Manage Lifespan for Mounted Taskiq Dashboard Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/run_as_mounted_app.md When mounting the Taskiq Dashboard, its lifespan function might not trigger automatically. This snippet shows how to manually manage the lifespan context for the dashboard application within your FastAPI app's lifespan. ```python from taskiq_dashboard import TaskiqDashboard import fastapi import contextlib import typing as tp admin_dashboard = TaskiqDashboard(...) @contextlib.asynccontextmanager async def lifespan(app: fastapi.FastAPI) -> tp.AsyncGenerator[None, None]: dashboard_app = admin_dashboard.application async with dashboard_app.router.lifespan_context(dashboard_app): yield app = fastapi.FastAPI(lifespan=lifespan) app.mount('/admin', admin_dashboard.application) ``` -------------------------------- ### Display Task Result or Error in Jinja2 Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html Jinja2 template snippet to display the task's result or error message. It includes conditional logic to show appropriate messages based on task status or if data is available. ```html {{ task_result | safe }} ``` ```html {{ task.error }} ``` -------------------------------- ### Disable Automatic Cleanup Source: https://github.com/danfimov/taskiq-dashboard/blob/main/docs/tutorial/cleanup.md Set this environment variable to false to disable all automatic cleanup tasks. This is useful if you prefer to manage cleanup manually or with external tools. ```bash export TASKIQ_DASHBOARD__CLEANUP__IS_ENABLED=false ``` -------------------------------- ### Format Datetime in Jinja2 Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html Jinja2 template snippet to format a datetime object into a 'YYYY-MM-DD HH:MM:SS' string. It handles cases where the datetime might be null. ```html {{ task.started_at.strftime('%Y-%m-%d %H:%M:%S') }} ``` ```html {{ task.finished_at.strftime('%Y-%m-%d %H:%M:%S') }} ``` -------------------------------- ### Manage Status Select Element Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/home.html Handles the UI interactions for a status selection dropdown, including ARIA attributes and visual cues for expansion. It manages the rotation of a chevron icon and the 'aria-expanded' attribute. ```javascript const selectEl = document.getElementById("status"); const chevronEl = document.getElementById("status-chevron"); const openChevron = () => { chevronEl && chevronEl.classList.add("-rotate-90"); selectEl.setAttribute("aria-expanded", "true"); }; const closeChevron = () => { chevronEl && chevronEl.classList.remove("-rotate-90"); selectEl.setAttribute("aria-expanded", "false"); }; // opening cases selectEl.addEventListener("pointerdown", openChevron); selectEl.addEventListener("click", openChevron); selectEl.addEventListener("keydown", (e) => { if (\["ArrowUp", "ArrowDown", " ", "Enter"\].includes(e.key)) openChevron(); }); // closing cases selectEl.addEventListener("blur", closeChevron); selectEl.addEventListener("change", closeChevron); selectEl.addEventListener("keyup", (e) => { if (e.key === "Escape") closeChevron(); }); document.addEventListener("pointerdown", (e) => { if (!selectEl.contains(e.target)) { closeChevron(); } }); ``` -------------------------------- ### Toggle Actions Menu JavaScript Source: https://github.com/danfimov/taskiq-dashboard/blob/main/taskiq_dashboard/api/templates/task_details.html JavaScript code to toggle the visibility of an actions menu when a button is clicked. It also handles closing the menu when clicking outside of it. ```javascript const btn = document.getElementById('actions-menu-button'); const menu = document.getElementById('actions-menu'); btn.addEventListener('click', () => menu.classList.toggle('hidden')); document.addEventListener('click', (e) => { if (!btn.contains(e.target) && !menu.contains(e.target)) { menu.classList.add('hidden'); } }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.