{% extends "base.html" %} {% from "components/ui.html" import confirm_dialog %} {% block content %}

{{ project.name }} {% if project.code_display %} {{ project.code_display }} {% endif %}

{{ project.client }}

{{ _('Entries Overview') }} {% if current_user.is_admin or has_any_permission(['edit_projects', 'archive_projects']) %} {{ _('Dashboard') }} {% if project.budget_amount %} {{ _('Budget Analysis') }} {% endif %} {% if current_user.is_admin or has_permission('edit_projects') %} {{ _('Edit Project') }} {% endif %} {% if current_user.is_admin or has_permission('edit_projects') %} {% if project.status == 'active' %}
{% elif project.status == 'inactive' %}
{% endif %} {% endif %} {% if current_user.is_admin or has_permission('archive_projects') %} {% if not project.is_archived %} {{ _('Archive') }} {% else %}
{% endif %} {% endif %} {% endif %}

Details

{{ _('Project Code') }}

{% if project.code_display %} {{ project.code_display }} {% else %} — {% endif %}

Description

{{ (project.description or 'No description provided.') | markdown | safe }}

Status

{% set status_map = { 'active': {'cls': 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300', 'label': _('Active')}, 'inactive': {'cls': 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', 'label': _('Inactive')}, 'archived': {'cls': 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200', 'label': _('Archived')}, } %} {% set st = status_map.get(project.status, status_map['inactive']) %} {{ st.label }}

Billing

{{ 'Billable' if project.billable else 'Not Billable' }} {% if project.hourly_rate %}({{ "%.2f"|format(project.hourly_rate) }}/hr){% endif %}

{% if project.is_archived and project.archived_at %}

{{ _('Archive Information') }}

{{ _('Archived on:') }} {{ project.archived_at|user_datetime('%Y-%m-%d %H:%M') }}
{% if project.archived_by_user %}
{{ _('Archived by:') }} {{ project.archived_by_user.full_name or project.archived_by_user.username }}
{% endif %} {% if project.archived_reason %}
{{ _('Reason:') }}

{{ project.archived_reason }}

{% endif %}
{% endif %}
{% if project.custom_fields %}

{{ _('Custom Fields') }}

{% for key, value in project.custom_fields.items() %}
{% set field_defs = custom_field_definitions_by_key|default({}) %} {% set field_definition = field_defs[key] if key in field_defs else None %}

{% if field_definition %}{{ field_definition.label }}{% else %}{{ key }}{% endif %}

{% set link_template = link_templates_by_field.get(key) if link_templates_by_field else None %} {% if link_template and value %} {# Render as clickable link using link template #} {% set rendered_url = link_template.render_url(value) %} {% if rendered_url %} {{ value }} {% else %} {{ value }} {% endif %} {% elif value is string and (value.startswith('http://') or value.startswith('https://')) %} {# Fallback: If the value looks like a URL, render it as a clickable link #} {{ value }} {% elif value is string and value.startswith('www.') %} {# Handle www. URLs #} {{ value }} {% else %} {{ value }} {% endif %}

{% endfor %}
{% endif %} {% set rendered_links = project.get_rendered_links() %} {% if rendered_links %}

{{ _('Quick Links') }}

{% for link in rendered_links %} {% if link.icon %} {% else %} {% endif %} {{ link.name }} {% endfor %}
{% endif %} {% if project.budget_amount %}

{{ _('Budget Overview') }}

{{ _('Details') }}
{% set consumed_amount = project.budget_consumed_amount|float if project.budget_consumed_amount else 0.0 %} {% set budget_amt = project.budget_amount|float %} {% set remaining = budget_amt - consumed_amount %} {% set percentage = (consumed_amount / budget_amt * 100) if budget_amt > 0 else 0 %} {% set threshold = project.budget_threshold_percent or 80 %}
{{ _('Budget Used') }} {{ "%.1f"|format(percentage) }}%
{{ _('Total Budget') }}
${{ "%.2f"|format(budget_amt) }}
{{ _('Consumed') }}
${{ "%.2f"|format(consumed_amount) }}
{{ _('Remaining') }}
${{ "%.2f"|format(remaining|abs) }} {% if remaining < 0 %}{{ _('over') }}{% endif %}
{{ _('Status') }}
{% if percentage >= 100 %} {{ _('Over') }} {% elif percentage >= threshold %} {{ _('Critical') }} {% elif percentage >= (threshold * 0.8) %} {{ _('Warning') }} {% else %} {{ _('Healthy') }} {% endif %}
{% endif %}

User Contributions

    {% for user_total in user_totals %}
  • {{ user_total.username }} {{ "%.2f"|format(user_total.total_hours) }} hrs
  • {% else %}
  • No hours logged yet.
  • {% endfor %}

{{ _('Attachments') }} {% if attachments %} ({{ attachments|length }}) {% endif %}

{% if current_user.is_admin or has_permission('edit_projects') %} {% endif %}
{% if current_user.is_admin or has_permission('edit_projects') %} {% endif %}
{% if attachments %} {% for attachment in attachments %}
{% if attachment.is_pdf %} {% elif attachment.is_image %} {% elif attachment.is_document %} {% else %} {% endif %}
{{ attachment.original_filename }} {% if attachment.description %}

{{ attachment.description }}

{% endif %}

{{ attachment.file_size_display }} • {{ attachment.uploaded_at|user_datetime('%Y-%m-%d %H:%M') if attachment.uploaded_at else '' }} {% if attachment.is_visible_to_client %} {{ _('Client Visible') }} {% endif %}

{% if current_user.is_admin or has_permission('edit_projects') %}
{% endif %}
{% endfor %} {% else %}

{{ _('No attachments yet') }}

{% endif %}

{{ _('Tasks for this project') }}

{% for task in tasks %} {% else %} {% endfor %}
{{ _('Name') }} {{ _('Priority') }} {{ _('Status') }} {{ _('Due') }} {{ _('Progress') }} {{ _('Actions') }}
{{ task.name }} {% set p = task.priority %} {% set pcls = {'low':'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', 'medium':'bg-sky-100 text-sky-700 dark:bg-sky-900/30 dark:text-sky-300', 'high':'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', 'urgent':'bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-300'}[p] if p in ['low','medium','high','urgent'] else 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300' %} {{ task.priority_display }} {% set s = task.status %} {% set scls = {'todo':'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300', 'in_progress':'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300', 'review':'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-300', 'done':'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', 'cancelled':'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-200'}[s] if s in ['todo','in_progress','review','done','cancelled'] else 'bg-slate-100 text-slate-700 dark:bg-slate-800 dark:text-slate-300' %} {{ task.status_display }} {% if task.due_date %} {% set overdue = task.is_overdue %} {{ task.due_date.strftime('%Y-%m-%d') }} {% else %} {% endif %} {% set pct = task.progress_percentage or 0 %}
{{ _('View') }}
{{ _('No tasks for this project.') }}
{% if current_user.is_admin or has_permission('delete_projects') %} {{ confirm_dialog( 'confirmDeleteProject-' ~ project.id, 'Delete Project', 'Are you sure you want to delete this project? This action cannot be undone.', 'Delete', 'Cancel', 'danger' ) }} {% endif %} {% endblock %} {% block scripts_extra %}{% endblock %}