This commit is contained in:
gsinghpal
2026-05-23 07:53:41 -04:00
parent 27e12dd544
commit 005daade55
50 changed files with 3300 additions and 42 deletions

View File

@@ -0,0 +1,198 @@
<?xml version="1.0" encoding="utf-8"?>
<templates xml:space="preserve">
<t t-name="fusion_clock.ShiftPlanner">
<div class="o_action fclk-planner" t-ref="root">
<div class="fclk-planner__toolbar">
<div>
<h2 class="fclk-planner__title">Shift Planner</h2>
<div class="fclk-planner__subtitle"><t t-esc="weekTitle"/></div>
</div>
<div class="fclk-planner__actions">
<button class="btn btn-light" t-on-click="() => this.previousWeek()" t-att-disabled="state.loading or state.saving">
<i class="fa fa-chevron-left"/>
</button>
<button class="btn btn-light" t-on-click="() => this.currentWeek()" t-att-disabled="state.loading or state.saving">This Week</button>
<button class="btn btn-light" t-on-click="() => this.nextWeek()" t-att-disabled="state.loading or state.saving">
<i class="fa fa-chevron-right"/>
</button>
<button class="btn btn-outline-secondary" t-on-click="() => this.copyPreviousWeek()" t-att-disabled="state.loading or state.saving">
<i class="fa fa-copy me-1"/> Copy Previous Week
</button>
<button class="btn btn-outline-secondary" t-on-click="() => this.exportXlsx()" t-att-disabled="state.loading or state.saving">
<i class="fa fa-file-excel-o me-1"/> Export XLSX
</button>
<button class="btn btn-primary" t-on-click="() => this.save()" t-att-disabled="state.loading or state.saving or !state.dirtyCount">
<t t-if="state.saving"><i class="fa fa-spinner fa-spin me-1"/></t>
<t t-else=""><i class="fa fa-save me-1"/></t>
Save
<t t-if="state.dirtyCount">(<t t-esc="state.dirtyCount"/>)</t>
</button>
</div>
</div>
<t t-if="state.error">
<div class="alert alert-danger mx-3 mt-3"><t t-esc="state.error"/></div>
</t>
<t t-if="state.invalidCount">
<div class="fclk-planner__warning">
<i class="fa fa-exclamation-triangle me-1"/>
<t t-esc="state.invalidCount"/> invalid cells need attention.
</div>
</t>
<t t-if="state.loading">
<div class="fclk-planner__loading">
<i class="fa fa-spinner fa-spin fa-2x"/>
<span>Loading shift planner...</span>
</div>
</t>
<t t-if="!state.loading and !state.error">
<div class="fclk-planner__table-wrap">
<table class="fclk-planner__table">
<colgroup>
<col class="fclk-planner__employee-col"/>
<t t-foreach="state.days" t-as="day" t-key="'col_' + day.date">
<col class="fclk-planner__shift-col"/>
<col class="fclk-planner__hours-col"/>
</t>
</colgroup>
<thead>
<tr>
<th class="fclk-planner__employee-head" rowspan="2">Employee</th>
<t t-foreach="state.days" t-as="day" t-key="day.date">
<th class="fclk-planner__day-head" colspan="2">
<div class="fclk-planner__weekday"><t t-esc="day.weekday"/></div>
<div class="fclk-planner__date"><t t-esc="day.label"/></div>
</th>
</t>
</tr>
<tr>
<t t-foreach="state.days" t-as="day" t-key="'sub_' + day.date">
<th class="fclk-planner__sub-head">Shift</th>
<th class="fclk-planner__sub-head fclk-planner__hours-head">Hours</th>
</t>
</tr>
</thead>
<tbody>
<t t-foreach="state.departments" t-as="department" t-key="department.id">
<tr class="fclk-planner__department-row">
<td t-att-colspan="1 + state.days.length * 2">
<button class="fclk-planner__department-toggle" t-on-click="() => this.toggleDepartment(department)">
<i t-att-class="isCollapsed(department) ? 'fa fa-chevron-right' : 'fa fa-chevron-down'"/>
<span><t t-esc="department.name"/></span>
<span class="fclk-planner__department-count">
<t t-esc="department.employee_ids.length"/> employees
</span>
</button>
</td>
</tr>
<t t-if="!isCollapsed(department)">
<t t-foreach="getDepartmentEmployees(department)" t-as="employee" t-key="employee.id">
<tr class="fclk-planner__employee-row">
<td class="fclk-planner__employee-cell">
<div class="fclk-planner__employee-name"><t t-esc="employee.name"/></div>
<div class="fclk-planner__employee-role" t-if="employee.job_title">
<t t-esc="employee.job_title"/>
</div>
</td>
<t t-foreach="state.days" t-as="day" t-key="employee.id + '_' + day.date">
<t t-set="cell" t-value="employee.cells[day.date]"/>
<td t-att-class="'fclk-planner__shift-cell ' + (cell.error ? 'fclk-planner__shift-cell--error ' : '') + (cell.source === 'fallback' ? 'fclk-planner__shift-cell--fallback ' : '') + (this.isActiveCell(employee, day) ? 'fclk-planner__shift-cell--active' : '')"
t-on-click="(ev) => this.openCellEditor(employee, day, ev)">
<input class="fclk-planner__shift-input"
t-att-value="cell.input"
t-att-title="cell.error || cell.label"
t-on-focus="(ev) => this.openCellEditor(employee, day, ev)"
t-on-change="(ev) => this.onCellInput(employee, day, ev)"
t-on-keydown="(ev) => this.onCellKeydown(employee, day, ev)"/>
<div class="fclk-planner__cell-error" t-if="cell.error">
<t t-esc="cell.error"/>
</div>
</td>
<td class="fclk-planner__hours-cell">
<t t-esc="cell.hours_display || '0:00'"/>
</td>
</t>
</tr>
</t>
</t>
</t>
</tbody>
</table>
</div>
<div t-if="state.editor.open"
t-ref="shiftEditor"
class="fclk-planner__cell-editor"
t-att-style="'top: ' + state.editor.top + 'px; left: ' + state.editor.left + 'px;'">
<div class="fclk-planner__editor-head">
<div class="fclk-planner__editor-person">
<div class="fclk-planner__editor-name"><t t-esc="state.editor.employeeName"/></div>
<div class="fclk-planner__editor-day"><t t-esc="state.editor.dayLabel"/></div>
</div>
<div class="fclk-planner__editor-hours">
<span><t t-esc="state.editor.hoursDisplay"/></span>
</div>
</div>
<div class="fclk-planner__quick-grid">
<t t-foreach="quickShiftOptions" t-as="option" t-key="option.key">
<button type="button"
class="fclk-planner__quick-chip"
t-on-click="() => this.selectQuickShift(option)">
<span class="fclk-planner__quick-label"><t t-esc="option.label"/></span>
<span class="fclk-planner__quick-detail"><t t-esc="option.detail"/></span>
</button>
</t>
</div>
<div class="fclk-planner__time-row">
<label class="fclk-planner__time-field">
<span>Start</span>
<select t-on-change="(ev) => this.onEditorStartChange(ev)">
<t t-foreach="timeOptions" t-as="option" t-key="'start_' + option.value">
<option t-att-value="option.value"
t-att-selected="option.value === state.editor.startValue">
<t t-esc="option.label"/>
</option>
</t>
</select>
</label>
<label class="fclk-planner__time-field">
<span>End</span>
<select t-on-change="(ev) => this.onEditorEndChange(ev)">
<t t-foreach="timeOptions" t-as="option" t-key="'end_' + option.value">
<option t-att-value="option.value"
t-att-selected="option.value === state.editor.endValue">
<t t-esc="option.label"/>
</option>
</t>
</select>
</label>
</div>
<div class="fclk-planner__editor-error" t-if="state.editor.error">
<t t-esc="state.editor.error"/>
</div>
<div class="fclk-planner__editor-actions">
<button type="button"
class="btn btn-sm btn-light"
t-on-click="() => this.clearActiveCell()">
<i class="fa fa-eraser me-1"/> Clear
</button>
<button type="button"
class="btn btn-sm btn-primary"
t-on-click="() => this.applyEditorRange(true)">
<i class="fa fa-check me-1"/> Done
</button>
</div>
</div>
</t>
</div>
</t>
</templates>