Skip to content

Commit 2c2093a

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

File tree

2 files changed

+153
-59
lines changed

2 files changed

+153
-59
lines changed

frontend/src/components/Type.jsx

Lines changed: 131 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,56 +2,136 @@ 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, useCallback } from 'react';
6+
import { ListGroup, Form, InputGroup, Button } from 'react-bootstrap';
77
import { TypesContext } from './TypesContext.js';
8+
import { v4 as uuidv4 } from 'uuid';
89

910
export function TypeBadge({ type, addClassName }) {
1011
const types = useContext(TypesContext);
1112
if (!type) return null;
1213
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>;
14+
if (!typeSelected || !typeSelected.doc) return null;
15+
16+
return (
17+
<span
18+
className={`typeBadge ${addClassName ?? ''}`}
19+
style={{ backgroundColor: typeSelected.doc.color }}
20+
>
21+
{typeSelected.doc.type_name}
22+
</span>
23+
);
1724
}
1825

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

23-
const filteredTypes = types.filter(type =>
24-
type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
46+
const handleAddNewType = () => {
47+
if (newType.trim()) {
48+
addNewType(newType, newColor)
49+
.then(() => {
50+
window.location.reload();
51+
})
52+
.catch((error) => {
53+
console.error('Erreur lors de l\'ajout du type:', error);
54+
});
55+
56+
setNewType('');
57+
setNewColor('#FF5733');
58+
}
59+
};
60+
61+
const filteredTypes = types.filter(
62+
(type) =>
63+
type.doc &&
64+
type.doc.type_name &&
65+
type.doc.type_name.toLowerCase().includes(searchTerm.toLowerCase())
2566
);
67+
2668
return (
2769
<>
28-
<h6 style={{ textAlign: 'left' }}>Select a type</h6>
29-
<input
70+
<h6 style={{ textAlign: 'left', marginBottom: '15px' }}>Select a type</h6>
71+
<Form.Control
3072
type="text"
3173
id="searchType"
3274
placeholder="Filter types..."
3375
value={searchTerm}
3476
onChange={(e) => setSearchTerm(e.target.value)}
35-
style={{ marginBottom: '10px', width: '100%', padding: '5px' }}
77+
style={{ marginBottom: '15px', padding: '10px', borderRadius: '8px' }}
3678
/>
79+
3780
<ListGroup style={{ textAlign: 'center', paddingTop: 0, paddingBottom: 20 }}>
3881
{filteredTypes.map((type, index) =>
3982
<ListGroup.Item action
4083
key={index}
41-
style={{ backgroundColor: type === typeSelected ? 'grey' : '' }}
42-
onClick={() => handleUpdate(type.id)}>
43-
<TypeBadge type={type.id}/>
84+
onClick={() => handleUpdate(type.id)}
85+
className="typeContainer"
86+
style={{ cursor: 'pointer' }}
87+
>
88+
<TypeBadge type={type.id} />
4489
</ListGroup.Item>
4590
)}
46-
{typeSelected ?
91+
{typeSelected && (
4792
<ListGroup.Item action
48-
key={'remove type'}
49-
style={{ color: 'red' }}
50-
onClick={() => handleUpdate('')}>
93+
key="remove-type"
94+
onClick={() => handleUpdate('')}
95+
style={{
96+
color: 'red',
97+
cursor: 'pointer',
98+
marginTop: '10px',
99+
textAlign: 'center'
100+
}}
101+
>
51102
Remove the current type
52103
</ListGroup.Item>
53-
: null}
104+
)}
54105
</ListGroup>
106+
107+
<div style={{ marginTop: '20px' }}>
108+
<h6 style={{ textAlign: 'left', marginBottom: '10px' }}>Create a new type</h6>
109+
<InputGroup style={{ marginBottom: '10px' }}>
110+
<Form.Control
111+
type="text"
112+
placeholder="New type name"
113+
value={newType}
114+
onChange={(e) => setNewType(e.target.value)}
115+
className="inputField"
116+
/>
117+
</InputGroup>
118+
119+
<InputGroup>
120+
<Form.Control
121+
type="color"
122+
value={newColor}
123+
onChange={(e) => setNewColor(e.target.value)}
124+
style={{ height: '40px', width: '40px', border: 'none', cursor: 'pointer' }}
125+
/>
126+
<Button
127+
variant="success"
128+
onClick={handleAddNewType}
129+
className="addButton"
130+
>
131+
Add Type
132+
</Button>
133+
</InputGroup>
134+
</div>
55135
</>
56136
);
57137
}
@@ -77,26 +157,43 @@ function Type({ metadata, editable, backend }) {
77157
.catch(console.error);
78158
};
79159

160+
const addNewType = async (newTypeName, newColor) => {
161+
const newId = uuidv4();
162+
const newTypeObject = {
163+
type_name: newTypeName,
164+
color: newColor,
165+
};
166+
167+
try {
168+
const response = await backend.putDocument(newTypeObject, newId);
169+
console.log('Type ajouté avec succès:', response);
170+
return response;
171+
} catch (error) {
172+
console.error('Erreur lors de l\'ajout du type:', error);
173+
throw new Error('L\'ajout du type a échoué. Veuillez réessayer.');
174+
}
175+
};
176+
80177
return (
81178
<div style={{ paddingTop: 10, paddingBottom: 30 }}>
82179
<div style={{ paddingTop: 0, justifyContent: 'flex-end' }}>
83180
<TypeBadge addClassName="typeSelected" type={typeSelected}/>
84181
{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>
182+
<TagFill
183+
onClick={handleEdit}
184+
className="icon typeIcon"
185+
title="Apply a label..."
186+
/>
94187
) : null}
95188
</div>
96-
{beingEdited ?
97-
<TypeList typeSelected={typeSelected} handleUpdate={handleUpdate}/>
98-
: null
99-
}
189+
{beingEdited && (
190+
<TypeList
191+
typeSelected={typeSelected}
192+
handleUpdate={handleUpdate}
193+
addNewType={addNewType}
194+
backend={backend}
195+
/>
196+
)}
100197
</div>
101198
);
102199
}

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)