diff --git a/frontend/scenarios/set_type.feature b/frontend/scenarios/set_type.feature
index e00214b3..bfff0070 100644
--- a/frontend/scenarios/set_type.feature
+++ b/frontend/scenarios/set_type.feature
@@ -24,3 +24,9 @@ Scénario: typé comme non typé
Quand je choisis "remove current type" comme type de glose
Alors la glose n'a pas de type
+Scénario: non typé avec type non existant
+
+ Soit un document dont je suis l'auteur affiché comme glose
+ Et une session active avec mon compte
+ Quand je qualifie le document avec un nouveau type de glose
+ Alors le nouveau type est le type de la glose
\ No newline at end of file
diff --git a/frontend/src/components/Type.jsx b/frontend/src/components/Type.jsx
index 48ab3c10..69410909 100644
--- a/frontend/src/components/Type.jsx
+++ b/frontend/src/components/Type.jsx
@@ -2,56 +2,136 @@ import '../styles/Metadata.css';
import '../styles/Type.css';
import { TagFill } from 'react-bootstrap-icons';
-import { useState, useContext } from 'react';
-import { ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap';
+import { useState, useEffect, useContext, useCallback } from 'react';
+import { ListGroup, Form, InputGroup, Button } from 'react-bootstrap';
import { TypesContext } from './TypesContext.js';
+import { v4 as uuidv4 } from 'uuid';
export function TypeBadge({ type, addClassName }) {
const types = useContext(TypesContext);
if (!type) return null;
const typeSelected = types.find((t) => t.id === type);
- if (!typeSelected) return;
- return
- {typeSelected.doc.type_name}
-
;
+ if (!typeSelected || !typeSelected.doc) return null;
+
+ return (
+
+ {typeSelected.doc.type_name}
+
+ );
}
-function TypeList({ typeSelected, handleUpdate }) {
- const types = useContext(TypesContext);
+function TypeList({ typeSelected, handleUpdate, addNewType, backend }) {
+ const [types, setTypes] = useState([]);
const [searchTerm, setSearchTerm] = useState('');
+ const [newType, setNewType] = useState('');
+ const [newColor, setNewColor] = useState('#FF5733');
+
+ const fetchTypes = useCallback(async () => {
+ try {
+ const response = await backend.getView({ view: 'types', options: ['include_docs'] });
+ setTypes(response);
+ console.log('Types récupérés:', response);
+ } catch (error) {
+ console.error('Erreur lors de la récupération des types:', error);
+ }
+ }, [backend]);
+
+ useEffect(() => {
+ fetchTypes();
+ }, [fetchTypes]);
- const filteredTypes = types.filter(type =>
- type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
+ const handleAddNewType = () => {
+ if (newType.trim()) {
+ addNewType(newType, newColor)
+ .then(() => {
+ window.location.reload();
+ })
+ .catch((error) => {
+ console.error('Erreur lors de l\'ajout du type:', error);
+ });
+
+ setNewType('');
+ setNewColor('#FF5733');
+ }
+ };
+
+ const filteredTypes = types.filter(
+ (type) =>
+ type.doc &&
+ type.doc.type_name &&
+ type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
);
+
return (
<>
- Select a type
- Select a type
+ setSearchTerm(e.target.value)}
- style={{ marginBottom: '10px', width: '100%', padding: '5px' }}
+ style={{ marginBottom: '15px', padding: '10px', borderRadius: '8px' }}
/>
+
{filteredTypes.map((type, index) =>
handleUpdate(type.id)}>
-
+ onClick={() => handleUpdate(type.id)}
+ className="typeContainer"
+ style={{ cursor: 'pointer' }}
+ >
+
)}
- {typeSelected ?
+ {typeSelected && (
handleUpdate('')}>
+ key="remove-type"
+ onClick={() => handleUpdate('')}
+ style={{
+ color: 'red',
+ cursor: 'pointer',
+ marginTop: '10px',
+ textAlign: 'center'
+ }}
+ >
Remove the current type
- : null}
+ )}
+
+
+
Create a new type
+
+ setNewType(e.target.value)}
+ className="inputField"
+ />
+
+
+
+ setNewColor(e.target.value)}
+ style={{ height: '40px', width: '40px', border: 'none', cursor: 'pointer' }}
+ />
+
+ Add Type
+
+
+
>
);
}
@@ -77,26 +157,43 @@ function Type({ metadata, editable, backend }) {
.catch(console.error);
};
+ const addNewType = async (newTypeName, newColor) => {
+ const newId = uuidv4();
+ const newTypeObject = {
+ type_name: newTypeName,
+ color: newColor,
+ };
+
+ try {
+ const response = await backend.putDocument(newTypeObject, newId);
+ console.log('Type ajouté avec succès:', response);
+ return response;
+ } catch (error) {
+ console.error('Erreur lors de l\'ajout du type:', error);
+ throw new Error('L\'ajout du type a échoué. Veuillez réessayer.');
+ }
+ };
+
return (
{editable ? (
- Apply a label...}
- >
-
-
+
) : null}
- {beingEdited ?
-
- : null
- }
+ {beingEdited && (
+
+ )}
);
}
diff --git a/frontend/src/styles/Type.css b/frontend/src/styles/Type.css
index d683730c..ead80051 100644
--- a/frontend/src/styles/Type.css
+++ b/frontend/src/styles/Type.css
@@ -1,28 +1,25 @@
-.typeBadge {
- margin: 10px 5px;
- text-transform: capitalize;
- width: fit-content;
- padding: 8px 20px!important;
- font-size: 12px;
- font-weight: bold;
- line-height: 1;
- color: white;
- text-align: center;
- vertical-align: baseline;
- border-radius: 20px;
- display: inline-block;
- word-break: break-word;
-}
-
-.typeIcon {
- opacity: 1 !important;
- cursor: pointer;
- transition: filter 0.3s ease;
- display: inline-block !important;
- visibility: visible !important;
+.typeContainer {
+ background-color: white;
+ padding: 10px;
+ border-radius: 12px;
+ margin-bottom: 8px; /* Moins d'espace entre les badges */
+ display: flex;
+ justify-content: center;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
-.typeIcon:hover {
- filter: brightness(1.5);
+.typeBadge {
+ margin: 0;
+ text-transform: capitalize;
+ width: fit-content;
+ padding: 8px 20px !important;
+ font-size: 12px;
+ font-weight: bold;
+ line-height: 1;
+ color: white;
+ text-align: center;
+ vertical-align: middle;
+ border-radius: 20px;
+ display: inline-block;
+ word-break: break-word;
}
-
diff --git a/frontend/tests/event.js b/frontend/tests/event.js
index 5adb6b2a..7fb6155e 100644
--- a/frontend/tests/event.js
+++ b/frontend/tests/event.js
@@ -47,6 +47,16 @@ Quand("je choisis {string} comme type de glose", (pattern) => {
cy.get('.list-group-item').first().click();
});
+Quand("je qualifie le document avec un nouveau type de glose", function() {
+ cy.get('.typeIcon').click();
+ this.randomType = [...Array(30)].map(() => Math.random().toString(36)[2]).join('');
+ cy.get('.inputField.form-control').type(this.randomType);
+ cy.get('.addButton').click();
+ cy.get('.typeIcon').click();
+ cy.get('#searchType').type(this.randomType);
+ cy.get('.list-group-item').first().click();
+});
+
Quand("je survole le texte :", (text) => {
cy.contains('p[title="Highlight in document"]', text.trim())
.trigger('mouseover');
diff --git a/frontend/tests/outcome.js b/frontend/tests/outcome.js
index a2c03c35..87c399da 100644
--- a/frontend/tests/outcome.js
+++ b/frontend/tests/outcome.js
@@ -86,6 +86,10 @@ Alors("la glose n'a pas de type", () => {
cy.get('.typeSelected').should('not.exist');
});
+Alors("le nouveau type est le type de la glose", function() {
+ cy.contains('.typeSelected', this.randomType);
+});
+
Alors("le texte du document principal est en surbrillance :", (text) => {
cy.contains('mark', text.trim()).should('exist');
});