Rapport_React/src/views/adherencepobdrivereport/helpers.js

239 lines
7.6 KiB
JavaScript

export const REPORT_TIMEOUT = 120000;
const REPORT_START = new Date(2026, 3, 1);
export const ABM_IDENTITY = [
'ZBM',
'RBM',
'ABM HQ',
'HQ Div',
'ABM Name',
'Emp ID',
'Reporting Manager (RBM)',
'Email',
'Approved Count per ABM',
'Remarks',
];
export const ABM_SUMMARY = [
'Working Days',
'Daily Target (POBs = Approved Count)',
'Month Target (POBs)',
'Total TMs Qualified (≥₹15k POB)',
'Achievement %',
'Total Chemists Qualified (≥₹15k POB)',
'Days Daily Target Met',
'Target Status',
];
export const TOTAL_TMS = 'Total TMs Qualified (≥₹15k POB)';
export const TOTAL_CHEMISTS = 'Total Chemists Qualified (≥₹15k POB)';
export const TAB_LABELS = {
abm: 'Details ABM wise',
region: 'Region Daily Winners',
territory: 'Territory Daily Winners',
};
function monthLabel(date) {
return date.toLocaleString('en-US', { month: 'long', year: 'numeric' });
}
export function currentMonth() {
return monthLabel(new Date());
}
export function reportMonths(now = new Date()) {
const end = new Date(now.getFullYear(), now.getMonth(), 1);
const months = [];
for (let cursor = new Date(REPORT_START); cursor <= end; cursor = new Date(cursor.getFullYear(), cursor.getMonth() + 1, 1)) {
months.push(monthLabel(cursor));
}
return months.length ? months : [currentMonth()];
}
export function canViewReport({ domain, permissions = [], designation }) {
if (domain !== 'team2') return false;
if (permissions.includes('all_data_access')) return true;
const value = String(designation ?? '').trim().toUpperCase();
return value === 'HO' || value === 'ADMIN';
}
export function exportFilename(month) {
const slug = String(month).replace(/[^A-Za-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
return `Adherence-Pack-POB-Drive-${slug || 'report'}.xlsx`;
}
function isNumeric(value) {
if (typeof value === 'number') return Number.isFinite(value);
if (typeof value !== 'string' || value.trim() === '') return false;
return Number.isFinite(Number(value));
}
export function field(row, key) {
if (!row || typeof row !== 'object') return '';
return row[key] ?? '';
}
function listRows(data, key) {
const rows = field(data, key);
if (Array.isArray(rows)) return rows;
if (rows && typeof rows === 'object') return Object.values(rows);
return [];
}
function orderedDays(days) {
if (!days || typeof days !== 'object') return [];
const list = [];
Object.entries(days).forEach(([label, day]) => {
if (!day || typeof day !== 'object') return;
list.push({
label: String(label),
date: String(day.date ?? ''),
type: String(day.type ?? 'W'),
value: day.value ?? 0,
chemistsQualified: day.chemistsQualified ?? 0,
});
});
return list.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));
}
function dayMap(days) {
const map = {};
orderedDays(days).forEach((day) => {
if (day.date !== '') map[day.date] = day;
});
return map;
}
export function dayValue(day, key) {
if (day?.type === 'H' || day?.[key] === 'Holiday') return 'Holiday';
const value = day?.[key] ?? 0;
return isNumeric(value) ? Number(value) : value;
}
export function dayHeader(label) {
const match = String(label).trim().match(/^(\S+)\s+([\s\S]+)$/);
return match ? `${match[1]}\n${match[2]}` : String(label);
}
export function isVacantName(name) {
return String(name ?? '').trim().toLowerCase().includes('vacant');
}
function dayTemplate(lists) {
for (const rows of lists) {
if (!rows.length) continue;
const days = orderedDays(field(rows[0], 'days'));
if (days.length) return days;
}
return [];
}
export function regionColumns(template) {
const columns = [];
template.forEach((day) => {
const header = dayHeader(day.label);
if (day.type === 'H') {
columns.push({ kind: 'holiday', type: 'H', date: day.date, header, day });
return;
}
columns.push({ kind: 'tm', type: 'W', date: day.date, header, day });
columns.push({ kind: 'chemist', type: 'W', date: day.date, header, day });
});
columns.push({ kind: 'total-tm', type: '', header: TOTAL_TMS });
columns.push({ kind: 'total-chemist', type: '', header: TOTAL_CHEMISTS });
return columns;
}
export function regionCell(row, column, days) {
if (column.kind === 'total-tm') return field(row, TOTAL_TMS);
if (column.kind === 'total-chemist') return field(row, TOTAL_CHEMISTS);
if (column.kind === 'holiday') return 'Holiday';
const day = days[column.date] ?? column.day;
return dayValue(day, column.kind === 'chemist' ? 'chemistsQualified' : 'value');
}
export function buildReport(data) {
const abmRows = listRows(data, 'abmReport');
const rbmRows = listRows(data, 'rbmReport');
const tmRows = listRows(data, 'tmReport');
const template = dayTemplate([abmRows, rbmRows, tmRows]);
return { abmRows, rbmRows, tmRows, template, dayMap };
}
function abmSection(report) {
const { abmRows, template } = report;
const dayTypes = [
...ABM_IDENTITY.map(() => ''),
...template.map((day) => (day.type === 'H' ? 'H' : 'W')),
...ABM_SUMMARY.map(() => ''),
];
const headers = [...ABM_IDENTITY, ...template.map((day) => dayHeader(day.label)), ...ABM_SUMMARY];
const rows = abmRows.map((row) => {
const days = dayMap(field(row, 'days'));
return {
cells: [
...ABM_IDENTITY.map((key) => field(row, key)),
...template.map((day) => dayValue(days[day.date] ?? day, 'value')),
...ABM_SUMMARY.map((key) => field(row, key)),
],
vacant: isVacantName(field(row, 'ABM HQ')) || isVacantName(field(row, 'ABM Name')),
};
});
return { id: 'abm', dayTypes, headers, rows };
}
function regionSection(report) {
const columns = regionColumns(report.template);
const dayTypes = ['', '', '', ...columns.map((column) => column.type)];
const headers = ['Region (RBM)', 'RBM Name', 'RBM Employee Code', ...columns.map((column) => column.header)];
const rows = report.rbmRows.map((row) => {
const days = dayMap(field(row, 'days'));
const cells = [
field(row, 'Region (RBM)'),
field(row, 'RBM Name'),
field(row, 'RBM Employee Code'),
...columns.map((column) => regionCell(row, column, days)),
];
return { cells, vacant: isVacantName(cells[0]) || isVacantName(cells[1]) };
});
return { id: 'region', dayTypes, headers, rows };
}
function territorySection(report) {
const { tmRows, template } = report;
const dayTypes = ['', '', '', ...template.map((day) => (day.type === 'H' ? 'H' : 'W')), ''];
const headers = ['Territory', 'TM Name', 'TM Employee Code', ...template.map((day) => dayHeader(day.label)), TOTAL_CHEMISTS];
const rows = tmRows.map((row) => {
const days = dayMap(field(row, 'days'));
const cells = [
field(row, 'Territory'),
field(row, 'TM Name'),
field(row, 'TM Employee Code'),
...template.map((day) => dayValue(days[day.date] ?? day, 'value')),
field(row, TOTAL_CHEMISTS),
];
return { cells, vacant: isVacantName(cells[0]) || isVacantName(cells[1]) };
});
return { id: 'territory', dayTypes, headers, rows };
}
export function buildSections(report) {
return [abmSection(report), regionSection(report), territorySection(report)];
}
export function headerClass(section, index) {
const type = section.dayTypes[index] ?? '';
if (type === 'H') return 'pob-day-h';
if (type === 'W') return 'pob-day-w';
const firstDay = section.dayTypes.findIndex((value) => value === 'W' || value === 'H');
return firstDay !== -1 && index < firstDay ? 'pob-identity' : 'pob-summary';
}
export function cellClass(section, row, cell, index) {
if (row.vacant) return '';
const type = section.dayTypes[index] ?? '';
return type === 'H' || cell === 'Holiday' ? 'pob-day-h' : '';
}