Reestruturação dos arquivos e pastas do código da extensão
This commit is contained in:
633
src/views/manage-time-view.js
Normal file
633
src/views/manage-time-view.js
Normal file
@@ -0,0 +1,633 @@
|
||||
(() => {
|
||||
function create(dependencies) {
|
||||
const {
|
||||
appState,
|
||||
escapeHtml,
|
||||
cleanString,
|
||||
normalizeString,
|
||||
formatDateInputDisplay,
|
||||
getDateInputValue,
|
||||
formatHours,
|
||||
getWeekStartDate,
|
||||
getLocalDateKey,
|
||||
getTimeManagementDayKey,
|
||||
isTimeManagementSaveEnabled,
|
||||
getTimeManagementRows,
|
||||
getTimeManagementActivityOptions,
|
||||
findTimeManagementServiceRequestByNumber,
|
||||
formatTimeManagementServiceRequestLabel,
|
||||
isNonServiceRequestSrNumber,
|
||||
findOptionValue,
|
||||
findOptionLabel,
|
||||
getTimeManagementTaskTypeOptions,
|
||||
formatTimeManagementHourPayloadValue,
|
||||
} = dependencies;
|
||||
|
||||
function renderTimeManagementModal() {
|
||||
const modal = appState.timeManagementModal;
|
||||
|
||||
if (!modal) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const selectedDate = modal.selectedDate || getDateInputValue(new Date());
|
||||
const selectedDateLabel = formatDateInputDisplay(selectedDate);
|
||||
const canSaveTimeManagement = isTimeManagementSaveEnabled(modal);
|
||||
const isSavingTimeManagement = Boolean(modal.isSaving);
|
||||
|
||||
return `
|
||||
<div class="arch-panel-extension-detail-overlay">
|
||||
<div class="arch-panel-extension-time-management-dialog" role="document">
|
||||
<header class="arch-panel-extension-detail-head">
|
||||
<div>
|
||||
<p class="arch-panel-extension-panel-label">Horas apontadas</p>
|
||||
<h3 class="arch-panel-extension-panel-title">Manage time</h3>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="arch-panel-extension-time-management-toolbar">
|
||||
<label class="arch-panel-extension-form-field">
|
||||
<span>Selected date</span>
|
||||
<span class="arch-panel-extension-date-picker">
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-date-step-button"
|
||||
data-action="time-management-date-week-prev"
|
||||
aria-label="Previous week"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true" focusable="false">
|
||||
<path d="M12.78 4.22a.75.75 0 0 1 0 1.06L8.06 10l4.72 4.72a.75.75 0 1 1-1.06 1.06l-5.25-5.25a.75.75 0 0 1 0-1.06l5.25-5.25a.75.75 0 0 1 1.06 0Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<span class="arch-panel-extension-date-input-shell">
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-date-picker-button"
|
||||
data-action="open-time-management-date-picker"
|
||||
aria-label="Open calendar"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true" focusable="false">
|
||||
<path d="M7 2a1 1 0 0 1 1 1v1h8V3a1 1 0 1 1 2 0v1h1.25A2.75 2.75 0 0 1 22 6.75v11.5A2.75 2.75 0 0 1 19.25 21H4.75A2.75 2.75 0 0 1 2 18.25V6.75A2.75 2.75 0 0 1 4.75 4H6V3a1 1 0 0 1 1-1ZM4.5 9.5v8.75c0 .69.56 1.25 1.25 1.25h12.5c.69 0 1.25-.56 1.25-1.25V9.5h-15Zm1.25-4c-.69 0-1.25.56-1.25 1.25V8h15V6.75c0-.69-.56-1.25-1.25-1.25H17v.5a1 1 0 1 1-2 0v-.5H9v.5a1 1 0 0 1-2 0v-.5H5.75Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
<input
|
||||
type="text"
|
||||
value="${escapeHtml(selectedDateLabel)}"
|
||||
aria-label="Selected date"
|
||||
readonly
|
||||
>
|
||||
${renderTimeManagementDatePicker(selectedDate)}
|
||||
</span>
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-date-step-button"
|
||||
data-action="time-management-date-week-next"
|
||||
aria-label="Next week"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true" focusable="false">
|
||||
<path d="M7.22 15.78a.75.75 0 0 1 0-1.06L11.94 10 7.22 5.28a.75.75 0 0 1 1.06-1.06l5.25 5.25a.75.75 0 0 1 0 1.06l-5.25 5.25a.75.75 0 0 1-1.06 0Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</span>
|
||||
</label>
|
||||
${renderTimeManagementHourIndicators(modal)}
|
||||
</div>
|
||||
|
||||
${renderTimeManagementBody(modal)}
|
||||
|
||||
<div class="arch-panel-extension-detail-actions">
|
||||
<div class="arch-panel-extension-time-management-footer-actions">
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-utility-button"
|
||||
data-action="time-management-add-sr"
|
||||
>
|
||||
Add SR
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-utility-button"
|
||||
data-action="time-management-add-non-service"
|
||||
>
|
||||
Add (Non-service)
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-utility-button is-outline"
|
||||
data-action="time-management-save"
|
||||
${canSaveTimeManagement && !isSavingTimeManagement ? "" : "disabled"}
|
||||
>
|
||||
${isSavingTimeManagement ? "Gravando..." : "Gravar"}
|
||||
</button>
|
||||
</div>
|
||||
${
|
||||
modal.saveErrorMessage
|
||||
? `<p class="arch-panel-extension-time-management-save-error">${escapeHtml(modal.saveErrorMessage)}</p>`
|
||||
: ""
|
||||
}
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-close"
|
||||
data-action="close-time-management"
|
||||
aria-label="Fechar apontamento de horas"
|
||||
>
|
||||
Fechar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementBody(modal) {
|
||||
if (modal.status === "loading") {
|
||||
return renderTimeManagementSkeleton();
|
||||
}
|
||||
|
||||
if (modal.status === "error") {
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-card is-error">
|
||||
<p class="arch-panel-extension-summary-label">Erro na requisição</p>
|
||||
<p class="arch-panel-extension-detail-subtitle">${escapeHtml(
|
||||
modal.errorMessage || "Não foi possÃvel carregar o apontamento."
|
||||
)}</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const rows = getTimeManagementRows(modal);
|
||||
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-card">
|
||||
${renderTimeManagementHeaderRow(modal.weekDays || [])}
|
||||
<div class="arch-panel-extension-time-management-rows">
|
||||
${
|
||||
rows.length
|
||||
? rows.map((row, index) => renderTimeManagementEntryRow(modal, row, index)).join("")
|
||||
: `<div class="arch-panel-extension-empty-state arch-panel-extension-time-management-empty">
|
||||
Nenhum apontamento de hora neste período
|
||||
</div>`
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementHeaderRow(weekDays) {
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-header-row" aria-hidden="true">
|
||||
<span class="arch-panel-extension-time-management-column-label">Service Request</span>
|
||||
<span class="arch-panel-extension-time-management-column-label">Activity</span>
|
||||
<span class="arch-panel-extension-time-management-column-label">Task Type</span>
|
||||
${(weekDays || []).map((day) => renderTimeManagementDayHeader(day)).join("")}
|
||||
<span class="arch-panel-extension-time-management-column-label" aria-label="Actions"></span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementDayHeader(day) {
|
||||
return `
|
||||
<span class="arch-panel-extension-time-management-day-head">
|
||||
<strong>${escapeHtml(day.dayLabel || "-")}</strong>
|
||||
<span>${escapeHtml(day.monthLabel || day.date || "-")}</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementEntryRow(modal, row, index) {
|
||||
const rowId = cleanString(row?.id) || `row-${index}`;
|
||||
const isHighlighted =
|
||||
cleanString(modal?.highlightRowId) &&
|
||||
cleanString(modal?.highlightRowId) === rowId;
|
||||
const selectedSr = cleanString(row?.selectedSrNumber);
|
||||
const activityOptions = getTimeManagementActivityOptions(modal, selectedSr);
|
||||
const selectedSrOption = findTimeManagementServiceRequestByNumber(
|
||||
modal,
|
||||
selectedSr
|
||||
);
|
||||
const isNonServiceRequest = isNonServiceRequestSrNumber(selectedSr);
|
||||
const selectedSrLabel =
|
||||
selectedSrOption?.label ||
|
||||
formatTimeManagementServiceRequestLabel(selectedSrOption) ||
|
||||
selectedSr;
|
||||
const selectedActivityValue =
|
||||
findOptionValue(activityOptions, row?.selectedActivityValue) ||
|
||||
cleanString(row?.selectedActivityValue) ||
|
||||
"";
|
||||
const selectedActivityLabel =
|
||||
findOptionLabel(activityOptions, selectedActivityValue) || selectedActivityValue;
|
||||
const taskTypeOptions = getTimeManagementTaskTypeOptions(modal, selectedSr);
|
||||
const selectedTaskTypeValue =
|
||||
findOptionValue(taskTypeOptions, row?.selectedTaskTypeValue) ||
|
||||
cleanString(row?.selectedTaskTypeValue) ||
|
||||
"";
|
||||
const selectedTaskTypeLabel =
|
||||
findOptionLabel(taskTypeOptions, selectedTaskTypeValue) || selectedTaskTypeValue;
|
||||
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-entry-row is-compact${isHighlighted ? " is-duplicate-highlight" : ""}" data-row-id="${escapeHtml(rowId)}">
|
||||
<input type="hidden" name="timeEntryId" value="${escapeHtml(row?.timeEntryId || "")}">
|
||||
<label class="arch-panel-extension-form-field">
|
||||
${renderSearchableCombobox({
|
||||
field: "sr",
|
||||
rowId,
|
||||
selectedValue: selectedSr,
|
||||
value: selectedSrLabel,
|
||||
placeholder: "Search SR",
|
||||
options: modal.serviceRequests || [],
|
||||
valueKey: "srNumber",
|
||||
labelKey: "label",
|
||||
fallbackLabelKey: "srTitle",
|
||||
disabled: isNonServiceRequest,
|
||||
})}
|
||||
</label>
|
||||
<label class="arch-panel-extension-form-field">
|
||||
${renderSearchableCombobox({
|
||||
field: "activity",
|
||||
rowId,
|
||||
selectedValue: selectedActivityValue,
|
||||
value: selectedActivityLabel,
|
||||
placeholder: "Search activity",
|
||||
options: activityOptions,
|
||||
valueKey: "value",
|
||||
labelKey: "label",
|
||||
metaKey: "value",
|
||||
disabled: isNonServiceRequest,
|
||||
})}
|
||||
</label>
|
||||
<label class="arch-panel-extension-form-field">
|
||||
${renderSearchableCombobox({
|
||||
field: "taskType",
|
||||
rowId,
|
||||
selectedValue: selectedTaskTypeValue,
|
||||
value: selectedTaskTypeLabel,
|
||||
placeholder: "Search task type",
|
||||
options: taskTypeOptions,
|
||||
valueKey: "value",
|
||||
labelKey: "label",
|
||||
})}
|
||||
</label>
|
||||
${(modal.weekDays || [])
|
||||
.map((day) =>
|
||||
renderTimeManagementDayInput(day, rowId, row?.dayHours)
|
||||
)
|
||||
.join("")}
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-time-management-delete"
|
||||
data-action="time-management-remove-row"
|
||||
data-row-id="${escapeHtml(rowId)}"
|
||||
aria-label="Remover linha de apontamento"
|
||||
title="Remover linha"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" aria-hidden="true" focusable="false">
|
||||
<path d="M7.5 2.5A1.5 1.5 0 0 1 9 1h2a1.5 1.5 0 0 1 1.5 1.5V3H16a.75.75 0 0 1 0 1.5h-.72l-.67 11.33A2.25 2.25 0 0 1 12.36 18H7.64a2.25 2.25 0 0 1-2.25-2.17L4.72 4.5H4A.75.75 0 0 1 4 3h3.5v-.5ZM9 3h2v-.5H9V3Zm-2.78 1.5.66 11.24c.02.42.36.76.76.76h4.72c.4 0 .74-.34.76-.76l.66-11.24H6.22Zm2.53 2.25c.41 0 .75.34.75.75v6a.75.75 0 0 1-1.5 0v-6c0-.41.34-.75.75-.75Zm3.25.75a.75.75 0 0 0-1.5 0v6a.75.75 0 0 0 1.5 0v-6Z"></path>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementSkeleton() {
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-card" aria-busy="true">
|
||||
<div class="arch-panel-extension-time-management-header-row">
|
||||
${["Service Request", "Activity", "Task Type"]
|
||||
.map(
|
||||
(label) =>
|
||||
`<span class="arch-panel-extension-time-management-column-label">${label}</span>`
|
||||
)
|
||||
.join("")}
|
||||
${Array.from({ length: 7 }, () => `
|
||||
<span class="arch-panel-extension-time-management-day-head">
|
||||
<span class="arch-panel-extension-skeleton arch-panel-extension-skeleton-title"></span>
|
||||
<span class="arch-panel-extension-skeleton arch-panel-extension-skeleton-text"></span>
|
||||
</span>
|
||||
`).join("")}
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="arch-panel-extension-time-management-entry-row">
|
||||
${Array.from({ length: 3 }, () => `
|
||||
<div class="arch-panel-extension-form-field">
|
||||
<span class="arch-panel-extension-skeleton arch-panel-extension-skeleton-control"></span>
|
||||
</div>
|
||||
`).join("")}
|
||||
${Array.from({ length: 7 }, () => `
|
||||
<div class="arch-panel-extension-time-management-day">
|
||||
<span class="arch-panel-extension-skeleton arch-panel-extension-skeleton-input"></span>
|
||||
</div>
|
||||
`).join("")}
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderSearchableCombobox({
|
||||
field,
|
||||
rowId,
|
||||
selectedValue,
|
||||
value,
|
||||
placeholder,
|
||||
options,
|
||||
valueKey,
|
||||
labelKey,
|
||||
fallbackLabelKey,
|
||||
metaKey,
|
||||
disabled = false,
|
||||
}) {
|
||||
const safeField = escapeHtml(field);
|
||||
const safeRowId = escapeHtml(rowId || "");
|
||||
const isDisabled = Boolean(disabled);
|
||||
const normalizedSelectedValue = normalizeString(selectedValue);
|
||||
const normalizedOptions = (options || [])
|
||||
.map((item) => ({
|
||||
value: cleanString(item?.[valueKey]),
|
||||
label:
|
||||
cleanString(item?.[labelKey]) ||
|
||||
cleanString(item?.[fallbackLabelKey]) ||
|
||||
cleanString(item?.[valueKey]),
|
||||
meta: cleanString(item?.[metaKey]),
|
||||
}))
|
||||
.filter((item) => item.value || item.label);
|
||||
|
||||
return `
|
||||
<span class="arch-panel-extension-combobox${isDisabled ? " is-disabled" : ""}" data-combobox-field="${safeField}" data-row-id="${safeRowId}">
|
||||
<span class="arch-panel-extension-combobox-value" title="${escapeHtml(value || "")}">
|
||||
${escapeHtml(value || placeholder || "")}
|
||||
</span>
|
||||
<input
|
||||
type="search"
|
||||
value=""
|
||||
placeholder="${escapeHtml(placeholder || "")}"
|
||||
data-combobox-field="${safeField}"
|
||||
data-row-id="${safeRowId}"
|
||||
autocomplete="off"
|
||||
spellcheck="false"
|
||||
role="combobox"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded="false"
|
||||
${isDisabled ? "disabled" : ""}
|
||||
>
|
||||
<span class="arch-panel-extension-combobox-arrow" aria-hidden="true">
|
||||
<svg viewBox="0 0 20 20" focusable="false">
|
||||
<path d="M5.23 7.21a.75.75 0 0 1 1.06.02L10 11.16l3.71-3.93a.75.75 0 1 1 1.08 1.04l-4.25 4.5a.75.75 0 0 1-1.08 0l-4.25-4.5a.75.75 0 0 1 .02-1.06Z"></path>
|
||||
</svg>
|
||||
</span>
|
||||
<span class="arch-panel-extension-combobox-menu" role="listbox">
|
||||
${
|
||||
normalizedOptions.length
|
||||
? normalizedOptions
|
||||
.map(
|
||||
(item, index) => {
|
||||
const isSelected =
|
||||
normalizedSelectedValue &&
|
||||
normalizeString(item.value) === normalizedSelectedValue;
|
||||
const meta =
|
||||
item.meta && normalizeString(item.meta) !== normalizeString(item.label)
|
||||
? item.meta
|
||||
: "";
|
||||
const optionId = `arch-panel-extension-combobox-${safeField}-${index}`;
|
||||
|
||||
return `
|
||||
<button
|
||||
id="${optionId}"
|
||||
type="button"
|
||||
class="arch-panel-extension-combobox-option${isSelected ? " is-selected" : ""}"
|
||||
data-action="time-management-combobox-option"
|
||||
data-field="${safeField}"
|
||||
data-row-id="${safeRowId}"
|
||||
data-value="${escapeHtml(item.value)}"
|
||||
data-search="${escapeHtml(`${item.label} ${item.value}`.toLowerCase())}"
|
||||
role="option"
|
||||
aria-selected="${isSelected ? "true" : "false"}"
|
||||
>
|
||||
<span class="arch-panel-extension-combobox-option-main">
|
||||
${escapeHtml(item.label || item.value)}
|
||||
</span>
|
||||
${
|
||||
meta
|
||||
? `<span class="arch-panel-extension-combobox-option-meta">${escapeHtml(meta)}</span>`
|
||||
: ""
|
||||
}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
)
|
||||
.join("")
|
||||
: `<span class="arch-panel-extension-combobox-empty">No options</span>`
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementDatePicker(selectedDate) {
|
||||
const picker = appState.timeManagementDatePicker;
|
||||
|
||||
if (!picker.isOpen) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const monthStart = new Date(picker.year, picker.month, 1);
|
||||
const calendarStart = getWeekStartDate(monthStart);
|
||||
const selectedDateKey = getLocalDateKey(selectedDate);
|
||||
const todayKey = getLocalDateKey(new Date());
|
||||
const monthLabel = new Intl.DateTimeFormat("en-US", {
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(monthStart);
|
||||
const days = Array.from({ length: 42 }, (_, index) => {
|
||||
const date = new Date(calendarStart);
|
||||
date.setDate(calendarStart.getDate() + index);
|
||||
const dateKey = getLocalDateKey(date);
|
||||
const className = [
|
||||
"arch-panel-extension-date-picker-day",
|
||||
date.getMonth() === picker.month ? "" : "is-muted",
|
||||
dateKey === todayKey ? "is-today" : "",
|
||||
dateKey === selectedDateKey ? "is-selected" : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return `
|
||||
<button
|
||||
type="button"
|
||||
class="${className}"
|
||||
data-action="time-management-date-picker-select"
|
||||
data-date-value="${escapeHtml(dateKey)}"
|
||||
>
|
||||
${date.getDate()}
|
||||
</button>
|
||||
`;
|
||||
});
|
||||
|
||||
return `
|
||||
<div class="arch-panel-extension-date-picker-popover">
|
||||
<div class="arch-panel-extension-date-picker-head">
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-calendar-nav"
|
||||
data-action="time-management-date-picker-prev"
|
||||
aria-label="Previous month"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
<strong>${escapeHtml(monthLabel)}</strong>
|
||||
<button
|
||||
type="button"
|
||||
class="arch-panel-extension-calendar-nav"
|
||||
data-action="time-management-date-picker-next"
|
||||
aria-label="Next month"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
</div>
|
||||
<div class="arch-panel-extension-date-picker-weekdays">
|
||||
${["S", "M", "T", "W", "T", "F", "S"].map((day) => `<span>${day}</span>`).join("")}
|
||||
</div>
|
||||
<div class="arch-panel-extension-date-picker-grid">
|
||||
${days.join("")}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function renderTimeManagementHourIndicators(modal) {
|
||||
if (modal?.status === "loading") {
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-hour-indicators" aria-label="Resumo de horas apontadas" aria-busy="true">
|
||||
${Array.from({ length: 4 }, () => `
|
||||
<span class="arch-panel-extension-time-management-hour-indicator">
|
||||
<span class="arch-panel-extension-skeleton arch-panel-extension-skeleton-text"></span>
|
||||
<strong class="arch-panel-extension-skeleton arch-panel-extension-skeleton-title"></strong>
|
||||
</span>
|
||||
`).join("")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return `
|
||||
<div class="arch-panel-extension-time-management-hour-indicators" aria-label="Resumo de horas apontadas">
|
||||
${buildTimeManagementHourIndicators(modal)
|
||||
.map(
|
||||
(indicator) => `
|
||||
<span class="arch-panel-extension-time-management-hour-indicator">
|
||||
<span>${escapeHtml(indicator.label)}</span>
|
||||
<strong>${escapeHtml(formatHours(indicator.value))}</strong>
|
||||
</span>
|
||||
`
|
||||
)
|
||||
.join("")}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function buildTimeManagementHourIndicators(modal) {
|
||||
const totals = getTimeManagementRows(modal).reduce(
|
||||
(accumulator, row) => {
|
||||
const rowTotal = getTimeManagementRowTotalHours(row);
|
||||
const category = getTimeManagementHourCategory(modal, row);
|
||||
|
||||
accumulator[category] += rowTotal;
|
||||
accumulator.total += rowTotal;
|
||||
|
||||
return accumulator;
|
||||
},
|
||||
{
|
||||
service: 0,
|
||||
nonService: 0,
|
||||
timeOff: 0,
|
||||
total: 0,
|
||||
}
|
||||
);
|
||||
|
||||
return [
|
||||
{ label: "Service", value: totals.service },
|
||||
{ label: "Non Service", value: totals.nonService },
|
||||
{ label: "Time Off", value: totals.timeOff },
|
||||
{ label: "Total hours", value: totals.total },
|
||||
];
|
||||
}
|
||||
|
||||
function getTimeManagementRowTotalHours(row) {
|
||||
return Object.values(row?.dayHours || {}).reduce(
|
||||
(sum, value) => sum + formatTimeManagementHourPayloadValue(value),
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function getTimeManagementHourCategory(modal, row) {
|
||||
if (isTimeManagementTimeOffRow(modal, row)) {
|
||||
return "timeOff";
|
||||
}
|
||||
|
||||
return isNonServiceRequestSrNumber(row?.selectedSrNumber)
|
||||
? "nonService"
|
||||
: "service";
|
||||
}
|
||||
|
||||
function isTimeManagementTimeOffRow(modal, row) {
|
||||
const taskTypeValue = cleanString(row?.selectedTaskTypeValue);
|
||||
const taskType = findTimeManagementTaskTypeByValue(modal, taskTypeValue);
|
||||
const candidates = [
|
||||
taskTypeValue,
|
||||
taskType?.label,
|
||||
taskType?.tags,
|
||||
].map((value) => normalizeString(value));
|
||||
|
||||
return candidates.some(
|
||||
(value) =>
|
||||
value === "TIME_OFF" ||
|
||||
value === "TIMEOFF" ||
|
||||
value.includes("TIME_OFF") ||
|
||||
value.includes("TIME OFF")
|
||||
);
|
||||
}
|
||||
|
||||
function findTimeManagementTaskTypeByValue(modal, value) {
|
||||
const normalizedValue = normalizeString(value);
|
||||
|
||||
if (!normalizedValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (modal?.taskTypes || []).find(
|
||||
(item) => normalizeString(item?.value) === normalizedValue
|
||||
) || null;
|
||||
}
|
||||
|
||||
function renderTimeManagementDayInput(
|
||||
day,
|
||||
rowId = "",
|
||||
dayHours = {}
|
||||
) {
|
||||
const dateKey = getTimeManagementDayKey(day);
|
||||
const value = cleanString(dayHours?.[dateKey]);
|
||||
|
||||
return `
|
||||
<label class="arch-panel-extension-time-management-day">
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
step="1"
|
||||
value="${escapeHtml(value)}"
|
||||
placeholder="0"
|
||||
data-action="time-management-hours-change"
|
||||
data-row-id="${escapeHtml(rowId || "")}"
|
||||
data-date-key="${escapeHtml(dateKey)}"
|
||||
aria-label="Horas apontadas em ${escapeHtml(day.date || "")}"
|
||||
>
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
|
||||
return {
|
||||
renderTimeManagementModal,
|
||||
};
|
||||
}
|
||||
|
||||
globalThis.ArchPanelManageTimeView = Object.freeze({
|
||||
create,
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user