Skip to content

Commit

Permalink
feat: download filtered csv (#83)
Browse files Browse the repository at this point in the history
* add download CSV button to group by section

* feat: added downloading groupBy CSV

* feat: replaced project CSV request with local CSV  generation
  • Loading branch information
devFra authored Sep 16, 2024
1 parent 4cae6a9 commit a344216
Show file tree
Hide file tree
Showing 8 changed files with 138 additions and 64 deletions.
59 changes: 37 additions & 22 deletions src/components/report/GropuByList.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { component$, useSignal } from '@builder.io/qwik';
import { component$, Signal, useSignal } from '@builder.io/qwik';
import { ReportTimeEntry } from '@models/report';
import { useGroupList } from 'src/hooks/report/useGroupList';
import { t } from 'src/locale/labels';
import { getReportTotalHours } from 'src/utils/chart';
import { getFormattedHours } from 'src/utils/timesheet';
import { UUID } from 'src/utils/uuid';
import { Button } from '../Button';
import { Select } from '../form/Select';
import { getIcon } from '../icons';

interface GroupByListProps {
data: ReportTimeEntry[];
from: Signal<Date>;
to: Signal<Date>;
}

export const GroupByList = component$<GroupByListProps>(({ data }) => {
export const GroupByList = component$<GroupByListProps>(({ data, from, to }) => {
const {
results,
valueL1Selected,
Expand All @@ -21,37 +25,48 @@ export const GroupByList = component$<GroupByListProps>(({ data }) => {
onChangeGroupL2,
onChangeGroupL3,
selectOptions,
} = useGroupList(data);
handlerDownloadCSV,
} = useGroupList(data, from, to);

const _selectOptions = useSignal(selectOptions);

return (
<div class='border-sourface-20 border-t py-3'>
<div class='flex flex-row items-center gap-2'>
<span>{t('GROUP_BY_LABEL')}</span>
<div class='flex items-center gap-2 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex items-center gap-2 sm:w-full sm:flex-col md:flex-auto md:flex-row lg:flex-auto lg:flex-row'>
<span>{t('GROUP_BY_LABEL')}</span>

<Select
id={UUID()}
value={valueL1Selected}
options={_selectOptions}
onChange$={onChangeGroupL1}
/>

<Select
id={UUID()}
value={valueL2Selected}
options={_selectOptions}
onChange$={onChangeGroupL2}
/>
<Select
id={UUID()}
value={valueL1Selected}
options={_selectOptions}
onChange$={onChangeGroupL1}
/>

{valueL2Selected.value != t('NONE_LABEL') && (
<Select
id={UUID()}
value={valueL3Selected}
value={valueL2Selected}
options={_selectOptions}
onChange$={onChangeGroupL3}
onChange$={onChangeGroupL2}
/>
)}

{valueL2Selected.value != t('NONE_LABEL') && (
<Select
id={UUID()}
value={valueL3Selected}
options={_selectOptions}
onChange$={onChangeGroupL3}
/>
)}
</div>

<div class='flex flex-none flex-row items-center gap-2'>
<Button variant={'link'} onClick$={handlerDownloadCSV}>
<span class='inline-flex items-start gap-1'>
{getIcon('Downlaod')} {t('REPORT_DOWNLOAD_CSV_LABEL')}
</span>
</Button>
</div>
</div>

<div class='relative overflow-x-auto'>
Expand Down
23 changes: 14 additions & 9 deletions src/components/report/ReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import { Customer } from '@models/customer';
import { ReportTimeEntry } from '@models/report';
import { AppContext } from 'src/app';
import { t } from 'src/locale/labels';
import { downloadReportCSV } from 'src/services/report';

import {
getReportBillableHours,
getReportTotalHours,
getTopCustomer,
getTopProject,
} from 'src/utils/chart';
import { openDownloadCSVDialog } from 'src/utils/csv';
import { formatDateString } from 'src/utils/dates';
import { handlePrint } from 'src/utils/handlePrint';
import { getReportCSV } from 'src/utils/report';
import { getFormattedHours } from 'src/utils/timesheet';
import { CSV_REPORT_PROJECTS_FILE_NAME } from '../../utils/constants';
import { capitalizeString } from '../../utils/string';
import { Button } from '../Button';
import { getIcon } from '../icons';
Expand Down Expand Up @@ -60,7 +63,11 @@ export const ReportHeader = component$<ReportHeaderProps>(
const downloadCSV = $(async () => {
if (from && to) {
appStore.isLoading = true;
await downloadReportCSV(formatDateString(from.value), formatDateString(to.value));
const CSV = await getReportCSV(data);
openDownloadCSVDialog(
CSV,
`${CSV_REPORT_PROJECTS_FILE_NAME}_${formatDateString(from.value)}_${formatDateString(to.value)}`
);
appStore.isLoading = false;
}
});
Expand Down Expand Up @@ -107,13 +114,11 @@ export const ReportHeader = component$<ReportHeaderProps>(
</span>
</Button>

{from && to && (
<Button variant={'link'} onClick$={downloadCSV}>
<span class='inline-flex items-start gap-1'>
{getIcon('Downlaod')} {t('REPORT_DOWNLOAD_CSV_LABEL')}
</span>
</Button>
)}
<Button variant={'link'} onClick$={downloadCSV}>
<span class='inline-flex items-start gap-1'>
{getIcon('Downlaod')} {t('REPORT_DOWNLOAD_CSV_LABEL')}
</span>
</Button>
</div>
</div>
</div>
Expand Down
20 changes: 18 additions & 2 deletions src/hooks/report/useGroupList.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { $, useComputed$, useContext, useSignal } from '@builder.io/qwik';
import { $, Signal, useComputed$, useContext, useSignal } from '@builder.io/qwik';
import { GroupByKeys, ReportTimeEntry } from '@models/report';
import { AppContext } from 'src/app';
import { t } from 'src/locale/labels';
import { groupData } from 'src/utils/chart';
import { CSV_REPORT_GROUPBY_FILE_NAME } from 'src/utils/constants';
import { openDownloadCSVDialog } from 'src/utils/csv';
import { formatDateString } from 'src/utils/dates';
import { getReportGroupByCSV } from 'src/utils/report';

export const useGroupList = (data: ReportTimeEntry[]) => {
export const useGroupList = (data: ReportTimeEntry[], from: Signal<Date>, to: Signal<Date>) => {
const appStore = useContext(AppContext);

const groupKeys = useSignal(['customer', 'project', 'task', 'name', 'date', 'description']);
Expand Down Expand Up @@ -80,6 +84,17 @@ export const useGroupList = (data: ReportTimeEntry[]) => {
];
});

const handlerDownloadCSV = $(async () => {
appStore.isLoading = true;
const CSV = await getReportGroupByCSV(results.value);

openDownloadCSVDialog(
CSV,
`${CSV_REPORT_GROUPBY_FILE_NAME}_${formatDateString(from.value)}_${formatDateString(to.value)}`
);
appStore.isLoading = false;
});

return {
results,
valueL1Selected,
Expand All @@ -89,5 +104,6 @@ export const useGroupList = (data: ReportTimeEntry[]) => {
onChangeGroupL2,
onChangeGroupL3,
selectOptions,
handlerDownloadCSV,
};
};
6 changes: 4 additions & 2 deletions src/pages/Report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ export const Report = component$(() => {
printableComponent={projectReportDetailsRef}
customer={selectedCustomerSig}
data={projectResults.data}
from={from}
to={to}
/>

<ProjectReportDetails
Expand All @@ -175,7 +177,7 @@ export const Report = component$(() => {
/>

{projectResults.data.length > 0 && (
<GroupByList data={projectResults.data} />
<GroupByList data={projectResults.data} from={from} to={to} />
)}
</div>
) : (
Expand All @@ -200,7 +202,7 @@ export const Report = component$(() => {
/>

{projectResults.data.length > 0 && (
<GroupByList data={projectResults.data} />
<GroupByList data={projectResults.data} from={from} to={to} />
)}
</div>
)}
Expand Down
28 changes: 0 additions & 28 deletions src/services/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Project, ProjectType } from '@models/project';
import { ReportProductivityItem, ReportTimeEntry } from '@models/report';
import { TimeEntry } from '@models/timeEntry';
import { getHttpResponse } from 'src/network/httpRequest';
import { CSV_REPORT_PROJECTS_FILE_NAME } from 'src/utils/constants';
import { UUID } from 'src/utils/uuid';

export const getProductivity = async (
Expand Down Expand Up @@ -65,30 +64,3 @@ export const getReportTimeEntry = async (from: string, to: string): Promise<Repo
};
});
};

export const downloadReportCSV = async (from: string, to: string) => {
const response = await getHttpResponse<string>(
{
path: `report/time-entries`,
params: {
from,
to,
format: 'csv',
},
},
'GET',
undefined,
true
);

const blob = new Blob([response as unknown as BlobPart], { type: 'text/csv' });
const a = document.createElement('a');

a.download = `${CSV_REPORT_PROJECTS_FILE_NAME}_${from}_${to}.csv`;
a.href = URL.createObjectURL(blob);
a.click();
setTimeout(() => {
URL.revokeObjectURL(a.href);
a.remove();
}, 200);
};
1 change: 1 addition & 0 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ export const INIT_TASK_VALUE = {
export const REPORT_LIST_RESULTS_PER_PAGE = 10;

export const CSV_REPORT_PROJECTS_FILE_NAME = 'Brickly_projects_report';
export const CSV_REPORT_GROUPBY_FILE_NAME = 'Brickly_groupby_report';
12 changes: 12 additions & 0 deletions src/utils/csv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const openDownloadCSVDialog = (content: string, fileName: string) => {
const blob = new Blob([content as unknown as BlobPart], { type: 'text/csv' });
const a = document.createElement('a');

a.download = `${fileName}.csv`;
a.href = URL.createObjectURL(blob);
a.click();
setTimeout(() => {
URL.revokeObjectURL(a.href);
a.remove();
}, 200);
};
53 changes: 52 additions & 1 deletion src/utils/report.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ProjectType } from '@models/project';
import { BarLegendColor } from '@models/report';
import { BarLegendColor, ReportGroupedData, ReportTimeEntry } from '@models/report';

export const getLegendBarColor = (type: string | ProjectType): BarLegendColor => {
switch (type) {
Expand Down Expand Up @@ -56,3 +56,54 @@ export const getHexFromType = (type: ProjectType) => {
}
}
};

const groupByToCSV = async (data: ReportGroupedData[], fathers: string[]) => {
let csv: string = '';

for (const child of Object.values(data)) {
let row: string = '';

if (fathers.length > 0) row += fathers.join(',') + ',';

row += `${child['key']},${child.duration}\n`;

if (child.subGroups !== undefined && child.subGroups.length > 0) {
row += await groupByToCSV(child.subGroups, [...fathers, child['key']]);
}

csv += row;
}

return csv;
};

export const getReportGroupByCSV = async (data: ReportGroupedData[]): Promise<string> => {
const CSV = await groupByToCSV(data, []);
return CSV;
};

export const getReportCSV = async (data: ReportTimeEntry[]): Promise<string> => {
const CSV = data.reduce((csv: String, entry: ReportTimeEntry) => {
const row =
[
entry.date,
entry.email,
entry.name,
entry.company,
entry.crew,
entry.customer,
entry.task.name,
entry.project.name,
entry.project.type,
entry.project.plannedHours,
entry.hours,
entry.description,
entry.startHour,
entry.endHour,
].join(',') + '\n';

return (csv += row);
}, 'DATE,EMAIL,NAME,COMPANY,CREW,CUSTOMER,PROJECT,TASK,PROJECT TYPE,PLANNED HOURS,HOURS,DESCRIPTION,START HOUR,END HOUR\n');

return CSV;
};

0 comments on commit a344216

Please sign in to comment.