codevdesign 1.0.31 → 1.0.33
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/assets/csqc.css +259 -0
- package/composants/csqcAide.vue +55 -0
- package/composants/csqcAlerteErreur.vue +87 -0
- package/composants/csqcChaise/chaiseConteneur.vue +367 -0
- package/composants/csqcChaise/chaiseItem.vue +54 -0
- package/composants/csqcCodeBudgetaireGenerique.vue +336 -0
- package/composants/csqcConfirmation.vue +75 -0
- package/composants/csqcDate.vue +57 -0
- package/composants/csqcDialogue.vue +118 -0
- package/composants/csqcEditeurTexteRiche.vue +380 -0
- package/composants/csqcEntete.vue +163 -0
- package/composants/csqcImportCSV.vue +125 -0
- package/composants/csqcModaleSaisie.vue +95 -0
- package/composants/csqcOptionSwitch.vue +120 -0
- package/composants/csqcRecherche.vue +213 -0
- package/composants/csqcRechercheUtilisateur.vue +197 -0
- package/composants/csqcSnackbar.vue +88 -0
- package/composants/csqcTable/csqcTable.vue +383 -0
- package/composants/csqcTable/csqcTableExportExcel.vue +58 -0
- package/composants/csqcTable/csqcTableModaleChoixColonnes.vue +586 -0
- package/composants/csqcTexteBilingue.vue +175 -0
- package/composants/csqcTiroir.vue +156 -0
- package/composants/gabarit/csqcMenu.vue +281 -0
- package/composants/gabarit/pivEntete.vue +205 -0
- package/composants/gabarit/pivPiedPage.vue +70 -0
- package/composants/gabarit/vueDefault.vue +5 -0
- package/composants/validateurs.ts +183 -0
- package/enums/choixLangue.ts +10 -0
- package/index.ts +74 -0
- package/locales/en.json +100 -0
- package/locales/fr.json +101 -0
- package/modeles/apiReponse.ts +12 -0
- package/modeles/assurancesAssuranceGeneraleGrics.ts +10 -0
- package/modeles/assurancesAssurancePersonnelleGrics.ts +13 -0
- package/modeles/assurancesContratGrics.ts +14 -0
- package/modeles/assurancesDetailsPrimeReguliereGrics.ts +12 -0
- package/modeles/assurancesDonneesAssureurGrics.ts +13 -0
- package/modeles/assurancesEmployeGrics.ts +11 -0
- package/modeles/assurancesGrics.ts +14 -0
- package/modeles/assurancesRegimeAssuranceGrics.ts +9 -0
- package/modeles/assurancesRegimeBaseEmployeurGrics.ts +9 -0
- package/modeles/assurancesRegimeBaseGrics.ts +9 -0
- package/modeles/composants/csqcMenuModele.ts +18 -0
- package/modeles/composants/datatableColonne.ts +31 -0
- package/modeles/composants/snackbar.ts +18 -0
- package/modeles/data.ts +24 -0
- package/modeles/droitIntervention.ts +14 -0
- package/modeles/employeAdresseGrics.ts +13 -0
- package/modeles/employeAdressesPersonnellesGrics.ts +12 -0
- package/modeles/employeAffectationCorpsEmploiGrics.ts +9 -0
- package/modeles/employeBanquesCongeBanqueGrics.ts +9 -0
- package/modeles/employeBanquesCongeGrics.ts +14 -0
- package/modeles/employeBanquesCongeRegimeAbsenceGrics.ts +9 -0
- package/modeles/employeCourrielsPersonnels.ts +9 -0
- package/modeles/employeCourrielsProfessionnels.ts +9 -0
- package/modeles/employeEmploisCategorieGrics.ts +9 -0
- package/modeles/employeEmploisClasseGrics.ts +9 -0
- package/modeles/employeEmploisCorpsEmploiGrics.ts +9 -0
- package/modeles/employeEmploisEtatEmploiGrics.ts +9 -0
- package/modeles/employeEmploisGrics.ts +37 -0
- package/modeles/employeEmploisGroupePaieGrics.ts +9 -0
- package/modeles/employeEmploisLieuTravailPrincipalGrics.ts +10 -0
- package/modeles/employeEmploisLieuxTravailSecondairesGrics.ts +10 -0
- package/modeles/employeEmploisRegimeAbsenceGrics.ts +9 -0
- package/modeles/employeEmploisSecteurGrics.ts +9 -0
- package/modeles/employeEmploisStatutEngagementGrics.ts +9 -0
- package/modeles/employeExperienceEmploiGrics.ts +9 -0
- package/modeles/employeExperienceEmployeGrics.ts +12 -0
- package/modeles/employeExperienceExperiencesGrics.ts +11 -0
- package/modeles/employeExperienceExperiencesTotalesGrics.ts +15 -0
- package/modeles/employeExperienceGrics.ts +17 -0
- package/modeles/employeGrics.ts +31 -0
- package/modeles/employeMinsLsCodev.ts +10 -0
- package/modeles/employeTelephoneGrics.ts +12 -0
- package/modeles/employeTelephonesPersonnelsGrics.ts +11 -0
- package/modeles/employeTelephonesProfessionnelsGrics.ts +11 -0
- package/modeles/groupeCE.ts +14 -0
- package/modeles/groupeCEIntervalle.ts +13 -0
- package/modeles/historiquesAbsenceBanqueGrics.ts +9 -0
- package/modeles/historiquesAbsenceGrics.ts +21 -0
- package/modeles/historiquesAbsenceLieuTravailGrics.ts +9 -0
- package/modeles/historiquesAbsenceSousBanqueGrics.ts +9 -0
- package/modeles/intervention.ts +35 -0
- package/modeles/motifsAbsenceBanque.ts +9 -0
- package/modeles/motifsAbsenceGrics.ts +17 -0
- package/modeles/motifsAbsenceRegimeAbsence.ts +9 -0
- package/modeles/motifsAbsenceSousMotifs.ts +9 -0
- package/modeles/motifsAbsenceTraitementBanques.ts +11 -0
- package/modeles/notificationGabaritDefaut.ts +10 -0
- package/modeles/response.ts +12 -0
- package/modeles/role.ts +31 -0
- package/modeles/roleMin.ts +12 -0
- package/modeles/syndicat.ts +26 -0
- package/modeles/syndicatGroupeCe.ts +10 -0
- package/modeles/syndicatResponsable.ts +15 -0
- package/modeles/syndicatUnite.ts +10 -0
- package/modeles/typeEnseignement.ts +14 -0
- package/modeles/typeTelephone.ts +12 -0
- package/modeles/unite.ts +23 -0
- package/modeles/uniteTypeEnseignement.ts +12 -0
- package/modeles/utilisateur.ts +15 -0
- package/outils/appAxios.ts +116 -0
- package/outils/csqcOutils.ts +366 -0
- package/outils/rafraichisseurToken.ts +187 -0
- package/package.json +11 -15
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/** ATTENTION
|
|
2
|
+
* Ne pas ajouter ou modifier de fonctions ici. Créer son propre fichier d'outils pour votre app.
|
|
3
|
+
* Ces outils sont des outils communs.
|
|
4
|
+
* */
|
|
5
|
+
|
|
6
|
+
class CSQCOutils {
|
|
7
|
+
racineLocalStorage = 'csqc_CodevDesign'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Concatène une string avec le marqueur de séparation demandée.
|
|
11
|
+
* @param texteOrigine Le texte d'origine
|
|
12
|
+
* @param texteAjoute Le texte a concaténer
|
|
13
|
+
* @param separateur (Default: ', ') Le séparateur à ajouter entre les deux textes.
|
|
14
|
+
* */
|
|
15
|
+
ConcateneTexte(texteOrigine: string, texteAjoute: string, separateur: string = ', ') {
|
|
16
|
+
let retour = texteOrigine
|
|
17
|
+
if (this.TexteVide(texteOrigine) === false) {
|
|
18
|
+
retour += separateur
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return retour + texteAjoute
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Retourne true si le texte n'est pas défini ou s'il est null ou vide. Tous les espaces sont retirés par défaut.
|
|
26
|
+
* @param texte Le texte a inspecter.
|
|
27
|
+
* @param retirerEspace (Default: true) Si true, les espaces seront enlevés du texte
|
|
28
|
+
*/
|
|
29
|
+
TexteVide(texte?: string | null, retirerEspace = true) {
|
|
30
|
+
let retour = texte === undefined || texte == null || texte === ''
|
|
31
|
+
if (retour === false && retirerEspace === true) {
|
|
32
|
+
const temp = texte
|
|
33
|
+
retour = temp?.replace(' ', '') === ''
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return retour
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Pad un texte avec des caractères à gauche
|
|
41
|
+
* @param texte Texte à padder
|
|
42
|
+
* @param caractere Caractère à utiliser
|
|
43
|
+
* @param longueurFinale Longueur finale à atteindre
|
|
44
|
+
*/
|
|
45
|
+
PadGauche(texte: string, caractere: string, longueurFinale: number) {
|
|
46
|
+
while (texte.length < longueurFinale) {
|
|
47
|
+
texte = caractere + texte
|
|
48
|
+
}
|
|
49
|
+
return texte
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pad un texte avec des caractères à droite
|
|
54
|
+
* @param texte Texte à padder
|
|
55
|
+
* @param caractere Caractère à utiliser
|
|
56
|
+
* @param longueurFinale Longueur finale à atteindre
|
|
57
|
+
*/
|
|
58
|
+
PadDroit(texte: string, caractere: string, longueurFinale: number) {
|
|
59
|
+
while (texte.length < longueurFinale) {
|
|
60
|
+
texte += caractere
|
|
61
|
+
}
|
|
62
|
+
return texte
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Transforme un nombre en string en arrondissant au nombre maximal de décimales.
|
|
67
|
+
* Ex : 14,57582 avec un affichage sur 2 décimales sera 15,58
|
|
68
|
+
* Si le nombre est 14,6 avec 2 décimales, le retour sera 14,6
|
|
69
|
+
* @param nombre Nombre à transformer en string
|
|
70
|
+
* @param nbDecimale Nombre maximal de décimales à afficher
|
|
71
|
+
*/
|
|
72
|
+
DecimaleVariable(nombre: number, nbDecimale: number) {
|
|
73
|
+
let valeurRetour = 0
|
|
74
|
+
while (valeurRetour === 0 && nbDecimale > 0) {
|
|
75
|
+
const multiplicateur = 10 ** (nbDecimale - 1)
|
|
76
|
+
valeurRetour = (nombre * multiplicateur) % 1
|
|
77
|
+
if (valeurRetour === 0) {
|
|
78
|
+
nbDecimale -= 1
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let retour = nombre.toFixed(nbDecimale)
|
|
82
|
+
// Retirer les derniers 0 ayant pu s'inscruster dû aux arrondissements.
|
|
83
|
+
while (retour.length > 1 && retour.lastIndexOf('0') === retour.length - 1) {
|
|
84
|
+
retour = retour.substring(0, retour.length - 1)
|
|
85
|
+
}
|
|
86
|
+
if (retour.lastIndexOf('.') === retour.length - 1) {
|
|
87
|
+
retour = retour.substring(0, retour.length - 1) // Retirer le point s'il termine le nombre
|
|
88
|
+
}
|
|
89
|
+
return retour.replace('.', ',')
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Transforme un nombre en string en y mettant le nombre de décimale à afficher.
|
|
94
|
+
* @param nombre Nombre à transformer en string
|
|
95
|
+
* @param nbDecimale Nombre de décimales à afficher
|
|
96
|
+
*/
|
|
97
|
+
DecimaleNonVariable(nombre: number, nbDecimale: number) {
|
|
98
|
+
return this.DecimaleVersString(nombre, nbDecimale, false).replace('.', ',')
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Transforme un boolean en string avec la première lettre minuscule.
|
|
103
|
+
* @param bool Bool à mettre en string
|
|
104
|
+
*/
|
|
105
|
+
BoolVersString(bool: boolean) {
|
|
106
|
+
if (bool === true) {
|
|
107
|
+
return 'true'
|
|
108
|
+
}
|
|
109
|
+
return 'false'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Transforme une string en nombre.
|
|
114
|
+
* @param texte string à mettre en number. Peut-être un entier ou une décimale avec virgule ou point
|
|
115
|
+
*/
|
|
116
|
+
StringVersNombre(texte: string) {
|
|
117
|
+
texte = texte.replace(',', '.') // On remplace les virgules par des points.
|
|
118
|
+
return Number(texte)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Transforme une string en number avec des options pour le nombre de décimales.
|
|
123
|
+
* @param nombre Nombre à transformer
|
|
124
|
+
* @param nbDecimale Nombre de décimales à afficher au maximum
|
|
125
|
+
* @param afficherDecimaleVariable Si true, alors on affiche que les décimales nécessaires,
|
|
126
|
+
* jusqu'à nbDecimale. Ex : 4,2 et 4,25 Si False, nous auront 4,20 et 4,25
|
|
127
|
+
* @param separateur Séparateur à utiliser pour la décimale.
|
|
128
|
+
* La virgule ou le point. La virgule est utilisée par défaut.
|
|
129
|
+
*/
|
|
130
|
+
DecimaleVersString(nombre: number, nbDecimale: number, afficherDecimaleVariable: boolean, separateur: string = ',') {
|
|
131
|
+
let retour = ''
|
|
132
|
+
if (afficherDecimaleVariable === true) {
|
|
133
|
+
retour = this.DecimaleVariable(nombre, nbDecimale)
|
|
134
|
+
} else {
|
|
135
|
+
retour = nombre.toFixed(nbDecimale)
|
|
136
|
+
}
|
|
137
|
+
retour.replace('.', separateur)
|
|
138
|
+
return retour
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Transforme une décimale en formateur heure:minute. Ex : 2,5 => 2:30
|
|
143
|
+
* @param heures Nombre à mettre en string de type heure.
|
|
144
|
+
* @param separateur Séparateur heure, minute. Le : est utilisé par défaut.
|
|
145
|
+
*/
|
|
146
|
+
DecimaleVersHeure(heures: number, separateur: string = ':') {
|
|
147
|
+
if (Math.round((heures % 1) * 60) < 10) {
|
|
148
|
+
return `${Math.floor(heures) + separateur}0${Math.round((heures % 1) * 60)}`
|
|
149
|
+
}
|
|
150
|
+
return Math.floor(heures) + separateur + Math.round((heures % 1) * 60)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Transforme différent type de fin de ligne comme des \n en <br />
|
|
155
|
+
* @param texte Texte à modifier et retourner
|
|
156
|
+
*/
|
|
157
|
+
FinLigneVersBR(texte: string) {
|
|
158
|
+
return texte.replace(/(?:\r\n|\r|\n)/g, '<br />')
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Transforme une date en string.
|
|
163
|
+
* Le principe est de séparer la string selon le séparateur et de créer une nouvelle date avec les 3 nombres que ça donne.
|
|
164
|
+
* @param dateStr Date en string
|
|
165
|
+
*/
|
|
166
|
+
StringVersDate(dateStr: string, separateur = '-') {
|
|
167
|
+
const parties = dateStr.split(separateur)
|
|
168
|
+
if (parties.length < 3) {
|
|
169
|
+
throw new Error(`Conversion de date invalide. Format incorrect, attendu (yyyy-mm-dd), reçu : ${dateStr}`)
|
|
170
|
+
}
|
|
171
|
+
const retourDateTemp = new Date(
|
|
172
|
+
this.StringVersNombre(parties[0]),
|
|
173
|
+
this.StringVersNombre(parties[1]) - 1,
|
|
174
|
+
this.StringVersNombre(parties[2]),
|
|
175
|
+
0,
|
|
176
|
+
0,
|
|
177
|
+
0,
|
|
178
|
+
0,
|
|
179
|
+
) // Les mois débutent le compte à 0.
|
|
180
|
+
return retourDateTemp
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Transforme une date en string sous la forme YYYY-mm-dd
|
|
185
|
+
* @param date Date à mettre en string
|
|
186
|
+
*/
|
|
187
|
+
DateVersString(date: Date) {
|
|
188
|
+
let month = `${date.getMonth() + 1}`
|
|
189
|
+
let day = `${date.getDate()}`
|
|
190
|
+
const year = date.getFullYear()
|
|
191
|
+
if (month.length < 2) {
|
|
192
|
+
month = `0${month}`
|
|
193
|
+
}
|
|
194
|
+
if (day.length < 2) {
|
|
195
|
+
day = `0${day}`
|
|
196
|
+
}
|
|
197
|
+
return [year, month, day].join('-')
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Retourne la valeur numérique d'un nombre sous forme de string. Si le texte n'est pas un nombre NAN sera retourné.
|
|
202
|
+
* @param texte Texte à transformer en nombre
|
|
203
|
+
*/
|
|
204
|
+
ValeurNumeriqueDepuisTexte(texte: string) {
|
|
205
|
+
return parseFloat(texte)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Fabriquer une nouvelle instance de l'objet à retournet et y assigne tous les attributs publiques de la source.
|
|
210
|
+
* Si un attribut n'existe pas dans la définition de la classe du type de retour, il sera quand même créé dynamiquement et aucune erreur ne sera soulevée
|
|
211
|
+
* Si le constructeur
|
|
212
|
+
* @param donneeBrut Objet de donnée brut source
|
|
213
|
+
*/
|
|
214
|
+
FabriquerUnObjetAnonyme(source: object) {
|
|
215
|
+
return JSON.parse(JSON.stringify(source))
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
Cloner(objetAnonyme: object) {
|
|
219
|
+
return this.FabriquerUnObjetAnonyme(objetAnonyme)
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Fabriquer une nouvelle instance de l'objet à retourner et lui assigne tous les attributs publiques de la source.
|
|
224
|
+
* Si un attribut n'existe pas dans la définition de la classe du type de retour, il sera quand même créé dynamiquement et aucune erreur ne sera soulevée
|
|
225
|
+
* @param source L'objet source
|
|
226
|
+
* @param destination L'objet destinataire
|
|
227
|
+
*/
|
|
228
|
+
CopierObjetUnNiveau(source: any, destination: any) {
|
|
229
|
+
Object.keys(source).forEach(k => {
|
|
230
|
+
if (!Array.isArray(source[k])) {
|
|
231
|
+
destination[k] = source[k]
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
return destination
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Bind les attributs d'un objet vers un autre. Les attributs de la source qui n'existe pas dans la destination seront créés automatiquement
|
|
240
|
+
* @param source Objet de donnée brut source
|
|
241
|
+
* @param destination Objet de donnée brut source
|
|
242
|
+
*/
|
|
243
|
+
BinderDonnee(source: any, destination: any) {
|
|
244
|
+
if (destination == null) {
|
|
245
|
+
destination = {}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
Object.keys(source).forEach(k => {
|
|
249
|
+
destination[k] = source[k]
|
|
250
|
+
})
|
|
251
|
+
return destination
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
SetThemeDark(valeur: any) {
|
|
256
|
+
localStorage.setItem(`${this.racineLocalStorage}ThemeDark`, valeur)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
GetThemeDark() {
|
|
260
|
+
const valeur = localStorage.getItem(`${this.racineLocalStorage}ThemeDark`)
|
|
261
|
+
|
|
262
|
+
if (typeof valeur === 'string') {
|
|
263
|
+
if (valeur === 'true') {
|
|
264
|
+
return true
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return false
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return valeur
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
GetUrlBaseAPI() {
|
|
274
|
+
let baseUrl = window.location.origin
|
|
275
|
+
if (this.TexteVide(window.Location.prototype.pathname) === false) {
|
|
276
|
+
baseUrl += window.Location.prototype.pathname
|
|
277
|
+
}
|
|
278
|
+
baseUrl += '/api/'
|
|
279
|
+
|
|
280
|
+
return baseUrl
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
estDateAvantOuEgaleAujourdhui(date: string, comparerHeure: boolean) {
|
|
284
|
+
const now = new Date()
|
|
285
|
+
const dateDebut = new Date(this.StringVersDate(date))
|
|
286
|
+
|
|
287
|
+
if (comparerHeure === false) {
|
|
288
|
+
now.setHours(0, 0, 0, 0)
|
|
289
|
+
dateDebut.setHours(0, 0, 0, 0)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (dateDebut <= now) {
|
|
293
|
+
return false
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return true
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
estDateAvantAujourdhui(date: string, comparerHeure: boolean) {
|
|
300
|
+
const now = new Date()
|
|
301
|
+
const dateDebut = new Date(this.StringVersDate(date))
|
|
302
|
+
|
|
303
|
+
if (comparerHeure === false) {
|
|
304
|
+
now.setHours(0, 0, 0, 0)
|
|
305
|
+
dateDebut.setHours(0, 0, 0, 0)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (dateDebut < now) {
|
|
309
|
+
return false
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
return true
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
estDateApresOuEgaleAujourdhui(date: string, comparerHeure: boolean) {
|
|
316
|
+
const now = new Date()
|
|
317
|
+
const dateDebut = new Date(this.StringVersDate(date))
|
|
318
|
+
|
|
319
|
+
if (comparerHeure === false) {
|
|
320
|
+
now.setHours(0, 0, 0, 0)
|
|
321
|
+
dateDebut.setHours(0, 0, 0, 0)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (dateDebut >= now) {
|
|
325
|
+
return false
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return true
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
estDateApresAujourdhui(date: string, comparerHeure: boolean) {
|
|
332
|
+
const now = new Date()
|
|
333
|
+
const dateDebut = new Date(this.StringVersDate(date))
|
|
334
|
+
|
|
335
|
+
if (comparerHeure === false) {
|
|
336
|
+
now.setHours(0, 0, 0, 0)
|
|
337
|
+
dateDebut.setHours(0, 0, 0, 0)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (dateDebut > now) {
|
|
341
|
+
return false
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
return true
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Converti un enum en objet clé-valeur pour usage dans la prop :items d'un v-select.
|
|
349
|
+
* @param _obj L'enum pour générer les clés-valeurs
|
|
350
|
+
* @returns Une liste d'objets du format { title: string, value: number }
|
|
351
|
+
*/
|
|
352
|
+
genererItemsAPartirDeEnum<T extends object>(_enum: T) {
|
|
353
|
+
const enumKeys = Object.keys(_enum).filter(e => isNaN(Number(e)))
|
|
354
|
+
const enumValues = Object.keys(_enum).filter(e => !isNaN(Number(e))).map(e => Number(e))
|
|
355
|
+
|
|
356
|
+
const ret: { title?: string, value: number}[] = []
|
|
357
|
+
|
|
358
|
+
enumValues.forEach(v => ret.push({ title: enumKeys[v], value: v}))
|
|
359
|
+
|
|
360
|
+
return ret;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const csqcOutils = new CSQCOutils()
|
|
365
|
+
|
|
366
|
+
export default csqcOutils
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
class RafraichisseurToken {
|
|
2
|
+
private intervalleEnSecondes = 15
|
|
3
|
+
private secondesAvantExpirationTokenPourRafraichir = 20
|
|
4
|
+
private skewSeconds = 5 // marge anti-derives d’horloge
|
|
5
|
+
private popupAffiche = false
|
|
6
|
+
private timerId: number | null = null
|
|
7
|
+
|
|
8
|
+
// Lance une seule fois
|
|
9
|
+
public async demarrer(nomTemoin: string | null, urlPortail: string): Promise<void> {
|
|
10
|
+
urlPortail = urlPortail.replace(/\/+$/, '')
|
|
11
|
+
if (nomTemoin == null || nomTemoin === '') nomTemoin = 'csqc_jeton_secure_expiration'
|
|
12
|
+
await this.verifierJeton(nomTemoin, urlPortail)
|
|
13
|
+
if (this.timerId != null) return
|
|
14
|
+
this.timerId = window.setInterval(() => {
|
|
15
|
+
this.verifierJeton(nomTemoin, urlPortail)
|
|
16
|
+
}, this.intervalleEnSecondes * 1000)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Permet d’arrêter le timer (ex: au logout / destroy)
|
|
20
|
+
public arreter(): void {
|
|
21
|
+
if (this.timerId != null) {
|
|
22
|
+
clearInterval(this.timerId)
|
|
23
|
+
this.timerId = null
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
public existeJeton = (nomTemoin: string) => {
|
|
28
|
+
return this.estJetonValide(nomTemoin)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
private async verifierJeton(nomTemoin: string, urlPortail: string): Promise<void> {
|
|
32
|
+
if (this.popupAffiche) return
|
|
33
|
+
|
|
34
|
+
if (!this.estJetonValide(nomTemoin)) {
|
|
35
|
+
this.rafraichir(nomTemoin, urlPortail)
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private estJetonValide(nomTemoin: string): boolean {
|
|
41
|
+
if (this.popupAffiche || !nomTemoin) return true //On fait semblant que c'est valide pour ne pas provoquer un autre affichage du popup.
|
|
42
|
+
|
|
43
|
+
const tokenEncode = this.lireCookie(nomTemoin)
|
|
44
|
+
if (!tokenEncode) {
|
|
45
|
+
return false
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let token: any
|
|
49
|
+
try {
|
|
50
|
+
token = this.parseJwt(tokenEncode)
|
|
51
|
+
} catch {
|
|
52
|
+
return false
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const exp = Number(token?.exp)
|
|
56
|
+
if (!Number.isFinite(exp)) {
|
|
57
|
+
// exp manquant/invalide → tente refresh
|
|
58
|
+
|
|
59
|
+
return false
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const now = Math.floor(Date.now() / 1000)
|
|
63
|
+
const refreshAt = exp - this.secondesAvantExpirationTokenPourRafraichir - this.skewSeconds
|
|
64
|
+
if (now >= refreshAt) {
|
|
65
|
+
return false
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return true
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async rafraichir(nomCookie: string, urlPortail: string): Promise<void> {
|
|
72
|
+
if (!nomCookie) return
|
|
73
|
+
const url = this.getRefreshUrl(urlPortail)
|
|
74
|
+
const controller = new AbortController()
|
|
75
|
+
const timeout = setTimeout(() => controller.abort(), 10_000)
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
//Première tentative sans iframe, pour la majorité des cas.
|
|
79
|
+
const resp = await fetch(url, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
credentials: 'include',
|
|
82
|
+
headers: { Accept: 'application/json' },
|
|
83
|
+
redirect: 'manual',
|
|
84
|
+
signal: controller.signal,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// redirection (souvent => login) → traiter comme non auth
|
|
88
|
+
|
|
89
|
+
if (resp.type === 'opaqueredirect' || resp.status === 302) {
|
|
90
|
+
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
91
|
+
return
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// OK ou No Content: le cookie devrait être réécrit
|
|
95
|
+
if (resp.status === 200 || resp.status === 204) {
|
|
96
|
+
const jeton = this.lireCookie(nomCookie)
|
|
97
|
+
if (!jeton) this.rafraichirParIframe(nomCookie, urlPortail)
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// non auth / expiré (401, 419) + IIS timeout (440)
|
|
102
|
+
if (resp.status === 401 || resp.status === 419 || resp.status === 440) {
|
|
103
|
+
this.rafraichirParIframe(nomCookie, urlPortail)
|
|
104
|
+
return
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
console.warn('Rafraichisseur token: statut inattendu', resp.status)
|
|
108
|
+
} catch (err: any) {
|
|
109
|
+
if (err?.name === 'AbortError') console.warn('RafraichisseurToken timeout')
|
|
110
|
+
else console.error('Erreur rafraichisseur de token', err)
|
|
111
|
+
// on réessaiera au prochain tick
|
|
112
|
+
} finally {
|
|
113
|
+
clearTimeout(timeout)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private rafraichirParIframe(nomCookie: string, urlPortail: string): void {
|
|
118
|
+
// Pour éviter les cross référence, on créé un iframe qui appel portail et force la MAJ du jeton ou l'invalidation du jeton, si jamais l'utilisateur n'est plus connecté
|
|
119
|
+
// ajax vers le refresh
|
|
120
|
+
let iframe = document.createElement('iframe')
|
|
121
|
+
const url = this.getRefreshUrl(urlPortail)
|
|
122
|
+
iframe.src = `${url}?urlRetour=${encodeURI(window.localStorage.href)}`
|
|
123
|
+
iframe.id = 'idRafrToken'
|
|
124
|
+
iframe.style.display = 'none'
|
|
125
|
+
document.body.appendChild(iframe)
|
|
126
|
+
iframe.onload = () => {
|
|
127
|
+
const jetonCSQC = this.lireCookie(nomCookie)
|
|
128
|
+
if (jetonCSQC == null || jetonCSQC === '') {
|
|
129
|
+
this.estDeconnecteAzure(urlPortail)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
iframe.remove()
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private estDeconnecteAzure(urlPortail: string): void {
|
|
137
|
+
//on envoie au portail, pas le choix
|
|
138
|
+
|
|
139
|
+
const retour = encodeURI(window.location.href)
|
|
140
|
+
window.open(`${urlPortail}/home/SeConnecter?urlRetour=${retour}`, '_self')
|
|
141
|
+
return
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
public deconnecterPortail(urlPortail: string): void {
|
|
145
|
+
window.location.replace(`${urlPortail}/deconnecte?urlRetour=${encodeURIComponent(window.location.href)}`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private lireCookie(nom: string): string | null {
|
|
149
|
+
if (!document.cookie) return null
|
|
150
|
+
const cookies = document.cookie.split(';').map(c => c.trim())
|
|
151
|
+
for (const cookie of cookies) {
|
|
152
|
+
if (cookie.startsWith(`${nom}=`)) {
|
|
153
|
+
try {
|
|
154
|
+
return decodeURIComponent(cookie.substring(nom.length + 1))
|
|
155
|
+
} catch {
|
|
156
|
+
return cookie.substring(nom.length + 1)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
164
|
+
private parseJwt(token: string): any {
|
|
165
|
+
const parts = token.split('.')
|
|
166
|
+
const base64Url = parts[1]
|
|
167
|
+
if (!base64Url) throw new Error('Invalid JWT format')
|
|
168
|
+
const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/')
|
|
169
|
+
const jsonPayload = decodeURIComponent(
|
|
170
|
+
atob(base64)
|
|
171
|
+
.split('')
|
|
172
|
+
.map(c => `%${`00${c.charCodeAt(0).toString(16)}`.slice(-2)}`)
|
|
173
|
+
.join(''),
|
|
174
|
+
)
|
|
175
|
+
return JSON.parse(jsonPayload)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// URL refresh selon env (dev = proxy Vite ; prod = portail)
|
|
179
|
+
private getRefreshUrl(urlPortail: string): string {
|
|
180
|
+
if (import.meta.env.MODE === 'development') return '/portail-refresh'
|
|
181
|
+
return urlPortail + '/home/Refresh'
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Instance
|
|
186
|
+
const rafraichisseurToken = new RafraichisseurToken()
|
|
187
|
+
export default rafraichisseurToken
|
package/package.json
CHANGED
|
@@ -1,36 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codevdesign",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.33",
|
|
4
4
|
"description": "Composants Vuetify 3 pour les projets Codev",
|
|
5
5
|
"files": [
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"types": "index.d.ts",
|
|
12
|
-
"sideEffects": [
|
|
13
|
-
"*.css",
|
|
14
|
-
"*.scss"
|
|
6
|
+
"./**/*.vue",
|
|
7
|
+
"./**/*.ts",
|
|
8
|
+
"./**/*.json",
|
|
9
|
+
"./**/*.css",
|
|
10
|
+
"./index.d.ts"
|
|
15
11
|
],
|
|
16
12
|
"scripts": {
|
|
17
13
|
"build": "vite build"
|
|
18
14
|
},
|
|
19
15
|
"dependencies": {
|
|
16
|
+
"vuetify": "^3.10.2",
|
|
17
|
+
"vue-i18n": "^11.0.0",
|
|
20
18
|
"@e965/xlsx": "^0.20.3",
|
|
21
19
|
"tinymce": "^8.0.2",
|
|
22
20
|
"tinymce-i18n": "^25.9.1",
|
|
23
21
|
"@tinymce/tinymce-vue": "^6.3.0"
|
|
24
22
|
},
|
|
25
|
-
"peerDependencies": {
|
|
26
|
-
"vue": "^3.5.0",
|
|
27
|
-
"vuetify": "^3.10.2",
|
|
28
|
-
"vue-i18n": "^11.0.0"
|
|
29
|
-
},
|
|
30
23
|
"devDependencies": {
|
|
31
24
|
"@types/node": "^22.13.5",
|
|
32
25
|
"@vitejs/plugin-vue": "^5.2.1",
|
|
33
26
|
"typescript": "^5.8.3",
|
|
34
27
|
"vite": "^7.0.0"
|
|
28
|
+
},
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"vue": "^3.5.0"
|
|
35
31
|
}
|
|
36
32
|
}
|