changes
This commit is contained in:
@@ -1,13 +1,194 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Redesigned /my/projects page: status columns + rich cards -->
|
||||
<template id="portal_my_projects" name="Fusion: My Projects (cards)">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="True"/>
|
||||
|
||||
<t t-set="_active_ids" t-value="[p.id for p in projects if (fp_project_cards or {}).get(p.id, {}).get('bucket') == 'active']"/>
|
||||
<t t-set="_idle_ids" t-value="[p.id for p in projects if (fp_project_cards or {}).get(p.id, {}).get('bucket') == 'idle']"/>
|
||||
<t t-set="_done_ids" t-value="[p.id for p in projects if (fp_project_cards or {}).get(p.id, {}).get('bucket') == 'done']"/>
|
||||
|
||||
<div class="fp_projects_page" t-att-data-default-group="fp_groupby or 'status'">
|
||||
|
||||
<!-- Page header / control bar -->
|
||||
<div class="fp_projects_header d-flex flex-wrap align-items-center gap-2 mb-3">
|
||||
<h3 class="mb-0 me-2">
|
||||
<i class="fa fa-folder-open-o me-2"/>Projects
|
||||
<span class="badge bg-secondary ms-2"><t t-esc="len(projects)"/></span>
|
||||
</h3>
|
||||
<div class="ms-auto d-flex flex-wrap align-items-center gap-2">
|
||||
<div class="input-group input-group-sm fp_projects_search_wrap">
|
||||
<span class="input-group-text bg-white"><i class="fa fa-search"/></span>
|
||||
<input type="search"
|
||||
class="form-control form-control-sm fp_projects_search"
|
||||
placeholder="Search projects..."/>
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm fp_projects_group_picker" role="group" aria-label="Group by">
|
||||
<button type="button" class="btn btn-outline-secondary active" data-group="status">By Status</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-group="customer">By Customer</button>
|
||||
<button type="button" class="btn btn-outline-secondary" data-group="none">Flat</button>
|
||||
</div>
|
||||
<div class="dropdown fp_projects_sort_picker">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Sort: <span class="fp_sort_label">Name</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item active" href="#" data-sort="name">Name</a></li>
|
||||
<li><a class="dropdown-item" href="#" data-sort="pct">% Complete</a></li>
|
||||
<li><a class="dropdown-item" href="#" data-sort="tasks">Task Count</a></li>
|
||||
<li><a class="dropdown-item" href="#" data-sort="activity">Last Activity</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Empty: no projects at all -->
|
||||
<t t-if="not projects">
|
||||
<div class="fp_projects_empty text-center py-5">
|
||||
<div class="fp_empty_glyph mb-3"><i class="fa fa-folder-open-o fa-3x text-muted"/></div>
|
||||
<h5 class="text-muted mb-1">You don't have any projects yet</h5>
|
||||
<p class="text-muted small mb-0">Once a project is shared with you, it will appear here.</p>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Columns + cards -->
|
||||
<t t-if="projects">
|
||||
<div class="fp_projects_cols">
|
||||
<t t-call="fusion_project_portal.fp_project_column">
|
||||
<t t-set="bucket_key" t-value="'active'"/>
|
||||
<t t-set="bucket_label" t-value="'Active'"/>
|
||||
<t t-set="bucket_dot" t-value="'green'"/>
|
||||
<t t-set="bucket_empty" t-value="'No active projects.'"/>
|
||||
<t t-set="bucket_ids" t-value="_active_ids"/>
|
||||
</t>
|
||||
<t t-call="fusion_project_portal.fp_project_column">
|
||||
<t t-set="bucket_key" t-value="'idle'"/>
|
||||
<t t-set="bucket_label" t-value="'Idle'"/>
|
||||
<t t-set="bucket_dot" t-value="'amber'"/>
|
||||
<t t-set="bucket_empty" t-value="'No idle projects.'"/>
|
||||
<t t-set="bucket_ids" t-value="_idle_ids"/>
|
||||
</t>
|
||||
<t t-call="fusion_project_portal.fp_project_column">
|
||||
<t t-set="bucket_key" t-value="'done'"/>
|
||||
<t t-set="bucket_label" t-value="'Done'"/>
|
||||
<t t-set="bucket_dot" t-value="'gray'"/>
|
||||
<t t-set="bucket_empty" t-value="'Completed projects will appear here.'"/>
|
||||
<t t-set="bucket_ids" t-value="_done_ids"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="fp_projects_no_match d-none text-center py-4 text-muted small">
|
||||
No projects match your search.
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-if="pager and pager.get('page_count', 1) > 1" class="mt-3">
|
||||
<t t-call="portal.pager"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<!-- Single column with its cards -->
|
||||
<template id="fp_project_column" name="Fusion: project status column">
|
||||
<div class="fp_projects_col" t-att-data-bucket="bucket_key">
|
||||
<div class="fp_projects_col_head">
|
||||
<span class="fp_col_label">
|
||||
<span t-attf-class="fp_dot fp_dot_{{ bucket_dot }}"/>
|
||||
<t t-esc="bucket_label"/>
|
||||
</span>
|
||||
<span class="fp_col_count" t-esc="len(bucket_ids or [])"/>
|
||||
</div>
|
||||
<div class="fp_projects_col_body">
|
||||
<t t-if="not bucket_ids">
|
||||
<div class="fp_col_empty"><t t-esc="bucket_empty"/></div>
|
||||
</t>
|
||||
<t t-foreach="projects" t-as="project">
|
||||
<t t-if="project.id in (bucket_ids or [])">
|
||||
<t t-call="fusion_project_portal.fp_project_card"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Rich project card (used inside columns and flat list) -->
|
||||
<template id="fp_project_card" name="Fusion: project card">
|
||||
<t t-set="_d" t-value="(fp_project_cards or {}).get(project.id, {})"/>
|
||||
<t t-set="_pct" t-value="_d.get('pct') or 0"/>
|
||||
<t t-set="_bucket" t-value="_d.get('bucket') or 'idle'"/>
|
||||
<t t-set="_total" t-value="_d.get('total_count') or 0"/>
|
||||
<t t-set="_done" t-value="_d.get('done_count') or 0"/>
|
||||
<t t-set="_open" t-value="_d.get('open_count') or 0"/>
|
||||
<t t-set="_alloc" t-value="_d.get('alloc_hours') or 0.0"/>
|
||||
<t t-set="_spent" t-value="_d.get('spent_hours') or 0.0"/>
|
||||
<t t-set="_assignees" t-value="_d.get('assignees') or []"/>
|
||||
<t t-set="_customer" t-value="_d.get('partner_name') or ''"/>
|
||||
<t t-set="_last" t-value="_d.get('last_activity')"/>
|
||||
|
||||
<a t-attf-href="/my/projects/{{ project.id }}"
|
||||
class="fp_project_card"
|
||||
t-att-data-bucket="_bucket"
|
||||
t-att-data-name="project.name"
|
||||
t-att-data-customer="_customer"
|
||||
t-att-data-pct="_pct"
|
||||
t-att-data-tasks="_total"
|
||||
t-att-data-activity="(_last and _last.isoformat()) or ''">
|
||||
<div class="fp_card_top">
|
||||
<span class="fp_card_title"><t t-esc="project.name"/></span>
|
||||
<span t-if="_pct" t-attf-class="fp_card_pct fp_pct_{{ 'high' if _pct >= 80 else ('mid' if _pct >= 40 else 'low') }}">
|
||||
<t t-esc="str(int(_pct)) + '%'"/>
|
||||
</span>
|
||||
<span t-if="not _pct" class="fp_card_pct fp_card_pct_muted">—</span>
|
||||
</div>
|
||||
<div class="fp_card_sub">
|
||||
<t t-if="_customer"><i class="fa fa-user-o me-1 text-muted"/><t t-esc="_customer"/></t>
|
||||
<t t-else=""><span class="text-muted">No customer set</span></t>
|
||||
</div>
|
||||
<div t-attf-class="fp_card_bar fp_bar_{{ 'high' if _pct >= 80 else ('mid' if _pct >= 40 else 'low') }}">
|
||||
<span t-attf-style="width: {{ _pct }}%;"/>
|
||||
</div>
|
||||
<div class="fp_card_stats">
|
||||
<span t-if="_total" class="fp_chip fp_chip_blue">
|
||||
<i class="fa fa-list-ul me-1"/><t t-esc="_total"/> task<t t-if="_total != 1">s</t>
|
||||
</span>
|
||||
<span t-if="not _total" class="fp_chip fp_chip_gray">No tasks</span>
|
||||
<span t-if="_done" class="fp_chip fp_chip_green">
|
||||
<i class="fa fa-check me-1"/><t t-esc="_done"/> done
|
||||
</span>
|
||||
<span t-if="_alloc or _spent" class="fp_chip fp_chip_cyan ms-auto">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
<t t-esc="'%.1fh' % _spent"/>
|
||||
<t t-if="_alloc"> / <t t-esc="'%.1fh' % _alloc"/></t>
|
||||
</span>
|
||||
</div>
|
||||
<div class="fp_card_footer">
|
||||
<div class="fp_avatars">
|
||||
<t t-foreach="_assignees[:4]" t-as="u">
|
||||
<span class="fp_avatar" t-att-title="u['name']">
|
||||
<img t-attf-src="/web/image/res.users/{{ u['id'] }}/avatar_128"
|
||||
t-att-alt="u['name']"
|
||||
onerror="this.style.display='none';this.parentElement.classList.add('fp_avatar_initials')"/>
|
||||
<span class="fp_avatar_text"><t t-esc="u['initials']"/></span>
|
||||
</span>
|
||||
</t>
|
||||
<span t-if="len(_assignees) > 4" class="fp_avatar fp_avatar_more">+<t t-esc="len(_assignees) - 4"/></span>
|
||||
</div>
|
||||
<span t-if="_last" class="fp_card_activity small text-muted">
|
||||
<i class="fa fa-history me-1"/><t t-esc="_last.strftime('%b %-d')" t-translation="off"/>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<!-- Status actions: Request Changes / Approve / Mark Done from the portal task page -->
|
||||
<template id="portal_my_task_inherit_state_actions"
|
||||
inherit_id="project.portal_my_task"
|
||||
name="Fusion: Status actions on portal task page"
|
||||
priority="55">
|
||||
<xpath expr="//div[@id='task_chat']" position="before">
|
||||
<div t-if="fp_can_create_task" class="card mt-4 mb-4 fp_state_card">
|
||||
<div t-if="fp_can_change_state" class="card mt-4 mb-4 fp_state_card">
|
||||
<div class="card-header bg-light d-flex align-items-center">
|
||||
<h5 class="mb-0"><i class="fa fa-flag me-2"/> Status</h5>
|
||||
<span class="ms-auto badge"
|
||||
@@ -163,16 +344,35 @@
|
||||
name="Fusion: Sub-tasks list inside parent task page">
|
||||
<xpath expr="//div[@id='task_chat']" position="before">
|
||||
<div class="card mt-4 mb-4 fp_subtasks_card">
|
||||
<div class="card-header bg-light d-flex align-items-center">
|
||||
<div class="card-header bg-light d-flex align-items-center flex-wrap gap-2">
|
||||
<h5 class="mb-0">
|
||||
<i class="fa fa-sitemap me-2"/>
|
||||
Sub-tasks
|
||||
<span class="badge bg-secondary ms-2">
|
||||
<t t-esc="len(fp_task_descendants or [])"/>
|
||||
</span>
|
||||
<span t-if="fp_descendant_total" class="badge bg-success ms-2"
|
||||
t-attf-title="{{ fp_descendant_done }} of {{ fp_descendant_total }} sub-tasks done">
|
||||
<i class="fa fa-check-circle me-1"/>
|
||||
<t t-esc="'%.0f' % (fp_descendant_pct or 0)"/>%
|
||||
</span>
|
||||
<span t-if="fp_descendant_alloc_hours or fp_descendant_spent_hours"
|
||||
class="badge bg-info text-dark ms-2"
|
||||
title="Spent / Allocated hours across sub-tasks">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
<t t-esc="'%.1fh' % (fp_descendant_spent_hours or 0)"/>
|
||||
<span t-if="fp_descendant_alloc_hours">
|
||||
/ <t t-esc="'%.1fh' % fp_descendant_alloc_hours"/>
|
||||
</span>
|
||||
</span>
|
||||
</h5>
|
||||
<input t-if="fp_task_descendants"
|
||||
type="search"
|
||||
class="form-control form-control-sm fp_subtask_search ms-auto"
|
||||
style="max-width: 260px;"
|
||||
placeholder="Search sub-tasks..."/>
|
||||
<button t-if="fp_can_create_task"
|
||||
class="btn btn-sm btn-primary ms-auto"
|
||||
class="btn btn-sm btn-primary"
|
||||
type="button"
|
||||
data-bs-toggle="collapse"
|
||||
t-attf-data-bs-target="#fp_form_root_{{ task.id }}"
|
||||
@@ -196,7 +396,7 @@
|
||||
<span t-attf-style="display:inline-block;width:{{ _d * 1.4 }}rem;"/>
|
||||
<i t-if="_d" class="fa fa-level-up fa-rotate-90 me-2 text-muted small"/>
|
||||
<a t-attf-href="/my/projects/{{ task.project_id.id }}/task/{{ sub.id }}"
|
||||
class="flex-grow-1">
|
||||
t-attf-class="flex-grow-1 #{'text-decoration-line-through text-muted' if sub.state in (fp_done_states or ()) else ''}">
|
||||
<span t-esc="sub.name"/>
|
||||
</a>
|
||||
<button t-if="fp_can_create_task"
|
||||
@@ -208,7 +408,23 @@
|
||||
title="Add sub-task here">
|
||||
<i class="fa fa-plus"/>
|
||||
</button>
|
||||
<span t-if="sub.stage_id" class="badge bg-light text-dark border ms-2">
|
||||
<t t-set="_fp_alloc" t-value="(fp_alloc_by_id or {}).get(sub.id, 0.0)"/>
|
||||
<t t-set="_fp_spent" t-value="(fp_spent_by_id or {}).get(sub.id, 0.0)"/>
|
||||
<span t-if="_fp_alloc or _fp_spent" class="badge bg-info text-dark ms-2 small"
|
||||
title="Spent / Allocated">
|
||||
<i class="fa fa-clock-o me-1"/>
|
||||
<t t-esc="'%.1fh' % (_fp_spent or 0)"/>
|
||||
<t t-if="_fp_alloc"> / <t t-esc="'%.1fh' % _fp_alloc"/></t>
|
||||
</span>
|
||||
<span t-if="sub.state in (fp_done_states or ())"
|
||||
class="badge bg-success ms-2">
|
||||
<i class="fa fa-check me-1"/> Done
|
||||
</span>
|
||||
<span t-elif="sub.state == '02_changes_requested'"
|
||||
class="badge bg-warning text-dark ms-2">
|
||||
<i class="fa fa-exclamation-circle me-1"/> Changes
|
||||
</span>
|
||||
<span t-elif="sub.stage_id" class="badge bg-light text-dark border ms-2">
|
||||
<t t-esc="sub.stage_id.name"/>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
Reference in New Issue
Block a user