@rsuci/shared-form-components 1.0.35 → 1.0.36

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.
@@ -1 +1 @@
1
- {"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAoBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAwPD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"FormRenderer.d.ts","sourceRoot":"","sources":["../../../src/components/form-renderer/FormRenderer.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAyC,MAAM,OAAO,CAAC;AAC9D,OAAO,EACL,iBAAiB,EACjB,eAAe,EACf,cAAc,EAIf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EAEtB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAmBlF;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,6BAA6B;IAC7B,UAAU,EAAE,iBAAiB,CAAC;IAC9B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAClD,oCAAoC;IACpC,MAAM,EAAE,kBAAkB,CAAC;IAC3B,iCAAiC;IACjC,SAAS,EAAE,qBAAqB,CAAC;IACjC,wBAAwB;IACxB,QAAQ,CAAC,EAAE,oBAAoB,CAAC;IAChC,wCAAwC;IACxC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C,iCAAiC;IACjC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,iCAAiC;IACjC,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AA0dD;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAgBpD,CAAC;AAEF,eAAe,YAAY,CAAC"}
@@ -4,16 +4,15 @@
4
4
  * RSU v2 - Package Partagé
5
5
  */
6
6
  'use client';
7
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
8
8
  import { useMemo, useCallback, useState } from 'react';
9
9
  // Contexte et Provider
10
10
  import { FormRendererProvider, useFormRendererContext } from './FormRendererContext';
11
11
  // Sous-composants
12
12
  import FormProgress from './FormProgress';
13
- import FormNavigationButtons from './FormNavigationButtons';
14
- import FormActions from './FormActions';
15
13
  import GroupeInstanceTabs from './GroupeInstanceTabs';
16
14
  import ConfirmationModal from './ConfirmationModal';
15
+ import ValidationModal from './ValidationModal';
17
16
  // Composants de rendu
18
17
  import VariableRenderer from '../VariableRenderer';
19
18
  // Hooks et utilitaires
@@ -26,8 +25,13 @@ const FormRendererInner = () => {
26
25
  const context = useFormRendererContext();
27
26
  const { formulaire, enquete, config, callbacks, services, geographicComponents, navigation, validation, instances, responses, updateResponse, hasUnsavedChanges, isSubmitting, effectiveDisabled, currentGroup, currentInstance, groupesWithInstances } = context;
28
27
  const { mode, features } = config;
29
- // État local pour les modals
28
+ const labels = config.labels || {};
29
+ // État local pour les modals et actions
30
30
  const [showCancelModal, setShowCancelModal] = useState(false);
31
+ const [showValidationModal, setShowValidationModal] = useState(false);
32
+ const [validationAction, setValidationAction] = useState('valider');
33
+ const [isSavingDraft, setIsSavingDraft] = useState(false);
34
+ const [isSubmittingForm, setIsSubmittingForm] = useState(false);
31
35
  // FormTree pour la visibilité des variables
32
36
  const { formTree, getVisibleVariables } = useFormTree(groupesWithInstances.length > 0 ? groupesWithInstances : formulaire?.groupes || [], responses, { debug: false });
33
37
  // Vérifier si une variable est visible
@@ -92,6 +96,76 @@ const FormRendererInner = () => {
92
96
  // Revenir à la première instance
93
97
  navigation.changeInstance(0);
94
98
  };
99
+ // Navigation - Validation avant navigation
100
+ const groupes = groupesWithInstances.length > 0 ? groupesWithInstances : formulaire?.groupes || [];
101
+ const canGoNext = validation.validateCurrentGroupRequiredFields(currentGroup, responses);
102
+ const isFormValid = validation.validateAllGroups(groupes, responses).isValid;
103
+ const handleNext = () => {
104
+ if (canGoNext && !navigation.isLastGroup) {
105
+ navigation.goToNextGroup();
106
+ }
107
+ };
108
+ const handlePrevious = () => {
109
+ if (!navigation.isFirstGroup) {
110
+ navigation.goToPreviousGroup();
111
+ }
112
+ };
113
+ // Handler pour la soumission
114
+ const handleSubmit = async () => {
115
+ if (effectiveDisabled || isSubmitting || isSubmittingForm)
116
+ return;
117
+ const result = validation.validateAllGroups(groupes, responses);
118
+ if (!result.isValid) {
119
+ if (callbacks.onValidationError) {
120
+ callbacks.onValidationError(result.errors);
121
+ }
122
+ return;
123
+ }
124
+ setIsSubmittingForm(true);
125
+ try {
126
+ await callbacks.onSubmit(responses);
127
+ }
128
+ finally {
129
+ setIsSubmittingForm(false);
130
+ }
131
+ };
132
+ // Handler pour le brouillon
133
+ const handleSaveDraft = async () => {
134
+ if (!callbacks.onSaveDraft || effectiveDisabled || isSavingDraft)
135
+ return;
136
+ setIsSavingDraft(true);
137
+ try {
138
+ await callbacks.onSaveDraft(responses);
139
+ }
140
+ finally {
141
+ setIsSavingDraft(false);
142
+ }
143
+ };
144
+ // Handlers pour la validation DR
145
+ const handleOpenValidationModal = (action) => {
146
+ setValidationAction(action);
147
+ setShowValidationModal(true);
148
+ };
149
+ const handleConfirmValidation = async (commentaire) => {
150
+ try {
151
+ if (callbacks.onValidateDR) {
152
+ await callbacks.onValidateDR(validationAction, commentaire);
153
+ }
154
+ setShowValidationModal(false);
155
+ }
156
+ catch (error) {
157
+ console.error('Erreur lors de la validation DR:', error);
158
+ throw error;
159
+ }
160
+ };
161
+ // Détermine si on affiche les boutons de validation DR
162
+ const showDRValidationButtons = mode === 'admin' &&
163
+ features?.drValidation &&
164
+ navigation.isLastGroup &&
165
+ enquete?.statut === 'VALIDE_ENQUETEUR' &&
166
+ callbacks.onValidateDR;
167
+ // Mode consultation (pas de bouton submit/valider)
168
+ const isConsultationMode = features?.consultationMode || false;
95
169
  // Vérification de la présence de groupes
96
170
  if (!formulaire || !formulaire.groupes || formulaire.groupes.length === 0) {
97
171
  return (_jsx("div", { className: "min-h-screen bg-gray-50 flex items-center justify-center", children: _jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-8 text-center max-w-md", children: [_jsx("div", { className: "text-red-500 mb-4", children: _jsx("svg", { className: "w-12 h-12 mx-auto", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 18.5c-.77.833.192 2.5 1.732 2.5z" }) }) }), _jsx("h3", { className: "text-lg font-medium text-gray-900 mb-2", children: "Formulaire non configur\u00E9" }), _jsx("p", { className: "text-gray-600 mb-4", children: "Ce formulaire ne contient aucun groupe de variables." }), _jsx("button", { onClick: callbacks.onCancel, className: "inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700", children: "Retour" })] }) }));
@@ -102,7 +176,15 @@ const FormRendererInner = () => {
102
176
  : variable.code;
103
177
  const currentValue = responses[responseKey]?.valeur;
104
178
  return (_jsx(VariableRenderer, { variable: variable, value: currentValue, onChange: (value) => handleVariableChange(variable, value), disabled: effectiveDisabled, numeroMembre: currentGroup?.estMultiple ? currentInstance?.numeroInstance : undefined, services: services, geographicComponents: geographicComponents, formulaireVariables: formulaire.variables, allResponses: responses, reponses: responses, interpolateVariableLabel: interpolateVariableLabel }, variable.id));
105
- }) }), currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mt-6 pt-6 border-t", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled }) }))] }), _jsxs("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4 space-y-4", children: [_jsx(FormNavigationButtons, {}), _jsx("div", { className: "border-t pt-4", children: _jsx(FormActions, { onCancel: handleCancelClick }) })] })] }), _jsx(ConfirmationModal, { isOpen: showCancelModal, title: "Annuler les modifications", message: "Vous avez des modifications non sauvegard\u00E9es. \u00CAtes-vous s\u00FBr de vouloir quitter ?", confirmText: "Quitter", cancelText: "Continuer", onConfirm: handleConfirmCancel, onCancel: () => setShowCancelModal(false), type: "danger" })] }));
179
+ }) }), currentGroup?.estMultiple && currentGroup.instances && (_jsx("div", { className: "mt-6 pt-6 border-t", children: _jsx(GroupeInstanceTabs, { groupe: currentGroup, currentInstanceIndex: navigation.navigationState.instanceIndex, responses: responses, onInstanceChange: handleInstanceChange, onInstanceAdded: handleInstanceAdded, onInstanceRemoved: handleInstanceRemoved, disabled: effectiveDisabled }) }))] }), _jsx("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4", children: _jsxs("div", { className: "flex flex-wrap justify-between items-center gap-2", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs("button", { type: "button", onClick: handlePrevious, disabled: navigation.isFirstGroup || effectiveDisabled, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!navigation.isFirstGroup && !effectiveDisabled
180
+ ? 'bg-orange-500 text-white hover:bg-orange-600'
181
+ : 'bg-gray-100 text-gray-400 cursor-not-allowed'}`, children: [_jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) }), _jsx("span", { className: "hidden sm:inline", children: labels.previousButton || 'Précédent' }), _jsx("span", { className: "sm:hidden", children: "Pr\u00E9c." })] }), mode === 'admin' && features?.saveDraft && callbacks.onSaveDraft && (_jsx("button", { type: "button", onClick: handleSaveDraft, disabled: effectiveDisabled || isSavingDraft || isSubmitting || isSubmittingForm, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${!effectiveDisabled && !isSavingDraft && !isSubmitting && !isSubmittingForm
182
+ ? 'bg-yellow-500 text-white hover:bg-yellow-600'
183
+ : 'bg-gray-300 text-gray-500 cursor-not-allowed'}`, children: isSavingDraft ? (_jsxs("span", { className: "flex items-center", children: [_jsxs("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), "Enregistrement..."] })) : (_jsxs(_Fragment, { children: [_jsx("span", { className: "hidden sm:inline", children: labels.draftButton || 'Enregistrer brouillon' }), _jsx("span", { className: "sm:hidden", children: "Brouillon" })] })) }))] }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsxs("button", { type: "button", onClick: handleCancelClick, disabled: isSubmitting || isSubmittingForm || isSavingDraft, className: "px-4 py-2 rounded-lg font-medium transition-colors bg-red-500 text-white hover:bg-red-600 disabled:opacity-50 disabled:cursor-not-allowed", children: [_jsx("span", { className: "hidden sm:inline", children: labels.cancelButton || 'Annuler' }), _jsx("span", { className: "sm:hidden", children: "Annuler" })] }), !navigation.isLastGroup && (_jsxs("button", { type: "button", onClick: handleNext, disabled: !canGoNext || effectiveDisabled, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${canGoNext && !effectiveDisabled
184
+ ? 'bg-green-600 text-white hover:bg-green-700'
185
+ : 'bg-gray-400 text-gray-200 cursor-not-allowed'}`, title: !canGoNext ? 'Veuillez remplir tous les champs obligatoires' : 'Passer au groupe suivant', children: [_jsx("span", { className: "hidden sm:inline", children: labels.nextButton || 'Suivant' }), _jsx("span", { className: "sm:hidden", children: "Suiv." }), _jsx("svg", { className: "h-4 w-4 ml-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" }) })] })), navigation.isLastGroup && !isConsultationMode && !showDRValidationButtons && (_jsx("button", { type: "button", onClick: handleSubmit, disabled: effectiveDisabled || isSubmitting || isSubmittingForm || !isFormValid, className: `flex items-center px-4 py-2 rounded-lg font-medium transition-colors ${isFormValid && !effectiveDisabled
186
+ ? 'bg-green-600 text-white hover:bg-green-700'
187
+ : 'bg-gray-400 text-gray-200 cursor-not-allowed'}`, title: !isFormValid ? 'Veuillez remplir tous les champs obligatoires' : '', children: isSubmitting || isSubmittingForm ? (_jsxs("span", { className: "flex items-center", children: [_jsxs("svg", { className: "animate-spin -ml-1 mr-2 h-4 w-4", fill: "none", viewBox: "0 0 24 24", children: [_jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }), _jsx("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })] }), "Envoi en cours..."] })) : (labels.submitButton || (mode === 'admin' ? 'Valider' : 'Soumettre')) })), showDRValidationButtons && (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: () => handleOpenValidationModal('rejeter'), disabled: isSubmitting || isSubmittingForm, className: "px-4 py-2 rounded-lg font-medium transition-colors bg-red-600 text-white hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center", children: [_jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }), labels.rejectDRButton || 'Rejeter'] }), _jsxs("button", { type: "button", onClick: () => handleOpenValidationModal('valider'), disabled: isSubmitting || isSubmittingForm, className: "px-4 py-2 rounded-lg font-medium transition-colors bg-green-600 text-white hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed flex items-center", children: [_jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" }) }), labels.validateDRButton || 'Valider'] })] }))] })] }) })] }), _jsx(ConfirmationModal, { isOpen: showCancelModal, title: "Annuler les modifications", message: "Vous avez des modifications non sauvegard\u00E9es. \u00CAtes-vous s\u00FBr de vouloir quitter ?", confirmText: "Quitter", cancelText: "Continuer", onConfirm: handleConfirmCancel, onCancel: () => setShowCancelModal(false), type: "danger" }), _jsx(ValidationModal, { isOpen: showValidationModal, onClose: () => setShowValidationModal(false), onConfirm: handleConfirmValidation, action: validationAction, enquetesCount: 1 })] }));
106
188
  };
107
189
  /**
108
190
  * Composant principal FormRenderer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsuci/shared-form-components",
3
- "version": "1.0.35",
3
+ "version": "1.0.36",
4
4
  "description": "Composants partagés de rendu de formulaires RSU v2 - Package local pour frontend Admin et Public",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",