code7-leia 1.0.16 → 1.0.20
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/index.es.js +2285 -2371
- package/package.json +1 -1
- package/src/components/Alert/index.tsx +59 -0
- package/src/components/Alert/styles.tsx +63 -0
- package/src/components/PersonasArea/index.tsx +83 -73
- package/src/components/PersonasArea/styles.ts +6 -5
- package/src/utils/languages/en.ts +2 -1
- package/src/utils/languages/es.ts +2 -1
- package/src/utils/languages/pt-br.ts +2 -1
package/package.json
CHANGED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import styled, { css } from 'styled-components';
|
|
3
|
+
import * as S from './styles';
|
|
4
|
+
import {
|
|
5
|
+
FaCheckCircle,
|
|
6
|
+
FaExclamationTriangle,
|
|
7
|
+
FaTimesCircle,
|
|
8
|
+
FaInfoCircle,
|
|
9
|
+
} from 'react-icons/fa';
|
|
10
|
+
|
|
11
|
+
import { BsFillInfoSquareFill } from "react-icons/bs";
|
|
12
|
+
|
|
13
|
+
type AlertStatus = 'success' | 'warning' | 'error' | 'info';
|
|
14
|
+
|
|
15
|
+
interface AlertProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'> {
|
|
16
|
+
status?: AlertStatus;
|
|
17
|
+
title?: React.ReactNode;
|
|
18
|
+
message?: React.ReactNode | string[];
|
|
19
|
+
icon?: React.ReactNode;
|
|
20
|
+
children?: React.ReactNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const icons = {
|
|
24
|
+
success: <FaCheckCircle />,
|
|
25
|
+
warning: <FaExclamationTriangle />,
|
|
26
|
+
error: <FaTimesCircle />,
|
|
27
|
+
info: <BsFillInfoSquareFill />,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const Alert: React.FC<AlertProps> = ({
|
|
31
|
+
status = 'info',
|
|
32
|
+
title,
|
|
33
|
+
message,
|
|
34
|
+
icon,
|
|
35
|
+
children,
|
|
36
|
+
...rest
|
|
37
|
+
}) => {
|
|
38
|
+
const renderMessage = () => {
|
|
39
|
+
if (Array.isArray(message)) {
|
|
40
|
+
return message.map((m, i) => <p key={i}>- {m}</p>);
|
|
41
|
+
}
|
|
42
|
+
return message ? <p>{message}</p> : null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<S.Container status={status} {...rest}>
|
|
47
|
+
<S.Content>
|
|
48
|
+
<S.Header>
|
|
49
|
+
<S.Icon>{icon || icons[status]}</S.Icon>
|
|
50
|
+
{title && <strong>{title}</strong>}
|
|
51
|
+
</S.Header>
|
|
52
|
+
<div>
|
|
53
|
+
{renderMessage()}
|
|
54
|
+
{children}
|
|
55
|
+
</div>
|
|
56
|
+
</S.Content>
|
|
57
|
+
</S.Container>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import styled, { css } from 'styled-components';
|
|
2
|
+
|
|
3
|
+
type AlertStatus = 'success' | 'warning' | 'error' | 'info';
|
|
4
|
+
|
|
5
|
+
const statusStyles = {
|
|
6
|
+
success: css`
|
|
7
|
+
background: var(--semantic-green-100-light);
|
|
8
|
+
border-color: var(--semantic-green-600-light);
|
|
9
|
+
color: var(--semantic-green-600-light);
|
|
10
|
+
`,
|
|
11
|
+
warning: css`
|
|
12
|
+
background: var(--semantic-orange-100-light);
|
|
13
|
+
border-color: var(--semantic-orange-600-light);
|
|
14
|
+
color: var(--semantic-orange-600-light);
|
|
15
|
+
`,
|
|
16
|
+
error: css`
|
|
17
|
+
background: var(--semantic-red-100-light);
|
|
18
|
+
border-color: var(--semantic-red-600-light);
|
|
19
|
+
color: var(--semantic-red-600-light);
|
|
20
|
+
`,
|
|
21
|
+
info: css`
|
|
22
|
+
background: var(--semantic-cyan-100-light);
|
|
23
|
+
border-color: var(--semantic-cyan-600-light);
|
|
24
|
+
color: var(--semantic-cyan-600-light);
|
|
25
|
+
`,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export const Container = styled.div<{ status: AlertStatus }>`
|
|
29
|
+
padding: 16px 24px;
|
|
30
|
+
border: 1px solid;
|
|
31
|
+
border-radius: var(--radius-small);
|
|
32
|
+
margin-bottom: 14px;
|
|
33
|
+
|
|
34
|
+
${({ status }) => statusStyles[status]}
|
|
35
|
+
p {
|
|
36
|
+
font-size: 14px;
|
|
37
|
+
margin: 4px 0;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
export const Content = styled.div`
|
|
42
|
+
display: flex;
|
|
43
|
+
gap: 12px;
|
|
44
|
+
flex-direction: column;
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
export const Header = styled.div `
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: center;
|
|
50
|
+
gap: 8px;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
export const Icon = styled.div`
|
|
54
|
+
display: flex;
|
|
55
|
+
align-items: center;
|
|
56
|
+
font-size: 18px;
|
|
57
|
+
|
|
58
|
+
svg {
|
|
59
|
+
width: 20px;
|
|
60
|
+
height: 20px;
|
|
61
|
+
}
|
|
62
|
+
`;
|
|
63
|
+
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { useState } from
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
2
|
import { getLanguage } from '../../utils/getLanguage';
|
|
3
|
-
import { FaPlus, FaCopy, FaEdit,
|
|
3
|
+
import { FaPlus, FaCopy, FaEdit, FaTrash } from 'react-icons/fa';
|
|
4
4
|
import Modal from '../Modal';
|
|
5
5
|
import Input from '../TestArea/components/InputTest';
|
|
6
6
|
import TextArea from '../TestArea/components/TextArea';
|
|
7
7
|
import Search from '../FileArea/components/Search';
|
|
8
8
|
import LengthCounter from '../LengthCounter';
|
|
9
9
|
|
|
10
|
-
import { useLeia } from
|
|
10
|
+
import { useLeia } from "../../contexts/LeiaProvider";
|
|
11
11
|
|
|
12
|
-
import * as S from
|
|
13
|
-
import
|
|
12
|
+
import * as S from "./styles";
|
|
13
|
+
import { Alert } from "../Alert";
|
|
14
14
|
|
|
15
15
|
interface Persona {
|
|
16
16
|
id: number;
|
|
@@ -18,29 +18,36 @@ interface Persona {
|
|
|
18
18
|
description: string;
|
|
19
19
|
prompt: string;
|
|
20
20
|
type?: string;
|
|
21
|
+
created_at?: string;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export const PersonasArea = () => {
|
|
24
25
|
const { language } = useLeia();
|
|
25
26
|
const t = getLanguage(language);
|
|
26
|
-
const [_,setPersonas] = useState<Persona[]>([]);
|
|
27
27
|
const { createPersona, personas, updatePersona, deletePersona } = useLeia();
|
|
28
28
|
|
|
29
|
-
const [search, setSearch] = useState(
|
|
29
|
+
const [search, setSearch] = useState("");
|
|
30
30
|
const [isOpen, setIsOpen] = useState(false);
|
|
31
31
|
const [editingPersona, setEditingPersona] = useState<any>(null);
|
|
32
32
|
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
|
33
33
|
const [personaToDelete, setPersonaToDelete] = useState<any>(null);
|
|
34
|
+
const [_, setPersonas] = useState<Persona[]>([]);
|
|
35
|
+
const [filteredPersonas, setFilteredPersonas] = useState<any>([]);
|
|
34
36
|
|
|
35
37
|
const [form, setForm] = useState({
|
|
36
|
-
name:
|
|
37
|
-
description:
|
|
38
|
-
prompt:
|
|
38
|
+
name: "",
|
|
39
|
+
description: "",
|
|
40
|
+
prompt: "",
|
|
39
41
|
});
|
|
40
42
|
|
|
43
|
+
const isFormValid =
|
|
44
|
+
form.name.trim() !== "" &&
|
|
45
|
+
form.description.trim() !== "" &&
|
|
46
|
+
form.prompt.trim() !== "";
|
|
47
|
+
|
|
41
48
|
const openCreate = () => {
|
|
42
49
|
setEditingPersona(null);
|
|
43
|
-
setForm({ name:
|
|
50
|
+
setForm({ name: "", description: "", prompt: "" });
|
|
44
51
|
setIsOpen(true);
|
|
45
52
|
};
|
|
46
53
|
|
|
@@ -65,13 +72,14 @@ export const PersonasArea = () => {
|
|
|
65
72
|
};
|
|
66
73
|
|
|
67
74
|
const handleSave = async () => {
|
|
75
|
+
if (!isFormValid) return;
|
|
68
76
|
try {
|
|
69
77
|
if (!editingPersona) {
|
|
70
78
|
const payload = {
|
|
71
79
|
name: form.name,
|
|
72
80
|
description: form.description,
|
|
73
81
|
prompt: form.prompt,
|
|
74
|
-
type:
|
|
82
|
+
type: "persona",
|
|
75
83
|
};
|
|
76
84
|
await createPersona(payload);
|
|
77
85
|
} else {
|
|
@@ -80,7 +88,7 @@ export const PersonasArea = () => {
|
|
|
80
88
|
|
|
81
89
|
setIsOpen(false);
|
|
82
90
|
} catch (err) {
|
|
83
|
-
console.error(
|
|
91
|
+
console.error("Erro ao salvar persona", err);
|
|
84
92
|
}
|
|
85
93
|
};
|
|
86
94
|
|
|
@@ -95,37 +103,22 @@ export const PersonasArea = () => {
|
|
|
95
103
|
setIsDeleteOpen(false);
|
|
96
104
|
setPersonaToDelete(null);
|
|
97
105
|
} catch (err) {
|
|
98
|
-
console.error(
|
|
106
|
+
console.error("Erro ao deletar persona", err);
|
|
99
107
|
}
|
|
100
108
|
};
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
<h3>{t.personas?.modalTitle}</h3>
|
|
110
|
-
<Tooltip
|
|
111
|
-
position="right"
|
|
112
|
-
width={'200px'}
|
|
113
|
-
render={() =>
|
|
114
|
-
t.personas?.modalTooltip
|
|
115
|
-
}
|
|
116
|
-
>
|
|
117
|
-
<>
|
|
118
|
-
<FaQuestionCircle color = "#8D9CA1" size={16}/>
|
|
119
|
-
</>
|
|
120
|
-
</Tooltip>
|
|
121
|
-
</S.ModalTitleContainer>
|
|
122
|
-
)
|
|
123
|
-
}
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
setFilteredPersonas(
|
|
112
|
+
personas?.filter((p) =>
|
|
113
|
+
p.name.toLowerCase().includes(search.toLowerCase()),
|
|
114
|
+
),
|
|
115
|
+
);
|
|
116
|
+
}, [personas]);
|
|
124
117
|
|
|
125
118
|
return (
|
|
126
119
|
<S.Container>
|
|
127
120
|
<S.Header>
|
|
128
|
-
<div className=
|
|
121
|
+
<div className="infos">
|
|
129
122
|
<h2>{t.personas?.title}</h2>
|
|
130
123
|
<p>{t.personas?.description}</p>
|
|
131
124
|
</div>
|
|
@@ -134,44 +127,49 @@ export const PersonasArea = () => {
|
|
|
134
127
|
</button>
|
|
135
128
|
</S.Header>
|
|
136
129
|
<S.Search>
|
|
137
|
-
<Search
|
|
130
|
+
<Search
|
|
131
|
+
placeholder={t.search}
|
|
132
|
+
setFiles={setFilteredPersonas}
|
|
133
|
+
initialFiles={personas}
|
|
134
|
+
/>
|
|
138
135
|
</S.Search>
|
|
139
136
|
<S.TableContainer>
|
|
140
137
|
<tbody>
|
|
141
138
|
{filteredPersonas?.map((persona: any) => (
|
|
142
|
-
|
|
143
|
-
<tr className = "tr-Row" key = {persona.id}>
|
|
139
|
+
<tr className="tr-Row" key={persona.id}>
|
|
144
140
|
<td>
|
|
145
141
|
<div className="info">
|
|
146
|
-
<p
|
|
142
|
+
<p>
|
|
143
|
+
<strong>{persona.name}</strong>
|
|
144
|
+
</p>
|
|
147
145
|
<span>{persona.description}</span>
|
|
148
146
|
</div>
|
|
149
147
|
</td>
|
|
150
|
-
<td className=
|
|
148
|
+
<td className="td-actions">
|
|
151
149
|
{persona.created_at && (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
150
|
+
<div className="actions">
|
|
151
|
+
<button onClick={() => openEdit(persona)}>
|
|
152
|
+
<FaEdit /> {t.edit}
|
|
153
|
+
</button>
|
|
154
|
+
<button onClick={() => openClone(persona)}>
|
|
155
|
+
<FaCopy /> {t.clone}
|
|
156
|
+
</button>
|
|
157
|
+
<button
|
|
158
|
+
className="button-delete"
|
|
159
|
+
onClick={() => openDelete(persona)}
|
|
160
|
+
>
|
|
161
|
+
<FaTrash /> {t.delete}
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
164
165
|
</td>
|
|
165
166
|
</tr>
|
|
166
|
-
|
|
167
|
-
|
|
168
167
|
))}
|
|
169
168
|
</tbody>
|
|
170
169
|
</S.TableContainer>
|
|
171
170
|
|
|
172
|
-
|
|
173
171
|
{isOpen && (
|
|
174
|
-
<Modal onClose={() => setIsOpen(false)} title={
|
|
172
|
+
<Modal onClose={() => setIsOpen(false)} title={t.personas?.modalTitle}>
|
|
175
173
|
<S.InputWrapper>
|
|
176
174
|
<Input
|
|
177
175
|
value={form.name}
|
|
@@ -182,30 +180,44 @@ export const PersonasArea = () => {
|
|
|
182
180
|
<Input
|
|
183
181
|
value={form.description}
|
|
184
182
|
placeholder={t.personas?.fields.description}
|
|
185
|
-
onChange={(value: any) =>
|
|
183
|
+
onChange={(value: any) =>
|
|
184
|
+
setForm({ ...form, description: value })
|
|
185
|
+
}
|
|
186
186
|
maxLength={200}
|
|
187
187
|
/>
|
|
188
188
|
</S.InputWrapper>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
189
|
+
<div className="textarea-wrapper">
|
|
190
|
+
<TextArea
|
|
191
|
+
label={t.personas?.fields.prompt}
|
|
192
|
+
maxLength={10000}
|
|
193
|
+
value={form.prompt}
|
|
194
|
+
onChange={(e: any) => setForm({ ...form, prompt: e.target.value })}
|
|
195
|
+
/>
|
|
196
|
+
<LengthCounter value={form.prompt} maxLength={10000} />
|
|
197
|
+
</div>
|
|
198
|
+
<Alert
|
|
199
|
+
title={t.personas?.alertTitle}
|
|
200
|
+
message={t.personas?.alertMessage}
|
|
194
201
|
/>
|
|
195
|
-
<LengthCounter value={form.prompt} maxLength={10000} />
|
|
196
202
|
|
|
197
203
|
<S.ModalActions>
|
|
198
204
|
<button onClick={() => setIsOpen(false)}>{t.buttons.cancel}</button>
|
|
199
|
-
<button
|
|
205
|
+
<button
|
|
206
|
+
onClick={handleSave}
|
|
207
|
+
disabled={!isFormValid}
|
|
208
|
+
style={{
|
|
209
|
+
opacity: isFormValid ? 1 : 0.5,
|
|
210
|
+
cursor: isFormValid ? "pointer" : "not-allowed",
|
|
211
|
+
}}
|
|
212
|
+
>
|
|
213
|
+
{t.save}
|
|
214
|
+
</button>
|
|
200
215
|
</S.ModalActions>
|
|
201
216
|
</Modal>
|
|
202
217
|
)}
|
|
203
218
|
|
|
204
219
|
{isDeleteOpen && personaToDelete && (
|
|
205
|
-
<Modal
|
|
206
|
-
onClose={() => setIsDeleteOpen(false)}
|
|
207
|
-
title={t.delete}
|
|
208
|
-
>
|
|
220
|
+
<Modal onClose={() => setIsDeleteOpen(false)} title={t.delete}>
|
|
209
221
|
<p>
|
|
210
222
|
{t.personas.modalAlert} <strong>{personaToDelete.name}</strong>?
|
|
211
223
|
</p>
|
|
@@ -214,9 +226,7 @@ export const PersonasArea = () => {
|
|
|
214
226
|
<button onClick={() => setIsDeleteOpen(false)}>
|
|
215
227
|
{t.buttons.cancel}
|
|
216
228
|
</button>
|
|
217
|
-
<button onClick={confirmDelete}>
|
|
218
|
-
{t.buttons.delete}
|
|
219
|
-
</button>
|
|
229
|
+
<button onClick={confirmDelete}>{t.buttons.delete}</button>
|
|
220
230
|
</S.ModalActions>
|
|
221
231
|
</Modal>
|
|
222
232
|
)}
|
|
@@ -3,6 +3,10 @@ import styled from 'styled-components';
|
|
|
3
3
|
export const Container = styled.div`
|
|
4
4
|
display: flex;
|
|
5
5
|
flex-direction: column;
|
|
6
|
+
|
|
7
|
+
.textarea-wrapper{
|
|
8
|
+
margin-bottom: 12px;
|
|
9
|
+
}
|
|
6
10
|
`;
|
|
7
11
|
|
|
8
12
|
export const Header = styled.div`
|
|
@@ -204,8 +208,5 @@ export const TableContainer = styled.table`
|
|
|
204
208
|
}
|
|
205
209
|
`;
|
|
206
210
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
gap: 8px;
|
|
210
|
-
align-items: center;
|
|
211
|
-
`;
|
|
211
|
+
|
|
212
|
+
|
|
@@ -17,7 +17,8 @@ export const enTranslation: Language = {
|
|
|
17
17
|
add: 'Add persona',
|
|
18
18
|
modalTitle: 'Persona',
|
|
19
19
|
modalAlert: 'Are you sure you want to delete',
|
|
20
|
-
|
|
20
|
+
alertTitle: 'Important',
|
|
21
|
+
alertMessage: "The AI-generated responses are based on the guidelines in this prompt; defining their content is the user's responsibility.",
|
|
21
22
|
|
|
22
23
|
fields: {
|
|
23
24
|
name: 'Name',
|
|
@@ -17,7 +17,8 @@ export const esTranslation: Language = {
|
|
|
17
17
|
add: 'Agregar persona',
|
|
18
18
|
modalTitle: 'Persona',
|
|
19
19
|
modalAlert: '¿Seguro que deseas eliminar',
|
|
20
|
-
|
|
20
|
+
alertTitle: 'Importante',
|
|
21
|
+
alertMessage: "Las respuestas generadas por IA se basan en las pautas de este mensaje; definir su contenido es responsabilidad del usuario.",
|
|
21
22
|
|
|
22
23
|
fields: {
|
|
23
24
|
name: 'Nombre',
|
|
@@ -17,7 +17,8 @@ export const ptTranslation: Language = {
|
|
|
17
17
|
add: 'Adicionar persona',
|
|
18
18
|
modalTitle: 'Persona',
|
|
19
19
|
modalAlert: 'Quer mesmo deletar',
|
|
20
|
-
|
|
20
|
+
alertTitle: 'Importante',
|
|
21
|
+
alertMessage: "As respostas geradas pela IA são baseadas nas orientações deste prompt, sendo responsabilidade do usuário a definição de seu conteúdo.",
|
|
21
22
|
|
|
22
23
|
fields: {
|
|
23
24
|
name: 'Nome',
|