Fix some code
This commit is contained in:
parent
2c329aa8a2
commit
1e6065ec7c
82
README.md
82
README.md
@ -1,54 +1,50 @@
|
|||||||
# React + TypeScript + Vite
|
# Application [4NK] - Interface Web
|
||||||
|
|
||||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
Cette application React fournit une interface pour interagir avec la plateforme [4NK]. Elle permet l'authentification des utilisateurs, la création de profils et de dossiers, ainsi que la visualisation des processus.
|
||||||
|
|
||||||
Currently, two official plugins are available:
|
## Fonctionnalités principales
|
||||||
|
|
||||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
|
- Authentification utilisateur via OAuth
|
||||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
- Création et gestion de profils utilisateur
|
||||||
|
- Création et gestion de dossiers
|
||||||
|
- Visualisation des processus
|
||||||
|
- Console de messages pour le suivi des événements
|
||||||
|
|
||||||
## Expanding the ESLint configuration
|
## Prérequis
|
||||||
|
|
||||||
If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
|
- Node.js (version 18 ou supérieure)
|
||||||
|
- npm ou yarn
|
||||||
|
|
||||||
```js
|
## Installation
|
||||||
export default tseslint.config({
|
|
||||||
extends: [
|
Pour installer les dépendances du projet, exécutez :
|
||||||
// Remove ...tseslint.configs.recommended and replace with this
|
|
||||||
...tseslint.configs.recommendedTypeChecked,
|
```bash
|
||||||
// Alternatively, use this for stricter rules
|
npm install
|
||||||
...tseslint.configs.strictTypeChecked,
|
|
||||||
// Optionally, add this for stylistic rules
|
|
||||||
...tseslint.configs.stylisticTypeChecked,
|
|
||||||
],
|
|
||||||
languageOptions: {
|
|
||||||
// other options...
|
|
||||||
parserOptions: {
|
|
||||||
project: ['./tsconfig.node.json', './tsconfig.app.json'],
|
|
||||||
tsconfigRootDir: import.meta.dirname,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
|
## Commandes disponibles
|
||||||
|
|
||||||
```js
|
### Démarrage de l'application
|
||||||
// eslint.config.js
|
|
||||||
import reactX from 'eslint-plugin-react-x'
|
|
||||||
import reactDom from 'eslint-plugin-react-dom'
|
|
||||||
|
|
||||||
export default tseslint.config({
|
**Commande principale** pour lancer l'application en mode développement :
|
||||||
plugins: {
|
|
||||||
// Add the react-x and react-dom plugins
|
```bash
|
||||||
'react-x': reactX,
|
npm run dev
|
||||||
'react-dom': reactDom,
|
|
||||||
},
|
|
||||||
rules: {
|
|
||||||
// other rules...
|
|
||||||
// Enable its recommended typescript rules
|
|
||||||
...reactX.configs['recommended-typescript'].rules,
|
|
||||||
...reactDom.configs.recommended.rules,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
L'application sera accessible à l'adresse : http://localhost:5173
|
||||||
|
|
||||||
|
## Communication avec la plateforme [4NK]
|
||||||
|
|
||||||
|
L'application communique avec la plateforme [4NK] via une iframe et un bus de messages. La communication est gérée par les classes `MessageBus` et `EventBus` dans le dossier `/src/sdk`.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
L'URL de l'iframe est définie dans `App.tsx` :
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const iframeUrl = 'https://dev3.4nkweb.com'
|
||||||
|
```
|
||||||
|
|
||||||
|
Pour modifier l'environnement cible, vous devez changer cette URL.
|
||||||
|
@ -147,6 +147,7 @@ function App() {
|
|||||||
onLogout={handleLogout}
|
onLogout={handleLogout}
|
||||||
onCreateProfile={handleOpenProfileModal}
|
onCreateProfile={handleOpenProfileModal}
|
||||||
onCreateFolder={handleOpenFolderModal}
|
onCreateFolder={handleOpenFolderModal}
|
||||||
|
iframeUrl={iframeUrl}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Structure flexible avec console à gauche et contenu à droite */}
|
{/* Structure flexible avec console à gauche et contenu à droite */}
|
||||||
|
@ -9,20 +9,14 @@ interface ControlPanelProps {
|
|||||||
iframeUrl?: string;
|
iframeUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Composant de panel de contrôle pour gérer les actions principales
|
|
||||||
*
|
|
||||||
* Fournit des boutons pour la connexion, la création de profil et le contrôle de visibilité
|
|
||||||
*/
|
|
||||||
function ControlPanel({
|
function ControlPanel({
|
||||||
onLogin = () => console.log('Connexion demandée'),
|
onLogin = () => console.log('Connexion demandée'),
|
||||||
onLogout = () => console.log('Déconnexion demandée'),
|
onLogout = () => console.log('Déconnexion demandée'),
|
||||||
onCreateProfile = () => console.log('Création de profil demandée'),
|
onCreateProfile = () => console.log('Création de profil demandée'),
|
||||||
onCreateFolder = () => console.log('Création de dossier demandée'),
|
onCreateFolder = () => console.log('Création de dossier demandée'),
|
||||||
isConnected = false,
|
isConnected = false,
|
||||||
iframeUrl = 'https://dev1.4nkweb.com'
|
iframeUrl = ''
|
||||||
}: ControlPanelProps) {
|
}: ControlPanelProps) {
|
||||||
// État pour gérer l'affichage du tooltip
|
|
||||||
const [showTooltip, setShowTooltip] = useState(false);
|
const [showTooltip, setShowTooltip] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -264,6 +264,7 @@
|
|||||||
0% {
|
0% {
|
||||||
box-shadow: 0 0 0 2px rgba(103, 58, 183, 0.8);
|
box-shadow: 0 0 0 2px rgba(103, 58, 183, 0.8);
|
||||||
}
|
}
|
||||||
|
|
||||||
100% {
|
100% {
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
@ -284,11 +285,8 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-cancel, .btn-submit {
|
.btn-cancel,
|
||||||
|
.btn-submit {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles spécifiques pour le composant FolderModal */
|
|
||||||
/* Les styles génériques de modal sont fournis par Modal.css */
|
|
||||||
|
|
||||||
|
@ -96,239 +96,239 @@ function FolderModal({
|
|||||||
return (
|
return (
|
||||||
<Modal isOpen={isOpen} onClose={onClose} title="Créer un nouveau dossier">
|
<Modal isOpen={isOpen} onClose={onClose} title="Créer un nouveau dossier">
|
||||||
<div className="folder-container">
|
<div className="folder-container">
|
||||||
<form className="folder-form" onSubmit={handleSubmit}>
|
<form className="folder-form" onSubmit={handleSubmit}>
|
||||||
<div className="form-section">
|
<div className="form-section">
|
||||||
<h3 className="section-title">Informations principales</h3>
|
<h3 className="section-title">Informations principales</h3>
|
||||||
<div className="form-row">
|
<div className="form-row">
|
||||||
<div className="form-field">
|
<div className="form-field">
|
||||||
<label>
|
<label>
|
||||||
Numéro de dossier <span className="required">*</span>
|
Numéro de dossier <span className="required">*</span>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
name="folderNumber"
|
name="folderNumber"
|
||||||
value={folderData.folderNumber}
|
value={folderData.folderNumber}
|
||||||
onChange={handleInputChange}
|
onChange={handleInputChange}
|
||||||
required
|
required
|
||||||
disabled={readOnly}
|
disabled={readOnly}
|
||||||
placeholder="ex: DOC-2025-001"
|
placeholder="ex: DOC-2025-001"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<label>
|
|
||||||
Nom <span className="required">*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
name="name"
|
|
||||||
value={folderData.name}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
disabled={readOnly}
|
|
||||||
placeholder="Nom du dossier"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-row">
|
|
||||||
<div className="form-field">
|
|
||||||
<label>
|
|
||||||
Type d'acte <span className="required">*</span>
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
name="deedType"
|
|
||||||
value={folderData.deedType}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
disabled={readOnly}
|
|
||||||
>
|
|
||||||
<option value="">Sélectionnez le type d'acte</option>
|
|
||||||
<option value="vente">Vente</option>
|
|
||||||
<option value="achat">Achat</option>
|
|
||||||
<option value="succession">Succession</option>
|
|
||||||
<option value="donation">Donation</option>
|
|
||||||
<option value="autre">Autre</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<label>
|
|
||||||
Statut <span className="required">*</span>
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
name="status"
|
|
||||||
value={folderData.status}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
required
|
|
||||||
disabled={readOnly}
|
|
||||||
>
|
|
||||||
<option value="active">Actif</option>
|
|
||||||
<option value="pending">En attente</option>
|
|
||||||
<option value="completed">Complété</option>
|
|
||||||
<option value="archived">Archivé</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-field">
|
|
||||||
<label>Description</label>
|
|
||||||
<textarea
|
|
||||||
name="description"
|
|
||||||
value={folderData.description}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={readOnly}
|
|
||||||
placeholder="Description du dossier"
|
|
||||||
rows={3}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{folderData.status === 'archived' && (
|
|
||||||
<div className="form-field">
|
|
||||||
<label>Description d'archivage</label>
|
|
||||||
<textarea
|
|
||||||
name="archived_description"
|
|
||||||
value={folderData.archived_description}
|
|
||||||
onChange={handleInputChange}
|
|
||||||
disabled={readOnly}
|
|
||||||
placeholder="Raison d'archivage"
|
|
||||||
rows={2}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-field">
|
||||||
<div className="form-section">
|
<label>
|
||||||
<h3 className="section-title">Clients</h3>
|
Nom <span className="required">*</span>
|
||||||
<div className="tag-list">
|
</label>
|
||||||
{folderData.customers.map((customer, index) => (
|
<input
|
||||||
<div key={index} className="tag-item">
|
type="text"
|
||||||
<span>{customer}</span>
|
name="name"
|
||||||
{!readOnly && (
|
value={folderData.name}
|
||||||
<button
|
onChange={handleInputChange}
|
||||||
type="button"
|
required
|
||||||
className="tag-remove"
|
disabled={readOnly}
|
||||||
onClick={() => removeCustomer(customer)}
|
placeholder="Nom du dossier"
|
||||||
aria-label="Supprimer ce client"
|
/>
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!readOnly && (
|
|
||||||
<div className="form-field tag-input-container">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={currentCustomer}
|
|
||||||
onChange={(e) => setCurrentCustomer(e.target.value)}
|
|
||||||
placeholder="Ajouter un client"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-add-tag"
|
|
||||||
onClick={addCustomer}
|
|
||||||
>
|
|
||||||
Ajouter
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="form-section">
|
<div className="form-row">
|
||||||
<h3 className="section-title">Parties prenantes</h3>
|
<div className="form-field">
|
||||||
<div className="tag-list">
|
<label>
|
||||||
{folderData.stakeholders.map((stakeholder, index) => (
|
Type d'acte <span className="required">*</span>
|
||||||
<div key={index} className="tag-item">
|
</label>
|
||||||
<span>{stakeholder}</span>
|
<select
|
||||||
{!readOnly && (
|
name="deedType"
|
||||||
<button
|
value={folderData.deedType}
|
||||||
type="button"
|
onChange={handleInputChange}
|
||||||
className="tag-remove"
|
required
|
||||||
onClick={() => removeStakeholder(stakeholder)}
|
disabled={readOnly}
|
||||||
aria-label="Supprimer cette partie prenante"
|
|
||||||
>
|
|
||||||
×
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!readOnly && (
|
|
||||||
<div className="form-field tag-input-container">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={currentStakeholder}
|
|
||||||
onChange={(e) => setCurrentStakeholder(e.target.value)}
|
|
||||||
placeholder="Ajouter une partie prenante"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-add-tag"
|
|
||||||
onClick={addStakeholder}
|
|
||||||
>
|
|
||||||
Ajouter
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-section">
|
|
||||||
<h3 className="section-title">Informations système</h3>
|
|
||||||
<div className="form-row">
|
|
||||||
<div className="form-field">
|
|
||||||
<label>Créé le</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={new Date(folderData.created_at).toLocaleDateString('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})}
|
|
||||||
disabled
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-field">
|
|
||||||
<label>Dernière mise à jour</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
value={new Date(folderData.updated_at).toLocaleDateString('fr-FR', {
|
|
||||||
day: '2-digit',
|
|
||||||
month: '2-digit',
|
|
||||||
year: 'numeric',
|
|
||||||
hour: '2-digit',
|
|
||||||
minute: '2-digit'
|
|
||||||
})}
|
|
||||||
disabled
|
|
||||||
readOnly
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="form-actions">
|
|
||||||
{onCancel && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="btn-cancel"
|
|
||||||
onClick={onCancel}
|
|
||||||
>
|
|
||||||
Annuler
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="btn-submit"
|
|
||||||
>
|
>
|
||||||
Enregistrer
|
<option value="">Sélectionnez le type d'acte</option>
|
||||||
|
<option value="vente">Vente</option>
|
||||||
|
<option value="achat">Achat</option>
|
||||||
|
<option value="succession">Succession</option>
|
||||||
|
<option value="donation">Donation</option>
|
||||||
|
<option value="autre">Autre</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="form-field">
|
||||||
|
<label>
|
||||||
|
Statut <span className="required">*</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
name="status"
|
||||||
|
value={folderData.status}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
required
|
||||||
|
disabled={readOnly}
|
||||||
|
>
|
||||||
|
<option value="active">Actif</option>
|
||||||
|
<option value="pending">En attente</option>
|
||||||
|
<option value="completed">Complété</option>
|
||||||
|
<option value="archived">Archivé</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-field">
|
||||||
|
<label>Description</label>
|
||||||
|
<textarea
|
||||||
|
name="description"
|
||||||
|
value={folderData.description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
placeholder="Description du dossier"
|
||||||
|
rows={3}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{folderData.status === 'archived' && (
|
||||||
|
<div className="form-field">
|
||||||
|
<label>Description d'archivage</label>
|
||||||
|
<textarea
|
||||||
|
name="archived_description"
|
||||||
|
value={folderData.archived_description}
|
||||||
|
onChange={handleInputChange}
|
||||||
|
disabled={readOnly}
|
||||||
|
placeholder="Raison d'archivage"
|
||||||
|
rows={2}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-section">
|
||||||
|
<h3 className="section-title">Clients</h3>
|
||||||
|
<div className="tag-list">
|
||||||
|
{folderData.customers.map((customer, index) => (
|
||||||
|
<div key={index} className="tag-item">
|
||||||
|
<span>{customer}</span>
|
||||||
|
{!readOnly && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="tag-remove"
|
||||||
|
onClick={() => removeCustomer(customer)}
|
||||||
|
aria-label="Supprimer ce client"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!readOnly && (
|
||||||
|
<div className="form-field tag-input-container">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={currentCustomer}
|
||||||
|
onChange={(e) => setCurrentCustomer(e.target.value)}
|
||||||
|
placeholder="Ajouter un client"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-add-tag"
|
||||||
|
onClick={addCustomer}
|
||||||
|
>
|
||||||
|
Ajouter
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="form-section">
|
||||||
|
<h3 className="section-title">Parties prenantes</h3>
|
||||||
|
<div className="tag-list">
|
||||||
|
{folderData.stakeholders.map((stakeholder, index) => (
|
||||||
|
<div key={index} className="tag-item">
|
||||||
|
<span>{stakeholder}</span>
|
||||||
|
{!readOnly && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="tag-remove"
|
||||||
|
onClick={() => removeStakeholder(stakeholder)}
|
||||||
|
aria-label="Supprimer cette partie prenante"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{!readOnly && (
|
||||||
|
<div className="form-field tag-input-container">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={currentStakeholder}
|
||||||
|
onChange={(e) => setCurrentStakeholder(e.target.value)}
|
||||||
|
placeholder="Ajouter une partie prenante"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-add-tag"
|
||||||
|
onClick={addStakeholder}
|
||||||
|
>
|
||||||
|
Ajouter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-section">
|
||||||
|
<h3 className="section-title">Informations système</h3>
|
||||||
|
<div className="form-row">
|
||||||
|
<div className="form-field">
|
||||||
|
<label>Créé le</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={new Date(folderData.created_at).toLocaleDateString('fr-FR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})}
|
||||||
|
disabled
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="form-field">
|
||||||
|
<label>Dernière mise à jour</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={new Date(folderData.updated_at).toLocaleDateString('fr-FR', {
|
||||||
|
day: '2-digit',
|
||||||
|
month: '2-digit',
|
||||||
|
year: 'numeric',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})}
|
||||||
|
disabled
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="form-actions">
|
||||||
|
{onCancel && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn-cancel"
|
||||||
|
onClick={onCancel}
|
||||||
|
>
|
||||||
|
Annuler
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
className="btn-submit"
|
||||||
|
>
|
||||||
|
Enregistrer
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -16,8 +16,8 @@ const formatTimestamp = (isoString: string): string => {
|
|||||||
|
|
||||||
// Vérifier si c'est aujourd'hui
|
// Vérifier si c'est aujourd'hui
|
||||||
const isToday = date.getDate() === now.getDate() &&
|
const isToday = date.getDate() === now.getDate() &&
|
||||||
date.getMonth() === now.getMonth() &&
|
date.getMonth() === now.getMonth() &&
|
||||||
date.getFullYear() === now.getFullYear();
|
date.getFullYear() === now.getFullYear();
|
||||||
|
|
||||||
if (isToday) {
|
if (isToday) {
|
||||||
// Format pour aujourd'hui: HH:MM:SS.ms
|
// Format pour aujourd'hui: HH:MM:SS.ms
|
||||||
@ -31,7 +31,7 @@ const formatTimestamp = (isoString: string): string => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
interface MessageConsoleProps {
|
interface MessageConsoleProps {
|
||||||
messages: {timestamp: string; data: any}[];
|
messages: { timestamp: string; data: any }[];
|
||||||
onClearMessages?: () => void;
|
onClearMessages?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ function MessageConsole({
|
|||||||
onClearMessages = () => console.log('Vidage des messages demandé')
|
onClearMessages = () => console.log('Vidage des messages demandé')
|
||||||
}: MessageConsoleProps) {
|
}: MessageConsoleProps) {
|
||||||
// État local pour stocker les messages reçus via l'EventBus
|
// État local pour stocker les messages reçus via l'EventBus
|
||||||
const [localMessages, setLocalMessages] = useState<{timestamp: string; data: any}[]>([]);
|
const [localMessages, setLocalMessages] = useState<{ timestamp: string; data: any }[]>([]);
|
||||||
|
|
||||||
// Tous les messages à afficher (externes + locaux), triés par timestamp
|
// Tous les messages à afficher (externes + locaux), triés par timestamp
|
||||||
const allMessages = [...externalMessages, ...localMessages].sort((a, b) => {
|
const allMessages = [...externalMessages, ...localMessages].sort((a, b) => {
|
||||||
|
@ -2,7 +2,7 @@ export default class EventBus {
|
|||||||
private static instance: EventBus;
|
private static instance: EventBus;
|
||||||
private listeners: Record<string, Array<(...args: any[]) => void>> = {};
|
private listeners: Record<string, Array<(...args: any[]) => void>> = {};
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() { }
|
||||||
|
|
||||||
public static getInstance(): EventBus {
|
public static getInstance(): EventBus {
|
||||||
if (!EventBus.instance) {
|
if (!EventBus.instance) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user