Skip to content

Commit d5ba5e2

Browse files
FEATURE: Attach a new type to a document (closes #19).
Co-authored-by: FredWantou <[email protected]>
1 parent 2d0b298 commit d5ba5e2

File tree

2 files changed

+154
-59
lines changed

2 files changed

+154
-59
lines changed

frontend/src/components/Type.jsx

Lines changed: 132 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,137 @@ import '../styles/Metadata.css';
22
import '../styles/Type.css';
33

44
import { TagFill } from 'react-bootstrap-icons';
5-
import { useState, useContext } from 'react';
6-
import { ListGroup, OverlayTrigger, Tooltip } from 'react-bootstrap';
5+
import { useState, useEffect, useContext } from 'react';
6+
import { ListGroup, Form, InputGroup, Button } from 'react-bootstrap';
7+
import { useCallback, useEffect } from 'react';
78
import { TypesContext } from './TypesContext.js';
9+
import { v4 as uuidv4 } from 'uuid';
810

911
export function TypeBadge({ type, addClassName }) {
1012
const types = useContext(TypesContext);
1113
if (!type) return null;
1214
const typeSelected = types.find((t) => t.id === type);
13-
if (!typeSelected) return;
14-
return <div style={{backgroundColor: typeSelected.doc.color}} className={`typeBadge ${addClassName ?? ''}`}>
15-
{typeSelected.doc.type_name}
16-
</div>;
15+
if (!typeSelected || !typeSelected.doc) return null;
16+
17+
return (
18+
<span
19+
className={`typeBadge ${addClassName ?? ''}`}
20+
style={{ backgroundColor: typeSelected.doc.color }}
21+
>
22+
{typeSelected.doc.type_name}
23+
</span>
24+
);
1725
}
1826

19-
function TypeList({ typeSelected, handleUpdate }) {
20-
const types = useContext(TypesContext);
27+
function TypeList({ typeSelected, handleUpdate, addNewType, backend }) {
28+
const [types, setTypes] = useState([]);
2129
const [searchTerm, setSearchTerm] = useState('');
30+
const [newType, setNewType] = useState('');
31+
const [newColor, setNewColor] = useState('#FF5733');
32+
33+
const fetchTypes = useCallback(async () => {
34+
try {
35+
const response = await backend.getView({ view: 'types', options: ['include_docs'] });
36+
setTypes(response);
37+
console.log('Types récupérés:', response);
38+
} catch (error) {
39+
console.error('Erreur lors de la récupération des types:', error);
40+
}
41+
}, [backend]);
42+
43+
useEffect(() => {
44+
fetchTypes();
45+
}, [fetchTypes]);
2246

23-
const filteredTypes = types.filter(type =>
24-
type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
47+
const handleAddNewType = () => {
48+
if (newType.trim()) {
49+
addNewType(newType, newColor)
50+
.then(() => {
51+
window.location.reload();
52+
})
53+
.catch((error) => {
54+
console.error('Erreur lors de l\'ajout du type:', error);
55+
});
56+
57+
setNewType('');
58+
setNewColor('#FF5733');
59+
}
60+
};
61+
62+
const filteredTypes = types.filter(
63+
(type) =>
64+
type.doc &&
65+
type.doc.type_name &&
66+
type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
2567
);
68+
2669
return (
2770
<>
28-
<h6 style={{ textAlign: 'left' }}>Select a type</h6>
29-
<input
71+
<h6 style={{ textAlign: 'left', marginBottom: '15px' }}>Select a type</h6>
72+
<Form.Control
3073
type="text"
3174
id="searchType"
3275
placeholder="Filter types..."
3376
value={searchTerm}
3477
onChange={(e) => setSearchTerm(e.target.value)}
35-
style={{ marginBottom: '10px', width: '100%', padding: '5px' }}
78+
style={{ marginBottom: '15px', padding: '10px', borderRadius: '8px' }}
3679
/>
80+
3781
<ListGroup style={{ textAlign: 'center', paddingTop: 0, paddingBottom: 20 }}>
3882
{filteredTypes.map((type, index) =>
3983
<ListGroup.Item action
4084
key={index}
41-
style={{ backgroundColor: type === typeSelected ? 'grey' : '' }}
42-
onClick={() => handleUpdate(type.id)}>
43-
<TypeBadge type={type.id}/>
85+
onClick={() => handleUpdate(type.id)}
86+
className="typeContainer"
87+
style={{ cursor: 'pointer' }}
88+
>
89+
<TypeBadge type={type.id} />
4490
</ListGroup.Item>
4591
)}
46-
{typeSelected ?
92+
{typeSelected && (
4793
<ListGroup.Item action
48-
key={'remove type'}
49-
style={{ color: 'red' }}
50-
onClick={() => handleUpdate('')}>
94+
key="remove-type"
95+
onClick={() => handleUpdate('')}
96+
style={{
97+
color: 'red',
98+
cursor: 'pointer',
99+
marginTop: '10px',
100+
textAlign: 'center'
101+
}}
102+
>
51103
Remove the current type
52104
</ListGroup.Item>
53-
: null}
105+
)}
54106
</ListGroup>
107+
108+
<div style={{ marginTop: '20px' }}>
109+
<h6 style={{ textAlign: 'left', marginBottom: '10px' }}>Create a new type</h6>
110+
<InputGroup style={{ marginBottom: '10px' }}>
111+
<Form.Control
112+
type="text"
113+
placeholder="New type name"
114+
value={newType}
115+
onChange={(e) => setNewType(e.target.value)}
116+
className="inputField"
117+
/>
118+
</InputGroup>
119+
120+
<InputGroup>
121+
<Form.Control
122+
type="color"
123+
value={newColor}
124+
onChange={(e) => setNewColor(e.target.value)}
125+
style={{ height: '40px', width: '40px', border: 'none', cursor: 'pointer' }}
126+
/>
127+
<Button
128+
variant="success"
129+
onClick={handleAddNewType}
130+
className="addButton"
131+
>
132+
Add Type
133+
</Button>
134+
</InputGroup>
135+
</div>
55136
</>
56137
);
57138
}
@@ -77,26 +158,43 @@ function Type({ metadata, editable, backend }) {
77158
.catch(console.error);
78159
};
79160

161+
const addNewType = async (newTypeName, newColor) => {
162+
const newId = uuidv4();
163+
const newTypeObject = {
164+
type_name: newTypeName,
165+
color: newColor,
166+
};
167+
168+
try {
169+
const response = await backend.putDocument(newTypeObject, newId);
170+
console.log('Type ajouté avec succès:', response);
171+
return response;
172+
} catch (error) {
173+
console.error('Erreur lors de l\'ajout du type:', error);
174+
throw new Error('L\'ajout du type a échoué. Veuillez réessayer.');
175+
}
176+
};
177+
80178
return (
81179
<div style={{ paddingTop: 10, paddingBottom: 30 }}>
82180
<div style={{ paddingTop: 0, justifyContent: 'flex-end' }}>
83181
<TypeBadge addClassName="typeSelected" type={typeSelected}/>
84182
{editable ? (
85-
<OverlayTrigger
86-
placement="top"
87-
overlay={<Tooltip id="tooltip-apply-label">Apply a label...</Tooltip>}
88-
>
89-
<TagFill
90-
onClick={handleEdit}
91-
className="icon typeIcon always-visible"
92-
/>
93-
</OverlayTrigger>
183+
<TagFill
184+
onClick={handleEdit}
185+
className="icon typeIcon"
186+
title="Apply a label..."
187+
/>
94188
) : null}
95189
</div>
96-
{beingEdited ?
97-
<TypeList typeSelected={typeSelected} handleUpdate={handleUpdate}/>
98-
: null
99-
}
190+
{beingEdited && (
191+
<TypeList
192+
typeSelected={typeSelected}
193+
handleUpdate={handleUpdate}
194+
addNewType={addNewType}
195+
backend={backend}
196+
/>
197+
)}
100198
</div>
101199
);
102200
}

frontend/src/styles/Type.css

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,25 @@
1-
.typeBadge {
2-
margin: 10px 5px;
3-
text-transform: capitalize;
4-
width: fit-content;
5-
padding: 8px 20px!important;
6-
font-size: 12px;
7-
font-weight: bold;
8-
line-height: 1;
9-
color: white;
10-
text-align: center;
11-
vertical-align: baseline;
12-
border-radius: 20px;
13-
display: inline-block;
14-
word-break: break-word;
15-
}
16-
17-
.typeIcon {
18-
opacity: 1 !important;
19-
cursor: pointer;
20-
transition: filter 0.3s ease;
21-
display: inline-block !important;
22-
visibility: visible !important;
1+
.typeContainer {
2+
background-color: white;
3+
padding: 10px;
4+
border-radius: 12px;
5+
margin-bottom: 8px; /* Moins d'espace entre les badges */
6+
display: flex;
7+
justify-content: center;
8+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
239
}
2410

25-
.typeIcon:hover {
26-
filter: brightness(1.5);
11+
.typeBadge {
12+
margin: 0;
13+
text-transform: capitalize;
14+
width: fit-content;
15+
padding: 8px 20px !important;
16+
font-size: 12px;
17+
font-weight: bold;
18+
line-height: 1;
19+
color: white;
20+
text-align: center;
21+
vertical-align: middle;
22+
border-radius: 20px;
23+
display: inline-block;
24+
word-break: break-word;
2725
}
28-

0 commit comments

Comments
 (0)