Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion locales/en/transit.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,15 @@
"PeriodsGroup": "Periods group",
"PeriodsGroups": "Periods groups",
"nTrip": "{{n}} trip",
"nTrips": "{{n}} trips"
"nTrips": "{{n}} trips",
"RemovePeriod": "Remove period",
"AddCustomPeriod": "Add a period",
"SaveAsTemplate": "Save as template",
"UpdateTemplate": "Update template",
"DeleteTemplate": "Delete template",
"SaveTemplateTitle": "Name of your periods group template:",
"SaveTemplate": "Save template",
"TemplateNamePlaceholder": "My periods template"
},
"transitUnit": {
"List": "Units",
Expand Down
10 changes: 9 additions & 1 deletion locales/fr/transit.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,15 @@
"PeriodsGroup": "Groupe de périodes",
"PeriodsGroups": "Groupes de périodes",
"nTrip": "{{n}} voyage",
"nTrips": "{{n}} voyages"
"nTrips": "{{n}} voyages",
"RemovePeriod": "Supprimer la période",
"AddCustomPeriod": "Ajouter une période",
"SaveAsTemplate": "Sauvegarder comme modèle",
"UpdateTemplate": "Mettre à jour le modèle",
"DeleteTemplate": "Supprimer le modèle",
"SaveTemplateTitle": "Le nom de votre modèle de groupe de périodes : ",
"SaveTemplate": "Sauvegarder le modèle",
"TemplateNamePlaceholder": "Mon modèle de groupe de périodes"
},
"transitUnit": {
"List": "Unités",
Expand Down
19 changes: 19 additions & 0 deletions packages/chaire-lib-common/src/config/defaultPreferences.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
/** Hexadecimal strings of the various colors that should be available */
colors: string[];
};
[key: string]: any;

Check warning on line 35 in packages/chaire-lib-common/src/config/defaultPreferences.config.ts

View workflow job for this annotation

GitHub Actions / code-lint

Unexpected any. Specify a different type
}

// TODO: Type more fields
Expand Down Expand Up @@ -280,6 +280,25 @@
endAtHour: 24
}
]
},
custom: {
name: {
fr: '+ Ajouter des périodes personnalisées',
en: '+ Add custom periods'
},
periods: [
{
shortname: 'custom_period_1',
name: {
fr: 'Période personnalisée 1',
en: 'Custom Period 1'
},
startAtHour: 8,
endAtHour: 10,
isCustom: true
}
],
isCustomizable: true
}
},
stations: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ export abstract class SaveableObjectForm<
});
};

protected openModal = (modalName: keyof this['state']) => (e?: any) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Est-il nécessaire de faire ce changement dans cette classe de base? Plutôt que dans la classe qui l'utilise? On va généralement privilégier d'attendre d'avoir au moins 2 ou 3 utilisations d'un feature avant de l'emmener dans les classes génériques (sinon, ça devient comme un API et il faut tester/supporter, etc)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En fait, au départ, j'avais écrit ces fonctions dans le fichier TransitScheduleEdit.tsx pour changer l'état du InputModal qu'on a créé. Mais en fouillant, j'avais trouvé qu'on avait déjà deux fonctions qui se ressemblait beaucoup openDeleteConfirmModal et openBackConfirmModal (idem à pour les fonctions de close). Alors, comme j'avais aussi besoin d'ovrir et de fermer la fenêtre InputModal, je me suis dit que je peux réutiliser ces fonctions de open et de close, avec une petite modification pour qu'elles soient plus génériques.
Je peux remettre les fonctions initiales dans TransitScheduleEdit sans problème!

if (e?.stopPropagation) e.stopPropagation();
this.setState({ [modalName]: true } as Pick<this['state'], typeof modalName>);
};

protected closeModal = (modalName: keyof this['state']) => (e?: any) => {
if (e?.stopPropagation) e.stopPropagation();
this.setState({ [modalName]: false } as Pick<this['state'], typeof modalName>);
};

protected async onBack(e: any): Promise<void> {
if (e && typeof e.stopPropagation === 'function') {
e.stopPropagation();
Expand Down
111 changes: 111 additions & 0 deletions packages/chaire-lib-frontend/src/components/input/InputModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { useState, useEffect } from 'react';
import Modal from 'react-modal';
import { useTranslation } from 'react-i18next';
import InputString from './InputString';

interface InputModalProps {
isOpen: boolean;
title: string;
onClose: () => void;
onConfirm: (value: string) => void;
confirmButtonColor?: string;
confirmButtonLabel?: string;
closeButtonLabel?: string;
inputPlaceholder?: string;
defaultValue?: string;
maxLength?: number;
type?: 'text' | 'email' | 'number';
pattern?: string;
autocompleteChoices?: ({ label: string; value: string } | string)[];
}

const InputModal: React.FC<InputModalProps> = ({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pourquoi un nouveau composant InputModal. Que manquait-il aux autres modal? ça prendrait une explication de pourquoi ce type est nécessaire

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lorsqu'on clique sur le bouton Sauvegarder comme modèle, notre idée est de permettre à l’utilisateur de nommer le modèle. Alors, je avais pensé qu’on pourrait créer une fenêtre modale qui combine les fonctionnalités de InputString.tsx et de ConfirmModal.tsx, pour que l’utilisateur puisse entrer le nom directement. Est-ce que vous pensez que c'est nécessaire? On pourrait également mettre un champs quelque part sur l'interface de modification de l'horaire directement et utiliser InputString, mais je pense qu'il serait peut-être moins intuitif que d'avoir une fenêtre de confirmation.

image

isOpen,
title,
onClose,
onConfirm,
confirmButtonColor = 'blue',
confirmButtonLabel,
closeButtonLabel,
defaultValue = '',
maxLength = 255,
type = 'text',
pattern,
autocompleteChoices = []
}) => {
const { t } = useTranslation('main');

const [inputValue, setInputValue] = useState(defaultValue);
const [isValid, setIsValid] = useState(true);

useEffect(() => {
setInputValue(defaultValue as string);
setIsValid(true);
}, [isOpen, defaultValue]);

const handleInputChange = (inputData: { value: any; valid: boolean }) => {
setInputValue(inputData.value);
setIsValid(inputData.valid);
};

const handleConfirm = () => {
if (isValid) {
onConfirm(inputValue);
onClose();
}
};

const handleCancel = () => {
onClose();
};

if (!process.env.IS_TESTING) {
Modal.setAppElement('#app');
}

return (
<Modal
isOpen={isOpen}
onRequestClose={handleCancel}
className="react-modal"
overlayClassName="react-modal-overlay"
contentLabel={title}
>
<div>
<div className="center">{title}</div>

<div className="input-modal-content">
<InputString
id="modal-input"
onValueUpdated={handleInputChange}
value={inputValue}
maxLength={maxLength}
type={type}
pattern={pattern}
autocompleteChoices={autocompleteChoices}
/>
{!isValid && <div className="apptr__input-error">{t('PleaseEnterValidValue')}</div>}
</div>

<div className={'tr__form-buttons-container _center'}>
<div className="center">
<button
className={`button ${confirmButtonColor || 'blue'}`}
onClick={handleConfirm}
disabled={!isValid}
>
{confirmButtonLabel || t('Confirm')}
</button>
</div>
<div className="center">
<button className="button grey" onClick={handleCancel}>
{closeButtonLabel || t('Cancel')}
</button>
</div>
</div>
</div>
</Modal>
);
};

export default InputModal;
16 changes: 13 additions & 3 deletions packages/chaire-lib-frontend/src/components/input/InputSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type choiceType = {
value: string;
disabled?: boolean;
label?: string;
className?: string;
choices?: choiceType[];
[key: string]: unknown;
};
Expand Down Expand Up @@ -67,7 +68,11 @@ const InputSelect: React.FunctionComponent<InputSelectProps> = ({
if (choice.choices) {
const childChoices = choice.choices.map((childChoice) => {
return (
<option key={`${childChoice.value}`} value={childChoice.value} disabled={choice.disabled}>
<option
key={`choice_${childChoice.value}`}
value={childChoice.value}
disabled={childChoice.disabled}
>
{childChoice.label || t(`${localePrefix}:${childChoice.value}`)}
</option>
);
Expand All @@ -79,7 +84,12 @@ const InputSelect: React.FunctionComponent<InputSelectProps> = ({
);
} else {
selectChoices.push(
<option key={`${choice.value}`} value={choice.value} disabled={choice.disabled}>
<option
key={`choice_${choice.value}`}
value={choice.value}
disabled={choice.disabled}
className={choice.className}
>
{choice.label || t(`${localePrefix}:${choice.value}`)}
</option>
);
Expand All @@ -97,7 +107,7 @@ const InputSelect: React.FunctionComponent<InputSelectProps> = ({
textOverflow: 'ellipsis'
}}
>
{!noBlank && <option key={'_blank'} value=""></option>}
{!noBlank && <option value=""></option>}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why remove the key here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oups! Je l'ai enlevé sans faire exprès. Je vais le remettre!

{selectChoices}
</select>
);
Expand Down
12 changes: 12 additions & 0 deletions packages/chaire-lib-frontend/src/styles/_buttons.scss
Original file line number Diff line number Diff line change
Expand Up @@ -415,4 +415,16 @@ label .button,
text-overflow: ellipsis;
width: 680px;
}
}

// Minh test
select {
option.custom-option-button {
color: white;
font-weight: bold;
}

option.user-template-option {
font-style: italic;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,23 @@ const TransitScheduleButton: React.FunctionComponent<ScheduleButtonProps> = (pro
const serviceId = props.schedule.attributes.service_id;
const periodsGroups = Preferences.get('transit.periods');
const periodsGroupShortname = props.schedule.attributes.periods_group_shortname;
const periodsGroupName = periodsGroupShortname
? periodsGroups[periodsGroupShortname].name[props.i18n.language]
: '';
// Add defensive check before accessing the template, otherwise the interface breaks
let periodsGroupName = '-';
if (periodsGroupShortname) {
if (periodsGroups[periodsGroupShortname]) {
periodsGroupName = periodsGroups[periodsGroupShortname].name[props.i18n.language];
} else {
props.schedule.set('periods_group_shortname', 'default');
try {
props.schedule.save(serviceLocator.socketEventManager);
} catch (e) {
console.warn('Could not save schedule with updated template', e);
}
if (periodsGroups['default'] && periodsGroups['default'].name) {
periodsGroupName = periodsGroups['default'].name[props.i18n.language];
}
}
}
const service = serviceLocator.collectionManager.get('services').getById(serviceId);
const tripsCount = props.schedule.tripsCount();

Expand Down
Loading
Loading