Skip to content

Commit

Permalink
Progressively enhance payroll element filters form and download
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Dec 19, 2023
1 parent ebd36f6 commit 7974881
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll": true
"source.fixAll": "explicit"
},
"[javascript]": {
"editor.formatOnSave": false
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "server",
"version": "1.0.0",
"description": "ERP building for cooperatives",
"author": "Mathieu MARCHOIS",
"author": "Fairness",
"license": "MIT",
"scripts": {
"prebuild": "rimraf dist",
Expand Down
4 changes: 4 additions & 0 deletions src/Infrastructure/Common/Utils/dateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ export const formatDate = (value: Date | string): string => {
export const formatHtmlDate = (value: Date): string => {
return format(value, 'yyyy-MM-dd');
};

export const formatHtmlYearMonth = (value: Date): string => {
return format(value, 'yyyy-MM');
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
Get,
UseGuards,
Render,
Query
Query,
Res,
Param
} from '@nestjs/common';
import { IQueryBus } from 'src/Application/IQueryBus';
import { GetUsersElementsQuery } from 'src/Application/HumanResource/Payslip/Query/GetUsersElementsQuery';
Expand All @@ -14,6 +16,9 @@ import { WithName } from 'src/Infrastructure/Common/ExtendedRouting/WithName';
import { TableCsvFactory } from 'src/Infrastructure/Tables/TableCsvFactory';
import { PayrollElementsTableFactory } from '../Table/PayrollElementsTableFactory';
import { GetPayrollElementsControllerDTO } from '../DTO/GetPayrollElementsControllerDTO';
import { Response } from 'express';
import { ITranslator } from 'src/Infrastructure/Translations/ITranslator';
import { formatHtmlYearMonth } from 'src/Infrastructure/Common/Utils/dateUtils';

@Controller('app/people/payroll_elements')
@UseGuards(IsAuthenticatedGuard)
Expand All @@ -22,31 +27,50 @@ export class GetPayrollElementsController {
@Inject('IQueryBus')
private readonly queryBus: IQueryBus,
private readonly tableCsvFactory: TableCsvFactory,
private readonly tableFactory: PayrollElementsTableFactory
private readonly tableFactory: PayrollElementsTableFactory,
@Inject('ITranslator')
private readonly translator: ITranslator
) {}

@Get()
@WithName('people_payroll_elements')
@Render('pages/payroll_elements/index.njk')
public async get(@Query() dto: GetPayrollElementsControllerDTO) {
const date =
dto.month !== undefined && dto.year !== undefined
? new Date(dto.year, dto.month, 15)
: new Date();
let date = new Date();

if (dto.year !== undefined && dto.month !== undefined) {
date = new Date(dto.year, dto.month - 1, 15);
}

const elements: UserElementsView[] = await this.queryBus.execute(
new GetUsersElementsQuery(date)
);

const table = this.tableFactory.create(elements);
const csv = this.tableCsvFactory.toCsv(table);

return {
table,
csv,
date,
year: date.getUTCFullYear(),
month: date.getUTCMonth()
month: date.getUTCMonth() + 1
};
}

@Get('/download/:year/:month')
@WithName('people_payroll_elements_download')
public async download(
@Param() dto: GetPayrollElementsControllerDTO,
@Res() res: Response
) {
const { table, date } = await this.get(dto);

const csv = this.tableCsvFactory.toCsv(table);
const filename = this.translator.translate('payroll-elements-filename', {
date: formatHtmlYearMonth(date)
});

res.setHeader('Content-Disposition', `attachment; filename=${filename}`);
res.setHeader('Content-Type', 'application/csv');
res.send(csv);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { IsNumber, IsOptional } from 'class-validator';
export class GetPayrollElementsControllerDTO {
@IsNumber()
@IsOptional()
public month: number;
public year: number;

@IsNumber()
@IsOptional()
public year: number;
public month: number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { formatFullName } from '../../Common/Utils/formatUtils';
import {
formatDate,
formatHtmlDate,
formatHtmlYearMonth,
minutesToHours
} from '../../Common/Utils/dateUtils';
import { ArrayUtils } from '../../Common/Utils/ArrayUtils';
Expand Down Expand Up @@ -44,7 +45,7 @@ export class NunjucksTemplates implements ITemplates {
value === 'now' ? new Date() : formatDate(value)
);
env.addFilter('htmlDate', value => formatHtmlDate(value));
env.addFilter('htmlYearMonth', value => formatHtmlDate(value).slice(0, 7));
env.addFilter('htmlYearMonth', value => formatHtmlYearMonth(value));
env.addFilter('longMonth', (month: number) =>
this.translator.translate('common-month-long', {
date: new Date(2023, month, 15)
Expand Down
17 changes: 14 additions & 3 deletions src/assets/customElements/autoForm.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
// @ts-check
import { onParsed } from '../lib/customElements';

export default class extends HTMLFormElement {
export default class extends HTMLElement {
connectedCallback() {
onParsed(() => {
for (const formControl of this) {
formControl.addEventListener('change', () => this.requestSubmit());
// Progressive enhancement:
// If this custom element activates, submit the form whenever
// a form control changes value, and remove any manual submit button.

const form = /** @type {HTMLFormElement} */ (this.querySelector('form'));
const submitBtn = this.querySelector('button[type="submit"]');

for (const formControl of form.elements) {
formControl.addEventListener('change', () => form.requestSubmit());
}

if (submitBtn) {
submitBtn.remove();
}
});
}
Expand Down
17 changes: 0 additions & 17 deletions src/assets/customElements/blobLink.js

This file was deleted.

10 changes: 7 additions & 3 deletions src/assets/customElements/fairCalendarFiltersForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ export default class extends HTMLElement {
const form = /** @type {HTMLFormElement} */ (this.querySelector('form'));

// Progressive enhancement:
// If this custom element activates, then we enable auto-submission and remove the manual submit button.
const submitBtn = /** @type {HTMLButtonElement} */ (this.querySelector('button[type="submit"]'));
const navigationTemplate = /** @type {HTMLTemplateElement} */ (this.querySelector('#faircalendar-filters-form-navigation'));
// If this custom element activates, enable auto-submission and remove the manual submit button.
const submitBtn = /** @type {HTMLButtonElement} */ (this.querySelector(
'button[type="submit"]'
));
const navigationTemplate = /** @type {HTMLTemplateElement} */ (this.querySelector(
'#faircalendar-filters-form-navigation'
));
const navigation = document.importNode(navigationTemplate.content, true);
navigationTemplate.after(navigation);
submitBtn.remove();
Expand Down
4 changes: 1 addition & 3 deletions src/assets/customElements/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// @ts-check
import autoForm from './autoForm';
import blobLink from './blobLink';
import clipboardButton from './clipboardButton';
import eventForm from './eventForm';
import fairCalendarFiltersForm from './fairCalendarFiltersForm';
import navMenuButton from './navMenuButton';
import themeToggler from './themeToggler';

customElements.define('pc-auto-form', autoForm, { extends: 'form' });
customElements.define('pc-blob-link', blobLink, { extends: 'a' });
customElements.define('pc-auto-form', autoForm);
customElements.define('pc-clipboard-button', clipboardButton, {
extends: 'button'
});
Expand Down
2 changes: 1 addition & 1 deletion src/templates/pages/faircalendar/_filters_form.njk
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
</select>
</div>
<button type="submit" class="pc-btn pc-btn--secondary">
{{ 'faircalendar-filters-submit'|trans }}
{{ 'common-form-update'|trans }}
</button>
</form>
</pc-faircalendar-filters-form>
Expand Down
45 changes: 25 additions & 20 deletions src/templates/pages/payroll_elements/_filters_form.njk
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
{% macro filters_form() %}
<form
is="pc-auto-form"
method="GET"
action="{{ path('people_payroll_elements') }}"
class="pc-cluster pc-gap"
style="--cluster-align: flex-end"
>
<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="month">{{ 'payroll-elements-filters-month-title'|trans }}</label>
<select name="month" id="month">
{% for monthValue in range(12) %}
<option value="{{ monthValue }}" {% if monthValue == month %}selected{% endif %}>{{ monthValue|longMonth|capitalize }}</option>
{% endfor %}
</select>
</div>
<pc-auto-form>
<form
method="GET"
action="{{ path('people_payroll_elements') }}"
class="pc-cluster pc-gap"
style="--cluster-align: flex-end"
>
<div class="pc-select-group pc-m" style="--m: 0">
<label class="pc-label" for="month">{{ 'payroll-elements-filters-month-title'|trans }}</label>
<select name="month" id="month">
{% for monthValue in range(1, 13) %}
<option value="{{ monthValue }}" {% if monthValue == month %}selected{% endif %}>{{ (monthValue - 1)|longMonth|capitalize }}</option>
{% endfor %}
</select>
</div>

<div class="pc-input-group pc-m" style="--m: 0">
<label class="pc-label" for="year">{{ 'payroll-elements-filters-year-title'|trans }}</label>
<input type="number" id="year" name="year" pattern="\d+" value="{{ year }}" style="--input-width: 12ch">
</div>
</form>
<div class="pc-input-group pc-m" style="--m: 0">
<label class="pc-label" for="year">{{ 'payroll-elements-filters-year-title'|trans }}</label>
<input type="number" id="year" name="year" pattern="\d+" value="{{ year }}" style="--input-width: 12ch">
</div>

<button type="submit" class="pc-btn pc-btn--secondary">
{{ 'common-form-update'|trans }}
</button>
</form>
</pc-auto-form>
{% endmacro %}
2 changes: 1 addition & 1 deletion src/templates/pages/payroll_elements/index.njk
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<div class="pc-cluster pc-gap" style="--cluster-justify: space-between; --cluster-align: center">
{{ filters_form() }}

<a is="pc-blob-link" data-blob-content="{{ csv }}" data-blob-mime-type="application/csv" download="{{ 'payroll-elements-filename'|trans({ date: date|htmlYearMonth }) }}" class="pc-btn">
<a class="pc-btn" href="{{ path('people_payroll_elements_download', {year: year, month: month}) }}">
{{ 'payroll-elements-download'|trans }}
</a>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/translations/fr-FR.ftl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
common-form-save = Enregistrer
common-form-update = Mettre à jour
common-form-delete = Supprimer
common-view = Voir
common-add = Ajouter
Expand Down Expand Up @@ -63,7 +64,6 @@ faircalendar-time-title = Temps passé
faircalendar-filters-month-title = Mois
faircalendar-filters-year-title = Année
faircalendar-filters-userId-title = Coopérateur·ice - salarié·e
faircalendar-filters-submit = Mettre à jour
faircalendar-overview-days = {$days ->
[0] 0
[1] 1 jour
Expand Down

0 comments on commit 7974881

Please sign in to comment.