@cnamts/synapse 0.0.14-alpha → 0.0.16-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/CookiesSelection/CookiesSelection.d.ts +26 -26
- package/dist/components/Customs/SyInputSelect/SyInputSelect.d.ts +2 -2
- package/dist/components/Customs/SySelect/SySelect.d.ts +24 -12
- package/dist/components/Customs/SySelect/locales.d.ts +3 -0
- package/dist/components/Customs/SyTextField/SyTextField.d.ts +1393 -3
- package/dist/components/DatePicker/DatePicker.d.ts +3532 -22
- package/dist/components/DatePicker/DateTextInput.d.ts +1408 -11
- package/dist/components/DialogBox/config.d.ts +1 -1
- package/dist/components/DownloadBtn/DownloadBtn.d.ts +2 -0
- package/dist/components/LangBtn/LangBtn.d.ts +467 -1
- package/dist/components/LangBtn/config.d.ts +1 -3
- package/dist/components/NirField/NirField.d.ts +2805 -15
- package/dist/components/PasswordField/PasswordField.d.ts +2 -2
- package/dist/components/PeriodField/PeriodField.d.ts +7345 -325
- package/dist/components/PhoneField/PhoneField.d.ts +3 -3
- package/dist/components/SelectBtnField/SelectBtnField.d.ts +1 -1
- package/dist/components/SkipLink/SkipLink.d.ts +3 -2
- package/dist/components/SyAlert/SyAlert.d.ts +72 -1
- package/dist/components/UploadWorkflow/UploadWorkflow.d.ts +26 -26
- package/dist/components/UserMenuBtn/UserMenuBtn.d.ts +2 -0
- package/dist/components/index.d.ts +2 -0
- package/dist/composables/date/useDateFormat.d.ts +2 -2
- package/dist/composables/date/useDateFormatDayjs.d.ts +23 -0
- package/dist/composables/date/useDateInitializationDayjs.d.ts +18 -0
- package/dist/design-system-v3.js +4314 -3987
- package/dist/design-system-v3.umd.cjs +1 -1
- package/dist/style.css +1 -1
- package/dist/vuetifyConfig.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/BackBtn/Accessibilite.stories.ts +4 -0
- package/src/components/BackBtn/BackBtn.vue +2 -1
- package/src/components/BackToTopBtn/Accessibilite.stories.ts +4 -0
- package/src/components/BackToTopBtn/BackToTopBtn.stories.ts +78 -21
- package/src/components/BackToTopBtn/BackToTopBtn.vue +15 -0
- package/src/components/BackToTopBtn/config.ts +2 -2
- package/src/components/BackToTopBtn/tests/__snapshots__/BackToTopBtn.spec.ts.snap +4 -4
- package/src/components/CopyBtn/Accessibilite.stories.ts +4 -0
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.stories.ts +2 -2
- package/src/components/Customs/SyBtnSelect/SyBtnSelect.vue +0 -1
- package/src/components/Customs/SyInputSelect/SyInputSelect.stories.ts +3 -3
- package/src/components/Customs/SyInputSelect/SyInputSelect.vue +4 -4
- package/src/components/Customs/SySelect/SySelect.stories.ts +4 -0
- package/src/components/Customs/SySelect/SySelect.vue +75 -10
- package/src/components/Customs/SySelect/locales.ts +3 -0
- package/src/components/Customs/SySelect/tests/SySelect.spec.ts +24 -2
- package/src/components/Customs/SyTextField/Accessibilite.stories.ts +7 -0
- package/src/components/Customs/SyTextField/SyTextField.stories.ts +14 -1
- package/src/components/Customs/SyTextField/SyTextField.vue +85 -20
- package/src/components/DatePicker/ComplexDatePicker/ComplexDatePicker.vue +795 -0
- package/src/components/DatePicker/DatePicker.stories.ts +432 -1
- package/src/components/DatePicker/DatePicker.vue +143 -76
- package/src/components/DatePicker/DatePickerValidation.mdx +338 -0
- package/src/components/DatePicker/DatePickerValidation.stories.ts +30 -0
- package/src/components/DatePicker/DateTextInput.vue +87 -135
- package/src/components/DatePicker/docExamples/DatePickerBidirectionalValidation.vue +282 -0
- package/src/components/DatePicker/docExamples/DatePickerValidationExamples.vue +535 -0
- package/src/components/DatePicker/tests/DatePicker.spec.ts +33 -32
- package/src/components/DatePicker/tests/DateTextInput.spec.ts +83 -35
- package/src/components/DialogBox/DialogBox.stories.ts +5 -2
- package/src/components/DialogBox/DialogBox.vue +1 -1
- package/src/components/DialogBox/config.ts +1 -1
- package/src/components/DownloadBtn/Accessibilite.stories.ts +4 -0
- package/src/components/DownloadBtn/DownloadBtn.stories.ts +17 -8
- package/src/components/DownloadBtn/DownloadBtn.vue +13 -6
- package/src/components/DownloadBtn/tests/__snapshots__/DownloadBtn.spec.ts.snap +0 -2
- package/src/components/FranceConnectBtn/Accessibilite.stories.ts +4 -0
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderMenuItem/HeaderMenuItem.vue +3 -0
- package/src/components/HeaderBar/HeaderBurgerMenu/HeaderSubMenu/HeaderSubMenu.vue +3 -0
- package/src/components/HeaderBar/HeaderBurgerMenu/menu.scss +19 -0
- package/src/components/HeaderBar/HeaderMenuBtn/HeaderMenuBtn.vue +12 -2
- package/src/components/LangBtn/Accessibilite.stories.ts +4 -0
- package/src/components/LangBtn/LangBtn.stories.ts +1 -4
- package/src/components/LangBtn/LangBtn.vue +68 -9
- package/src/components/LangBtn/config.ts +0 -1
- package/src/components/LangBtn/tests/LangBtn.spec.ts +30 -2
- package/src/components/PageContainer/Accessibilite.stories.ts +36 -23
- package/src/components/PaginatedTable/PaginatedTable.stories.ts +144 -18
- package/src/components/PasswordField/PasswordField.stories.ts +6 -6
- package/src/components/PasswordField/PasswordField.vue +3 -3
- package/src/components/PeriodField/PeriodField.vue +4 -4
- package/src/components/PhoneField/PhoneField.stories.ts +216 -24
- package/src/components/PhoneField/PhoneField.vue +32 -2
- package/src/components/PhoneField/tests/PhoneField.spec.ts +161 -14
- package/src/components/RatingPicker/NumberPicker/NumberPicker.vue +2 -1
- package/src/components/RatingPicker/RatingPicker.stories.ts +1 -1
- package/src/components/SkipLink/Accessibilite.stories.ts +8 -0
- package/src/components/SkipLink/SkipLink.vue +11 -9
- package/src/components/SkipLink/tests/__snapshots__/skipLink.spec.ts.snap +7 -4
- package/src/components/SkipLink/tests/skipLink.spec.ts +120 -6
- package/src/components/SyAlert/Accessibilite.stories.ts +4 -0
- package/src/components/SyAlert/SyAlert.mdx +3 -7
- package/src/components/SyAlert/SyAlert.stories.ts +19 -12
- package/src/components/SyAlert/SyAlert.vue +88 -51
- package/src/components/SyAlert/tests/SyAlert.spec.ts +20 -2
- package/src/components/SyAlert/tests/__snapshots__/SyAlert.spec.ts.snap +83 -75
- package/src/components/UserMenuBtn/UserMenuBtn.stories.ts +56 -0
- package/src/components/UserMenuBtn/UserMenuBtn.vue +4 -2
- package/src/components/UserMenuBtn/tests/UserMenuBtn.spec.ts +41 -0
- package/src/components/index.ts +2 -0
- package/src/composables/date/useDateFormat.ts +17 -1
- package/src/composables/date/useDateFormatDayjs.ts +84 -0
- package/src/composables/date/useDateInitializationDayjs.ts +133 -0
- package/src/composables/rules/useFieldValidation.ts +26 -3
- package/src/stories/Accessibilite/Avancement/Avancement.mdx +12 -0
- package/src/stories/Accessibilite/Avancement/Avancement.stories.ts +134 -0
- package/src/stories/Accessibilite/KitDePreAudit/Echantillonnage.mdx +1 -1
- package/src/stories/GuideDuDev/LesBreackingChanges.mdx +31 -2
- package/src/components/LangBtn/tests/Config.spec.ts +0 -24
|
@@ -2,86 +2,94 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`Alert > render correctly 1`] = `
|
|
4
4
|
<div
|
|
5
|
-
class="
|
|
6
|
-
alert
|
|
7
|
-
alert--success
|
|
8
|
-
text-success
|
|
9
|
-
v-alert
|
|
10
|
-
v-alert--border
|
|
11
|
-
v-alert--border-start
|
|
12
|
-
v-alert--density-default
|
|
13
|
-
v-alert--variant-tonal
|
|
14
|
-
v-theme--light
|
|
15
|
-
"
|
|
5
|
+
class="sy-alert"
|
|
16
6
|
message="message"
|
|
17
7
|
role="alert"
|
|
8
|
+
title="title"
|
|
18
9
|
>
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
10
|
+
<div
|
|
11
|
+
class="
|
|
12
|
+
alert
|
|
13
|
+
alert--success
|
|
14
|
+
text-success
|
|
15
|
+
v-alert
|
|
16
|
+
v-alert--border
|
|
17
|
+
v-alert--border-start
|
|
18
|
+
v-alert--density-default
|
|
19
|
+
v-alert--variant-tonal
|
|
20
|
+
v-theme--light
|
|
21
|
+
"
|
|
22
|
+
message="message"
|
|
23
|
+
role="alert"
|
|
24
|
+
>
|
|
25
|
+
<!---->
|
|
26
|
+
<span class="v-alert__underlay"></span>
|
|
27
|
+
<div class="v-alert__border"></div>
|
|
28
|
+
<div class="v-alert__prepend">
|
|
29
|
+
<i
|
|
30
|
+
aria-hidden="true"
|
|
31
|
+
class="
|
|
32
|
+
11.59L6
|
|
33
|
+
12
|
|
34
|
+
12
|
|
35
|
+
12
|
|
36
|
+
12
|
|
37
|
+
12
|
|
38
|
+
12
|
|
39
|
+
12S6.5
|
|
40
|
+
12S7.59
|
|
41
|
+
13L10
|
|
42
|
+
14.17L7.41
|
|
43
|
+
16.41
|
|
44
|
+
16.41
|
|
45
|
+
17.5
|
|
46
|
+
17.5
|
|
47
|
+
17L18
|
|
48
|
+
2
|
|
49
|
+
2
|
|
50
|
+
2
|
|
51
|
+
2
|
|
52
|
+
20
|
|
53
|
+
20
|
|
54
|
+
20
|
|
55
|
+
20
|
|
56
|
+
20C7.59
|
|
57
|
+
20M16.59
|
|
58
|
+
22
|
|
59
|
+
22
|
|
60
|
+
22
|
|
61
|
+
22
|
|
62
|
+
2C6.5
|
|
63
|
+
2M12
|
|
64
|
+
4
|
|
65
|
+
4
|
|
66
|
+
4
|
|
67
|
+
4
|
|
68
|
+
6.5
|
|
69
|
+
7.58L10
|
|
70
|
+
7.58Z
|
|
71
|
+
7.59
|
|
72
|
+
9L16.59
|
|
73
|
+
M12
|
|
74
|
+
alert-icon
|
|
75
|
+
mdi
|
|
76
|
+
notranslate
|
|
77
|
+
v-icon
|
|
78
|
+
v-theme--light
|
|
79
|
+
"
|
|
80
|
+
role="presentation"
|
|
81
|
+
style="font-size: 1.5rem; height: 1.5rem; width: 1.5rem;"
|
|
82
|
+
></i>
|
|
80
83
|
</div>
|
|
84
|
+
<div class="v-alert__content">
|
|
85
|
+
<div class="v-alert-title">
|
|
86
|
+
title
|
|
87
|
+
</div>
|
|
88
|
+
<!---->
|
|
89
|
+
slot content
|
|
90
|
+
</div>
|
|
91
|
+
<!---->
|
|
81
92
|
<!---->
|
|
82
|
-
slot content
|
|
83
93
|
</div>
|
|
84
|
-
<!---->
|
|
85
|
-
<!---->
|
|
86
94
|
</div>
|
|
87
95
|
`;
|
|
@@ -44,6 +44,13 @@ const meta = {
|
|
|
44
44
|
type: { summary: 'string' },
|
|
45
45
|
},
|
|
46
46
|
},
|
|
47
|
+
'logoutText': {
|
|
48
|
+
control: 'text',
|
|
49
|
+
description: 'Texte dans le bouton de déconnexion',
|
|
50
|
+
table: {
|
|
51
|
+
type: { summary: 'string' },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
47
54
|
'additionalInformation': {
|
|
48
55
|
control: 'text',
|
|
49
56
|
description: 'Informations supplémentaires sur l\'utilisateur (ex: rôle, service, etc.)',
|
|
@@ -376,6 +383,55 @@ export const CustomFullName: Story = {
|
|
|
376
383
|
},
|
|
377
384
|
}
|
|
378
385
|
|
|
386
|
+
export const CustomLogoutText: Story = {
|
|
387
|
+
parameters: {
|
|
388
|
+
sourceCode: [
|
|
389
|
+
{
|
|
390
|
+
name: 'Template',
|
|
391
|
+
code: `<template>
|
|
392
|
+
<UserMenuBtn
|
|
393
|
+
v-model="selected"
|
|
394
|
+
:menu-items="menuItems"
|
|
395
|
+
:logout-text="Déconnexion"
|
|
396
|
+
/>
|
|
397
|
+
</template>`,
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
name: 'Script',
|
|
401
|
+
code: `<script setup lang="ts">
|
|
402
|
+
import { ref } from 'vue'
|
|
403
|
+
import { UserMenuBtn } from '@cnamts/synapse'
|
|
404
|
+
|
|
405
|
+
const selected = ref(null)
|
|
406
|
+
const menuItems = ref([
|
|
407
|
+
{ text: 'Administration', value: 'Administration' },
|
|
408
|
+
{ text: 'Profil', value: 'Profil' },
|
|
409
|
+
{ text: 'Paramètres', value: 'Paramètres' },
|
|
410
|
+
])
|
|
411
|
+
|
|
412
|
+
const logoutText = ref('Déconnexion')
|
|
413
|
+
</script>`,
|
|
414
|
+
},
|
|
415
|
+
],
|
|
416
|
+
},
|
|
417
|
+
args: {
|
|
418
|
+
...Default.args,
|
|
419
|
+
logoutText: 'Déconnexion',
|
|
420
|
+
},
|
|
421
|
+
render: (args) => {
|
|
422
|
+
return {
|
|
423
|
+
components: { UserMenuBtn },
|
|
424
|
+
setup() {
|
|
425
|
+
return { args }
|
|
426
|
+
},
|
|
427
|
+
template: `
|
|
428
|
+
<div class="pa-4">
|
|
429
|
+
<UserMenuBtn v-bind="args"/>
|
|
430
|
+
</div>`,
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
}
|
|
434
|
+
|
|
379
435
|
export const CustomAdditionalInformation: Story = {
|
|
380
436
|
parameters: {
|
|
381
437
|
sourceCode: [
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
hideLogoutBtn?: boolean
|
|
16
16
|
isMobileView?: boolean
|
|
17
17
|
hideUserIcon?: boolean
|
|
18
|
+
logoutText?: string
|
|
18
19
|
}>(), {
|
|
19
20
|
menuItems: () => [],
|
|
20
21
|
additionalInformation: 'Information supplémentaire',
|
|
@@ -22,6 +23,7 @@
|
|
|
22
23
|
hideLogoutBtn: false,
|
|
23
24
|
isMobileView: false,
|
|
24
25
|
hideUserIcon: false,
|
|
26
|
+
logoutText: 'Logout',
|
|
25
27
|
})
|
|
26
28
|
|
|
27
29
|
const modelValue = defineModel<MenuItem | null>({
|
|
@@ -74,8 +76,8 @@
|
|
|
74
76
|
class="mr-4"
|
|
75
77
|
v-bind="options['logoutIcon']"
|
|
76
78
|
/>
|
|
77
|
-
<VListItemTitle>
|
|
78
|
-
|
|
79
|
+
<VListItemTitle class="logout">
|
|
80
|
+
{{ props.logoutText }}
|
|
79
81
|
</VListItemTitle>
|
|
80
82
|
</div>
|
|
81
83
|
</VListItem>
|
|
@@ -122,4 +122,45 @@ describe('UserMenuBtn', () => {
|
|
|
122
122
|
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
123
123
|
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['test-value'])
|
|
124
124
|
})
|
|
125
|
+
|
|
126
|
+
it('possède la prop logoutText custom', async () => {
|
|
127
|
+
const customLogoutText = 'Déconnexion'
|
|
128
|
+
const wrapper = mount(UserMenuBtn, {
|
|
129
|
+
global: {
|
|
130
|
+
plugins: [vuetify],
|
|
131
|
+
},
|
|
132
|
+
props: {
|
|
133
|
+
modelValue: null,
|
|
134
|
+
logoutText: customLogoutText,
|
|
135
|
+
hideLogoutBtn: false,
|
|
136
|
+
menuItems: [{ text: 'Item 1', value: 'item1' }],
|
|
137
|
+
fullName: 'John Doe',
|
|
138
|
+
isMobileView: false,
|
|
139
|
+
hideUserIcon: false,
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
// Vérifier directement que la prop est correctement passée au composant
|
|
144
|
+
expect(wrapper.props('logoutText')).toBe(customLogoutText)
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
it('possède la prop logoutText par défaut', async () => {
|
|
148
|
+
const defaultLogoutText = 'Logout'
|
|
149
|
+
const wrapper = mount(UserMenuBtn, {
|
|
150
|
+
global: {
|
|
151
|
+
plugins: [vuetify],
|
|
152
|
+
},
|
|
153
|
+
props: {
|
|
154
|
+
modelValue: null,
|
|
155
|
+
hideLogoutBtn: false,
|
|
156
|
+
menuItems: [{ text: 'Item 1', value: 'item1' }],
|
|
157
|
+
fullName: 'John Doe',
|
|
158
|
+
isMobileView: false,
|
|
159
|
+
hideUserIcon: false,
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Vérifier directement que la prop est correctement passée au composant
|
|
164
|
+
expect(wrapper.props('logoutText')).toBe(defaultLogoutText)
|
|
165
|
+
})
|
|
125
166
|
})
|
package/src/components/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { useFieldValidation } from '../composables/rules/useFieldValidation'
|
|
|
3
3
|
export type { RuleOptions } from '../composables/rules/useFieldValidation'
|
|
4
4
|
export { useNotificationService } from '../services/NotificationService'
|
|
5
5
|
export { useValidation } from '../composables/validation/useValidation'
|
|
6
|
+
export { useDateFormat } from '../composables/date/useDateFormatDayjs'
|
|
6
7
|
export { default as BackBtn } from './BackBtn/BackBtn.vue'
|
|
7
8
|
export { default as BackToTopBtn } from './BackToTopBtn/BackToTopBtn.vue'
|
|
8
9
|
export { default as ChipList } from './ChipList/ChipList.vue'
|
|
@@ -42,6 +43,7 @@ export { default as HeaderMenuBtn } from './HeaderBar/HeaderMenuBtn/HeaderMenuBt
|
|
|
42
43
|
export { default as HeaderLoading } from './HeaderLoading/HeaderLoading.vue'
|
|
43
44
|
export { default as HeaderNavigationBar } from './HeaderNavigationBar/HeaderNavigationBar.vue'
|
|
44
45
|
export { default as HeaderToolbar } from './HeaderToolbar/HeaderToolbar.vue'
|
|
46
|
+
export { indicatifs } from './PhoneField/indicatifs'
|
|
45
47
|
export { default as LangBtn } from './LangBtn/LangBtn.vue'
|
|
46
48
|
export { default as Logo } from './Logo/Logo.vue'
|
|
47
49
|
export { default as LogoBrandSection } from './LogoBrandSection/LogoBrandSection.vue'
|
|
@@ -8,9 +8,25 @@
|
|
|
8
8
|
* @param format - Le format de la date (ex: 'DD/MM/YYYY')
|
|
9
9
|
* @returns Un objet Date ou null si la chaîne n'est pas valide
|
|
10
10
|
*/
|
|
11
|
-
export const parseDate = (dateString: string, format: string): Date | null => {
|
|
11
|
+
export const parseDate = (dateString: string | Date | null, format: string): Date | null => {
|
|
12
|
+
// Si dateString est null ou undefined, retourner null
|
|
12
13
|
if (!dateString) return null
|
|
13
14
|
|
|
15
|
+
// Si dateString est déjà un objet Date, le retourner directement
|
|
16
|
+
if (dateString instanceof Date) {
|
|
17
|
+
return dateString
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Si dateString n'est pas une chaîne de caractères, convertir en chaîne ou retourner null
|
|
21
|
+
if (typeof dateString !== 'string') {
|
|
22
|
+
try {
|
|
23
|
+
dateString = String(dateString)
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
14
30
|
// Créer un mapping des positions des éléments de date selon le format
|
|
15
31
|
const separator = format.includes('/') ? '/' : format.includes('-') ? '-' : '.'
|
|
16
32
|
const parts = format.split(separator)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable pour le formatage et le parsing des dates avec dayjs
|
|
3
|
+
*/
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
|
6
|
+
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
|
7
|
+
import utc from 'dayjs/plugin/utc'
|
|
8
|
+
import timezone from 'dayjs/plugin/timezone'
|
|
9
|
+
import 'dayjs/locale/fr'
|
|
10
|
+
|
|
11
|
+
// Initialiser les plugins dayjs
|
|
12
|
+
dayjs.extend(customParseFormat)
|
|
13
|
+
dayjs.extend(localizedFormat)
|
|
14
|
+
dayjs.extend(utc)
|
|
15
|
+
dayjs.extend(timezone)
|
|
16
|
+
dayjs.locale('fr')
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Parse une chaîne de caractères en objet Date selon un format spécifié
|
|
20
|
+
* @param dateString - La chaîne de caractères à parser
|
|
21
|
+
* @param format - Le format de la date (ex: 'DD/MM/YYYY')
|
|
22
|
+
* @returns Un objet Date ou null si la chaîne n'est pas valide
|
|
23
|
+
*/
|
|
24
|
+
export const parseDate = (dateString: string | Date | null, format: string): Date | null => {
|
|
25
|
+
// Si dateString est null ou undefined, retourner null
|
|
26
|
+
if (!dateString) return null
|
|
27
|
+
|
|
28
|
+
// Si dateString est déjà un objet Date, le retourner directement
|
|
29
|
+
if (dateString instanceof Date) {
|
|
30
|
+
return dateString
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Si dateString n'est pas une chaîne de caractères, convertir en chaîne ou retourner null
|
|
34
|
+
if (typeof dateString !== 'string') {
|
|
35
|
+
try {
|
|
36
|
+
dateString = String(dateString)
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Utiliser dayjs pour parser la date
|
|
44
|
+
const parsedDate = dayjs(dateString, format, true)
|
|
45
|
+
|
|
46
|
+
if (!parsedDate.isValid()) return null
|
|
47
|
+
|
|
48
|
+
// Extraire les composants de la date pour créer une date UTC
|
|
49
|
+
// Cela évite les problèmes de décalage de fuseau horaire
|
|
50
|
+
return dayjs.utc()
|
|
51
|
+
.year(parsedDate.year())
|
|
52
|
+
.month(parsedDate.month())
|
|
53
|
+
.date(parsedDate.date())
|
|
54
|
+
.hour(0)
|
|
55
|
+
.minute(0)
|
|
56
|
+
.second(0)
|
|
57
|
+
.millisecond(0)
|
|
58
|
+
.toDate()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Formate une date selon un format spécifié
|
|
63
|
+
* @param date - La date à formater
|
|
64
|
+
* @param format - Le format de sortie (ex: 'DD/MM/YYYY')
|
|
65
|
+
* @returns La date formatée en chaîne de caractères
|
|
66
|
+
*/
|
|
67
|
+
export const formatDate = (date: Date | null, format: string): string => {
|
|
68
|
+
if (!date) return ''
|
|
69
|
+
|
|
70
|
+
return dayjs(date).format(format)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Hook composable pour le formatage et le parsing des dates
|
|
75
|
+
* @returns Fonctions de formatage et parsing de dates
|
|
76
|
+
*/
|
|
77
|
+
export function useDateFormat() {
|
|
78
|
+
return {
|
|
79
|
+
parseDate,
|
|
80
|
+
formatDate,
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export default useDateFormat
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composable pour l'initialisation des dates dans le DatePicker avec dayjs
|
|
3
|
+
*/
|
|
4
|
+
import dayjs from 'dayjs'
|
|
5
|
+
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
|
6
|
+
import utc from 'dayjs/plugin/utc'
|
|
7
|
+
import timezone from 'dayjs/plugin/timezone'
|
|
8
|
+
import 'dayjs/locale/fr'
|
|
9
|
+
|
|
10
|
+
// Initialiser les plugins dayjs
|
|
11
|
+
dayjs.extend(customParseFormat)
|
|
12
|
+
dayjs.extend(utc)
|
|
13
|
+
dayjs.extend(timezone)
|
|
14
|
+
dayjs.locale('fr')
|
|
15
|
+
|
|
16
|
+
// Types
|
|
17
|
+
export type DateValue = string | [string, string] | null
|
|
18
|
+
export type DateInput = string | string[] | null | object
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Parse une date en utilisant dayjs et retourne une Date UTC
|
|
22
|
+
* @param dateStr - La chaîne de date à analyser
|
|
23
|
+
* @param format - Le format de la date
|
|
24
|
+
* @returns Une Date ou null si la date est invalide
|
|
25
|
+
*/
|
|
26
|
+
const parseToUTCDate = (dateStr: string, format: string): Date | null => {
|
|
27
|
+
if (!dayjs(dateStr, format).isValid()) return null
|
|
28
|
+
|
|
29
|
+
// Extraire les composants de la date à partir de la chaîne
|
|
30
|
+
const dateParts = dayjs(dateStr, format)
|
|
31
|
+
|
|
32
|
+
// Créer une date UTC avec les composants exacts pour éviter les décalages de fuseau horaire
|
|
33
|
+
// Utiliser set pour définir explicitement l'année, le mois et le jour
|
|
34
|
+
return dayjs.utc()
|
|
35
|
+
.year(dateParts.year())
|
|
36
|
+
.month(dateParts.month())
|
|
37
|
+
.date(dateParts.date())
|
|
38
|
+
.hour(0)
|
|
39
|
+
.minute(0)
|
|
40
|
+
.second(0)
|
|
41
|
+
.millisecond(0)
|
|
42
|
+
.toDate()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Initialise les dates sélectionnées à partir d'une valeur d'entrée
|
|
47
|
+
* @param modelValue - La valeur d'entrée (peut être une chaîne, un tableau, null ou un objet)
|
|
48
|
+
* @param displayFormat - Le format d'affichage des dates
|
|
49
|
+
* @param returnFormat - Le format de retour des dates (optionnel)
|
|
50
|
+
* @returns Une date, un tableau de dates ou null
|
|
51
|
+
*/
|
|
52
|
+
export const initializeSelectedDates = (
|
|
53
|
+
modelValue: DateInput | null,
|
|
54
|
+
displayFormat: string,
|
|
55
|
+
returnFormat: string = '',
|
|
56
|
+
): Date | Date[] | null => {
|
|
57
|
+
if (!modelValue) return null
|
|
58
|
+
|
|
59
|
+
// Déterminer le format à utiliser pour l'analyse
|
|
60
|
+
const parseFormat = returnFormat || displayFormat
|
|
61
|
+
|
|
62
|
+
if (Array.isArray(modelValue)) {
|
|
63
|
+
if (modelValue.length >= 2) {
|
|
64
|
+
// Essayer d'abord avec le format de retour, puis avec le format d'affichage
|
|
65
|
+
let dates = [
|
|
66
|
+
parseToUTCDate(modelValue[0], parseFormat),
|
|
67
|
+
parseToUTCDate(modelValue[1], parseFormat),
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
// Si l'une des dates est invalide avec le format de retour, essayer avec le format d'affichage
|
|
71
|
+
if (dates.some(date => date === null) && returnFormat) {
|
|
72
|
+
dates = [
|
|
73
|
+
parseToUTCDate(modelValue[0], displayFormat),
|
|
74
|
+
parseToUTCDate(modelValue[1], displayFormat),
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Vérifie si l'une des dates est toujours invalide
|
|
79
|
+
if (dates.some(date => date === null)) {
|
|
80
|
+
return []
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Vérifie si la première date est après la seconde
|
|
84
|
+
if (dates[0] && dates[1] && dayjs(dates[0]).isAfter(dayjs(dates[1]))) {
|
|
85
|
+
return []
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Filtrer les dates nulles et convertir en tableau de Date
|
|
89
|
+
return dates.filter((date): date is Date => date !== null)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (modelValue.length === 1) {
|
|
93
|
+
// Essayer d'abord avec le format de retour, puis avec le format d'affichage
|
|
94
|
+
let date = parseToUTCDate(modelValue[0], parseFormat)
|
|
95
|
+
|
|
96
|
+
// Si la date est invalide avec le format de retour, essayer avec le format d'affichage
|
|
97
|
+
if (date === null && returnFormat) {
|
|
98
|
+
date = parseToUTCDate(modelValue[0], displayFormat)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return date === null ? [] : [date]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return []
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Si modelValue est un objet, on le convertit en chaîne
|
|
108
|
+
if (typeof modelValue === 'object') {
|
|
109
|
+
return null
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Essayer d'abord avec le format de retour, puis avec le format d'affichage
|
|
113
|
+
let date = parseToUTCDate(modelValue, parseFormat)
|
|
114
|
+
|
|
115
|
+
// Si la date est invalide avec le format de retour, essayer avec le format d'affichage
|
|
116
|
+
if (date === null && returnFormat) {
|
|
117
|
+
date = parseToUTCDate(modelValue, displayFormat)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return date
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Hook composable pour l'initialisation des dates
|
|
125
|
+
* @returns Fonction d'initialisation des dates
|
|
126
|
+
*/
|
|
127
|
+
export function useDateInitialization() {
|
|
128
|
+
return {
|
|
129
|
+
initializeSelectedDates,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default useDateInitialization
|
|
@@ -177,16 +177,30 @@ export function useFieldValidation() {
|
|
|
177
177
|
|
|
178
178
|
case 'notBeforeToday': {
|
|
179
179
|
const dateValue = new Date(value)
|
|
180
|
+
// Réinitialiser l'heure à minuit pour ne comparer que les dates
|
|
181
|
+
dateValue.setHours(0, 0, 0, 0)
|
|
182
|
+
|
|
183
|
+
// Créer une date aujourd'hui à minuit pour comparaison
|
|
184
|
+
const today = new Date()
|
|
185
|
+
today.setHours(0, 0, 0, 0)
|
|
186
|
+
|
|
180
187
|
return createValidationResult(
|
|
181
|
-
dateValue >=
|
|
188
|
+
dateValue >= today,
|
|
182
189
|
options.message || options.warningMessage || `${identifier} ne peut pas être antérieur à aujourd'hui.`,
|
|
183
190
|
)
|
|
184
191
|
}
|
|
185
192
|
|
|
186
193
|
case 'notAfterToday': {
|
|
187
194
|
const dateValue = new Date(value)
|
|
195
|
+
// Réinitialiser l'heure à minuit pour ne comparer que les dates
|
|
196
|
+
dateValue.setHours(0, 0, 0, 0)
|
|
197
|
+
|
|
198
|
+
// Créer une date aujourd'hui à minuit pour comparaison
|
|
199
|
+
const today = new Date()
|
|
200
|
+
today.setHours(0, 0, 0, 0)
|
|
201
|
+
|
|
188
202
|
return createValidationResult(
|
|
189
|
-
dateValue <=
|
|
203
|
+
dateValue <= today,
|
|
190
204
|
options.message || options.warningMessage || `${identifier} ne peut pas être postérieur à aujourd'hui.`,
|
|
191
205
|
)
|
|
192
206
|
}
|
|
@@ -262,8 +276,17 @@ export function useFieldValidation() {
|
|
|
262
276
|
return { error: 'Date de référence invalide' }
|
|
263
277
|
}
|
|
264
278
|
|
|
279
|
+
// Normaliser les deux dates en réinitialisant les heures/minutes/secondes
|
|
280
|
+
dateValue.setHours(0, 0, 0, 0)
|
|
281
|
+
referenceDate.setHours(0, 0, 0, 0)
|
|
282
|
+
|
|
283
|
+
// Comparer les dates normalisées
|
|
284
|
+
const isSameDate = dateValue.getFullYear() === referenceDate.getFullYear()
|
|
285
|
+
&& dateValue.getMonth() === referenceDate.getMonth()
|
|
286
|
+
&& dateValue.getDate() === referenceDate.getDate()
|
|
287
|
+
|
|
265
288
|
return createValidationResult(
|
|
266
|
-
|
|
289
|
+
isSameDate,
|
|
267
290
|
options.message || options.warningMessage || `${identifier} doit être exactement le ${options.date}.`,
|
|
268
291
|
)
|
|
269
292
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Story, Meta } from '@storybook/addon-docs/blocks';
|
|
2
|
+
import * as AvancementStories from './Avancement.stories.ts';
|
|
3
|
+
|
|
4
|
+
<Meta of={AvancementStories} />
|
|
5
|
+
|
|
6
|
+
# Outils de pré-audit
|
|
7
|
+
|
|
8
|
+
<Story of={AvancementStories.PreAudit} />
|
|
9
|
+
|
|
10
|
+
# Contrôle manuel
|
|
11
|
+
|
|
12
|
+
<Story of={AvancementStories.Manuel} />
|