Fermer les listes de pays et de départements
Nationalité, pays de naissance, département de naissance et pays de l'adresse étaient des champs libres. « France », « FRANCE », « Française », « FR » et « france » désignent la même chose et se retrouvaient toutes en base. Le registre unique du personnel les imprime telles quelles, et un contrôle y lit cinq pays. Le code ISO 3166-1 est désormais stocké, jamais le libellé : il ne change pas d'orthographe, tient dans deux caractères, et c'est lui que reprennent les déclarations sociales. Le libellé n'est plus qu'un affichage, et le registre imprime le gentilé résolu depuis le code. La liste des pays couvre ce qu'un commerce de détail français rencontre — Europe, pourtour méditerranéen, Afrique francophone, principaux pays d'origine relevés par l'INSEE. Elle n'est pas universelle et ne le prétend pas : un pays absent s'ajoute en une ligne, plutôt que de rouvrir la saisie libre pour tout le monde. La France est en tête du sélecteur, le reste par ordre alphabétique ; un classement par fréquence supposée aurait été deviné, et se lirait comme un jugement. Les départements portent leur code officiel, Corse comprise — 2A et 2B, jamais 20, qui n'existe plus depuis 1976 mais survit dans les saisies manuelles. Les trois départements du régime local d'Alsace-Moselle sont identifiés à part : deux jours fériés de plus et un régime de maintien de salaire distinct, ce qui change la planification d'un établissement à quinze kilomètres d'un autre. Un dossier saisi avant la liste porte encore un libellé en clair. Le sélecteur n'y trouve pas d'option, affiche « non renseigné », et la première correction du dossier écrit le code. La valeur n'est ni perdue en silence ni opposée à qui vient corriger un champ voisin. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fab497acdc
commit
ef3dd5c027
5 files changed
+384
-14
No files matched your search
@@ -11,6 +11,13 @@ import {
|
||||
genderLabel,
|
||||
maritalStatusLabel,
|
||||
} from '@/domain/hr/civil-status';
|
||||
import {
|
||||
COUNTRY_OPTIONS,
|
||||
NATIONALITY_OPTIONS,
|
||||
countryLabel,
|
||||
nationalityLabel,
|
||||
} from '@/domain/legal/countries';
|
||||
import { DEPARTMENTS, departmentLabel } from '@/domain/legal/departments';
|
||||
import {
|
||||
updateProfileAction,
|
||||
updateSensitiveAction,
|
||||
@@ -153,22 +160,31 @@ export function PersonalInfoPanel({
|
||||
defaultValue={profile.birthName}
|
||||
/>
|
||||
<Field label="Nom de famille" name="lastName" required defaultValue={profile.lastName} />
|
||||
<Field label="Nationalité" name="nationality" defaultValue={profile.nationality} />
|
||||
<Select
|
||||
label="Nationalité"
|
||||
name="nationality"
|
||||
defaultValue={profile.nationality}
|
||||
options={NATIONALITY_OPTIONS}
|
||||
/>
|
||||
<Field
|
||||
label="Date de naissance"
|
||||
name="birthDate"
|
||||
type="date"
|
||||
defaultValue={profile.birthDate}
|
||||
/>
|
||||
<Field
|
||||
<Select
|
||||
label="Pays de naissance"
|
||||
name="birthCountry"
|
||||
defaultValue={profile.birthCountry}
|
||||
options={COUNTRY_OPTIONS}
|
||||
/>
|
||||
<Field
|
||||
{/* Renseigné pour une naissance en France ; laissé vide
|
||||
ailleurs, où la notion n'existe pas. */}
|
||||
<Select
|
||||
label="Département de naissance"
|
||||
name="birthDepartment"
|
||||
defaultValue={profile.birthDepartment}
|
||||
options={DEPARTMENTS}
|
||||
/>
|
||||
<Field
|
||||
label="Commune de naissance"
|
||||
@@ -226,7 +242,12 @@ export function PersonalInfoPanel({
|
||||
<Field label="Code postal" name="postalCode" defaultValue={profile.postalCode} />
|
||||
<Field label="Ville" name="city" defaultValue={profile.city} />
|
||||
</div>
|
||||
<Field label="Pays" name="country" defaultValue={profile.country} />
|
||||
<Select
|
||||
label="Pays"
|
||||
name="country"
|
||||
defaultValue={profile.country}
|
||||
options={COUNTRY_OPTIONS}
|
||||
/>
|
||||
</div>
|
||||
</InfoCard>
|
||||
|
||||
@@ -267,10 +288,13 @@ export function PersonalInfoPanel({
|
||||
<InfoRow label="Prénom" value={profile.firstName} />
|
||||
<InfoRow label="Nom de naissance" value={profile.birthName} />
|
||||
<InfoRow label="Nom de famille" value={profile.lastName} />
|
||||
<InfoRow label="Nationalité" value={profile.nationality} />
|
||||
<InfoRow label="Nationalité" value={nationalityLabel(profile.nationality || null)} />
|
||||
<InfoRow label="Date de naissance" value={readableDate(profile.birthDate)} tnum />
|
||||
<InfoRow label="Pays de naissance" value={profile.birthCountry} />
|
||||
<InfoRow label="Département de naissance" value={profile.birthDepartment} />
|
||||
<InfoRow label="Pays de naissance" value={countryLabel(profile.birthCountry || null)} />
|
||||
<InfoRow
|
||||
label="Département de naissance"
|
||||
value={departmentLabel(profile.birthDepartment || null)}
|
||||
/>
|
||||
<InfoRow label="Commune de naissance" value={profile.birthPlace} />
|
||||
<InfoRow
|
||||
label="Situation familiale"
|
||||
@@ -295,7 +319,7 @@ export function PersonalInfoPanel({
|
||||
<InfoRow label="Complément d’adresse" value={profile.addressLine2} />
|
||||
<InfoRow label="Code postal" value={profile.postalCode} tnum />
|
||||
<InfoRow label="Ville" value={profile.city} />
|
||||
<InfoRow label="Pays" value={profile.country} />
|
||||
<InfoRow label="Pays" value={countryLabel(profile.country || null)} />
|
||||
</InfoCard>
|
||||
|
||||
<InfoCard title="Contact d’urgence">
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
/**
|
||||
* Pays et nationalités — pour le dossier salarié et le registre du personnel.
|
||||
*
|
||||
* Liste fermée plutôt que champ libre. « France », « FRANCE », « Française »,
|
||||
* « FR » et « france » désignaient la même chose et se retrouvaient toutes en
|
||||
* base ; le registre unique du personnel les imprime telles quelles, et un
|
||||
* contrôle y lit cinq pays.
|
||||
*
|
||||
* Le **code ISO 3166-1 alpha-2** est stocké, jamais le libellé : il ne change
|
||||
* pas d'orthographe, tient dans deux caractères, et c'est lui que reprennent
|
||||
* les déclarations sociales. Le libellé reste un affichage.
|
||||
*
|
||||
* La liste couvre les nationalités rencontrées dans un commerce de détail
|
||||
* français : l'Europe, le pourtour méditerranéen, l'Afrique francophone, et les
|
||||
* principaux pays d'origine relevés par l'INSEE. Elle n'est pas universelle et
|
||||
* ne prétend pas l'être — un pays absent s'ajoute ici, en une ligne, plutôt que
|
||||
* de rouvrir la saisie libre pour tout le monde.
|
||||
*/
|
||||
|
||||
export interface Country {
|
||||
/** ISO 3166-1 alpha-2. */
|
||||
code: string;
|
||||
name: string;
|
||||
/** Gentilé au féminin singulier, forme retenue au registre. */
|
||||
nationality: string;
|
||||
}
|
||||
|
||||
export const COUNTRIES: readonly Country[] = [
|
||||
{ code: 'FR', name: 'France', nationality: 'Française' },
|
||||
{ code: 'DE', name: 'Allemagne', nationality: 'Allemande' },
|
||||
{ code: 'AD', name: 'Andorre', nationality: 'Andorrane' },
|
||||
{ code: 'AO', name: 'Angola', nationality: 'Angolaise' },
|
||||
{ code: 'DZ', name: 'Algérie', nationality: 'Algérienne' },
|
||||
{ code: 'AR', name: 'Argentine', nationality: 'Argentine' },
|
||||
{ code: 'AM', name: 'Arménie', nationality: 'Arménienne' },
|
||||
{ code: 'AU', name: 'Australie', nationality: 'Australienne' },
|
||||
{ code: 'AT', name: 'Autriche', nationality: 'Autrichienne' },
|
||||
{ code: 'BE', name: 'Belgique', nationality: 'Belge' },
|
||||
{ code: 'BJ', name: 'Bénin', nationality: 'Béninoise' },
|
||||
{ code: 'BY', name: 'Biélorussie', nationality: 'Biélorusse' },
|
||||
{ code: 'BO', name: 'Bolivie', nationality: 'Bolivienne' },
|
||||
{ code: 'BA', name: 'Bosnie-Herzégovine', nationality: 'Bosnienne' },
|
||||
{ code: 'BR', name: 'Brésil', nationality: 'Brésilienne' },
|
||||
{ code: 'BG', name: 'Bulgarie', nationality: 'Bulgare' },
|
||||
{ code: 'BF', name: 'Burkina Faso', nationality: 'Burkinabè' },
|
||||
{ code: 'KH', name: 'Cambodge', nationality: 'Cambodgienne' },
|
||||
{ code: 'CM', name: 'Cameroun', nationality: 'Camerounaise' },
|
||||
{ code: 'CA', name: 'Canada', nationality: 'Canadienne' },
|
||||
{ code: 'CV', name: 'Cap-Vert', nationality: 'Cap-verdienne' },
|
||||
{ code: 'CL', name: 'Chili', nationality: 'Chilienne' },
|
||||
{ code: 'CN', name: 'Chine', nationality: 'Chinoise' },
|
||||
{ code: 'CY', name: 'Chypre', nationality: 'Chypriote' },
|
||||
{ code: 'CO', name: 'Colombie', nationality: 'Colombienne' },
|
||||
{ code: 'KM', name: 'Comores', nationality: 'Comorienne' },
|
||||
{ code: 'CG', name: 'Congo', nationality: 'Congolaise' },
|
||||
{ code: 'CD', name: 'Congo (RDC)', nationality: 'Congolaise (RDC)' },
|
||||
{ code: 'KR', name: 'Corée du Sud', nationality: 'Sud-coréenne' },
|
||||
{ code: 'CI', name: "Côte d'Ivoire", nationality: 'Ivoirienne' },
|
||||
{ code: 'HR', name: 'Croatie', nationality: 'Croate' },
|
||||
{ code: 'DK', name: 'Danemark', nationality: 'Danoise' },
|
||||
{ code: 'DJ', name: 'Djibouti', nationality: 'Djiboutienne' },
|
||||
{ code: 'EG', name: 'Égypte', nationality: 'Égyptienne' },
|
||||
{ code: 'AE', name: 'Émirats arabes unis', nationality: 'Émirienne' },
|
||||
{ code: 'EC', name: 'Équateur', nationality: 'Équatorienne' },
|
||||
{ code: 'ES', name: 'Espagne', nationality: 'Espagnole' },
|
||||
{ code: 'EE', name: 'Estonie', nationality: 'Estonienne' },
|
||||
{ code: 'US', name: 'États-Unis', nationality: 'Américaine' },
|
||||
{ code: 'ET', name: 'Éthiopie', nationality: 'Éthiopienne' },
|
||||
{ code: 'FI', name: 'Finlande', nationality: 'Finlandaise' },
|
||||
{ code: 'GA', name: 'Gabon', nationality: 'Gabonaise' },
|
||||
{ code: 'GE', name: 'Géorgie', nationality: 'Géorgienne' },
|
||||
{ code: 'GH', name: 'Ghana', nationality: 'Ghanéenne' },
|
||||
{ code: 'GR', name: 'Grèce', nationality: 'Grecque' },
|
||||
{ code: 'GN', name: 'Guinée', nationality: 'Guinéenne' },
|
||||
{ code: 'HT', name: 'Haïti', nationality: 'Haïtienne' },
|
||||
{ code: 'HU', name: 'Hongrie', nationality: 'Hongroise' },
|
||||
{ code: 'IN', name: 'Inde', nationality: 'Indienne' },
|
||||
{ code: 'ID', name: 'Indonésie', nationality: 'Indonésienne' },
|
||||
{ code: 'IQ', name: 'Irak', nationality: 'Irakienne' },
|
||||
{ code: 'IR', name: 'Iran', nationality: 'Iranienne' },
|
||||
{ code: 'IE', name: 'Irlande', nationality: 'Irlandaise' },
|
||||
{ code: 'IS', name: 'Islande', nationality: 'Islandaise' },
|
||||
{ code: 'IL', name: 'Israël', nationality: 'Israélienne' },
|
||||
{ code: 'IT', name: 'Italie', nationality: 'Italienne' },
|
||||
{ code: 'JP', name: 'Japon', nationality: 'Japonaise' },
|
||||
{ code: 'JO', name: 'Jordanie', nationality: 'Jordanienne' },
|
||||
{ code: 'KZ', name: 'Kazakhstan', nationality: 'Kazakhe' },
|
||||
{ code: 'KE', name: 'Kenya', nationality: 'Kényane' },
|
||||
{ code: 'XK', name: 'Kosovo', nationality: 'Kosovare' },
|
||||
{ code: 'LV', name: 'Lettonie', nationality: 'Lettone' },
|
||||
{ code: 'LB', name: 'Liban', nationality: 'Libanaise' },
|
||||
{ code: 'LY', name: 'Libye', nationality: 'Libyenne' },
|
||||
{ code: 'LT', name: 'Lituanie', nationality: 'Lituanienne' },
|
||||
{ code: 'LU', name: 'Luxembourg', nationality: 'Luxembourgeoise' },
|
||||
{ code: 'MK', name: 'Macédoine du Nord', nationality: 'Macédonienne' },
|
||||
{ code: 'MG', name: 'Madagascar', nationality: 'Malgache' },
|
||||
{ code: 'MY', name: 'Malaisie', nationality: 'Malaisienne' },
|
||||
{ code: 'ML', name: 'Mali', nationality: 'Malienne' },
|
||||
{ code: 'MT', name: 'Malte', nationality: 'Maltaise' },
|
||||
{ code: 'MA', name: 'Maroc', nationality: 'Marocaine' },
|
||||
{ code: 'MU', name: 'Maurice', nationality: 'Mauricienne' },
|
||||
{ code: 'MR', name: 'Mauritanie', nationality: 'Mauritanienne' },
|
||||
{ code: 'MX', name: 'Mexique', nationality: 'Mexicaine' },
|
||||
{ code: 'MD', name: 'Moldavie', nationality: 'Moldave' },
|
||||
{ code: 'MC', name: 'Monaco', nationality: 'Monégasque' },
|
||||
{ code: 'ME', name: 'Monténégro', nationality: 'Monténégrine' },
|
||||
{ code: 'MZ', name: 'Mozambique', nationality: 'Mozambicaine' },
|
||||
{ code: 'NE', name: 'Niger', nationality: 'Nigérienne' },
|
||||
{ code: 'NG', name: 'Nigeria', nationality: 'Nigériane' },
|
||||
{ code: 'NO', name: 'Norvège', nationality: 'Norvégienne' },
|
||||
{ code: 'NZ', name: 'Nouvelle-Zélande', nationality: 'Néo-zélandaise' },
|
||||
{ code: 'PK', name: 'Pakistan', nationality: 'Pakistanaise' },
|
||||
{ code: 'NL', name: 'Pays-Bas', nationality: 'Néerlandaise' },
|
||||
{ code: 'PE', name: 'Pérou', nationality: 'Péruvienne' },
|
||||
{ code: 'PH', name: 'Philippines', nationality: 'Philippine' },
|
||||
{ code: 'PL', name: 'Pologne', nationality: 'Polonaise' },
|
||||
{ code: 'PT', name: 'Portugal', nationality: 'Portugaise' },
|
||||
{ code: 'RO', name: 'Roumanie', nationality: 'Roumaine' },
|
||||
{ code: 'GB', name: 'Royaume-Uni', nationality: 'Britannique' },
|
||||
{ code: 'RU', name: 'Russie', nationality: 'Russe' },
|
||||
{ code: 'RW', name: 'Rwanda', nationality: 'Rwandaise' },
|
||||
{ code: 'SN', name: 'Sénégal', nationality: 'Sénégalaise' },
|
||||
{ code: 'RS', name: 'Serbie', nationality: 'Serbe' },
|
||||
{ code: 'SG', name: 'Singapour', nationality: 'Singapourienne' },
|
||||
{ code: 'SK', name: 'Slovaquie', nationality: 'Slovaque' },
|
||||
{ code: 'SI', name: 'Slovénie', nationality: 'Slovène' },
|
||||
{ code: 'SD', name: 'Soudan', nationality: 'Soudanaise' },
|
||||
{ code: 'LK', name: 'Sri Lanka', nationality: 'Sri-lankaise' },
|
||||
{ code: 'SE', name: 'Suède', nationality: 'Suédoise' },
|
||||
{ code: 'CH', name: 'Suisse', nationality: 'Suisse' },
|
||||
{ code: 'SY', name: 'Syrie', nationality: 'Syrienne' },
|
||||
{ code: 'TW', name: 'Taïwan', nationality: 'Taïwanaise' },
|
||||
{ code: 'TD', name: 'Tchad', nationality: 'Tchadienne' },
|
||||
{ code: 'CZ', name: 'Tchéquie', nationality: 'Tchèque' },
|
||||
{ code: 'TH', name: 'Thaïlande', nationality: 'Thaïlandaise' },
|
||||
{ code: 'TG', name: 'Togo', nationality: 'Togolaise' },
|
||||
{ code: 'TN', name: 'Tunisie', nationality: 'Tunisienne' },
|
||||
{ code: 'TR', name: 'Turquie', nationality: 'Turque' },
|
||||
{ code: 'UA', name: 'Ukraine', nationality: 'Ukrainienne' },
|
||||
{ code: 'UY', name: 'Uruguay', nationality: 'Uruguayenne' },
|
||||
{ code: 'VE', name: 'Venezuela', nationality: 'Vénézuélienne' },
|
||||
{ code: 'VN', name: 'Viêt Nam', nationality: 'Vietnamienne' },
|
||||
];
|
||||
|
||||
const BY_CODE = new Map(COUNTRIES.map((country) => [country.code, country]));
|
||||
|
||||
/**
|
||||
* Options du sélecteur de pays : la France en tête, le reste par ordre
|
||||
* alphabétique.
|
||||
*
|
||||
* Placer le pays le plus fréquent en premier évite un défilement à chaque
|
||||
* saisie. L'ordre alphabétique ensuite, parce qu'un classement par fréquence
|
||||
* supposée serait deviné, et se lirait comme un jugement.
|
||||
*/
|
||||
export const COUNTRY_OPTIONS: ReadonlyArray<readonly [string, string]> = [
|
||||
['FR', 'France'],
|
||||
...COUNTRIES.filter((country) => country.code !== 'FR')
|
||||
.map((country) => [country.code, country.name] as const)
|
||||
.sort((a, b) => a[1].localeCompare(b[1], 'fr')),
|
||||
];
|
||||
|
||||
/** Mêmes pays, mais nommés par leur gentilé. */
|
||||
export const NATIONALITY_OPTIONS: ReadonlyArray<readonly [string, string]> = [
|
||||
['FR', 'Française'],
|
||||
...COUNTRIES.filter((country) => country.code !== 'FR')
|
||||
.map((country) => [country.code, country.nationality] as const)
|
||||
.sort((a, b) => a[1].localeCompare(b[1], 'fr')),
|
||||
];
|
||||
|
||||
export function countryLabel(code: string | null): string | null {
|
||||
if (!code) return null;
|
||||
// Les dossiers saisis avant la liste portent un libellé en clair : le rendre
|
||||
// tel quel vaut mieux qu'afficher un vide sur une donnée qui existe.
|
||||
return BY_CODE.get(code)?.name ?? code;
|
||||
}
|
||||
|
||||
export function nationalityLabel(code: string | null): string | null {
|
||||
if (!code) return null;
|
||||
return BY_CODE.get(code)?.nationality ?? code;
|
||||
}
|
||||
|
||||
export function isCountryCode(value: string): boolean {
|
||||
return BY_CODE.has(value);
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* Départements français — pour le lieu de naissance et le rattachement d'un
|
||||
* établissement.
|
||||
*
|
||||
* Liste fermée plutôt que champ libre : « 54 », « Meurthe-et-Moselle »,
|
||||
* « Meurthe et Moselle » et « meurthe-et-moselle » désignent le même
|
||||
* département et se retrouvaient tous les quatre en base. Le registre unique du
|
||||
* personnel les imprime tels quels, et un contrôle y lit quatre départements.
|
||||
*
|
||||
* Le code est stocké, pas le libellé : il tient dans deux caractères, ne change
|
||||
* pas d'orthographe, et c'est lui que reprennent les déclarations sociales.
|
||||
*
|
||||
* La Corse porte `2A` et `2B` — jamais `20`, qui n'existe plus depuis 1976 mais
|
||||
* survit dans beaucoup de saisies manuelles.
|
||||
*/
|
||||
export const DEPARTMENTS: ReadonlyArray<readonly [string, string]> = [
|
||||
['01', 'Ain'],
|
||||
['02', 'Aisne'],
|
||||
['03', 'Allier'],
|
||||
['04', 'Alpes-de-Haute-Provence'],
|
||||
['05', 'Hautes-Alpes'],
|
||||
['06', 'Alpes-Maritimes'],
|
||||
['07', 'Ardèche'],
|
||||
['08', 'Ardennes'],
|
||||
['09', 'Ariège'],
|
||||
['10', 'Aube'],
|
||||
['11', 'Aude'],
|
||||
['12', 'Aveyron'],
|
||||
['13', 'Bouches-du-Rhône'],
|
||||
['14', 'Calvados'],
|
||||
['15', 'Cantal'],
|
||||
['16', 'Charente'],
|
||||
['17', 'Charente-Maritime'],
|
||||
['18', 'Cher'],
|
||||
['19', 'Corrèze'],
|
||||
['2A', 'Corse-du-Sud'],
|
||||
['2B', 'Haute-Corse'],
|
||||
['21', "Côte-d'Or"],
|
||||
['22', "Côtes-d'Armor"],
|
||||
['23', 'Creuse'],
|
||||
['24', 'Dordogne'],
|
||||
['25', 'Doubs'],
|
||||
['26', 'Drôme'],
|
||||
['27', 'Eure'],
|
||||
['28', 'Eure-et-Loir'],
|
||||
['29', 'Finistère'],
|
||||
['30', 'Gard'],
|
||||
['31', 'Haute-Garonne'],
|
||||
['32', 'Gers'],
|
||||
['33', 'Gironde'],
|
||||
['34', 'Hérault'],
|
||||
['35', 'Ille-et-Vilaine'],
|
||||
['36', 'Indre'],
|
||||
['37', 'Indre-et-Loire'],
|
||||
['38', 'Isère'],
|
||||
['39', 'Jura'],
|
||||
['40', 'Landes'],
|
||||
['41', 'Loir-et-Cher'],
|
||||
['42', 'Loire'],
|
||||
['43', 'Haute-Loire'],
|
||||
['44', 'Loire-Atlantique'],
|
||||
['45', 'Loiret'],
|
||||
['46', 'Lot'],
|
||||
['47', 'Lot-et-Garonne'],
|
||||
['48', 'Lozère'],
|
||||
['49', 'Maine-et-Loire'],
|
||||
['50', 'Manche'],
|
||||
['51', 'Marne'],
|
||||
['52', 'Haute-Marne'],
|
||||
['53', 'Mayenne'],
|
||||
['54', 'Meurthe-et-Moselle'],
|
||||
['55', 'Meuse'],
|
||||
['56', 'Morbihan'],
|
||||
['57', 'Moselle'],
|
||||
['58', 'Nièvre'],
|
||||
['59', 'Nord'],
|
||||
['60', 'Oise'],
|
||||
['61', 'Orne'],
|
||||
['62', 'Pas-de-Calais'],
|
||||
['63', 'Puy-de-Dôme'],
|
||||
['64', 'Pyrénées-Atlantiques'],
|
||||
['65', 'Hautes-Pyrénées'],
|
||||
['66', 'Pyrénées-Orientales'],
|
||||
['67', 'Bas-Rhin'],
|
||||
['68', 'Haut-Rhin'],
|
||||
['69', 'Rhône'],
|
||||
['70', 'Haute-Saône'],
|
||||
['71', 'Saône-et-Loire'],
|
||||
['72', 'Sarthe'],
|
||||
['73', 'Savoie'],
|
||||
['74', 'Haute-Savoie'],
|
||||
['75', 'Paris'],
|
||||
['76', 'Seine-Maritime'],
|
||||
['77', 'Seine-et-Marne'],
|
||||
['78', 'Yvelines'],
|
||||
['79', 'Deux-Sèvres'],
|
||||
['80', 'Somme'],
|
||||
['81', 'Tarn'],
|
||||
['82', 'Tarn-et-Garonne'],
|
||||
['83', 'Var'],
|
||||
['84', 'Vaucluse'],
|
||||
['85', 'Vendée'],
|
||||
['86', 'Vienne'],
|
||||
['87', 'Haute-Vienne'],
|
||||
['88', 'Vosges'],
|
||||
['89', 'Yonne'],
|
||||
['90', 'Territoire de Belfort'],
|
||||
['91', 'Essonne'],
|
||||
['92', 'Hauts-de-Seine'],
|
||||
['93', 'Seine-Saint-Denis'],
|
||||
['94', 'Val-de-Marne'],
|
||||
['95', "Val-d'Oise"],
|
||||
['971', 'Guadeloupe'],
|
||||
['972', 'Martinique'],
|
||||
['973', 'Guyane'],
|
||||
['974', 'La Réunion'],
|
||||
['976', 'Mayotte'],
|
||||
];
|
||||
|
||||
const LABELS = new Map(DEPARTMENTS);
|
||||
|
||||
export const DEPARTMENT_CODES: readonly string[] = DEPARTMENTS.map(
|
||||
([code]) => code,
|
||||
);
|
||||
|
||||
export function departmentLabel(code: string | null): string | null {
|
||||
if (!code) return null;
|
||||
const label = LABELS.get(code);
|
||||
return label ? `${code} — ${label}` : code;
|
||||
}
|
||||
|
||||
export function isDepartmentCode(value: string): boolean {
|
||||
return LABELS.has(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Départements du régime local d'Alsace-Moselle.
|
||||
*
|
||||
* Deux jours fériés de plus — Vendredi saint et 26 décembre — et un régime de
|
||||
* maintien de salaire distinct. Un établissement de Moselle ne se planifie donc
|
||||
* pas comme un établissement de Meurthe-et-Moselle, à quinze kilomètres de là.
|
||||
*/
|
||||
export const ALSACE_MOSELLE = ['57', '67', '68'] as const;
|
||||
|
||||
export function isAlsaceMoselle(code: string | null): boolean {
|
||||
return code !== null && (ALSACE_MOSELLE as readonly string[]).includes(code);
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import { revalidatePath } from 'next/cache';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { AuthorizationError, can } from '@/domain/access/authorize';
|
||||
import { COUNTRIES } from '@/domain/legal/countries';
|
||||
import { DEPARTMENT_CODES } from '@/domain/legal/departments';
|
||||
import { recordAudit } from '@/server/audit';
|
||||
import { mutate } from '@/server/context';
|
||||
import { encryptOptional } from '@/server/crypto';
|
||||
@@ -44,6 +46,8 @@ const optionalEnum = <T extends readonly string[]>(values: T) =>
|
||||
)
|
||||
.transform((value) => (value === '' ? null : value));
|
||||
|
||||
const COUNTRY_CODES = COUNTRIES.map((country) => country.code);
|
||||
|
||||
const profileInput = z.object({
|
||||
membershipId: z.string().min(1),
|
||||
gender: optionalEnum(['FEMALE', 'MALE', 'UNSPECIFIED'] as const),
|
||||
@@ -52,9 +56,14 @@ const profileInput = z.object({
|
||||
lastName: z.string().trim().min(1, 'Nom requis').max(80),
|
||||
birthDate: optionalText(10),
|
||||
birthPlace: optionalText(120),
|
||||
birthCountry: optionalText(80),
|
||||
birthDepartment: optionalText(80),
|
||||
nationality: optionalText(80),
|
||||
// Codes de liste fermée, pas des libellés. Un dossier saisi avant la liste
|
||||
// porte encore « France » en clair : le sélecteur ne trouve pas d'option
|
||||
// correspondante, affiche « non renseigné », et la première correction du
|
||||
// dossier remplace la valeur par son code. Elle n'est ni perdue en silence
|
||||
// ni opposée à qui vient corriger un champ voisin.
|
||||
birthCountry: optionalEnum(COUNTRY_CODES),
|
||||
birthDepartment: optionalEnum(DEPARTMENT_CODES),
|
||||
nationality: optionalEnum(COUNTRY_CODES),
|
||||
maritalStatus: optionalEnum([
|
||||
'SINGLE',
|
||||
'MARRIED',
|
||||
@@ -87,7 +96,7 @@ const profileInput = z.object({
|
||||
addressLine2: optionalText(180),
|
||||
postalCode: optionalText(12),
|
||||
city: optionalText(120),
|
||||
country: optionalText(80),
|
||||
country: optionalEnum(COUNTRY_CODES),
|
||||
emergencyContactName: optionalText(120),
|
||||
emergencyContactPhone: optionalText(30),
|
||||
});
|
||||
|
||||
@@ -2,6 +2,8 @@ import 'server-only';
|
||||
|
||||
import { cache } from 'react';
|
||||
|
||||
import { nationalityLabel } from '@/domain/legal/countries';
|
||||
|
||||
import { genderShort } from '@/domain/hr/civil-status';
|
||||
import type { RupPerson } from '@/domain/legal/rup';
|
||||
import { peopleWithGaps } from '@/domain/legal/rup';
|
||||
@@ -82,7 +84,9 @@ export const getRegisterData = cache(async function getRegisterData(
|
||||
'',
|
||||
firstName: contract.membership.profile?.firstName ?? '',
|
||||
sex: genderShort(contract.membership.profile?.gender ?? null),
|
||||
nationality: contract.membership.profile?.nationality ?? null,
|
||||
nationality: nationalityLabel(
|
||||
contract.membership.profile?.nationality ?? null,
|
||||
),
|
||||
birthDate: contract.membership.profile?.birthDate ?? null,
|
||||
jobTitle: contract.jobTitleId
|
||||
? (jobTitleNames.get(contract.jobTitleId) ?? null)
|
||||
@@ -152,7 +156,9 @@ export const listRegisterLocations = cache(
|
||||
'',
|
||||
firstName: contract.membership.profile?.firstName ?? '',
|
||||
sex: genderShort(contract.membership.profile?.gender ?? null),
|
||||
nationality: contract.membership.profile?.nationality ?? null,
|
||||
nationality: nationalityLabel(
|
||||
contract.membership.profile?.nationality ?? null,
|
||||
),
|
||||
birthDate: contract.membership.profile?.birthDate ?? null,
|
||||
jobTitle: contract.jobTitleId ? 'x' : null,
|
||||
qualification: contract.classification,
|
||||
|
||||
Reference in new issue
Block a user