@lastbrain/ai-ui-react 1.0.11 → 1.0.12
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.
- package/dist/components/AiInput.d.ts.map +1 -1
- package/dist/components/AiInput.js +1 -1
- package/dist/components/AiPromptPanel.d.ts +14 -1
- package/dist/components/AiPromptPanel.d.ts.map +1 -1
- package/dist/components/AiPromptPanel.js +255 -7
- package/dist/components/AiSettingsButton.d.ts.map +1 -1
- package/dist/components/AiSettingsButton.js +1 -1
- package/dist/components/AiStatusButton.d.ts.map +1 -1
- package/dist/components/AiStatusButton.js +106 -11
- package/dist/examples/AiImageGenerator.d.ts +34 -0
- package/dist/examples/AiImageGenerator.d.ts.map +1 -0
- package/dist/examples/AiImageGenerator.js +85 -0
- package/dist/examples/AiPromptPanelAdvanced.d.ts +20 -0
- package/dist/examples/AiPromptPanelAdvanced.d.ts.map +1 -0
- package/dist/examples/AiPromptPanelAdvanced.js +222 -0
- package/dist/examples/ExternalIntegration.d.ts +2 -0
- package/dist/examples/ExternalIntegration.d.ts.map +1 -0
- package/dist/examples/ExternalIntegration.js +2 -0
- package/dist/hooks/useAiStatus.d.ts.map +1 -1
- package/dist/hooks/useAiStatus.js +3 -0
- package/dist/hooks/useModelManagement.d.ts +32 -0
- package/dist/hooks/useModelManagement.d.ts.map +1 -0
- package/dist/hooks/useModelManagement.js +135 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/styles/inline.d.ts.map +1 -1
- package/dist/styles/inline.js +10 -1
- package/dist/utils/modelManagement.d.ts +29 -0
- package/dist/utils/modelManagement.d.ts.map +1 -0
- package/dist/utils/modelManagement.js +80 -0
- package/package.json +2 -2
- package/src/components/AiInput.tsx +3 -0
- package/src/components/AiPromptPanel.tsx +502 -24
- package/src/components/AiSettingsButton.tsx +4 -2
- package/src/components/AiStatusButton.tsx +185 -39
- package/src/examples/AiImageGenerator.tsx +214 -0
- package/src/examples/AiPromptPanelAdvanced.tsx +381 -0
- package/src/examples/ExternalIntegration.ts +55 -0
- package/src/hooks/useAiStatus.ts +4 -0
- package/src/hooks/useModelManagement.ts +210 -0
- package/src/index.ts +8 -0
- package/src/styles/inline.ts +10 -1
- package/src/utils/modelManagement.ts +130 -0
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
import type { AiStatus } from "@lastbrain/ai-ui-core";
|
|
4
4
|
import { useState, useRef, useLayoutEffect, type CSSProperties } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
|
+
import {
|
|
7
|
+
BarChart3,
|
|
8
|
+
Settings,
|
|
9
|
+
FileText,
|
|
10
|
+
History as HistoryIcon,
|
|
11
|
+
FolderPlus,
|
|
12
|
+
} from "lucide-react";
|
|
6
13
|
import { aiStyles, calculateTooltipPosition } from "../styles/inline";
|
|
7
14
|
|
|
8
15
|
export interface AiStatusButtonProps {
|
|
@@ -36,6 +43,14 @@ export function AiStatusButton({
|
|
|
36
43
|
typeof value === "number" ? value.toLocaleString() : "0";
|
|
37
44
|
const formatFixed = (value: number | null | undefined, digits: number) =>
|
|
38
45
|
typeof value === "number" ? value.toFixed(digits) : "0.00";
|
|
46
|
+
const formatStorage = (valueMb: number | null | undefined) => {
|
|
47
|
+
const mb = typeof valueMb === "number" ? valueMb : 0;
|
|
48
|
+
if (mb >= 1024) {
|
|
49
|
+
const gb = mb / 1024;
|
|
50
|
+
return `${gb.toFixed(2)} GB`;
|
|
51
|
+
}
|
|
52
|
+
return `${mb.toFixed(2)} MB`;
|
|
53
|
+
};
|
|
39
54
|
const safeNumber = (value: number | null | undefined) =>
|
|
40
55
|
typeof value === "number" ? value : 0;
|
|
41
56
|
const clampPercentage = (value: number | null | undefined) =>
|
|
@@ -81,6 +96,17 @@ export function AiStatusButton({
|
|
|
81
96
|
strokeLinecap="round"
|
|
82
97
|
transform={`rotate(-90 ${size / 2} ${size / 2})`}
|
|
83
98
|
/>
|
|
99
|
+
<text
|
|
100
|
+
x={size / 2}
|
|
101
|
+
y={size / 2}
|
|
102
|
+
textAnchor="middle"
|
|
103
|
+
dominantBaseline="central"
|
|
104
|
+
fontSize="7px"
|
|
105
|
+
fill={color}
|
|
106
|
+
fontWeight="600"
|
|
107
|
+
>
|
|
108
|
+
{percentage.toFixed(0)}%
|
|
109
|
+
</text>
|
|
84
110
|
</svg>
|
|
85
111
|
);
|
|
86
112
|
};
|
|
@@ -352,65 +378,185 @@ export function AiStatusButton({
|
|
|
352
378
|
<div style={aiStyles.tooltipRow}>
|
|
353
379
|
<span style={aiStyles.tooltipLabel}>Total:</span>
|
|
354
380
|
<span style={aiStyles.tooltipValue}>
|
|
355
|
-
{
|
|
356
|
-
{
|
|
381
|
+
{formatStorage(storageUsed)} /{" "}
|
|
382
|
+
{formatStorage(storageAllocated)}
|
|
357
383
|
</span>
|
|
358
384
|
{renderUsageCircle(storagePercentage)}
|
|
359
385
|
</div>
|
|
360
386
|
</div>
|
|
361
387
|
|
|
362
|
-
<div
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
388
|
+
<div
|
|
389
|
+
style={{
|
|
390
|
+
...aiStyles.tooltipActions,
|
|
391
|
+
width: "100%",
|
|
392
|
+
|
|
393
|
+
flexDirection: "row",
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
<button
|
|
397
|
+
onClick={() =>
|
|
398
|
+
window.open(
|
|
399
|
+
"https://prompt.lastbrain.io/auth/ai/tokens",
|
|
400
|
+
"_blank"
|
|
401
|
+
)
|
|
402
|
+
}
|
|
403
|
+
style={{
|
|
404
|
+
background: "transparent",
|
|
405
|
+
border: "none",
|
|
406
|
+
borderRadius: "4px",
|
|
407
|
+
padding: "14px",
|
|
408
|
+
cursor: "pointer",
|
|
409
|
+
display: "flex",
|
|
410
|
+
alignItems: "center",
|
|
411
|
+
justifyContent: "center",
|
|
412
|
+
color: "#8b5cf6",
|
|
413
|
+
transition: "all 0.2s ease",
|
|
414
|
+
}}
|
|
368
415
|
onMouseEnter={(e) => {
|
|
369
|
-
Object.assign(
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
);
|
|
416
|
+
Object.assign(e.currentTarget.style, {
|
|
417
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
418
|
+
});
|
|
373
419
|
}}
|
|
374
420
|
onMouseLeave={(e) => {
|
|
375
|
-
Object.assign(e.currentTarget.style,
|
|
421
|
+
Object.assign(e.currentTarget.style, {
|
|
422
|
+
background: "transparent",
|
|
423
|
+
});
|
|
376
424
|
}}
|
|
425
|
+
title="Dashboard"
|
|
377
426
|
>
|
|
378
|
-
|
|
379
|
-
</
|
|
380
|
-
<
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
427
|
+
<BarChart3 size={18} />
|
|
428
|
+
</button>
|
|
429
|
+
<button
|
|
430
|
+
onClick={() =>
|
|
431
|
+
window.open(
|
|
432
|
+
"https://prompt.lastbrain.io/auth/ai/history",
|
|
433
|
+
"_blank"
|
|
434
|
+
)
|
|
435
|
+
}
|
|
436
|
+
style={{
|
|
437
|
+
background: "transparent",
|
|
438
|
+
border: "none",
|
|
439
|
+
borderRadius: "4px",
|
|
440
|
+
padding: "14px",
|
|
441
|
+
cursor: "pointer",
|
|
442
|
+
display: "flex",
|
|
443
|
+
alignItems: "center",
|
|
444
|
+
justifyContent: "center",
|
|
445
|
+
color: "#8b5cf6",
|
|
446
|
+
transition: "all 0.2s ease",
|
|
447
|
+
}}
|
|
385
448
|
onMouseEnter={(e) => {
|
|
386
|
-
Object.assign(
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
);
|
|
449
|
+
Object.assign(e.currentTarget.style, {
|
|
450
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
451
|
+
});
|
|
390
452
|
}}
|
|
391
453
|
onMouseLeave={(e) => {
|
|
392
|
-
Object.assign(e.currentTarget.style,
|
|
454
|
+
Object.assign(e.currentTarget.style, {
|
|
455
|
+
background: "transparent",
|
|
456
|
+
});
|
|
393
457
|
}}
|
|
458
|
+
title="History"
|
|
394
459
|
>
|
|
395
|
-
|
|
396
|
-
</
|
|
397
|
-
<
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
460
|
+
<HistoryIcon size={18} />
|
|
461
|
+
</button>
|
|
462
|
+
<button
|
|
463
|
+
onClick={() =>
|
|
464
|
+
window.open(
|
|
465
|
+
"https://prompt.lastbrain.io/auth/ai/settings",
|
|
466
|
+
"_blank"
|
|
467
|
+
)
|
|
468
|
+
}
|
|
469
|
+
style={{
|
|
470
|
+
background: "transparent",
|
|
471
|
+
border: "none",
|
|
472
|
+
borderRadius: "4px",
|
|
473
|
+
padding: "14px",
|
|
474
|
+
cursor: "pointer",
|
|
475
|
+
display: "flex",
|
|
476
|
+
alignItems: "center",
|
|
477
|
+
justifyContent: "center",
|
|
478
|
+
color: "#8b5cf6",
|
|
479
|
+
transition: "all 0.2s ease",
|
|
480
|
+
}}
|
|
481
|
+
onMouseEnter={(e) => {
|
|
482
|
+
Object.assign(e.currentTarget.style, {
|
|
483
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
484
|
+
});
|
|
485
|
+
}}
|
|
486
|
+
onMouseLeave={(e) => {
|
|
487
|
+
Object.assign(e.currentTarget.style, {
|
|
488
|
+
background: "transparent",
|
|
489
|
+
});
|
|
490
|
+
}}
|
|
491
|
+
title="Settings"
|
|
492
|
+
>
|
|
493
|
+
<Settings size={18} />
|
|
494
|
+
</button>
|
|
495
|
+
<button
|
|
496
|
+
onClick={() =>
|
|
497
|
+
window.open(
|
|
498
|
+
"https://prompt.lastbrain.io/auth/ai/prompts",
|
|
499
|
+
"_blank"
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
style={{
|
|
503
|
+
background: "transparent",
|
|
504
|
+
border: "none",
|
|
505
|
+
borderRadius: "4px",
|
|
506
|
+
padding: "14px",
|
|
507
|
+
cursor: "pointer",
|
|
508
|
+
display: "flex",
|
|
509
|
+
alignItems: "center",
|
|
510
|
+
justifyContent: "center",
|
|
511
|
+
color: "#8b5cf6",
|
|
512
|
+
transition: "all 0.2s ease",
|
|
513
|
+
}}
|
|
514
|
+
onMouseEnter={(e) => {
|
|
515
|
+
Object.assign(e.currentTarget.style, {
|
|
516
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
517
|
+
});
|
|
518
|
+
}}
|
|
519
|
+
onMouseLeave={(e) => {
|
|
520
|
+
Object.assign(e.currentTarget.style, {
|
|
521
|
+
background: "transparent",
|
|
522
|
+
});
|
|
523
|
+
}}
|
|
524
|
+
title="New Prompt"
|
|
525
|
+
>
|
|
526
|
+
<FileText size={18} />
|
|
527
|
+
</button>
|
|
528
|
+
<button
|
|
529
|
+
onClick={() =>
|
|
530
|
+
window.open(
|
|
531
|
+
"https://prompt.lastbrain.io/auth/folder",
|
|
532
|
+
"_blank"
|
|
533
|
+
)
|
|
534
|
+
}
|
|
535
|
+
style={{
|
|
536
|
+
background: "transparent",
|
|
537
|
+
border: "none",
|
|
538
|
+
padding: "14px",
|
|
539
|
+
cursor: "pointer",
|
|
540
|
+
display: "flex",
|
|
541
|
+
alignItems: "center",
|
|
542
|
+
justifyContent: "center",
|
|
543
|
+
color: "#8b5cf6",
|
|
544
|
+
transition: "all 0.2s ease",
|
|
545
|
+
}}
|
|
402
546
|
onMouseEnter={(e) => {
|
|
403
|
-
Object.assign(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
);
|
|
547
|
+
Object.assign(e.currentTarget.style, {
|
|
548
|
+
background: "rgba(139, 92, 246, 0.1)",
|
|
549
|
+
});
|
|
407
550
|
}}
|
|
408
551
|
onMouseLeave={(e) => {
|
|
409
|
-
Object.assign(e.currentTarget.style,
|
|
552
|
+
Object.assign(e.currentTarget.style, {
|
|
553
|
+
background: "transparent",
|
|
554
|
+
});
|
|
410
555
|
}}
|
|
556
|
+
title="New Folder"
|
|
411
557
|
>
|
|
412
|
-
|
|
413
|
-
</
|
|
558
|
+
<FolderPlus size={18} />
|
|
559
|
+
</button>
|
|
414
560
|
</div>
|
|
415
561
|
</div>,
|
|
416
562
|
document.body
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { useModelManagement } from "../hooks/useModelManagement";
|
|
2
|
+
|
|
3
|
+
interface ModelManagementExampleProps {
|
|
4
|
+
/** Clé API LastBrain pour l'authentification */
|
|
5
|
+
apiKey?: string;
|
|
6
|
+
/** URL de base de l'API */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
/** Catégorie de modèles à gérer */
|
|
9
|
+
category?: "text" | "image" | "audio" | "video";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Composant exemple montrant comment utiliser le hook useModelManagement
|
|
14
|
+
* Retourne les données et handlers pour les passer à d'autres composants
|
|
15
|
+
*/
|
|
16
|
+
export function useImageModelManagement({
|
|
17
|
+
apiKey,
|
|
18
|
+
baseUrl,
|
|
19
|
+
category = "image",
|
|
20
|
+
}: ModelManagementExampleProps = {}) {
|
|
21
|
+
const {
|
|
22
|
+
availableModels,
|
|
23
|
+
userModels,
|
|
24
|
+
loading,
|
|
25
|
+
error,
|
|
26
|
+
toggleModel,
|
|
27
|
+
isModelActive,
|
|
28
|
+
getActiveModels,
|
|
29
|
+
getInactiveModels,
|
|
30
|
+
} = useModelManagement({
|
|
31
|
+
apiKey,
|
|
32
|
+
baseUrl,
|
|
33
|
+
category,
|
|
34
|
+
autoFetch: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const handleModelToggle = async (modelId: string, isActive: boolean) => {
|
|
38
|
+
try {
|
|
39
|
+
await toggleModel(modelId, isActive);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
console.error("Erreur lors de la modification du modèle:", error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
availableModels,
|
|
48
|
+
userModels,
|
|
49
|
+
loading,
|
|
50
|
+
error,
|
|
51
|
+
toggleModel: handleModelToggle,
|
|
52
|
+
isModelActive,
|
|
53
|
+
getActiveModels,
|
|
54
|
+
getInactiveModels,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Composant exemple affichant les statistiques des modèles
|
|
60
|
+
*/
|
|
61
|
+
export function ModelManagementStats({
|
|
62
|
+
apiKey,
|
|
63
|
+
baseUrl,
|
|
64
|
+
category = "image",
|
|
65
|
+
}: ModelManagementExampleProps = {}) {
|
|
66
|
+
const {
|
|
67
|
+
availableModels,
|
|
68
|
+
loading,
|
|
69
|
+
error,
|
|
70
|
+
getActiveModels,
|
|
71
|
+
getInactiveModels,
|
|
72
|
+
} = useModelManagement({
|
|
73
|
+
apiKey,
|
|
74
|
+
baseUrl,
|
|
75
|
+
category,
|
|
76
|
+
autoFetch: true,
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (loading) {
|
|
80
|
+
return <div className="p-4 text-center">Chargement des modèles...</div>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (error) {
|
|
84
|
+
return (
|
|
85
|
+
<div className="p-4 border border-red-200 rounded-lg bg-red-50">
|
|
86
|
+
<p className="text-red-600">Erreur: {error}</p>
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<div className="grid grid-cols-3 gap-4">
|
|
93
|
+
<div className="p-3 bg-green-100 rounded-lg">
|
|
94
|
+
<div className="text-sm text-green-700">Modèles activés</div>
|
|
95
|
+
<div className="text-2xl font-bold text-green-800">
|
|
96
|
+
{getActiveModels().length}
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div className="p-3 bg-blue-100 rounded-lg">
|
|
100
|
+
<div className="text-sm text-blue-700">Modèles disponibles</div>
|
|
101
|
+
<div className="text-2xl font-bold text-blue-800">
|
|
102
|
+
{availableModels.length}
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="p-3 bg-orange-100 rounded-lg">
|
|
106
|
+
<div className="text-sm text-orange-700">Non utilisés</div>
|
|
107
|
+
<div className="text-2xl font-bold text-orange-800">
|
|
108
|
+
{getInactiveModels().length}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Composant exemple pour afficher et gérer une liste de modèles
|
|
117
|
+
*/
|
|
118
|
+
export function ModelManagementList({
|
|
119
|
+
apiKey,
|
|
120
|
+
baseUrl,
|
|
121
|
+
category = "image",
|
|
122
|
+
onModelToggle,
|
|
123
|
+
}: ModelManagementExampleProps & {
|
|
124
|
+
onModelToggle?: (modelId: string, isActive: boolean) => void;
|
|
125
|
+
}) {
|
|
126
|
+
const {
|
|
127
|
+
availableModels,
|
|
128
|
+
userModels,
|
|
129
|
+
loading,
|
|
130
|
+
error,
|
|
131
|
+
toggleModel,
|
|
132
|
+
isModelActive,
|
|
133
|
+
} = useModelManagement({
|
|
134
|
+
apiKey,
|
|
135
|
+
baseUrl,
|
|
136
|
+
category,
|
|
137
|
+
autoFetch: true,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const handleToggle = async (modelId: string) => {
|
|
141
|
+
const isCurrentlyActive = isModelActive(modelId);
|
|
142
|
+
const newState = !isCurrentlyActive;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
await toggleModel(modelId, newState);
|
|
146
|
+
onModelToggle?.(modelId, newState);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error("Erreur lors de la modification du modèle:", error);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
if (loading) {
|
|
153
|
+
return <div className="p-4 text-center">Chargement des modèles...</div>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (error) {
|
|
157
|
+
return (
|
|
158
|
+
<div className="p-4 border border-red-200 rounded-lg bg-red-50">
|
|
159
|
+
<p className="text-red-600">Erreur: {error}</p>
|
|
160
|
+
</div>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div className="space-y-3">
|
|
166
|
+
<h3 className="text-lg font-semibold">Modèles {category}</h3>
|
|
167
|
+
{availableModels.map((model) => {
|
|
168
|
+
const isActive = isModelActive(model.id);
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div
|
|
172
|
+
key={model.id}
|
|
173
|
+
className="flex items-center justify-between p-3 border rounded-lg hover:bg-gray-50"
|
|
174
|
+
>
|
|
175
|
+
<div className="flex items-center gap-3">
|
|
176
|
+
<div
|
|
177
|
+
className={`w-3 h-3 rounded-full ${isActive ? "bg-green-500" : "bg-gray-300"}`}
|
|
178
|
+
/>
|
|
179
|
+
<div>
|
|
180
|
+
<div className="flex items-center gap-2">
|
|
181
|
+
<span className="font-medium">{model.name}</span>
|
|
182
|
+
{model.isPro && (
|
|
183
|
+
<span className="px-2 py-1 text-xs bg-blue-100 text-blue-800 rounded">
|
|
184
|
+
PRO
|
|
185
|
+
</span>
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
{model.description && (
|
|
189
|
+
<p className="text-sm text-gray-500">{model.description}</p>
|
|
190
|
+
)}
|
|
191
|
+
<div className="flex items-center gap-4 mt-1 text-xs text-gray-400">
|
|
192
|
+
<span>Fournisseur: {model.provider}</span>
|
|
193
|
+
{model.costPer1M && (
|
|
194
|
+
<span>Coût: ${model.costPer1M}/1M tokens</span>
|
|
195
|
+
)}
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<button
|
|
200
|
+
onClick={() => handleToggle(model.id)}
|
|
201
|
+
className={`px-3 py-1 text-sm rounded ${
|
|
202
|
+
isActive
|
|
203
|
+
? "bg-red-100 text-red-700 hover:bg-red-200"
|
|
204
|
+
: "bg-green-100 text-green-700 hover:bg-green-200"
|
|
205
|
+
}`}
|
|
206
|
+
>
|
|
207
|
+
{isActive ? "Désactiver" : "Activer"}
|
|
208
|
+
</button>
|
|
209
|
+
</div>
|
|
210
|
+
);
|
|
211
|
+
})}
|
|
212
|
+
</div>
|
|
213
|
+
);
|
|
214
|
+
}
|