Skip to content

Commit

Permalink
feat: implementing pdf export
Browse files Browse the repository at this point in the history
  • Loading branch information
andreasz97 committed Sep 27, 2024
1 parent 49bfa1b commit b1ed6b5
Show file tree
Hide file tree
Showing 8 changed files with 249 additions and 173 deletions.
52 changes: 52 additions & 0 deletions src/components/form/OptionDropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { JSXOutput, QRL, component$, useVisibleTask$ } from '@builder.io/qwik';
import { initFlowbite } from 'flowbite';
import { Button } from '../Button';

interface optionDropdownInterface {
id: string;
icon: JSXOutput;
label?: string;
options: { value: string; onChange: QRL }[];
hidden?: boolean;
}

export const OptionDropdown = component$<optionDropdownInterface>(
({ id, icon, label, options, hidden }) => {
const buttonId = `dropdown-menu-icon-button-${id}`;
const dropdownId = `dropdown-dots-${id}`;

useVisibleTask$(() => {
initFlowbite();
});

return (
<form class={['relative w-full', hidden ? 'hidden' : 'block']}>
<Button variant={'link'} id={buttonId} data-dropdown-toggle={dropdownId}>
<span class='inline-flex items-start gap-1'>
{icon}
{label}
</span>
</Button>

<div
id={dropdownId}
class='z-10 hidden w-44 divide-y divide-gray-100 rounded-lg bg-white shadow dark:divide-gray-600 dark:bg-gray-700'
>
<ul
class='py-2 text-sm text-gray-700 dark:text-gray-200'
aria-labelledby={buttonId}
>
{options.map((option) => (
<li
class='block px-4 py-2 hover:bg-gray-100 dark:hover:bg-gray-600 dark:hover:text-white'
onClick$={option.onChange}
>
{option.value}
</li>
))}
</ul>
</div>
</form>
);
}
);
35 changes: 26 additions & 9 deletions src/components/report/GropuByList.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { component$, Signal, 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 { handlePrint } from 'src/utils/handlePrint';
import { getFormattedHours } from 'src/utils/timesheet';
import { UUID } from 'src/utils/uuid';
import { Button } from '../Button';
import { OptionDropdown } from '../form/OptionDropdown';
import { Select } from '../form/Select';
import { getIcon } from '../icons';

Expand All @@ -29,10 +30,16 @@ export const GroupByList = component$<GroupByListProps>(({ data, from, to }) =>
} = useGroupList(data, from, to);

const _selectOptions = useSignal(selectOptions);
const groupByRef = useSignal<HTMLElement>();

return (
<div class='border-sourface-20 border-t py-3'>
<div class='flex items-center gap-2 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div ref={groupByRef} class='border-sourface-20 border-t py-3'>
<div class='brickly-logo-pdf-download flex hidden justify-end'>
<div class='px-6 py-4 sm:text-center [&_svg]:sm:inline'>
{getIcon('BricklyRedLogo')}
</div>
</div>
<div class='hide-on-pdf-download 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>

Expand Down Expand Up @@ -61,11 +68,21 @@ export const GroupByList = component$<GroupByListProps>(({ data, from, to }) =>
</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('Download')} {t('REPORT_DOWNLOAD_CSV_LABEL')}
</span>
</Button>
<OptionDropdown
id='download-dropdown-groupby'
icon={getIcon('Download')}
label={t('REPORT_DOWNLOAD_LABEL')}
options={[
{
value: t('CSV_LABEL'),
onChange: handlerDownloadCSV,
},
{
value: t('PDF_LABEL'),
onChange: $(() => handlePrint(groupByRef)),
},
]}
/>
</div>
</div>

Expand Down
195 changes: 95 additions & 100 deletions src/components/report/ProjectReportDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,121 +19,116 @@ interface ProjectReportDetailsProps {
data: ReportTimeEntry[];
from: Signal<Date>;
to: Signal<Date>;
ref: Signal<HTMLElement | undefined>;
}

export const ProjectReportDetails = component$<ProjectReportDetailsProps>(
({ data, from, to, ref }) => {
const daysSeries = useComputed$(() => {
return columnChartSeriesAdapter(data, from.value, to.value);
});
// ____ PROJECTS _____ //
const groupByProjectSeries = useComputed$(() => {
return donutChartGroupByProjectsAdapter(data);
});

const listGroupByProjectSeries = useComputed$(() => {
return listGroupByProjectsAdapter(data);
});

// ____ USERS _____ //

const groupByUserSeries = useComputed$(() => {
return donutChartGroupByUsesAdapter(data);
});

const listGroupByUserSeries = useComputed$(() => {
return listGroupByUsersAdapter(data);
});

// ____ TASK _____ //

const groupByTaskSeries = useComputed$(() => {
return donutChartGroupByTaskAdapter(data);
});

const listGroupByTaskSeries = useComputed$(() => {
return listGroupByTaskAdapter(data);
});

return (
<div class='flex flex-col divide-y divide-surface-70 p-3' ref={ref}>
<ColumnChart data={daysSeries} />

{/* ____ PROJECTS _____ */}

{groupByProjectSeries.value.series.length > 0 &&
listGroupByProjectSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>
{t('PROJECT_LABEL')}
</h3>
</div>
export const ProjectReportDetails = component$<ProjectReportDetailsProps>(({ data, from, to }) => {
const daysSeries = useComputed$(() => {
return columnChartSeriesAdapter(data, from.value, to.value);
});
// ____ PROJECTS _____ //
const groupByProjectSeries = useComputed$(() => {
return donutChartGroupByProjectsAdapter(data);
});

<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByProjectSeries} />
</div>
</div>
const listGroupByProjectSeries = useComputed$(() => {
return listGroupByProjectsAdapter(data);
});

// ____ USERS _____ //

const groupByUserSeries = useComputed$(() => {
return donutChartGroupByUsesAdapter(data);
});

const listGroupByUserSeries = useComputed$(() => {
return listGroupByUsersAdapter(data);
});

// ____ TASK _____ //

const groupByTaskSeries = useComputed$(() => {
return donutChartGroupByTaskAdapter(data);
});

const listGroupByTaskSeries = useComputed$(() => {
return listGroupByTaskAdapter(data);
});

return (
<div class='flex flex-col divide-y divide-surface-70 p-3'>
<ColumnChart data={daysSeries} />

{/* ____ PROJECTS _____ */}

{groupByProjectSeries.value.series.length > 0 &&
listGroupByProjectSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>{t('PROJECT_LABEL')}</h3>
</div>

<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByProjectSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByProjectSeries} />
</div>
</div>
)}

{/* ____ USERS _____ */}
<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByProjectSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
</div>
</div>
)}

{groupByUserSeries.value.series.length > 0 &&
listGroupByUserSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>{t('USER_LABEL')}</h3>
</div>
{/* ____ USERS _____ */}

<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByUserSeries} />
</div>
</div>
{groupByUserSeries.value.series.length > 0 &&
listGroupByUserSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>{t('USER_LABEL')}</h3>
</div>

<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByUserSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByUserSeries} />
</div>
</div>
)}

{/* ____ TASK _____ */}
<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByUserSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
</div>
</div>
)}

{groupByTaskSeries.value.series.length > 0 &&
listGroupByTaskSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>{t('TASK_LABEL')}</h3>
</div>
{/* ____ TASK _____ */}

<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByTaskSeries} />
</div>
</div>
{groupByTaskSeries.value.series.length > 0 &&
listGroupByTaskSeries.value.length > 0 && (
<div class='flex gap-1 py-6 sm:flex-col md:flex-row md:justify-between lg:flex-row lg:justify-between'>
<div class='flex-none'>
<h3 class='text-xl font-bold text-dark-grey'>{t('TASK_LABEL')}</h3>
</div>

<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByTaskSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
<div class='flex-1 items-center text-center'>
<div class='max-w-lg'>
<DonutChart data={groupByTaskSeries} />
</div>
</div>
)}
</div>
);
}
);

<div class='flex-1 sm:w-full'>
<ReportList
data={listGroupByTaskSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
</div>
</div>
)}
</div>
);
});
57 changes: 27 additions & 30 deletions src/components/report/ProjectReportPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,38 @@ interface ProjectReportPreviewProps {
data: ReportTimeEntry[];
from: Signal<Date>;
to: Signal<Date>;
ref: Signal<HTMLElement | undefined>;
}

export const ProjectReportPreview = component$<ProjectReportPreviewProps>(
({ data, from, to, ref }) => {
const daysSeries = useComputed$(() => {
return columnChartSeriesAdapter(data, from.value, to.value);
});
export const ProjectReportPreview = component$<ProjectReportPreviewProps>(({ data, from, to }) => {
const daysSeries = useComputed$(() => {
return columnChartSeriesAdapter(data, from.value, to.value);
});

// ____ PROJECTS _____ //
const groupByProjectSeries = useComputed$(() => {
return donutChartGroupByProjectsAdapter(data);
});
// ____ PROJECTS _____ //
const groupByProjectSeries = useComputed$(() => {
return donutChartGroupByProjectsAdapter(data);
});

const listGroupByProjectSeries = useComputed$(() => {
return listGroupByProjectsAdapter(data);
});
const listGroupByProjectSeries = useComputed$(() => {
return listGroupByProjectsAdapter(data);
});

return (
<div class='flex flex-col' ref={ref}>
<div class='flex sm:flex-col md:flex-row lg:flex-row'>
<div class='flex-1'>
<DonutChart data={groupByProjectSeries} />
</div>

<div class='flex-auto'>
<ReportList
data={listGroupByProjectSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
</div>
return (
<div class='flex flex-col'>
<div class='flex sm:flex-col md:flex-row lg:flex-row'>
<div class='flex-1'>
<DonutChart data={groupByProjectSeries} />
</div>

<ColumnChart data={daysSeries} />
<div class='flex-auto'>
<ReportList
data={listGroupByProjectSeries}
resultsPerPage={REPORT_LIST_RESULTS_PER_PAGE}
/>
</div>
</div>
);
}
);

<ColumnChart data={daysSeries} />
</div>
);
});
Loading

0 comments on commit b1ed6b5

Please sign in to comment.