@workspace-platform/ui 0.1.2 → 0.2.1
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/admin.d.mts +2 -0
- package/dist/admin.d.ts +2 -0
- package/dist/admin.js +19 -0
- package/dist/admin.js.map +1 -0
- package/dist/admin.mjs +1 -0
- package/dist/admin.mjs.map +1 -0
- package/dist/index.d.mts +962 -0
- package/dist/index.d.ts +962 -0
- package/dist/index.js +4158 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4114 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +17 -9
package/dist/index.js
ADDED
|
@@ -0,0 +1,4158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
AVAILABLE_AGENTS: () => AVAILABLE_AGENTS,
|
|
34
|
+
ArtefactViewerDialog: () => ArtefactViewerDialog,
|
|
35
|
+
Badge: () => Badge,
|
|
36
|
+
CATEGORY_STYLES: () => CATEGORY_STYLES,
|
|
37
|
+
ChartRenderer: () => ChartRenderer,
|
|
38
|
+
ConfirmModal: () => ConfirmModal,
|
|
39
|
+
ContextPanel: () => ContextPanel,
|
|
40
|
+
CreatePrivateWorkspaceModal: () => CreatePrivateWorkspaceModal,
|
|
41
|
+
CreateWorkspaceModal: () => CreateWorkspaceModal,
|
|
42
|
+
EMPTY_FILTERS: () => EMPTY_FILTERS,
|
|
43
|
+
FILTER_PILLS: () => FILTER_PILLS,
|
|
44
|
+
ICON_COLOR: () => ICON_COLOR,
|
|
45
|
+
IGNORED_FIELDS: () => IGNORED_FIELDS,
|
|
46
|
+
MEMBER_ROLE_LABELS: () => MEMBER_ROLE_LABELS,
|
|
47
|
+
MemberAvatar: () => MemberAvatar,
|
|
48
|
+
MembersPanel: () => MembersPanel,
|
|
49
|
+
MessageFeed: () => MessageFeed,
|
|
50
|
+
MessageFilterDialog: () => MessageFilterDialog,
|
|
51
|
+
MessageItem: () => MessageItem,
|
|
52
|
+
Modal: () => Modal,
|
|
53
|
+
Pagination: () => Pagination,
|
|
54
|
+
ROLE_LABELS: () => ROLE_LABELS,
|
|
55
|
+
STATUS_LABELS: () => STATUS_LABELS,
|
|
56
|
+
SyncPanel: () => SyncPanel,
|
|
57
|
+
TableRenderer: () => TableRenderer,
|
|
58
|
+
Tooltip: () => Tooltip,
|
|
59
|
+
WORKSPACE_FIELD_LABELS: () => WORKSPACE_FIELD_LABELS,
|
|
60
|
+
WORKSPACE_STATUS_LABELS: () => WORKSPACE_STATUS_LABELS,
|
|
61
|
+
WorkspaceCard: () => WorkspaceCard,
|
|
62
|
+
WorkspaceEvent: () => WorkspaceEvent,
|
|
63
|
+
WorkspaceList: () => WorkspaceList,
|
|
64
|
+
WorkspaceProvider: () => WorkspaceProvider,
|
|
65
|
+
WorkspaceRow: () => WorkspaceRow,
|
|
66
|
+
cn: () => cn,
|
|
67
|
+
createWorkspaceApis: () => createWorkspaceApis,
|
|
68
|
+
createWorkspaceClient: () => createWorkspaceClient,
|
|
69
|
+
createWorkspaceListStore: () => createWorkspaceListStore,
|
|
70
|
+
createWorkspaceSocket: () => createWorkspaceSocket,
|
|
71
|
+
fixMojibake: () => fixMojibake,
|
|
72
|
+
formatWorkspaceExport: () => formatWorkspaceExport,
|
|
73
|
+
getInitials: () => getInitials,
|
|
74
|
+
useWorkspaceApi: () => useWorkspaceApi,
|
|
75
|
+
useWorkspaceContext: () => useWorkspaceContext,
|
|
76
|
+
workspaceStreamdownComponents: () => workspaceStreamdownComponents
|
|
77
|
+
});
|
|
78
|
+
module.exports = __toCommonJS(src_exports);
|
|
79
|
+
|
|
80
|
+
// src/provider.tsx
|
|
81
|
+
var import_react = require("react");
|
|
82
|
+
|
|
83
|
+
// src/api/client.ts
|
|
84
|
+
function createWorkspaceClient(config) {
|
|
85
|
+
function buildHeaders(extra) {
|
|
86
|
+
const base = {
|
|
87
|
+
"Content-Type": "application/json",
|
|
88
|
+
...extra
|
|
89
|
+
};
|
|
90
|
+
const token = config.getAuthToken();
|
|
91
|
+
if (token) base["Authorization"] = `Bearer ${token}`;
|
|
92
|
+
if (config.extraHeaders) Object.assign(base, config.extraHeaders);
|
|
93
|
+
return base;
|
|
94
|
+
}
|
|
95
|
+
async function req(path, options) {
|
|
96
|
+
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
97
|
+
...options,
|
|
98
|
+
headers: buildHeaders(options?.headers)
|
|
99
|
+
});
|
|
100
|
+
if (res.status === 204) return void 0;
|
|
101
|
+
const json = await res.json();
|
|
102
|
+
if (!res.ok) {
|
|
103
|
+
const rawMessage = json?.message ?? `Error ${res.status}`;
|
|
104
|
+
const errorMessage = Array.isArray(rawMessage) ? rawMessage.join(", ") : typeof rawMessage === "string" ? rawMessage : JSON.stringify(rawMessage);
|
|
105
|
+
throw new Error(errorMessage);
|
|
106
|
+
}
|
|
107
|
+
return json;
|
|
108
|
+
}
|
|
109
|
+
async function reqBlob(path) {
|
|
110
|
+
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
111
|
+
headers: buildHeaders()
|
|
112
|
+
});
|
|
113
|
+
if (!res.ok) {
|
|
114
|
+
const text = await res.text().catch(() => "");
|
|
115
|
+
throw new Error(`Error ${res.status}: ${text}`);
|
|
116
|
+
}
|
|
117
|
+
return res.blob();
|
|
118
|
+
}
|
|
119
|
+
async function upload(path, formData) {
|
|
120
|
+
const headers = {};
|
|
121
|
+
const token = config.getAuthToken();
|
|
122
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
123
|
+
if (config.extraHeaders) Object.assign(headers, config.extraHeaders);
|
|
124
|
+
const res = await fetch(`${config.baseUrl}${path}`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers,
|
|
127
|
+
body: formData
|
|
128
|
+
});
|
|
129
|
+
if (!res.ok) {
|
|
130
|
+
const json = await res.json().catch(() => ({}));
|
|
131
|
+
throw new Error(
|
|
132
|
+
json?.message ?? `Upload failed (${res.status})`
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
return res.json();
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
get: (path) => req(path),
|
|
139
|
+
post: (path, body) => req(path, { method: "POST", body: JSON.stringify(body) }),
|
|
140
|
+
patch: (path, body) => req(path, { method: "PATCH", body: JSON.stringify(body) }),
|
|
141
|
+
delete: (path) => req(path, { method: "DELETE" }),
|
|
142
|
+
upload: (path, formData) => upload(path, formData),
|
|
143
|
+
getBlob: (path) => reqBlob(path)
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// src/socket/client.ts
|
|
148
|
+
var import_socket = require("socket.io-client");
|
|
149
|
+
function createWorkspaceSocket(config) {
|
|
150
|
+
let socket = null;
|
|
151
|
+
function getSocket() {
|
|
152
|
+
if (!socket) {
|
|
153
|
+
const token = config.getAuthToken();
|
|
154
|
+
socket = (0, import_socket.io)(`${config.baseUrl}/workspaces`, {
|
|
155
|
+
autoConnect: false,
|
|
156
|
+
auth: { token: token ?? "" }
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
return socket;
|
|
160
|
+
}
|
|
161
|
+
function connect(userId) {
|
|
162
|
+
const s = getSocket();
|
|
163
|
+
if (!s.connected) s.connect();
|
|
164
|
+
const uid = userId ?? config.currentUserId;
|
|
165
|
+
if (uid) {
|
|
166
|
+
if (s.connected) {
|
|
167
|
+
s.emit("authenticate", { userId: uid });
|
|
168
|
+
} else {
|
|
169
|
+
s.once("connect", () => {
|
|
170
|
+
s.emit("authenticate", { userId: uid });
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return s;
|
|
175
|
+
}
|
|
176
|
+
function disconnect() {
|
|
177
|
+
socket?.disconnect();
|
|
178
|
+
socket = null;
|
|
179
|
+
}
|
|
180
|
+
return { getSocket, connect, disconnect };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// src/provider.tsx
|
|
184
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
185
|
+
var WorkspaceContext = (0, import_react.createContext)(null);
|
|
186
|
+
function WorkspaceProvider({
|
|
187
|
+
config,
|
|
188
|
+
children
|
|
189
|
+
}) {
|
|
190
|
+
const value = (0, import_react.useMemo)(() => {
|
|
191
|
+
const apiClient = createWorkspaceClient(config);
|
|
192
|
+
const socket = createWorkspaceSocket(config);
|
|
193
|
+
return { config, apiClient, socket };
|
|
194
|
+
}, [config.baseUrl, config.currentUserId]);
|
|
195
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WorkspaceContext.Provider, { value, children });
|
|
196
|
+
}
|
|
197
|
+
function useWorkspaceContext() {
|
|
198
|
+
const ctx = (0, import_react.useContext)(WorkspaceContext);
|
|
199
|
+
if (!ctx)
|
|
200
|
+
throw new Error(
|
|
201
|
+
"useWorkspaceContext must be used within <WorkspaceProvider>"
|
|
202
|
+
);
|
|
203
|
+
return ctx;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/types/index.ts
|
|
207
|
+
var WorkspaceEvent = /* @__PURE__ */ ((WorkspaceEvent2) => {
|
|
208
|
+
WorkspaceEvent2["WORKSPACE_CREATED"] = "workspace.created";
|
|
209
|
+
WorkspaceEvent2["WORKSPACE_UPDATED"] = "workspace.updated";
|
|
210
|
+
WorkspaceEvent2["WORKSPACE_DELETED"] = "workspace.deleted";
|
|
211
|
+
WorkspaceEvent2["WORKSPACE_STATUS_CHANGED"] = "workspace.statusChanged";
|
|
212
|
+
WorkspaceEvent2["WORKSPACE_ADDED"] = "workspace.added";
|
|
213
|
+
WorkspaceEvent2["WORKSPACE_REMOVED"] = "workspace.removed";
|
|
214
|
+
WorkspaceEvent2["WORKSPACE_MEMBER_ADDED"] = "workspace.member.added";
|
|
215
|
+
WorkspaceEvent2["WORKSPACE_MEMBER_UPDATED"] = "workspace.member.updated";
|
|
216
|
+
WorkspaceEvent2["WORKSPACE_MEMBER_REMOVED"] = "workspace.member.removed";
|
|
217
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_CREATED"] = "workspace.message.created";
|
|
218
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_UPDATED"] = "workspace.message.updated";
|
|
219
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_DELETED"] = "workspace.message.deleted";
|
|
220
|
+
WorkspaceEvent2["WORKSPACE_MEMBER_USER_MENTIONED"] = "workspace.member.user.mentioned";
|
|
221
|
+
WorkspaceEvent2["WORKSPACE_MEMBER_AGENT_MENTIONED"] = "workspace.member.agent.mentioned";
|
|
222
|
+
WorkspaceEvent2["WORKSPACE_ARTEFACT_CREATED"] = "workspace.artefact.created";
|
|
223
|
+
WorkspaceEvent2["WORKSPACE_TYPING_STARTED"] = "workspace.typing.started";
|
|
224
|
+
WorkspaceEvent2["WORKSPACE_TYPING_STOPPED"] = "workspace.typing.stopped";
|
|
225
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_REACTION_CREATED"] = "workspace.message.reaction.created";
|
|
226
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_REACTION_DELETED"] = "workspace.message.reaction.deleted";
|
|
227
|
+
WorkspaceEvent2["WORKSPACE_MESSAGE_PINNED"] = "workspace.message.pinned";
|
|
228
|
+
WorkspaceEvent2["PRIVATE_WORKSPACE_CREATED"] = "private.workspace.created";
|
|
229
|
+
WorkspaceEvent2["PRIVATE_WORKSPACE_MESSAGE_CREATED"] = "private.workspace.message.created";
|
|
230
|
+
WorkspaceEvent2["PRIVATE_WORKSPACE_MEMBER_ADDED"] = "private.workspace.member.added";
|
|
231
|
+
return WorkspaceEvent2;
|
|
232
|
+
})(WorkspaceEvent || {});
|
|
233
|
+
|
|
234
|
+
// src/types/activity.ts
|
|
235
|
+
var FILTER_PILLS = [
|
|
236
|
+
{
|
|
237
|
+
label: "Tout",
|
|
238
|
+
value: void 0,
|
|
239
|
+
entityType: void 0,
|
|
240
|
+
category: void 0
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
label: "Contexte",
|
|
244
|
+
value: "context",
|
|
245
|
+
entityType: "workspaces",
|
|
246
|
+
category: "context"
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
label: "Membres",
|
|
250
|
+
value: "members",
|
|
251
|
+
entityType: "workspace_members",
|
|
252
|
+
category: "members"
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
label: "Artefacts",
|
|
256
|
+
value: "artefacts",
|
|
257
|
+
entityType: "workspace_artefacts",
|
|
258
|
+
category: "artefacts"
|
|
259
|
+
}
|
|
260
|
+
];
|
|
261
|
+
var WORKSPACE_FIELD_LABELS = {
|
|
262
|
+
name: "Nom",
|
|
263
|
+
type: "Type",
|
|
264
|
+
scope: "P\xE9rim\xE8tre",
|
|
265
|
+
entities: "Entit\xE9s",
|
|
266
|
+
entitiesWithSecrets: "Entit\xE9s",
|
|
267
|
+
status: "Statut",
|
|
268
|
+
description: "Description"
|
|
269
|
+
};
|
|
270
|
+
var WORKSPACE_STATUS_LABELS = {
|
|
271
|
+
ACTIVE: "Actif",
|
|
272
|
+
ARCHIVED: "Archiv\xE9",
|
|
273
|
+
DRAFT: "Brouillon",
|
|
274
|
+
CLOSED: "Cl\xF4tur\xE9"
|
|
275
|
+
};
|
|
276
|
+
var MEMBER_ROLE_LABELS = {
|
|
277
|
+
owner: "Propri\xE9taire",
|
|
278
|
+
participant: "Participant",
|
|
279
|
+
viewer: "Lecteur"
|
|
280
|
+
};
|
|
281
|
+
var IGNORED_FIELDS = [
|
|
282
|
+
"_id",
|
|
283
|
+
"__v",
|
|
284
|
+
"updatedAt",
|
|
285
|
+
"createdAt",
|
|
286
|
+
"createdBy",
|
|
287
|
+
"updatedBy",
|
|
288
|
+
"workspace"
|
|
289
|
+
];
|
|
290
|
+
var CATEGORY_STYLES = {
|
|
291
|
+
context: {
|
|
292
|
+
dot: "bg-primary/15 border-primary/30",
|
|
293
|
+
badge: "bg-primary/10 text-primary",
|
|
294
|
+
label: "Contexte"
|
|
295
|
+
},
|
|
296
|
+
members: {
|
|
297
|
+
dot: "bg-info/15 border-info/30",
|
|
298
|
+
badge: "bg-info/10 text-info",
|
|
299
|
+
label: "Membre"
|
|
300
|
+
},
|
|
301
|
+
artefacts: {
|
|
302
|
+
dot: "bg-success/15 border-success/30",
|
|
303
|
+
badge: "bg-success/10 text-success",
|
|
304
|
+
label: "Artefact"
|
|
305
|
+
},
|
|
306
|
+
other: {
|
|
307
|
+
dot: "bg-border",
|
|
308
|
+
badge: "bg-border text-text-muted",
|
|
309
|
+
label: "Autre"
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
var ICON_COLOR = {
|
|
313
|
+
created: "text-primary",
|
|
314
|
+
updated: "text-primary",
|
|
315
|
+
deleted: "text-danger",
|
|
316
|
+
member_added: "text-info",
|
|
317
|
+
member_removed: "text-danger",
|
|
318
|
+
member_role: "text-info",
|
|
319
|
+
artefact_added: "text-success",
|
|
320
|
+
artefact_deleted: "text-danger",
|
|
321
|
+
archived: "text-text-muted",
|
|
322
|
+
generic: "text-text-muted"
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// src/api/index.ts
|
|
326
|
+
function createWorkspaceApis(client) {
|
|
327
|
+
const workspaceTypeApi = {
|
|
328
|
+
list: () => client.get("/workspace-types"),
|
|
329
|
+
get: (id) => client.get(`/workspace-types/${id}`),
|
|
330
|
+
create: (payload) => client.post("/workspace-types", payload),
|
|
331
|
+
update: (id, payload) => client.patch(`/workspace-types/${id}`, payload),
|
|
332
|
+
delete: (id) => client.delete(`/workspace-types/${id}`)
|
|
333
|
+
};
|
|
334
|
+
const workspaceApi = {
|
|
335
|
+
list: (params) => {
|
|
336
|
+
const cleanParams = Object.fromEntries(
|
|
337
|
+
Object.entries(params || {}).filter(([, v]) => v != null && v !== "" && v !== "all").map(([k, v]) => [k, String(v)])
|
|
338
|
+
);
|
|
339
|
+
const qs = Object.keys(cleanParams).length > 0 ? "?" + new URLSearchParams(cleanParams).toString() : "";
|
|
340
|
+
return client.get(`/workspaces${qs}`);
|
|
341
|
+
},
|
|
342
|
+
get: (id) => client.get(`/workspaces/${id}`),
|
|
343
|
+
create: (payload) => client.post("/workspaces", payload),
|
|
344
|
+
update: (id, payload) => client.patch(`/workspaces/${id}`, payload),
|
|
345
|
+
updateStatus: (id, status) => client.patch(`/workspaces/${id}/status`, { status }),
|
|
346
|
+
delete: (id) => client.delete(`/workspaces/${id}`),
|
|
347
|
+
startTyping: (id) => client.post(`/workspaces/${id}/writing/start`, {}),
|
|
348
|
+
stopTyping: (id) => client.post(`/workspaces/${id}/writing/stop`, {}),
|
|
349
|
+
timeline: (workspaceId, params) => {
|
|
350
|
+
const qs = params ? "?" + new URLSearchParams(
|
|
351
|
+
Object.fromEntries(
|
|
352
|
+
Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
|
|
353
|
+
)
|
|
354
|
+
).toString() : "";
|
|
355
|
+
return client.get(`/workspaces/${workspaceId}/timeline${qs}`);
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
const memberApi = {
|
|
359
|
+
list: (workspaceId) => client.get(`/workspaces/${workspaceId}/members`),
|
|
360
|
+
add: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/members`, payload),
|
|
361
|
+
updateRole: (workspaceId, memberId, role) => client.patch(`/workspaces/${workspaceId}/members/${memberId}`, { role }),
|
|
362
|
+
remove: (workspaceId, memberId) => client.delete(`/workspaces/${workspaceId}/members/${memberId}`)
|
|
363
|
+
};
|
|
364
|
+
const messageApi = {
|
|
365
|
+
list: (workspaceId, params) => {
|
|
366
|
+
const qs = params ? "?" + new URLSearchParams(
|
|
367
|
+
Object.fromEntries(
|
|
368
|
+
Object.entries(params).filter(([, v]) => v != null).map(([k, v]) => [k, String(v)])
|
|
369
|
+
)
|
|
370
|
+
).toString() : "";
|
|
371
|
+
return client.get(`/workspaces/${workspaceId}/messages${qs}`);
|
|
372
|
+
},
|
|
373
|
+
send: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/messages`, payload),
|
|
374
|
+
update: (workspaceId, messageId, payload) => client.patch(`/workspaces/${workspaceId}/messages/${messageId}`, payload),
|
|
375
|
+
delete: (workspaceId, messageId) => client.delete(`/workspaces/${workspaceId}/messages/${messageId}`),
|
|
376
|
+
togglePin: (workspaceId, messageId) => client.patch(`/workspaces/${workspaceId}/messages/${messageId}/pin`, {})
|
|
377
|
+
};
|
|
378
|
+
const artefactApi = {
|
|
379
|
+
upload: async (workspaceId, files, messageId, artefactType) => {
|
|
380
|
+
const formData = new FormData();
|
|
381
|
+
files.forEach((f) => formData.append("files", f));
|
|
382
|
+
if (messageId) formData.append("messageId", messageId);
|
|
383
|
+
if (artefactType) formData.append("type", artefactType);
|
|
384
|
+
return client.upload(`/workspaces/${workspaceId}/artefacts`, formData);
|
|
385
|
+
},
|
|
386
|
+
list: (workspaceId, artefactType) => {
|
|
387
|
+
const query = artefactType ? `?type=${artefactType}` : "";
|
|
388
|
+
return client.get(
|
|
389
|
+
`/workspaces/${workspaceId}/artefacts${query}`
|
|
390
|
+
);
|
|
391
|
+
},
|
|
392
|
+
listPaginated: (workspaceId, params) => {
|
|
393
|
+
const entries = [];
|
|
394
|
+
if (params) {
|
|
395
|
+
if (params.type) entries.push(["type", params.type]);
|
|
396
|
+
if (params.limit != null) entries.push(["limit", String(params.limit)]);
|
|
397
|
+
if (params.offset != null) entries.push(["offset", String(params.offset)]);
|
|
398
|
+
}
|
|
399
|
+
const qs = entries.length > 0 ? "?" + new URLSearchParams(entries).toString() : "";
|
|
400
|
+
return client.get(
|
|
401
|
+
`/workspaces/${workspaceId}/artefacts${qs}`
|
|
402
|
+
);
|
|
403
|
+
},
|
|
404
|
+
delete: (workspaceId, artefactId) => client.delete(`/workspaces/${workspaceId}/artefacts/${artefactId}`),
|
|
405
|
+
getSignedUrl: (fileId) => client.get(`/files/${fileId}/signed-url`),
|
|
406
|
+
getDownloadBlob: (fileId) => client.getBlob(`/files/${fileId}/download`),
|
|
407
|
+
messagesWithArtefacts: (workspaceId, params) => {
|
|
408
|
+
const entries = [];
|
|
409
|
+
if (params) {
|
|
410
|
+
if (params.limit != null) entries.push(["limit", String(params.limit)]);
|
|
411
|
+
if (params.offset != null) entries.push(["offset", String(params.offset)]);
|
|
412
|
+
if (params.sort) entries.push(["sort", params.sort]);
|
|
413
|
+
if (params.members && params.members.length > 0)
|
|
414
|
+
entries.push(["members", params.members.join(",")]);
|
|
415
|
+
if (params.concernsMe) entries.push(["concernsMe", "true"]);
|
|
416
|
+
if (params.pinned) entries.push(["pinned", "true"]);
|
|
417
|
+
}
|
|
418
|
+
const qs = entries.length > 0 ? "?" + new URLSearchParams(entries).toString() : "";
|
|
419
|
+
return client.get(
|
|
420
|
+
`/workspaces/${workspaceId}/messages/with-artefacts${qs}`
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
};
|
|
424
|
+
const reactionApi = {
|
|
425
|
+
toggle: (workspaceId, messageId, reaction) => client.post(
|
|
426
|
+
`/workspaces/${workspaceId}/messages/${messageId}/reactions/toggle`,
|
|
427
|
+
{ reaction }
|
|
428
|
+
)
|
|
429
|
+
};
|
|
430
|
+
const agentApi = {
|
|
431
|
+
list: (search) => {
|
|
432
|
+
const qs = search ? `?search=${encodeURIComponent(search)}` : "";
|
|
433
|
+
return client.get(`/workspace-agents${qs}`);
|
|
434
|
+
},
|
|
435
|
+
create: (body) => client.post("/workspace-agents", body),
|
|
436
|
+
update: (id, body) => client.patch(`/workspace-agents/${id}`, body),
|
|
437
|
+
delete: (id) => client.delete(`/workspace-agents/${id}`),
|
|
438
|
+
getDeleteInfo: (id) => client.get(`/workspace-agents/${id}/delete-info`)
|
|
439
|
+
};
|
|
440
|
+
const summaryApi = {
|
|
441
|
+
generate: (workspaceId) => client.post(`/workspaces/${workspaceId}/summaries/generate`, {}),
|
|
442
|
+
getLatest: (workspaceId) => client.get(`/workspaces/${workspaceId}/summaries/latest`),
|
|
443
|
+
getHistory: (workspaceId) => client.get(`/workspaces/${workspaceId}/summaries`),
|
|
444
|
+
create: (workspaceId, payload) => client.post(`/workspaces/${workspaceId}/summaries`, payload),
|
|
445
|
+
update: (workspaceId, summaryId, payload) => client.patch(
|
|
446
|
+
`/workspaces/${workspaceId}/summaries/${summaryId}`,
|
|
447
|
+
payload
|
|
448
|
+
)
|
|
449
|
+
};
|
|
450
|
+
const webhookApi = {
|
|
451
|
+
list: (params) => {
|
|
452
|
+
const cleanParams = Object.fromEntries(
|
|
453
|
+
Object.entries(params || {}).filter(([, v]) => v !== void 0 && v !== "")
|
|
454
|
+
);
|
|
455
|
+
const qs = Object.keys(cleanParams).length > 0 ? "?" + new URLSearchParams(cleanParams).toString() : "";
|
|
456
|
+
return client.get(`/webhook-subscriptions${qs}`);
|
|
457
|
+
},
|
|
458
|
+
create: (data) => client.post("/webhook-subscriptions", data),
|
|
459
|
+
update: (id, data) => client.patch(`/webhook-subscriptions/${id}`, data),
|
|
460
|
+
delete: (id) => client.delete(`/webhook-subscriptions/${id}`)
|
|
461
|
+
};
|
|
462
|
+
const syncApi = {
|
|
463
|
+
getAvailableProviders: () => client.get("/workspaces/sync/providers").then((res) => res.providers),
|
|
464
|
+
getStatus: (workspaceId) => client.get(`/workspaces/${workspaceId}/sync-status`),
|
|
465
|
+
toggleSync: (workspaceId, provider, enabled) => client.patch(`/workspaces/${workspaceId}/sync`, { provider, enabled })
|
|
466
|
+
};
|
|
467
|
+
return {
|
|
468
|
+
workspaceTypeApi,
|
|
469
|
+
workspaceApi,
|
|
470
|
+
memberApi,
|
|
471
|
+
messageApi,
|
|
472
|
+
artefactApi,
|
|
473
|
+
reactionApi,
|
|
474
|
+
agentApi,
|
|
475
|
+
summaryApi,
|
|
476
|
+
webhookApi,
|
|
477
|
+
syncApi
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// src/store/useWorkspaceListStore.ts
|
|
482
|
+
var import_zustand = require("zustand");
|
|
483
|
+
var PAGE_SIZE = 12;
|
|
484
|
+
function createWorkspaceListStore(apis) {
|
|
485
|
+
return (0, import_zustand.create)()((set, get) => ({
|
|
486
|
+
workspaces: [],
|
|
487
|
+
total: 0,
|
|
488
|
+
membersMap: {},
|
|
489
|
+
types: [],
|
|
490
|
+
tab: "group",
|
|
491
|
+
page: 1,
|
|
492
|
+
pageSize: PAGE_SIZE,
|
|
493
|
+
totalPages: 0,
|
|
494
|
+
filters: { search: "", status: "ACTIVE", type: "all" },
|
|
495
|
+
initialLoading: true,
|
|
496
|
+
fetching: false,
|
|
497
|
+
hydrated: false,
|
|
498
|
+
setTab: (tab) => {
|
|
499
|
+
const prev = get().tab;
|
|
500
|
+
if (prev === tab) return;
|
|
501
|
+
set({
|
|
502
|
+
tab,
|
|
503
|
+
page: 1,
|
|
504
|
+
workspaces: [],
|
|
505
|
+
total: 0,
|
|
506
|
+
totalPages: 0,
|
|
507
|
+
membersMap: {}
|
|
508
|
+
});
|
|
509
|
+
get().fetchWorkspaces();
|
|
510
|
+
},
|
|
511
|
+
setFilters: (filters) => {
|
|
512
|
+
set({ filters, page: 1 });
|
|
513
|
+
},
|
|
514
|
+
setPage: (page) => {
|
|
515
|
+
set({ page });
|
|
516
|
+
},
|
|
517
|
+
fetchTypes: async () => {
|
|
518
|
+
try {
|
|
519
|
+
const types = await apis.workspaceTypeApi.list();
|
|
520
|
+
set({ types });
|
|
521
|
+
} catch (err) {
|
|
522
|
+
console.error("Failed to load workspace types:", err);
|
|
523
|
+
}
|
|
524
|
+
},
|
|
525
|
+
fetchWorkspaces: async () => {
|
|
526
|
+
const { filters, page, pageSize, hydrated, tab } = get();
|
|
527
|
+
if (!hydrated) set({ initialLoading: true });
|
|
528
|
+
set({ fetching: true });
|
|
529
|
+
try {
|
|
530
|
+
const skip = (page - 1) * pageSize;
|
|
531
|
+
const result = await apis.workspaceApi.list({
|
|
532
|
+
...filters,
|
|
533
|
+
isPrivate: tab === "private",
|
|
534
|
+
limit: pageSize,
|
|
535
|
+
skip
|
|
536
|
+
});
|
|
537
|
+
const wsList = result.data;
|
|
538
|
+
const totalPages = Math.ceil(result.total / pageSize);
|
|
539
|
+
const map = {};
|
|
540
|
+
await Promise.all(
|
|
541
|
+
wsList.map(async (ws) => {
|
|
542
|
+
try {
|
|
543
|
+
map[ws._id] = await apis.memberApi.list(ws._id);
|
|
544
|
+
} catch {
|
|
545
|
+
map[ws._id] = [];
|
|
546
|
+
}
|
|
547
|
+
})
|
|
548
|
+
);
|
|
549
|
+
set({
|
|
550
|
+
workspaces: wsList,
|
|
551
|
+
total: result.total,
|
|
552
|
+
totalPages,
|
|
553
|
+
membersMap: map,
|
|
554
|
+
hydrated: true,
|
|
555
|
+
initialLoading: false,
|
|
556
|
+
fetching: false
|
|
557
|
+
});
|
|
558
|
+
} catch (err) {
|
|
559
|
+
console.error("Failed to load workspaces:", err);
|
|
560
|
+
set({ initialLoading: false, fetching: false });
|
|
561
|
+
}
|
|
562
|
+
},
|
|
563
|
+
prependWorkspace: (ws, members = []) => {
|
|
564
|
+
set((state) => {
|
|
565
|
+
if (state.workspaces.some((w) => w._id === ws._id)) return state;
|
|
566
|
+
return {
|
|
567
|
+
workspaces: [ws, ...state.workspaces],
|
|
568
|
+
total: state.total + 1,
|
|
569
|
+
totalPages: Math.ceil((state.total + 1) / state.pageSize),
|
|
570
|
+
membersMap: { ...state.membersMap, [ws._id]: members }
|
|
571
|
+
};
|
|
572
|
+
});
|
|
573
|
+
},
|
|
574
|
+
removeWorkspace: (workspaceId) => {
|
|
575
|
+
set((state) => {
|
|
576
|
+
const next = { ...state.membersMap };
|
|
577
|
+
delete next[workspaceId];
|
|
578
|
+
const newTotal = Math.max(0, state.total - 1);
|
|
579
|
+
return {
|
|
580
|
+
workspaces: state.workspaces.filter((w) => w._id !== workspaceId),
|
|
581
|
+
total: newTotal,
|
|
582
|
+
totalPages: Math.ceil(newTotal / state.pageSize),
|
|
583
|
+
membersMap: next
|
|
584
|
+
};
|
|
585
|
+
});
|
|
586
|
+
},
|
|
587
|
+
refresh: async () => {
|
|
588
|
+
const { fetchWorkspaces } = get();
|
|
589
|
+
await fetchWorkspaces();
|
|
590
|
+
}
|
|
591
|
+
}));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// src/hooks/useWorkspaceApi.ts
|
|
595
|
+
var import_react2 = require("react");
|
|
596
|
+
function useWorkspaceApi() {
|
|
597
|
+
const { apiClient } = useWorkspaceContext();
|
|
598
|
+
return (0, import_react2.useMemo)(() => createWorkspaceApis(apiClient), [apiClient]);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// src/utils/constants.ts
|
|
602
|
+
var AVAILABLE_AGENTS = [
|
|
603
|
+
{ memberId: "james-agent", memberName: "James", memberType: "agent" },
|
|
604
|
+
{ memberId: "eclaireur-agent", memberName: "\xC9claireur IA", memberType: "agent" },
|
|
605
|
+
{ memberId: "sentinelle-agent", memberName: "Sentinelle", memberType: "agent" },
|
|
606
|
+
{ memberId: "archiviste-agent", memberName: "Archiviste", memberType: "agent" },
|
|
607
|
+
{ memberId: "stratege-agent", memberName: "Strat\xE8ge", memberType: "agent" }
|
|
608
|
+
];
|
|
609
|
+
var ROLE_LABELS = {
|
|
610
|
+
owner: "Propri\xE9taire",
|
|
611
|
+
participant: "Participant",
|
|
612
|
+
viewer: "Observateur"
|
|
613
|
+
};
|
|
614
|
+
var STATUS_LABELS = {
|
|
615
|
+
ACTIVE: "Actif",
|
|
616
|
+
CLOSED: "Ferm\xE9",
|
|
617
|
+
ARCHIVED: "Archiv\xE9"
|
|
618
|
+
};
|
|
619
|
+
function getInitials(name) {
|
|
620
|
+
return name.split(/\s+/).map((w) => w[0]).join("").toUpperCase().slice(0, 2);
|
|
621
|
+
}
|
|
622
|
+
function fixMojibake(str) {
|
|
623
|
+
try {
|
|
624
|
+
const bytes = new Uint8Array(str.split("").map((c) => c.charCodeAt(0)));
|
|
625
|
+
return new TextDecoder("utf-8").decode(bytes);
|
|
626
|
+
} catch {
|
|
627
|
+
return str;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// src/utils/formatExport.ts
|
|
632
|
+
var ROLE_LABELS2 = {
|
|
633
|
+
owner: "Propri\xE9taire",
|
|
634
|
+
participant: "Participant",
|
|
635
|
+
viewer: "Observateur"
|
|
636
|
+
};
|
|
637
|
+
var MEMBER_TYPE_LABELS = {
|
|
638
|
+
user: "Utilisateur",
|
|
639
|
+
agent: "Agent IA"
|
|
640
|
+
};
|
|
641
|
+
function formatDate(iso) {
|
|
642
|
+
return new Date(iso).toLocaleDateString("fr-FR", {
|
|
643
|
+
weekday: "long",
|
|
644
|
+
year: "numeric",
|
|
645
|
+
month: "long",
|
|
646
|
+
day: "numeric"
|
|
647
|
+
});
|
|
648
|
+
}
|
|
649
|
+
function formatDateTime(iso) {
|
|
650
|
+
return new Date(iso).toLocaleString("fr-FR", {
|
|
651
|
+
year: "numeric",
|
|
652
|
+
month: "2-digit",
|
|
653
|
+
day: "2-digit",
|
|
654
|
+
hour: "2-digit",
|
|
655
|
+
minute: "2-digit"
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
function getTypeName(workspace) {
|
|
659
|
+
if (!workspace.type) return "\u2014";
|
|
660
|
+
if (typeof workspace.type === "object") return workspace.type.name;
|
|
661
|
+
return workspace.type;
|
|
662
|
+
}
|
|
663
|
+
function fixMojibake2(text) {
|
|
664
|
+
return text.replace(/é/g, "\xE9").replace(/è/g, "\xE8").replace(/à /g, "\xE0").replace(/ç/g, "\xE7").replace(/ô/g, "\xF4").replace(/î/g, "\xEE").replace(/â/g, "\xE2").replace(/ë/g, "\xEB").replace(/ü/g, "\xFC");
|
|
665
|
+
}
|
|
666
|
+
function formatWorkspaceExport(data) {
|
|
667
|
+
const { workspace, members, messages } = data;
|
|
668
|
+
const lines = [];
|
|
669
|
+
lines.push(`# \u{1F4CB} Export \u2014 ${workspace.name}`);
|
|
670
|
+
lines.push("");
|
|
671
|
+
lines.push(`> Export g\xE9n\xE9r\xE9 le **${formatDateTime((/* @__PURE__ */ new Date()).toISOString())}**`);
|
|
672
|
+
lines.push("");
|
|
673
|
+
lines.push("---");
|
|
674
|
+
lines.push("");
|
|
675
|
+
lines.push("## \u{1F4CC} Contexte");
|
|
676
|
+
lines.push("");
|
|
677
|
+
lines.push(`| Champ | Valeur |`);
|
|
678
|
+
lines.push(`| ----- | ------ |`);
|
|
679
|
+
lines.push(`| **Nom** | ${workspace.name} |`);
|
|
680
|
+
lines.push(`| **Type** | ${getTypeName(workspace)} |`);
|
|
681
|
+
lines.push(`| **Scope** | ${workspace.scope || "\u2014"} |`);
|
|
682
|
+
lines.push(`| **Statut** | ${workspace.status} |`);
|
|
683
|
+
lines.push(`| **Cr\xE9\xE9 le** | ${formatDate(workspace.createdAt)} |`);
|
|
684
|
+
lines.push(`| **Mis \xE0 jour le** | ${formatDate(workspace.updatedAt)} |`);
|
|
685
|
+
const displayEntities = workspace.entitiesWithSecrets ?? [];
|
|
686
|
+
if (displayEntities.length > 0) {
|
|
687
|
+
lines.push("");
|
|
688
|
+
lines.push("### Entit\xE9s");
|
|
689
|
+
lines.push("");
|
|
690
|
+
lines.push("| Cl\xE9 | Valeur | Secret |");
|
|
691
|
+
lines.push("| --- | ------ | ------ |");
|
|
692
|
+
for (const ent of displayEntities) {
|
|
693
|
+
lines.push(
|
|
694
|
+
`| ${ent.key} | ${ent.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : ent.value} | ${ent.isSecret ? "\u{1F512} Oui" : "Non"} |`
|
|
695
|
+
);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
lines.push("");
|
|
699
|
+
lines.push("---");
|
|
700
|
+
lines.push("");
|
|
701
|
+
lines.push(`## \u{1F465} Membres (${members.length})`);
|
|
702
|
+
lines.push("");
|
|
703
|
+
lines.push("| Nom | Type | R\xF4le | Rejoint le |");
|
|
704
|
+
lines.push("| --- | ---- | ---- | ---------- |");
|
|
705
|
+
for (const m of members) {
|
|
706
|
+
lines.push(
|
|
707
|
+
`| ${m.memberName} | ${MEMBER_TYPE_LABELS[m.memberType] || m.memberType} | ${ROLE_LABELS2[m.role] || m.role} | ${m.joinedAt || m.createdAt ? formatDate(m.joinedAt || m.createdAt) : "\u2014"} |`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
lines.push("");
|
|
711
|
+
lines.push("---");
|
|
712
|
+
lines.push("");
|
|
713
|
+
lines.push(`## \u{1F4AC} Discussion (${messages.length} messages)`);
|
|
714
|
+
lines.push("");
|
|
715
|
+
if (messages.length === 0) {
|
|
716
|
+
lines.push("_Aucun message dans ce workspace._");
|
|
717
|
+
} else {
|
|
718
|
+
let currentDay = "";
|
|
719
|
+
for (const msg of messages) {
|
|
720
|
+
const day = formatDate(msg.createdAt);
|
|
721
|
+
if (day !== currentDay) {
|
|
722
|
+
currentDay = day;
|
|
723
|
+
lines.push(`### \u{1F4C5} ${day}`);
|
|
724
|
+
lines.push("");
|
|
725
|
+
}
|
|
726
|
+
const timeStr = new Date(msg.createdAt).toLocaleTimeString("fr-FR", {
|
|
727
|
+
hour: "2-digit",
|
|
728
|
+
minute: "2-digit"
|
|
729
|
+
});
|
|
730
|
+
const senderIcon = msg.senderType === "agent" ? "\u{1F916}" : "\u{1F464}";
|
|
731
|
+
lines.push(`**${senderIcon} ${msg.senderName}** \u2014 _${timeStr}_`);
|
|
732
|
+
lines.push("");
|
|
733
|
+
lines.push(msg.content);
|
|
734
|
+
lines.push("");
|
|
735
|
+
if (msg.artefacts && msg.artefacts.length > 0) {
|
|
736
|
+
lines.push("> \u{1F4CE} Pi\xE8ces jointes :");
|
|
737
|
+
for (const art of msg.artefacts) {
|
|
738
|
+
lines.push(`> - \`${fixMojibake2(art.file.originalName)}\` (${art.type})`);
|
|
739
|
+
}
|
|
740
|
+
lines.push("");
|
|
741
|
+
}
|
|
742
|
+
lines.push("---");
|
|
743
|
+
lines.push("");
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
lines.push("");
|
|
747
|
+
lines.push(`_Fin de l'export \u2014 ${workspace.name}_`);
|
|
748
|
+
return lines.join("\n");
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// src/components/ui/Modal.tsx
|
|
752
|
+
var import_react3 = require("react");
|
|
753
|
+
var import_react_dom = require("react-dom");
|
|
754
|
+
var import_lucide_react = require("lucide-react");
|
|
755
|
+
|
|
756
|
+
// src/components/ui/cn.ts
|
|
757
|
+
var import_clsx = require("clsx");
|
|
758
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
759
|
+
function cn(...inputs) {
|
|
760
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
// src/components/ui/Modal.tsx
|
|
764
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
765
|
+
var MODAL_SIZE_CLASS = {
|
|
766
|
+
sm: "w-full min-w-0 max-w-sm",
|
|
767
|
+
md: "w-full min-w-0 max-w-md",
|
|
768
|
+
lg: "w-full min-w-0 max-w-lg",
|
|
769
|
+
xl: "w-full min-w-0 max-w-xl",
|
|
770
|
+
"2xl": "w-full min-w-0 max-w-2xl",
|
|
771
|
+
"3xl": "w-full min-w-0 max-w-3xl",
|
|
772
|
+
"4xl": "w-full min-w-0 max-w-4xl",
|
|
773
|
+
"5xl": "w-full min-w-0 max-w-5xl",
|
|
774
|
+
"6xl": "w-full min-w-0 max-w-6xl",
|
|
775
|
+
"7xl": "w-full min-w-0 max-w-7xl",
|
|
776
|
+
full: "w-full min-w-0 max-w-[calc(100vw-2rem)] sm:max-w-[min(96vw,90rem)]"
|
|
777
|
+
};
|
|
778
|
+
function Modal({
|
|
779
|
+
open,
|
|
780
|
+
onClose,
|
|
781
|
+
children,
|
|
782
|
+
zIndexClass = "z-[100]",
|
|
783
|
+
backdropClassName = "bg-black/40 backdrop-blur-sm",
|
|
784
|
+
size,
|
|
785
|
+
panelClassName,
|
|
786
|
+
className,
|
|
787
|
+
closeOnBackdropClick = false,
|
|
788
|
+
closeOnEscape = true,
|
|
789
|
+
ariaLabelledBy: ariaLabelledByProp,
|
|
790
|
+
ariaDescribedBy: ariaDescribedByProp,
|
|
791
|
+
title,
|
|
792
|
+
description,
|
|
793
|
+
titleIcon,
|
|
794
|
+
headingId,
|
|
795
|
+
modalHeaderVariant = "strip",
|
|
796
|
+
hideCloseButton = false,
|
|
797
|
+
headerTrailing,
|
|
798
|
+
closeButtonAriaLabel = "Fermer",
|
|
799
|
+
closeButtonDisabled = false,
|
|
800
|
+
closeButtonClassName,
|
|
801
|
+
closeIconSize = 20,
|
|
802
|
+
headerClassName,
|
|
803
|
+
titleClassName,
|
|
804
|
+
descriptionClassName = "text-text-secondary mt-1 text-sm"
|
|
805
|
+
}) {
|
|
806
|
+
const [mounted, setMounted] = (0, import_react3.useState)(false);
|
|
807
|
+
const autoHeadingId = (0, import_react3.useId)();
|
|
808
|
+
const autoDescIdByModal = (0, import_react3.useId)();
|
|
809
|
+
const hasHeader = title != null;
|
|
810
|
+
const resolvedHeadingId = headingId ?? ariaLabelledByProp ?? autoHeadingId;
|
|
811
|
+
const assignAutoDescriptionId = hasHeader && description != null && ariaDescribedByProp === void 0 && (typeof description === "string" || typeof description === "number");
|
|
812
|
+
const resolvedAriaDescribedBy = assignAutoDescriptionId ? autoDescIdByModal : ariaDescribedByProp;
|
|
813
|
+
const resolvedAriaLabelledBy = hasHeader ? ariaLabelledByProp ?? resolvedHeadingId : ariaLabelledByProp;
|
|
814
|
+
(0, import_react3.useEffect)(() => {
|
|
815
|
+
setMounted(true);
|
|
816
|
+
}, []);
|
|
817
|
+
(0, import_react3.useEffect)(() => {
|
|
818
|
+
if (!open) return;
|
|
819
|
+
const prevOverflow = document.body.style.overflow;
|
|
820
|
+
document.body.style.overflow = "hidden";
|
|
821
|
+
return () => {
|
|
822
|
+
document.body.style.overflow = prevOverflow;
|
|
823
|
+
};
|
|
824
|
+
}, [open]);
|
|
825
|
+
(0, import_react3.useEffect)(() => {
|
|
826
|
+
if (!open || !closeOnEscape) return;
|
|
827
|
+
const onKeyDown = (e) => {
|
|
828
|
+
if (e.key === "Escape") onClose();
|
|
829
|
+
};
|
|
830
|
+
window.addEventListener("keydown", onKeyDown);
|
|
831
|
+
return () => window.removeEventListener("keydown", onKeyDown);
|
|
832
|
+
}, [open, closeOnEscape, onClose]);
|
|
833
|
+
const defaultCloseClasses = closeButtonClassName ?? "text-text-secondary hover:text-text rounded p-1 transition-colors";
|
|
834
|
+
const trailing = headerTrailing ?? (!hideCloseButton ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
835
|
+
"button",
|
|
836
|
+
{
|
|
837
|
+
type: "button",
|
|
838
|
+
disabled: closeButtonDisabled,
|
|
839
|
+
onClick: onClose,
|
|
840
|
+
className: cn(defaultCloseClasses, closeButtonDisabled && "opacity-40"),
|
|
841
|
+
"aria-label": closeButtonAriaLabel,
|
|
842
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react.X, { size: closeIconSize })
|
|
843
|
+
}
|
|
844
|
+
) : null);
|
|
845
|
+
const descriptionSlot = description == null ? null : typeof description === "string" || typeof description === "number" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
846
|
+
"p",
|
|
847
|
+
{
|
|
848
|
+
id: assignAutoDescriptionId ? autoDescIdByModal : void 0,
|
|
849
|
+
className: descriptionClassName,
|
|
850
|
+
children: description
|
|
851
|
+
}
|
|
852
|
+
) : description;
|
|
853
|
+
const headerShellClass = modalHeaderVariant === "strip" ? "border-border flex shrink-0 items-start justify-between gap-3 border-b px-6 py-4" : "mb-4 flex shrink-0 items-start justify-between gap-3";
|
|
854
|
+
const modalHeaderMarkup = hasHeader ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: cn(headerShellClass, headerClassName), children: [
|
|
855
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex min-w-0 flex-1 items-start gap-3", children: [
|
|
856
|
+
titleIcon != null ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "mt-0.5 shrink-0", children: titleIcon }) : null,
|
|
857
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
858
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
859
|
+
"h2",
|
|
860
|
+
{
|
|
861
|
+
id: resolvedHeadingId,
|
|
862
|
+
className: cn("text-text text-lg font-semibold", titleClassName),
|
|
863
|
+
children: title
|
|
864
|
+
}
|
|
865
|
+
),
|
|
866
|
+
descriptionSlot
|
|
867
|
+
] })
|
|
868
|
+
] }),
|
|
869
|
+
trailing ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "flex shrink-0 items-start", children: trailing }) : null
|
|
870
|
+
] }) : null;
|
|
871
|
+
if (!mounted || !open) return null;
|
|
872
|
+
return (0, import_react_dom.createPortal)(
|
|
873
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
874
|
+
"div",
|
|
875
|
+
{
|
|
876
|
+
className: cn(
|
|
877
|
+
"fixed inset-0 flex items-center justify-center p-4",
|
|
878
|
+
zIndexClass,
|
|
879
|
+
backdropClassName,
|
|
880
|
+
className
|
|
881
|
+
),
|
|
882
|
+
onClick: closeOnBackdropClick ? onClose : void 0,
|
|
883
|
+
"aria-hidden": false,
|
|
884
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
885
|
+
"div",
|
|
886
|
+
{
|
|
887
|
+
role: "dialog",
|
|
888
|
+
"aria-modal": "true",
|
|
889
|
+
"aria-labelledby": resolvedAriaLabelledBy,
|
|
890
|
+
"aria-describedby": resolvedAriaDescribedBy,
|
|
891
|
+
className: cn(size ? MODAL_SIZE_CLASS[size] : void 0, panelClassName),
|
|
892
|
+
onClick: (e) => e.stopPropagation(),
|
|
893
|
+
children: [
|
|
894
|
+
modalHeaderMarkup,
|
|
895
|
+
children
|
|
896
|
+
]
|
|
897
|
+
}
|
|
898
|
+
)
|
|
899
|
+
}
|
|
900
|
+
),
|
|
901
|
+
document.body
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// src/components/ui/Badge.tsx
|
|
906
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
907
|
+
var VARIANT_CLASSES = {
|
|
908
|
+
default: "bg-[var(--color-bg,#f1f5f9)] text-[var(--color-text-muted,#64748b)] border-[var(--color-border,#e2e8f0)]",
|
|
909
|
+
primary: "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)] border-[var(--color-primary,#3b82f6)]/30",
|
|
910
|
+
success: "bg-emerald-50 text-emerald-700 border-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-400 dark:border-emerald-500/20",
|
|
911
|
+
warning: "bg-amber-50 text-amber-700 border-amber-200 dark:bg-amber-500/10 dark:text-amber-400 dark:border-amber-500/20",
|
|
912
|
+
danger: "bg-red-50 text-red-700 border-red-200 dark:bg-red-500/10 dark:text-red-400 dark:border-red-500/20",
|
|
913
|
+
info: "bg-sky-50 text-sky-700 border-sky-200 dark:bg-sky-500/10 dark:text-sky-400 dark:border-sky-500/20"
|
|
914
|
+
};
|
|
915
|
+
var SIZE_CLASSES = {
|
|
916
|
+
xs: "px-1.5 py-0.5 text-[10px] font-medium",
|
|
917
|
+
sm: "px-2 py-0.5 text-[11px] font-medium",
|
|
918
|
+
md: "px-2.5 py-1 text-xs font-semibold"
|
|
919
|
+
};
|
|
920
|
+
var Badge = ({
|
|
921
|
+
variant = "default",
|
|
922
|
+
size = "sm",
|
|
923
|
+
className,
|
|
924
|
+
children,
|
|
925
|
+
title
|
|
926
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
927
|
+
"span",
|
|
928
|
+
{
|
|
929
|
+
title,
|
|
930
|
+
className: cn(
|
|
931
|
+
"inline-flex items-center rounded-full border leading-none",
|
|
932
|
+
VARIANT_CLASSES[variant],
|
|
933
|
+
SIZE_CLASSES[size],
|
|
934
|
+
className
|
|
935
|
+
),
|
|
936
|
+
children
|
|
937
|
+
}
|
|
938
|
+
);
|
|
939
|
+
|
|
940
|
+
// src/components/ui/Tooltip.tsx
|
|
941
|
+
var import_react4 = require("react");
|
|
942
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
943
|
+
var POSITION = {
|
|
944
|
+
top: "bottom-full left-1/2 -translate-x-1/2 mb-2",
|
|
945
|
+
bottom: "top-full left-1/2 -translate-x-1/2 mt-2",
|
|
946
|
+
left: "right-full top-1/2 -translate-y-1/2 mr-2",
|
|
947
|
+
right: "left-full top-1/2 -translate-y-1/2 ml-2"
|
|
948
|
+
};
|
|
949
|
+
var Tooltip = ({
|
|
950
|
+
text,
|
|
951
|
+
children,
|
|
952
|
+
position = "top",
|
|
953
|
+
className,
|
|
954
|
+
delay = 400
|
|
955
|
+
}) => {
|
|
956
|
+
const [visible, setVisible] = (0, import_react4.useState)(false);
|
|
957
|
+
const timerRef = (0, import_react4.useRef)(null);
|
|
958
|
+
const show = () => {
|
|
959
|
+
timerRef.current = setTimeout(() => setVisible(true), delay);
|
|
960
|
+
};
|
|
961
|
+
const hide = () => {
|
|
962
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
963
|
+
setVisible(false);
|
|
964
|
+
};
|
|
965
|
+
(0, import_react4.useEffect)(() => () => {
|
|
966
|
+
if (timerRef.current) clearTimeout(timerRef.current);
|
|
967
|
+
}, []);
|
|
968
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
969
|
+
"div",
|
|
970
|
+
{
|
|
971
|
+
className: "relative inline-flex",
|
|
972
|
+
onMouseEnter: show,
|
|
973
|
+
onMouseLeave: hide,
|
|
974
|
+
onFocus: show,
|
|
975
|
+
onBlur: hide,
|
|
976
|
+
children: [
|
|
977
|
+
children,
|
|
978
|
+
visible && text && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
979
|
+
"div",
|
|
980
|
+
{
|
|
981
|
+
role: "tooltip",
|
|
982
|
+
className: cn(
|
|
983
|
+
"pointer-events-none absolute z-50 max-w-xs rounded-lg px-2.5 py-1.5",
|
|
984
|
+
"bg-[#1e293b] text-white text-[11px] leading-snug whitespace-nowrap shadow-lg",
|
|
985
|
+
POSITION[position],
|
|
986
|
+
className
|
|
987
|
+
),
|
|
988
|
+
children: text
|
|
989
|
+
}
|
|
990
|
+
)
|
|
991
|
+
]
|
|
992
|
+
}
|
|
993
|
+
);
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
// src/components/ui/ConfirmModal.tsx
|
|
997
|
+
var import_react5 = __toESM(require("react"));
|
|
998
|
+
var import_lucide_react2 = require("lucide-react");
|
|
999
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1000
|
+
var ConfirmModal = ({
|
|
1001
|
+
open,
|
|
1002
|
+
onClose,
|
|
1003
|
+
onConfirm,
|
|
1004
|
+
title,
|
|
1005
|
+
description,
|
|
1006
|
+
confirmText = "Confirmer",
|
|
1007
|
+
cancelText = "Annuler",
|
|
1008
|
+
danger = false
|
|
1009
|
+
}) => {
|
|
1010
|
+
const [loading, setLoading] = import_react5.default.useState(false);
|
|
1011
|
+
(0, import_react5.useEffect)(() => {
|
|
1012
|
+
if (!open) return;
|
|
1013
|
+
const handler = (e) => {
|
|
1014
|
+
if (e.key === "Escape" && !loading) onClose();
|
|
1015
|
+
};
|
|
1016
|
+
document.addEventListener("keydown", handler);
|
|
1017
|
+
return () => document.removeEventListener("keydown", handler);
|
|
1018
|
+
}, [open, loading, onClose]);
|
|
1019
|
+
if (!open) return null;
|
|
1020
|
+
const handleConfirm = async () => {
|
|
1021
|
+
setLoading(true);
|
|
1022
|
+
try {
|
|
1023
|
+
await onConfirm();
|
|
1024
|
+
} finally {
|
|
1025
|
+
setLoading(false);
|
|
1026
|
+
onClose();
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1030
|
+
"div",
|
|
1031
|
+
{
|
|
1032
|
+
className: "fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
1033
|
+
style: { backgroundColor: "rgba(0,0,0,0.5)" },
|
|
1034
|
+
onClick: (e) => {
|
|
1035
|
+
if (e.target === e.currentTarget && !loading) onClose();
|
|
1036
|
+
},
|
|
1037
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1038
|
+
"div",
|
|
1039
|
+
{
|
|
1040
|
+
className: cn(
|
|
1041
|
+
"relative flex w-full max-w-sm flex-col gap-4 rounded-2xl border p-6 shadow-2xl",
|
|
1042
|
+
"bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)]"
|
|
1043
|
+
),
|
|
1044
|
+
role: "dialog",
|
|
1045
|
+
"aria-modal": "true",
|
|
1046
|
+
"aria-labelledby": "confirm-title",
|
|
1047
|
+
children: [
|
|
1048
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1049
|
+
"button",
|
|
1050
|
+
{
|
|
1051
|
+
onClick: onClose,
|
|
1052
|
+
disabled: loading,
|
|
1053
|
+
className: "absolute top-4 right-4 rounded-full p-1 text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] transition-colors",
|
|
1054
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react2.X, { size: 16 })
|
|
1055
|
+
}
|
|
1056
|
+
),
|
|
1057
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-start gap-3", children: [
|
|
1058
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: cn(
|
|
1059
|
+
"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full",
|
|
1060
|
+
danger ? "bg-red-100 dark:bg-red-500/10" : "bg-[var(--color-bg,#f1f5f9)]"
|
|
1061
|
+
), children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react2.AlertTriangle, { size: 18, className: danger ? "text-red-500" : "text-[var(--color-text-muted,#94a3b8)]" }) }),
|
|
1062
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "min-w-0 flex-1 pt-0.5", children: [
|
|
1063
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h3", { id: "confirm-title", className: "text-sm font-semibold text-[var(--color-text,#0f172a)]", children: title }),
|
|
1064
|
+
description && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mt-1 text-xs text-[var(--color-text-secondary,#64748b)] leading-relaxed", children: description })
|
|
1065
|
+
] })
|
|
1066
|
+
] }),
|
|
1067
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-end gap-2", children: [
|
|
1068
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1069
|
+
"button",
|
|
1070
|
+
{
|
|
1071
|
+
onClick: onClose,
|
|
1072
|
+
disabled: loading,
|
|
1073
|
+
className: "rounded-lg border border-[var(--color-border,#e2e8f0)] px-4 py-1.5 text-xs font-medium text-[var(--color-text-muted,#64748b)] hover:text-[var(--color-text,#0f172a)] transition-colors disabled:opacity-50",
|
|
1074
|
+
children: cancelText
|
|
1075
|
+
}
|
|
1076
|
+
),
|
|
1077
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
|
|
1078
|
+
"button",
|
|
1079
|
+
{
|
|
1080
|
+
onClick: handleConfirm,
|
|
1081
|
+
disabled: loading,
|
|
1082
|
+
className: cn(
|
|
1083
|
+
"flex items-center gap-1.5 rounded-lg px-4 py-1.5 text-xs font-medium text-white transition-colors disabled:opacity-70",
|
|
1084
|
+
danger ? "bg-red-500 hover:bg-red-600" : "bg-[var(--color-primary,#3b82f6)] hover:bg-[var(--color-primary-hover,#2563eb)]"
|
|
1085
|
+
),
|
|
1086
|
+
children: [
|
|
1087
|
+
loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react2.Loader2, { size: 12, className: "animate-spin" }),
|
|
1088
|
+
confirmText
|
|
1089
|
+
]
|
|
1090
|
+
}
|
|
1091
|
+
)
|
|
1092
|
+
] })
|
|
1093
|
+
]
|
|
1094
|
+
}
|
|
1095
|
+
)
|
|
1096
|
+
}
|
|
1097
|
+
);
|
|
1098
|
+
};
|
|
1099
|
+
|
|
1100
|
+
// src/components/MemberAvatar.tsx
|
|
1101
|
+
var import_lucide_react3 = require("lucide-react");
|
|
1102
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1103
|
+
var SIZES = {
|
|
1104
|
+
xs: { container: "h-6 w-6", text: "text-[9px]", icon: 10 },
|
|
1105
|
+
sm: { container: "h-7 w-7", text: "text-[10px]", icon: 12 },
|
|
1106
|
+
md: { container: "h-9 w-9", text: "text-xs", icon: 16 },
|
|
1107
|
+
lg: { container: "h-10 w-10", text: "text-sm", icon: 18 }
|
|
1108
|
+
};
|
|
1109
|
+
var MemberAvatar = ({
|
|
1110
|
+
memberName,
|
|
1111
|
+
memberType,
|
|
1112
|
+
avatarUrl,
|
|
1113
|
+
size = "md",
|
|
1114
|
+
className,
|
|
1115
|
+
variant = "primary"
|
|
1116
|
+
}) => {
|
|
1117
|
+
const s = SIZES[size];
|
|
1118
|
+
if (memberType === "agent") {
|
|
1119
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn("flex flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-violet-100 to-indigo-100 ring-1 ring-indigo-200/60", s.container, className), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_lucide_react3.Brain, { size: s.icon, className: "text-indigo-600" }) });
|
|
1120
|
+
}
|
|
1121
|
+
if (avatarUrl) {
|
|
1122
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: avatarUrl, alt: memberName, className: cn("flex-shrink-0 rounded-full object-cover ring-2 ring-white", s.container, className) });
|
|
1123
|
+
}
|
|
1124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: cn("flex flex-shrink-0 items-center justify-center rounded-full font-semibold", s.container, s.text, variant === "currentUser" ? "bg-primary text-white shadow-md" : variant === "owner" ? "bg-warning-light text-warning" : "bg-primary-light text-primary", className), children: getInitials(memberName) });
|
|
1125
|
+
};
|
|
1126
|
+
|
|
1127
|
+
// src/components/Pagination.tsx
|
|
1128
|
+
var import_lucide_react4 = require("lucide-react");
|
|
1129
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1130
|
+
var NavButton = ({
|
|
1131
|
+
onClick,
|
|
1132
|
+
disabled,
|
|
1133
|
+
children,
|
|
1134
|
+
title
|
|
1135
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1136
|
+
"button",
|
|
1137
|
+
{
|
|
1138
|
+
onClick,
|
|
1139
|
+
disabled,
|
|
1140
|
+
title,
|
|
1141
|
+
className: cn(
|
|
1142
|
+
"flex h-9 w-9 items-center justify-center rounded-lg border text-sm transition-all duration-200",
|
|
1143
|
+
disabled ? "border-border-light text-text-placeholder cursor-not-allowed opacity-40" : "border-border bg-surface text-text-secondary hover:bg-primary/8 hover:border-primary/30 hover:text-primary active:scale-95"
|
|
1144
|
+
),
|
|
1145
|
+
children
|
|
1146
|
+
}
|
|
1147
|
+
);
|
|
1148
|
+
var Pagination = ({
|
|
1149
|
+
currentPage,
|
|
1150
|
+
totalPages,
|
|
1151
|
+
onPageChange,
|
|
1152
|
+
loading = false
|
|
1153
|
+
}) => {
|
|
1154
|
+
if (totalPages <= 1) return null;
|
|
1155
|
+
const getVisiblePages = () => {
|
|
1156
|
+
const pages2 = [];
|
|
1157
|
+
const maxVisible = 5;
|
|
1158
|
+
if (totalPages <= maxVisible + 2) {
|
|
1159
|
+
for (let i = 1; i <= totalPages; i++) pages2.push(i);
|
|
1160
|
+
return pages2;
|
|
1161
|
+
}
|
|
1162
|
+
pages2.push(1);
|
|
1163
|
+
const start = Math.max(2, currentPage - 1);
|
|
1164
|
+
const end = Math.min(totalPages - 1, currentPage + 1);
|
|
1165
|
+
if (start > 2) pages2.push("ellipsis");
|
|
1166
|
+
for (let i = start; i <= end; i++) pages2.push(i);
|
|
1167
|
+
if (end < totalPages - 1) pages2.push("ellipsis");
|
|
1168
|
+
pages2.push(totalPages);
|
|
1169
|
+
return pages2;
|
|
1170
|
+
};
|
|
1171
|
+
const pages = getVisiblePages();
|
|
1172
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "mt-6 flex items-center justify-center gap-1.5", children: [
|
|
1173
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1174
|
+
NavButton,
|
|
1175
|
+
{
|
|
1176
|
+
onClick: () => onPageChange(1),
|
|
1177
|
+
disabled: currentPage === 1 || loading,
|
|
1178
|
+
title: "Premi\xE8re page",
|
|
1179
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ChevronsLeft, { size: 16 })
|
|
1180
|
+
}
|
|
1181
|
+
),
|
|
1182
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1183
|
+
NavButton,
|
|
1184
|
+
{
|
|
1185
|
+
onClick: () => onPageChange(currentPage - 1),
|
|
1186
|
+
disabled: currentPage === 1 || loading,
|
|
1187
|
+
title: "Page pr\xE9c\xE9dente",
|
|
1188
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ChevronLeft, { size: 16 })
|
|
1189
|
+
}
|
|
1190
|
+
),
|
|
1191
|
+
pages.map(
|
|
1192
|
+
(p, idx) => p === "ellipsis" ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1193
|
+
"span",
|
|
1194
|
+
{
|
|
1195
|
+
className: "text-text-muted flex h-9 w-9 items-center justify-center text-sm",
|
|
1196
|
+
children: "\u2026"
|
|
1197
|
+
},
|
|
1198
|
+
`ellipsis-${idx}`
|
|
1199
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1200
|
+
"button",
|
|
1201
|
+
{
|
|
1202
|
+
onClick: () => onPageChange(p),
|
|
1203
|
+
disabled: loading,
|
|
1204
|
+
className: cn(
|
|
1205
|
+
"flex h-9 w-9 items-center justify-center rounded-lg text-sm font-medium transition-all duration-200",
|
|
1206
|
+
p === currentPage ? "bg-primary text-white shadow-sm shadow-primary/30" : "border-border bg-surface text-text-secondary hover:bg-primary/8 hover:border-primary/30 hover:text-primary border active:scale-95"
|
|
1207
|
+
),
|
|
1208
|
+
children: p
|
|
1209
|
+
},
|
|
1210
|
+
p
|
|
1211
|
+
)
|
|
1212
|
+
),
|
|
1213
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1214
|
+
NavButton,
|
|
1215
|
+
{
|
|
1216
|
+
onClick: () => onPageChange(currentPage + 1),
|
|
1217
|
+
disabled: currentPage === totalPages || loading,
|
|
1218
|
+
title: "Page suivante",
|
|
1219
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ChevronRight, { size: 16 })
|
|
1220
|
+
}
|
|
1221
|
+
),
|
|
1222
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
1223
|
+
NavButton,
|
|
1224
|
+
{
|
|
1225
|
+
onClick: () => onPageChange(totalPages),
|
|
1226
|
+
disabled: currentPage === totalPages || loading,
|
|
1227
|
+
title: "Derni\xE8re page",
|
|
1228
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_lucide_react4.ChevronsRight, { size: 16 })
|
|
1229
|
+
}
|
|
1230
|
+
)
|
|
1231
|
+
] });
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
// src/components/WorkspaceCard.tsx
|
|
1235
|
+
var import_lucide_react5 = require("lucide-react");
|
|
1236
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1237
|
+
var WorkspaceCard = ({
|
|
1238
|
+
workspace,
|
|
1239
|
+
members = [],
|
|
1240
|
+
avatarUsers,
|
|
1241
|
+
onClick,
|
|
1242
|
+
currentUserId
|
|
1243
|
+
}) => {
|
|
1244
|
+
const isPrivate = workspace.isPrivate === true;
|
|
1245
|
+
const typeName = typeof workspace.type === "object" && workspace.type !== null ? workspace.type.name : workspace.type;
|
|
1246
|
+
const userRole = currentUserId ? members.find((m) => m.memberId === currentUserId)?.role : void 0;
|
|
1247
|
+
const userMembers = members.filter((m) => m.memberType === "user");
|
|
1248
|
+
const agentMembers = members.filter((m) => m.memberType === "agent");
|
|
1249
|
+
const otherMember = isPrivate ? members.find((m) => m.memberId !== currentUserId) : null;
|
|
1250
|
+
const otherAvatarUrl = otherMember ? avatarUsers?.find((u) => u.email.toLowerCase() === otherMember.memberId.toLowerCase())?.avatarUrl : void 0;
|
|
1251
|
+
const displayEntities = workspace.entitiesWithSecrets ?? [];
|
|
1252
|
+
const isActive = workspace.status === "ACTIVE";
|
|
1253
|
+
const cardClass = cn(
|
|
1254
|
+
"group bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] relative flex cursor-pointer flex-col rounded-2xl border p-5 transition-all duration-300",
|
|
1255
|
+
"shadow-sm hover:shadow-md hover:-translate-y-1 overflow-hidden"
|
|
1256
|
+
);
|
|
1257
|
+
if (isPrivate) {
|
|
1258
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cardClass, onClick, children: [
|
|
1259
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center justify-between", children: [
|
|
1260
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)] flex items-center gap-1.5 rounded-lg px-2.5 py-1 text-[11px] font-semibold tracking-wide uppercase", children: [
|
|
1261
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.MessageSquare, { size: 11 }),
|
|
1262
|
+
" Priv\xE9"
|
|
1263
|
+
] }),
|
|
1264
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ArrowUpRight, { size: 14, className: "text-[var(--color-text-muted,#94a3b8)] group-hover:text-[var(--color-primary,#3b82f6)] transition-all duration-200 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" })
|
|
1265
|
+
] }),
|
|
1266
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-4 flex items-center gap-3", children: [
|
|
1267
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(MemberAvatar, { memberName: otherMember?.memberName ?? "Inconnu", memberType: "user", avatarUrl: otherAvatarUrl, size: "lg" }),
|
|
1268
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1269
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h4", { className: "text-[var(--color-text,#0f172a)] group-hover:text-[var(--color-primary,#3b82f6)] text-[15px] leading-snug font-bold transition-colors duration-200", children: otherMember?.memberName ?? "Utilisateur inconnu" }),
|
|
1270
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[var(--color-text-muted,#94a3b8)] truncate text-xs", children: otherMember?.memberId ?? "" })
|
|
1271
|
+
] })
|
|
1272
|
+
] }),
|
|
1273
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mt-auto", children: [
|
|
1274
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "border-[var(--color-border,#e2e8f0)] mb-3 border-t" }),
|
|
1275
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] flex items-center gap-1 text-[11px]", children: [
|
|
1276
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Calendar, { size: 11, className: "opacity-50" }),
|
|
1277
|
+
new Date(workspace.updatedAt).toLocaleDateString("fr-FR")
|
|
1278
|
+
] }) })
|
|
1279
|
+
] })
|
|
1280
|
+
] });
|
|
1281
|
+
}
|
|
1282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: cardClass, onClick, children: [
|
|
1283
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
1284
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex items-center gap-2", children: typeName && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)] rounded-lg px-2.5 py-1 text-[11px] font-semibold tracking-wide uppercase", children: typeName }) }),
|
|
1285
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
1286
|
+
userRole && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: cn(
|
|
1287
|
+
"rounded-lg px-2 py-0.5 text-[10px] font-semibold",
|
|
1288
|
+
userRole === "owner" ? "bg-amber-50 text-amber-600 dark:bg-amber-500/10 dark:text-amber-400" : userRole === "participant" ? "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)]" : "bg-[var(--color-bg,#f8fafc)] text-[var(--color-text-muted,#94a3b8)]"
|
|
1289
|
+
), children: ROLE_LABELS[userRole] ?? userRole }),
|
|
1290
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Badge, { variant: isActive ? "success" : "default", size: "xs", children: STATUS_LABELS[workspace.status] ?? workspace.status })
|
|
1291
|
+
] })
|
|
1292
|
+
] }),
|
|
1293
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-3", children: [
|
|
1294
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("h4", { className: "text-[var(--color-text,#0f172a)] group-hover:text-[var(--color-primary,#3b82f6)] mb-1 text-[15px] leading-snug font-bold transition-colors duration-200", children: workspace.name }),
|
|
1295
|
+
workspace.scope && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("p", { className: "text-[var(--color-text-secondary,#475569)] line-clamp-2 text-[13px] leading-relaxed", children: workspace.scope })
|
|
1296
|
+
] }),
|
|
1297
|
+
displayEntities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mb-3 flex flex-wrap gap-1.5", children: [
|
|
1298
|
+
displayEntities.slice(0, 3).map((ent) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "bg-[var(--color-bg,#f8fafc)] border-[var(--color-border,#e2e8f0)] inline-flex items-center gap-1 rounded-lg border px-2 py-1 text-[10px]", children: [
|
|
1299
|
+
ent.isSecret && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Lock, { size: 9, className: "text-amber-500 shrink-0" }),
|
|
1300
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] font-medium", children: [
|
|
1301
|
+
ent.key,
|
|
1302
|
+
":"
|
|
1303
|
+
] }),
|
|
1304
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("span", { className: "text-[var(--color-text,#0f172a)]", children: ent.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : ent.value })
|
|
1305
|
+
] }, ent.key)),
|
|
1306
|
+
displayEntities.length > 3 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] self-center text-[10px]", children: [
|
|
1307
|
+
"+",
|
|
1308
|
+
displayEntities.length - 3
|
|
1309
|
+
] })
|
|
1310
|
+
] }),
|
|
1311
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "mt-auto", children: [
|
|
1312
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "border-[var(--color-border,#e2e8f0)] mb-3 border-t" }),
|
|
1313
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1314
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
1315
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center", children: [
|
|
1316
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "flex -space-x-2", children: userMembers.slice(0, 4).map((m) => /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Tooltip, { text: `${m.memberName} (${ROLE_LABELS[m.role] ?? m.role})`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1317
|
+
MemberAvatar,
|
|
1318
|
+
{
|
|
1319
|
+
memberName: m.memberName,
|
|
1320
|
+
memberType: m.memberType,
|
|
1321
|
+
avatarUrl: avatarUsers?.find((u) => u.email.toLowerCase() === m.memberId.toLowerCase())?.avatarUrl,
|
|
1322
|
+
size: "xs"
|
|
1323
|
+
}
|
|
1324
|
+
) }, m._id)) }),
|
|
1325
|
+
userMembers.length > 4 && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] ml-1.5 text-[11px] font-medium", children: [
|
|
1326
|
+
"+",
|
|
1327
|
+
userMembers.length - 4
|
|
1328
|
+
] })
|
|
1329
|
+
] }),
|
|
1330
|
+
agentMembers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Tooltip, { text: `${agentMembers.length} agent(s): ${agentMembers.map((a) => a.memberName).join(", ")}`, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)] flex items-center gap-1 rounded-md px-1.5 py-0.5 text-[11px] font-medium", children: [
|
|
1331
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Brain, { size: 12 }),
|
|
1332
|
+
" ",
|
|
1333
|
+
agentMembers.length
|
|
1334
|
+
] }) })
|
|
1335
|
+
] }),
|
|
1336
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1337
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] flex items-center gap-1 text-[11px]", children: [
|
|
1338
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.Calendar, { size: 11, className: "opacity-50" }),
|
|
1339
|
+
new Date(workspace.updatedAt).toLocaleDateString("fr-FR")
|
|
1340
|
+
] }),
|
|
1341
|
+
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_lucide_react5.ArrowUpRight, { size: 14, className: "text-[var(--color-text-muted,#94a3b8)] group-hover:text-[var(--color-primary,#3b82f6)] transition-all duration-200 group-hover:translate-x-0.5 group-hover:-translate-y-0.5" })
|
|
1342
|
+
] })
|
|
1343
|
+
] })
|
|
1344
|
+
] })
|
|
1345
|
+
] });
|
|
1346
|
+
};
|
|
1347
|
+
|
|
1348
|
+
// src/components/WorkspaceRow.tsx
|
|
1349
|
+
var import_lucide_react6 = require("lucide-react");
|
|
1350
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1351
|
+
var WorkspaceRow = ({
|
|
1352
|
+
workspace,
|
|
1353
|
+
members = [],
|
|
1354
|
+
avatarUsers,
|
|
1355
|
+
onClick,
|
|
1356
|
+
currentUserId
|
|
1357
|
+
}) => {
|
|
1358
|
+
const typeName = typeof workspace.type === "object" && workspace.type !== null ? workspace.type.name : workspace.type;
|
|
1359
|
+
const userRole = currentUserId ? members.find((m) => m.memberId === currentUserId)?.role : void 0;
|
|
1360
|
+
const userMembers = members.filter((m) => m.memberType === "user");
|
|
1361
|
+
const agentMembers = members.filter((m) => m.memberType === "agent");
|
|
1362
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
1363
|
+
"div",
|
|
1364
|
+
{
|
|
1365
|
+
className: "bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] hover:bg-[var(--color-primary-light,#eff6ff)]/30 flex cursor-pointer items-center gap-4 rounded-lg border px-4 py-3 transition-colors",
|
|
1366
|
+
onClick,
|
|
1367
|
+
children: [
|
|
1368
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1369
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1370
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[var(--color-text,#0f172a)] truncate font-semibold", children: workspace.name }),
|
|
1371
|
+
typeName && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { size: "xs", title: typeName, children: typeName })
|
|
1372
|
+
] }),
|
|
1373
|
+
workspace.scope && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("p", { className: "text-[var(--color-text-muted,#94a3b8)] mt-0.5 truncate text-xs", children: workspace.scope })
|
|
1374
|
+
] }),
|
|
1375
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1376
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("div", { className: "flex -space-x-1.5", children: userMembers.slice(0, 3).map((m) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Tooltip, { text: m.memberName, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
1377
|
+
MemberAvatar,
|
|
1378
|
+
{
|
|
1379
|
+
memberName: m.memberName,
|
|
1380
|
+
memberType: m.memberType,
|
|
1381
|
+
avatarUrl: avatarUsers?.find((u) => u.email.toLowerCase() === m.memberId.toLowerCase())?.avatarUrl,
|
|
1382
|
+
size: "xs"
|
|
1383
|
+
}
|
|
1384
|
+
) }, m._id)) }),
|
|
1385
|
+
agentMembers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] flex items-center gap-0.5 text-[10px]", children: [
|
|
1386
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react6.Brain, { size: 10 }),
|
|
1387
|
+
" ",
|
|
1388
|
+
agentMembers.length
|
|
1389
|
+
] })
|
|
1390
|
+
] }),
|
|
1391
|
+
userRole && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: cn(
|
|
1392
|
+
"rounded-full px-2 py-0.5 text-[10px] font-medium",
|
|
1393
|
+
userRole === "owner" ? "bg-amber-50 text-amber-600 dark:bg-amber-500/10 dark:text-amber-400" : "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)]"
|
|
1394
|
+
), children: ROLE_LABELS[userRole] ?? userRole }),
|
|
1395
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Badge, { variant: workspace.status === "ACTIVE" ? "success" : "default", size: "xs", children: STATUS_LABELS[workspace.status] ?? workspace.status }),
|
|
1396
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-[10px] whitespace-nowrap", children: new Date(workspace.updatedAt).toLocaleDateString("fr-FR") })
|
|
1397
|
+
]
|
|
1398
|
+
}
|
|
1399
|
+
);
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
// src/components/WorkspaceList.tsx
|
|
1403
|
+
var import_react6 = __toESM(require("react"));
|
|
1404
|
+
var import_lucide_react7 = require("lucide-react");
|
|
1405
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
1406
|
+
var SkeletonCard = () => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] animate-pulse rounded-2xl border p-5", children: [
|
|
1407
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-3 flex items-center justify-between", children: [
|
|
1408
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-5 w-20 rounded-lg" }),
|
|
1409
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-4 w-14 rounded-lg" })
|
|
1410
|
+
] }),
|
|
1411
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-3", children: [
|
|
1412
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] mb-2 h-4 w-3/4 rounded" }),
|
|
1413
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-3 w-1/2 rounded" })
|
|
1414
|
+
] }),
|
|
1415
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mt-auto", children: [
|
|
1416
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "border-[var(--color-border,#e2e8f0)] mb-3 border-t" }),
|
|
1417
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1418
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex -space-x-2", children: [0, 1, 2].map((i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-6 w-6 rounded-full" }, i)) }),
|
|
1419
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-3 w-16 rounded" })
|
|
1420
|
+
] })
|
|
1421
|
+
] })
|
|
1422
|
+
] });
|
|
1423
|
+
var WorkspaceList = ({
|
|
1424
|
+
workspaces,
|
|
1425
|
+
total,
|
|
1426
|
+
membersMap = {},
|
|
1427
|
+
avatarUsers,
|
|
1428
|
+
types = [],
|
|
1429
|
+
loading = false,
|
|
1430
|
+
fetching = false,
|
|
1431
|
+
onOpen,
|
|
1432
|
+
title = "Workspaces",
|
|
1433
|
+
showViewToggle = true,
|
|
1434
|
+
currentUserId,
|
|
1435
|
+
headerActions,
|
|
1436
|
+
hideHeader = false,
|
|
1437
|
+
hideFilters = false,
|
|
1438
|
+
filters,
|
|
1439
|
+
onFiltersChange,
|
|
1440
|
+
currentPage = 1,
|
|
1441
|
+
totalPages = 1,
|
|
1442
|
+
onPageChange
|
|
1443
|
+
}) => {
|
|
1444
|
+
const [viewMode, setViewMode] = import_react6.default.useState("grid");
|
|
1445
|
+
const search = filters?.search ?? "";
|
|
1446
|
+
const statusFilter = filters?.status ?? "ACTIVE";
|
|
1447
|
+
const typeFilter = filters?.type ?? "all";
|
|
1448
|
+
const statusOptions = [
|
|
1449
|
+
{ value: "all", label: "Tous" },
|
|
1450
|
+
{ value: "ACTIVE", label: "Actifs" },
|
|
1451
|
+
{ value: "ARCHIVED", label: "Archiv\xE9s" }
|
|
1452
|
+
];
|
|
1453
|
+
const showSkeletons = loading || fetching && workspaces.length === 0;
|
|
1454
|
+
const Header = () => /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-5 flex items-center justify-between", children: [
|
|
1455
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-3", children: [
|
|
1456
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-primary-light,#eff6ff)] flex h-9 w-9 items-center justify-center rounded-xl", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.Layers, { size: 18, className: "text-[var(--color-primary,#3b82f6)]" }) }),
|
|
1457
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1458
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("h2", { className: "text-[var(--color-text,#0f172a)] text-lg font-bold tracking-tight", children: title }),
|
|
1459
|
+
total != null && !showSkeletons && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("p", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: [
|
|
1460
|
+
total,
|
|
1461
|
+
" workspace",
|
|
1462
|
+
total !== 1 ? "s" : ""
|
|
1463
|
+
] })
|
|
1464
|
+
] })
|
|
1465
|
+
] }),
|
|
1466
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1467
|
+
showViewToggle && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "bg-[var(--color-bg,#f8fafc)] border-[var(--color-border,#e2e8f0)] flex rounded-lg border p-0.5", children: [
|
|
1468
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1469
|
+
"button",
|
|
1470
|
+
{
|
|
1471
|
+
className: cn(
|
|
1472
|
+
"rounded-md p-1.5 transition-all duration-200",
|
|
1473
|
+
viewMode === "grid" ? "bg-[var(--color-primary,#3b82f6)] text-white shadow-sm" : "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)]"
|
|
1474
|
+
),
|
|
1475
|
+
onClick: () => setViewMode("grid"),
|
|
1476
|
+
title: "Vue grille",
|
|
1477
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.LayoutGrid, { size: 14 })
|
|
1478
|
+
}
|
|
1479
|
+
),
|
|
1480
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1481
|
+
"button",
|
|
1482
|
+
{
|
|
1483
|
+
className: cn(
|
|
1484
|
+
"rounded-md p-1.5 transition-all duration-200",
|
|
1485
|
+
viewMode === "list" ? "bg-[var(--color-primary,#3b82f6)] text-white shadow-sm" : "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)]"
|
|
1486
|
+
),
|
|
1487
|
+
onClick: () => setViewMode("list"),
|
|
1488
|
+
title: "Vue liste",
|
|
1489
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.List, { size: 14 })
|
|
1490
|
+
}
|
|
1491
|
+
)
|
|
1492
|
+
] }),
|
|
1493
|
+
headerActions
|
|
1494
|
+
] })
|
|
1495
|
+
] });
|
|
1496
|
+
if (showSkeletons) {
|
|
1497
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1498
|
+
!hideHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Header, {}),
|
|
1499
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(SkeletonCard, {}, i)) })
|
|
1500
|
+
] });
|
|
1501
|
+
}
|
|
1502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
1503
|
+
!hideHeader && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Header, {}),
|
|
1504
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "mb-5 flex flex-wrap items-center gap-3", children: [
|
|
1505
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "relative min-w-[220px] flex-1", children: [
|
|
1506
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.Search, { size: 15, className: "text-[var(--color-text-muted,#94a3b8)] absolute top-1/2 left-3.5 -translate-y-1/2" }),
|
|
1507
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1508
|
+
"input",
|
|
1509
|
+
{
|
|
1510
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] w-full rounded-xl border py-2.5 pr-3 pl-10 text-sm transition-all duration-200 focus:border-[var(--color-primary,#3b82f6)]/50 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary,#3b82f6)]/10 placeholder:text-[var(--color-text-muted,#94a3b8)]",
|
|
1511
|
+
placeholder: "Rechercher un workspace\u2026",
|
|
1512
|
+
value: search,
|
|
1513
|
+
onChange: (e) => onFiltersChange?.({ search: e.target.value, status: statusFilter, type: typeFilter })
|
|
1514
|
+
}
|
|
1515
|
+
)
|
|
1516
|
+
] }),
|
|
1517
|
+
!hideFilters && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
1518
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "flex items-center gap-1.5", children: [
|
|
1519
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.Filter, { size: 13, className: "text-[var(--color-text-muted,#94a3b8)] mr-0.5" }),
|
|
1520
|
+
statusOptions.map(({ value, label }) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1521
|
+
"button",
|
|
1522
|
+
{
|
|
1523
|
+
className: cn(
|
|
1524
|
+
"rounded-full px-3 py-1.5 text-xs font-medium transition-all duration-200",
|
|
1525
|
+
statusFilter === value ? "bg-[var(--color-primary,#3b82f6)] text-white shadow-sm" : "bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] text-[var(--color-text-muted,#94a3b8)] hover:bg-[var(--color-primary-light,#eff6ff)] hover:text-[var(--color-primary,#3b82f6)] border"
|
|
1526
|
+
),
|
|
1527
|
+
onClick: () => onFiltersChange?.({ search, status: value, type: typeFilter }),
|
|
1528
|
+
children: label
|
|
1529
|
+
},
|
|
1530
|
+
value
|
|
1531
|
+
))
|
|
1532
|
+
] }),
|
|
1533
|
+
types.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
1534
|
+
"select",
|
|
1535
|
+
{
|
|
1536
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] rounded-xl border px-3 py-2 text-xs transition-all duration-200 focus:border-[var(--color-primary,#3b82f6)]/50 focus:outline-none focus:ring-2 focus:ring-[var(--color-primary,#3b82f6)]/10",
|
|
1537
|
+
value: typeFilter,
|
|
1538
|
+
onChange: (e) => onFiltersChange?.({ search, status: statusFilter, type: e.target.value }),
|
|
1539
|
+
children: [
|
|
1540
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: "all", children: "Tous les types" }),
|
|
1541
|
+
types.map((t) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("option", { value: t._id, children: t.name }, t._id))
|
|
1542
|
+
]
|
|
1543
|
+
}
|
|
1544
|
+
)
|
|
1545
|
+
] })
|
|
1546
|
+
] }),
|
|
1547
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: cn("transition-opacity duration-300", fetching ? "opacity-60" : "opacity-100"), children: [
|
|
1548
|
+
workspaces.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "text-[var(--color-text-muted,#94a3b8)] border-[var(--color-border,#e2e8f0)] flex flex-col items-center justify-center rounded-2xl border border-dashed py-12 text-center", children: [
|
|
1549
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "bg-[var(--color-bg,#f8fafc)] mb-3 flex h-12 w-12 items-center justify-center rounded-full", children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_lucide_react7.Layers, { size: 22, className: "opacity-40" }) }),
|
|
1550
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "mb-1 text-sm font-medium", children: "Aucun workspace trouv\xE9" }),
|
|
1551
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "text-xs", children: search || statusFilter !== "all" || typeFilter !== "all" ? "Modifiez vos filtres pour voir plus de r\xE9sultats" : "Cr\xE9ez un workspace pour commencer \xE0 collaborer" })
|
|
1552
|
+
] }) : viewMode === "grid" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1553
|
+
WorkspaceCard,
|
|
1554
|
+
{
|
|
1555
|
+
workspace: ws,
|
|
1556
|
+
members: membersMap[ws._id],
|
|
1557
|
+
avatarUsers,
|
|
1558
|
+
onClick: () => onOpen(ws._id),
|
|
1559
|
+
currentUserId
|
|
1560
|
+
},
|
|
1561
|
+
ws._id
|
|
1562
|
+
)) }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "flex flex-col gap-2", children: workspaces.map((ws) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
1563
|
+
WorkspaceRow,
|
|
1564
|
+
{
|
|
1565
|
+
workspace: ws,
|
|
1566
|
+
members: membersMap[ws._id],
|
|
1567
|
+
avatarUsers,
|
|
1568
|
+
onClick: () => onOpen(ws._id),
|
|
1569
|
+
currentUserId
|
|
1570
|
+
},
|
|
1571
|
+
ws._id
|
|
1572
|
+
)) }),
|
|
1573
|
+
onPageChange && totalPages > 1 && /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Pagination, { currentPage, totalPages, onPageChange, loading: fetching })
|
|
1574
|
+
] })
|
|
1575
|
+
] });
|
|
1576
|
+
};
|
|
1577
|
+
|
|
1578
|
+
// src/components/CreateWorkspaceModal.tsx
|
|
1579
|
+
var import_react7 = require("react");
|
|
1580
|
+
var import_lucide_react8 = require("lucide-react");
|
|
1581
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1582
|
+
var STEP_TITLES = ["Type de workspace", "Contexte", "Membres", "Confirmation"];
|
|
1583
|
+
var CreateWorkspaceModal = ({ open, onClose, onCreated }) => {
|
|
1584
|
+
const titleId = (0, import_react7.useId)();
|
|
1585
|
+
const stepDescriptionId = (0, import_react7.useId)();
|
|
1586
|
+
const { config } = useWorkspaceContext();
|
|
1587
|
+
const apis = useWorkspaceApi();
|
|
1588
|
+
const [step, setStep] = (0, import_react7.useState)(0);
|
|
1589
|
+
const [loading, setLoading] = (0, import_react7.useState)(false);
|
|
1590
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
1591
|
+
const [types, setTypes] = (0, import_react7.useState)([]);
|
|
1592
|
+
const [typesLoading, setTypesLoading] = (0, import_react7.useState)(false);
|
|
1593
|
+
const [selectedTypeId, setSelectedTypeId] = (0, import_react7.useState)(null);
|
|
1594
|
+
const [name, setName] = (0, import_react7.useState)("");
|
|
1595
|
+
const [scope, setScope] = (0, import_react7.useState)("");
|
|
1596
|
+
const [entities, setEntities] = (0, import_react7.useState)([]);
|
|
1597
|
+
const [syncWithSlack, setSyncWithSlack] = (0, import_react7.useState)(false);
|
|
1598
|
+
const [slackAvailable, setSlackAvailable] = (0, import_react7.useState)(false);
|
|
1599
|
+
const [memberSearch, setMemberSearch] = (0, import_react7.useState)("");
|
|
1600
|
+
const [members, setMembers] = (0, import_react7.useState)([]);
|
|
1601
|
+
const [agents, setAgents] = (0, import_react7.useState)([]);
|
|
1602
|
+
const [searchResults, setSearchResults] = (0, import_react7.useState)([]);
|
|
1603
|
+
const [searchingUsers, setSearchingUsers] = (0, import_react7.useState)(false);
|
|
1604
|
+
const [avatarMap, setAvatarMap] = (0, import_react7.useState)({});
|
|
1605
|
+
const [agentSearch, setAgentSearch] = (0, import_react7.useState)("");
|
|
1606
|
+
const [showAgentDropdown, setShowAgentDropdown] = (0, import_react7.useState)(false);
|
|
1607
|
+
const [agentSearchFocused, setAgentSearchFocused] = (0, import_react7.useState)(false);
|
|
1608
|
+
const agentSearchInputRef = (0, import_react7.useRef)(null);
|
|
1609
|
+
const invitedUsers = (0, import_react7.useMemo)(() => members.filter((m) => m.memberType === "user"), [members]);
|
|
1610
|
+
const invitedAgents = (0, import_react7.useMemo)(() => members.filter((m) => m.memberType === "agent"), [members]);
|
|
1611
|
+
const unselectedAgents = (0, import_react7.useMemo)(() => agents.filter((a) => !members.some((m) => m.memberId === a.agentId) && (agentSearch.trim() === "" || a.agentName.toLowerCase().includes(agentSearch.toLowerCase()))), [agents, members, agentSearch]);
|
|
1612
|
+
(0, import_react7.useEffect)(() => {
|
|
1613
|
+
if (!config.resolveAvatars || invitedUsers.length === 0) return;
|
|
1614
|
+
config.resolveAvatars(invitedUsers.map((m) => m.memberId)).then(setAvatarMap).catch(() => {
|
|
1615
|
+
});
|
|
1616
|
+
}, [invitedUsers.length]);
|
|
1617
|
+
(0, import_react7.useEffect)(() => {
|
|
1618
|
+
if (memberSearch.length < 2 || !config.searchUsers) {
|
|
1619
|
+
setSearchResults([]);
|
|
1620
|
+
return;
|
|
1621
|
+
}
|
|
1622
|
+
let cancelled = false;
|
|
1623
|
+
setSearchingUsers(true);
|
|
1624
|
+
config.searchUsers(memberSearch).then((r) => {
|
|
1625
|
+
if (!cancelled) setSearchResults(r);
|
|
1626
|
+
}).catch(() => {
|
|
1627
|
+
if (!cancelled) setSearchResults([]);
|
|
1628
|
+
}).finally(() => {
|
|
1629
|
+
if (!cancelled) setSearchingUsers(false);
|
|
1630
|
+
});
|
|
1631
|
+
return () => {
|
|
1632
|
+
cancelled = true;
|
|
1633
|
+
};
|
|
1634
|
+
}, [memberSearch]);
|
|
1635
|
+
const filteredUsers = (0, import_react7.useMemo)(() => searchResults.filter((u) => u.email !== config.currentUserId && !members.some((m) => m.memberId === u.email)), [searchResults, members, config.currentUserId]);
|
|
1636
|
+
(0, import_react7.useEffect)(() => {
|
|
1637
|
+
if (!open) return;
|
|
1638
|
+
setTypesLoading(true);
|
|
1639
|
+
Promise.all([apis.workspaceTypeApi.list(), apis.agentApi.list()]).then(([wt, ag]) => {
|
|
1640
|
+
setTypes(wt);
|
|
1641
|
+
setAgents(ag);
|
|
1642
|
+
}).catch(() => {
|
|
1643
|
+
setTypes([]);
|
|
1644
|
+
setAgents([]);
|
|
1645
|
+
}).finally(() => setTypesLoading(false));
|
|
1646
|
+
apis.syncApi.getAvailableProviders().then((providers) => setSlackAvailable(providers.some((p) => p.id === "slack" && p.enabled))).catch(() => setSlackAvailable(false));
|
|
1647
|
+
}, [open]);
|
|
1648
|
+
(0, import_react7.useEffect)(() => {
|
|
1649
|
+
if (!selectedTypeId) return;
|
|
1650
|
+
const t = types.find((tp) => tp._id === selectedTypeId);
|
|
1651
|
+
if (t?.entitiesWithSecrets && t.entitiesWithSecrets.length > 0) {
|
|
1652
|
+
setEntities(t.entitiesWithSecrets.map((e) => ({ key: e.key, value: e.value, isSecret: e.isSecret })));
|
|
1653
|
+
}
|
|
1654
|
+
}, [selectedTypeId, types]);
|
|
1655
|
+
(0, import_react7.useEffect)(() => {
|
|
1656
|
+
if (!open) {
|
|
1657
|
+
setStep(0);
|
|
1658
|
+
setSelectedTypeId(null);
|
|
1659
|
+
setName("");
|
|
1660
|
+
setScope("");
|
|
1661
|
+
setEntities([]);
|
|
1662
|
+
setSyncWithSlack(false);
|
|
1663
|
+
setMembers([]);
|
|
1664
|
+
setMemberSearch("");
|
|
1665
|
+
setError(null);
|
|
1666
|
+
}
|
|
1667
|
+
}, [open]);
|
|
1668
|
+
const selectedType = types.find((t) => t._id === selectedTypeId);
|
|
1669
|
+
const canAdvance = () => {
|
|
1670
|
+
switch (step) {
|
|
1671
|
+
case 0:
|
|
1672
|
+
return !!selectedTypeId;
|
|
1673
|
+
case 1:
|
|
1674
|
+
return name.trim().length > 0;
|
|
1675
|
+
default:
|
|
1676
|
+
return true;
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
const addMember = (memberId, memberName, memberType) => {
|
|
1680
|
+
if (members.some((m) => m.memberId === memberId)) return;
|
|
1681
|
+
setMembers((prev) => [...prev, { memberId, memberName, memberType, role: "participant" }]);
|
|
1682
|
+
setMemberSearch("");
|
|
1683
|
+
};
|
|
1684
|
+
const removeMember = (memberId) => setMembers((prev) => prev.filter((m) => m.memberId !== memberId));
|
|
1685
|
+
const handleCreate = async () => {
|
|
1686
|
+
setLoading(true);
|
|
1687
|
+
setError(null);
|
|
1688
|
+
try {
|
|
1689
|
+
const entitiesWithSecrets = entities.filter((e) => e.key.trim()).map((e) => ({ key: e.key.trim(), value: e.value, isSecret: e.isSecret }));
|
|
1690
|
+
const payload = {
|
|
1691
|
+
name: name.trim(),
|
|
1692
|
+
type: selectedTypeId || void 0,
|
|
1693
|
+
scope: scope.trim() || void 0,
|
|
1694
|
+
entitiesWithSecrets: entitiesWithSecrets.length > 0 ? entitiesWithSecrets : void 0,
|
|
1695
|
+
members: members.length > 0 ? members.map((m) => ({ memberId: m.memberId, memberName: m.memberName, memberType: m.memberType, role: m.role })) : void 0,
|
|
1696
|
+
syncWithSlack: syncWithSlack || void 0
|
|
1697
|
+
};
|
|
1698
|
+
const ws = await apis.workspaceApi.create(payload);
|
|
1699
|
+
onCreated?.(ws);
|
|
1700
|
+
onClose();
|
|
1701
|
+
} catch (err) {
|
|
1702
|
+
setError(err.message);
|
|
1703
|
+
} finally {
|
|
1704
|
+
setLoading(false);
|
|
1705
|
+
}
|
|
1706
|
+
};
|
|
1707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(Modal, { open, onClose, zIndexClass: "z-50", size: "md", closeOnBackdropClick: false, backdropClassName: "bg-black/40", panelClassName: "bg-surface border-border flex h-[600px] max-h-[min(90dvh,100vh)] flex-col overflow-hidden rounded-2xl border shadow-xl", headingId: titleId, ariaLabelledBy: titleId, ariaDescribedBy: stepDescriptionId, title: "Nouveau Workspace", description: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("p", { id: stepDescriptionId, className: "text-text-muted text-xs", children: [
|
|
1708
|
+
"\xC9tape ",
|
|
1709
|
+
step + 1,
|
|
1710
|
+
"/4 \u2014 ",
|
|
1711
|
+
STEP_TITLES[step]
|
|
1712
|
+
] }), headerClassName: "items-center", closeButtonClassName: "text-text-muted hover:text-text transition-colors", children: [
|
|
1713
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex shrink-0 gap-1 px-6 pt-4", children: [0, 1, 2, 3].map((s) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("h-1 flex-1 rounded-full transition-colors", s <= step ? "bg-primary" : "bg-border-light") }, s)) }),
|
|
1714
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children: [
|
|
1715
|
+
step === 0 && (typesLoading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex h-[220px] items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Loader2, { size: 20, className: "text-primary animate-spin" }) }) : types.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-muted py-8 text-center text-sm", children: "Aucun type disponible" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "grid grid-cols-2 gap-2", children: types.map((t) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: cn("flex flex-col items-start gap-1 rounded-xl border p-3 text-left transition-all", selectedTypeId === t._id ? "border-primary bg-primary-light shadow-sm" : "border-border hover:border-primary/50 hover:bg-bg"), onClick: () => setSelectedTypeId(t._id), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text text-sm font-medium", children: t.name }) }, t._id)) })),
|
|
1716
|
+
step === 1 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
1717
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
1718
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { className: "text-text-muted mb-1 block text-xs font-medium", children: "Nom du workspace" }),
|
|
1719
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "border-border bg-bg text-text focus:border-primary w-full rounded-lg border px-3 py-2 text-sm focus:outline-none", placeholder: `Ex: ${selectedType?.name ?? "Workspace"} - ...`, value: name, onChange: (e) => setName(e.target.value), autoFocus: true })
|
|
1720
|
+
] }),
|
|
1721
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
1722
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { className: "text-text-muted mb-1 block text-xs font-medium", children: "Scope" }),
|
|
1723
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "border-border bg-bg text-text focus:border-primary w-full rounded-lg border px-3 py-2 text-sm focus:outline-none", placeholder: "Ex: production, engineering...", value: scope, onChange: (e) => setScope(e.target.value) })
|
|
1724
|
+
] }),
|
|
1725
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
1726
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("label", { className: "text-text-muted mb-1 block text-xs font-medium", children: "Entit\xE9s" }),
|
|
1727
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1728
|
+
entities.map((e, idx) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1729
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "border-border bg-bg text-text focus:border-primary w-28 rounded border px-2 py-1 text-xs focus:outline-none", value: e.key, onChange: (ev) => {
|
|
1730
|
+
const arr = [...entities];
|
|
1731
|
+
arr[idx].key = ev.target.value;
|
|
1732
|
+
setEntities(arr);
|
|
1733
|
+
}, placeholder: "Cl\xE9" }),
|
|
1734
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { className: "border-border bg-bg text-text focus:border-primary flex-1 rounded border px-2 py-1 text-xs focus:outline-none", value: e.value, onChange: (ev) => {
|
|
1735
|
+
const arr = [...entities];
|
|
1736
|
+
arr[idx].value = ev.target.value;
|
|
1737
|
+
setEntities(arr);
|
|
1738
|
+
}, placeholder: "Valeur" }),
|
|
1739
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: `rounded p-1 transition-colors ${e.isSecret ? "bg-warning/15 text-warning" : "text-text-muted hover:text-text hover:bg-bg"}`, onClick: () => {
|
|
1740
|
+
const arr = [...entities];
|
|
1741
|
+
arr[idx].isSecret = !arr[idx].isSecret;
|
|
1742
|
+
setEntities(arr);
|
|
1743
|
+
}, children: e.isSecret ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Lock, { size: 12 }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Unlock, { size: 12 }) }),
|
|
1744
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { className: "text-text-muted hover:text-danger", onClick: () => setEntities(entities.filter((_, i) => i !== idx)), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Trash2, { size: 14 }) })
|
|
1745
|
+
] }, idx)),
|
|
1746
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { className: "text-primary hover:bg-primary-light flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors", onClick: () => setEntities([...entities, { key: "", value: "", isSecret: false }]), children: [
|
|
1747
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Plus, { size: 12 }),
|
|
1748
|
+
" Ajouter une entit\xE9"
|
|
1749
|
+
] })
|
|
1750
|
+
] })
|
|
1751
|
+
] }),
|
|
1752
|
+
slackAvailable && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: cn("border-border bg-bg hover:border-primary/50 flex cursor-pointer items-start gap-3 rounded-xl border p-3 transition-colors", syncWithSlack && "border-primary bg-primary-light/40"), children: [
|
|
1753
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "checkbox", className: "accent-primary mt-0.5 h-4 w-4 shrink-0 cursor-pointer", checked: syncWithSlack, onChange: (e) => setSyncWithSlack(e.target.checked) }),
|
|
1754
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [
|
|
1755
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-text flex items-center gap-1.5 text-sm font-medium", children: [
|
|
1756
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Slack, { size: 14, className: "text-primary" }),
|
|
1757
|
+
" Synchroniser avec Slack"
|
|
1758
|
+
] }),
|
|
1759
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text-muted text-[11px] leading-snug", children: "Un canal Slack priv\xE9 sera cr\xE9\xE9 et les membres utilisateurs y seront invit\xE9s automatiquement." })
|
|
1760
|
+
] })
|
|
1761
|
+
] })
|
|
1762
|
+
] }),
|
|
1763
|
+
step === 2 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-5", children: [
|
|
1764
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1765
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1766
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: "text-text-muted flex items-center gap-1.5 text-xs font-semibold tracking-wider uppercase", children: [
|
|
1767
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Users, { size: 14, className: "text-primary" }),
|
|
1768
|
+
" Membres Utilisateurs"
|
|
1769
|
+
] }),
|
|
1770
|
+
invitedUsers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "bg-primary-light text-primary rounded-full px-2 py-0.5 text-[10px] font-bold", children: [
|
|
1771
|
+
invitedUsers.length,
|
|
1772
|
+
" invit\xE9",
|
|
1773
|
+
invitedUsers.length > 1 ? "s" : ""
|
|
1774
|
+
] })
|
|
1775
|
+
] }),
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative", children: [
|
|
1777
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { type: "search", name: "workspace-member-search", autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": "true", className: "border-border bg-bg text-text focus:border-primary focus:ring-primary/10 placeholder:text-text-muted/65 w-full rounded-xl border px-3.5 py-2.5 text-sm transition-all focus:ring-2 focus:outline-none", placeholder: "Rechercher un membre par nom ou identifiant...", value: memberSearch, onChange: (e) => setMemberSearch(e.target.value) }),
|
|
1778
|
+
memberSearch.length >= 2 && searchingUsers && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "absolute top-1/2 right-3.5 flex -translate-y-1/2 items-center justify-center py-3", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Loader2, { size: 16, className: "text-primary animate-spin" }) }),
|
|
1779
|
+
filteredUsers.length > 0 && memberSearch.length >= 2 && !searchingUsers && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "border-border bg-surface divide-border/30 absolute top-full left-0 z-20 mt-1 flex max-h-48 w-full flex-col divide-y overflow-y-auto rounded-xl border shadow-md", children: filteredUsers.slice(0, 5).map((u) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "hover:bg-primary-light/50 flex cursor-pointer items-center justify-between px-3.5 py-2.5 text-xs transition-colors", onClick: () => addMember(u.email, u.displayName, "user"), children: [
|
|
1780
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2.5", children: [
|
|
1781
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "bg-primary-light text-primary ring-primary/10 flex h-7 w-7 items-center justify-center rounded-full text-[10px] font-semibold ring-1", children: getInitials(u.displayName) }),
|
|
1782
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col", children: [
|
|
1783
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text font-medium", children: u.displayName }),
|
|
1784
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text-muted text-[10px]", children: u.email })
|
|
1785
|
+
] })
|
|
1786
|
+
] }),
|
|
1787
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Plus, { size: 14, className: "text-primary/75" })
|
|
1788
|
+
] }, u.id)) })
|
|
1789
|
+
] }),
|
|
1790
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mt-1 flex flex-col gap-1.5", children: invitedUsers.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "bg-bg/40 border-border/40 flex items-center justify-center rounded-xl border border-dashed py-4 text-center", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-muted text-xs italic", children: "Aucun utilisateur invit\xE9 pour le moment." }) }) : invitedUsers.map((m) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "bg-bg-subtle hover:bg-bg border-border/30 flex items-center gap-3 rounded-xl border p-2.5 transition-all duration-200", children: [
|
|
1791
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemberAvatar, { memberName: m.memberName, memberType: "user", avatarUrl: avatarMap[m.memberId.toLowerCase()], size: "sm", variant: m.role === "owner" ? "owner" : "primary" }),
|
|
1792
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1793
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-text truncate text-xs font-semibold", children: m.memberName }),
|
|
1794
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-text-muted truncate text-[10px]", children: m.memberId })
|
|
1795
|
+
] }),
|
|
1796
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
1797
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("select", { className: "border-border bg-surface text-text focus:border-primary rounded-lg border px-2 py-1 text-[10px] font-medium focus:outline-none", value: m.role, onChange: (e) => setMembers(members.map((x) => x.memberId === m.memberId ? { ...x, role: e.target.value } : x)), children: [
|
|
1798
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "owner", children: "Propri\xE9taire" }),
|
|
1799
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "participant", children: "Participant" }),
|
|
1800
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("option", { value: "viewer", children: "Observateur" })
|
|
1801
|
+
] }),
|
|
1802
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "text-text-muted hover:text-danger hover:bg-danger-light/20 rounded-lg p-1.5 transition-colors", onClick: () => removeMember(m.memberId), children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.X, { size: 14 }) })
|
|
1803
|
+
] })
|
|
1804
|
+
] }, m.memberId)) })
|
|
1805
|
+
] }),
|
|
1806
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1807
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
1808
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("label", { className: "text-text-muted flex items-center gap-1.5 text-xs font-semibold tracking-wider uppercase", children: [
|
|
1809
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Brain, { size: 14, className: "text-primary" }),
|
|
1810
|
+
" Agents IA"
|
|
1811
|
+
] }),
|
|
1812
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "bg-primary-light text-primary rounded-full px-2 py-0.5 text-[10px] font-bold", children: [
|
|
1813
|
+
invitedAgents.length,
|
|
1814
|
+
"/",
|
|
1815
|
+
agents.length
|
|
1816
|
+
] })
|
|
1817
|
+
] }),
|
|
1818
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "relative", children: [
|
|
1819
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: cn("border-border bg-bg focus-within:border-primary focus-within:ring-primary/10 flex min-h-[44px] cursor-text flex-wrap items-center gap-1.5 rounded-xl border px-3 py-1.5 text-sm transition-all focus-within:ring-2", agentSearchFocused && "border-primary ring-primary/10 ring-2"), onClick: () => agentSearchInputRef.current?.focus(), children: [
|
|
1820
|
+
invitedAgents.map((m) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "bg-primary-light/50 border-primary/20 text-primary flex items-center gap-1 rounded-lg border py-1 pr-1.5 pl-2 text-xs font-medium", children: [
|
|
1821
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Brain, { size: 10, className: "text-primary/80" }),
|
|
1822
|
+
m.memberName,
|
|
1823
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("button", { type: "button", className: "hover:bg-primary-light text-primary rounded-md p-0.5 transition-colors", onClick: (e) => {
|
|
1824
|
+
e.stopPropagation();
|
|
1825
|
+
removeMember(m.memberId);
|
|
1826
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.X, { size: 10 }) })
|
|
1827
|
+
] }, m.memberId)),
|
|
1828
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("input", { ref: agentSearchInputRef, type: "search", name: "workspace-agent-search", autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": "true", className: "text-text placeholder:text-text-muted/65 min-w-[100px] flex-1 border-none bg-transparent p-0.5 text-xs focus:ring-0 focus:outline-none", placeholder: invitedAgents.length === 0 ? "Rechercher et inviter des agents..." : "", value: agentSearch, onChange: (e) => {
|
|
1829
|
+
setAgentSearch(e.target.value);
|
|
1830
|
+
setShowAgentDropdown(true);
|
|
1831
|
+
}, onFocus: () => {
|
|
1832
|
+
setAgentSearchFocused(true);
|
|
1833
|
+
setShowAgentDropdown(true);
|
|
1834
|
+
}, onBlur: () => setTimeout(() => {
|
|
1835
|
+
setAgentSearchFocused(false);
|
|
1836
|
+
setShowAgentDropdown(false);
|
|
1837
|
+
}, 200) })
|
|
1838
|
+
] }),
|
|
1839
|
+
showAgentDropdown && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "border-border bg-surface divide-border/30 absolute z-20 mt-1.5 flex max-h-48 w-full flex-col divide-y overflow-y-auto rounded-xl border shadow-lg", children: unselectedAgents.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("p", { className: "text-text-muted p-3 text-center text-xs italic", children: agentSearch.trim().length > 0 ? "Aucun agent ne correspond." : "Tous les agents ont \xE9t\xE9 invit\xE9s." }) : unselectedAgents.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "hover:bg-primary-light/50 flex cursor-pointer items-center justify-between px-3.5 py-2.5 text-xs transition-colors", onClick: () => {
|
|
1840
|
+
addMember(agent.agentId, agent.agentName, "agent");
|
|
1841
|
+
setAgentSearch("");
|
|
1842
|
+
}, children: [
|
|
1843
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex items-center gap-2.5", children: [
|
|
1844
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "bg-surface border-border flex h-7 w-7 items-center justify-center rounded-full border", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Brain, { size: 12, className: "text-primary" }) }),
|
|
1845
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text font-medium", children: agent.agentName })
|
|
1846
|
+
] }),
|
|
1847
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Plus, { size: 14, className: "text-primary/75" })
|
|
1848
|
+
] }, agent.agentId)) })
|
|
1849
|
+
] })
|
|
1850
|
+
] })
|
|
1851
|
+
] }),
|
|
1852
|
+
step === 3 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
1853
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "bg-bg rounded-xl p-4", children: [
|
|
1854
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mb-3 flex items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { children: [
|
|
1855
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "text-text text-sm font-semibold", children: name || "Sans nom" }),
|
|
1856
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-text-muted text-xs", children: [
|
|
1857
|
+
selectedType?.name ?? "\u2014",
|
|
1858
|
+
" \u2022 ",
|
|
1859
|
+
scope || "Sans scope"
|
|
1860
|
+
] })
|
|
1861
|
+
] }) }),
|
|
1862
|
+
entities.filter((e) => e.key.trim() && e.value.trim()).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "mb-2 flex flex-wrap gap-1", children: entities.filter((e) => e.key.trim() && e.value.trim()).map((e, i) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "border-border inline-flex items-center gap-1 rounded border px-2 py-0.5 text-xs", children: [
|
|
1863
|
+
e.isSecret && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Lock, { size: 10, className: "text-warning shrink-0" }),
|
|
1864
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-text-muted mr-1 font-medium", children: [
|
|
1865
|
+
e.key,
|
|
1866
|
+
":"
|
|
1867
|
+
] }),
|
|
1868
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text", children: e.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : e.value })
|
|
1869
|
+
] }, i)) }),
|
|
1870
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "text-text-muted flex items-center gap-3 text-xs", children: [
|
|
1871
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
1872
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Users, { size: 12 }),
|
|
1873
|
+
" ",
|
|
1874
|
+
members.length,
|
|
1875
|
+
" membre",
|
|
1876
|
+
members.length > 1 ? "s" : ""
|
|
1877
|
+
] }),
|
|
1878
|
+
syncWithSlack && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-primary flex items-center gap-1 font-medium", children: [
|
|
1879
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Slack, { size: 12 }),
|
|
1880
|
+
" Synchronis\xE9 avec Slack"
|
|
1881
|
+
] })
|
|
1882
|
+
] }),
|
|
1883
|
+
members.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border-border/30 mt-3 border-t pt-3", children: [
|
|
1884
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-text-muted mb-2 block text-[10px] font-bold tracking-wider uppercase", children: [
|
|
1885
|
+
"Membres et Agents invit\xE9s (",
|
|
1886
|
+
members.length,
|
|
1887
|
+
")"
|
|
1888
|
+
] }),
|
|
1889
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "flex max-h-40 flex-col gap-1.5 overflow-y-auto pr-1", children: members.map((m) => /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "bg-bg-subtle/50 flex items-center gap-2.5 rounded-lg p-2 text-xs", children: [
|
|
1890
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(MemberAvatar, { memberName: m.memberName, memberType: m.memberType, avatarUrl: m.memberType === "user" ? avatarMap[m.memberId.toLowerCase()] : void 0, size: "xs", variant: m.role === "owner" ? "owner" : "primary" }),
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1892
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)("span", { className: "text-text truncate font-medium", children: m.memberName }),
|
|
1893
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("span", { className: "text-text-muted ml-2 text-[10px]", children: [
|
|
1894
|
+
"(",
|
|
1895
|
+
m.memberType === "agent" ? "Agent" : m.role === "owner" ? "Propri\xE9taire" : m.role === "viewer" ? "Observateur" : "Participant",
|
|
1896
|
+
")"
|
|
1897
|
+
] })
|
|
1898
|
+
] })
|
|
1899
|
+
] }, m.memberId)) })
|
|
1900
|
+
] })
|
|
1901
|
+
] }),
|
|
1902
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: "bg-danger-light text-danger rounded-lg px-3 py-2 text-xs", children: error })
|
|
1903
|
+
] })
|
|
1904
|
+
] }),
|
|
1905
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("div", { className: "border-border flex shrink-0 items-center justify-between border-t px-6 py-4", children: [
|
|
1906
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: cn("text-text-muted hover:text-text flex items-center gap-1 text-sm transition-colors", step === 0 && "invisible"), onClick: () => setStep((s) => s - 1), disabled: loading, children: [
|
|
1907
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.ArrowLeft, { size: 14 }),
|
|
1908
|
+
" Pr\xE9c\xE9dent"
|
|
1909
|
+
] }),
|
|
1910
|
+
step < 3 ? /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: cn("flex items-center gap-1 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors", canAdvance() ? "bg-primary hover:bg-primary-hover" : "bg-border cursor-not-allowed"), onClick: () => setStep((s) => s + 1), disabled: !canAdvance(), children: [
|
|
1911
|
+
"Suivant ",
|
|
1912
|
+
/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.ArrowRight, { size: 14 })
|
|
1913
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)("button", { type: "button", className: "bg-primary hover:bg-primary-hover flex items-center gap-1 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors disabled:opacity-50", onClick: handleCreate, disabled: loading, children: [
|
|
1914
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Loader2, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react8.Check, { size: 14 }),
|
|
1915
|
+
" Cr\xE9er le workspace"
|
|
1916
|
+
] })
|
|
1917
|
+
] })
|
|
1918
|
+
] });
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
// src/components/CreatePrivateWorkspaceModal.tsx
|
|
1922
|
+
var import_react8 = require("react");
|
|
1923
|
+
var import_lucide_react9 = require("lucide-react");
|
|
1924
|
+
var import_jsx_runtime12 = require("react/jsx-runtime");
|
|
1925
|
+
var CreatePrivateWorkspaceModal = ({ open, onClose, onCreated }) => {
|
|
1926
|
+
const titleId = (0, import_react8.useId)();
|
|
1927
|
+
const { config } = useWorkspaceContext();
|
|
1928
|
+
const apis = useWorkspaceApi();
|
|
1929
|
+
const [loading, setLoading] = (0, import_react8.useState)(false);
|
|
1930
|
+
const [error, setError] = (0, import_react8.useState)(null);
|
|
1931
|
+
const [search, setSearch] = (0, import_react8.useState)("");
|
|
1932
|
+
const [searchResults, setSearchResults] = (0, import_react8.useState)([]);
|
|
1933
|
+
const [searchingUsers, setSearchingUsers] = (0, import_react8.useState)(false);
|
|
1934
|
+
const [selectedUser, setSelectedUser] = (0, import_react8.useState)(null);
|
|
1935
|
+
const searchInputRef = (0, import_react8.useRef)(null);
|
|
1936
|
+
(0, import_react8.useEffect)(() => {
|
|
1937
|
+
if (search.length < 2 || !config.searchUsers) {
|
|
1938
|
+
setSearchResults([]);
|
|
1939
|
+
return;
|
|
1940
|
+
}
|
|
1941
|
+
let cancelled = false;
|
|
1942
|
+
setSearchingUsers(true);
|
|
1943
|
+
config.searchUsers(search).then((r) => {
|
|
1944
|
+
if (!cancelled) setSearchResults(r);
|
|
1945
|
+
}).catch(() => {
|
|
1946
|
+
if (!cancelled) setSearchResults([]);
|
|
1947
|
+
}).finally(() => {
|
|
1948
|
+
if (!cancelled) setSearchingUsers(false);
|
|
1949
|
+
});
|
|
1950
|
+
return () => {
|
|
1951
|
+
cancelled = true;
|
|
1952
|
+
};
|
|
1953
|
+
}, [search]);
|
|
1954
|
+
const filteredUsers = (0, import_react8.useMemo)(() => searchResults.filter((u) => u.email !== config.currentUserId), [searchResults, config.currentUserId]);
|
|
1955
|
+
(0, import_react8.useEffect)(() => {
|
|
1956
|
+
if (!open) {
|
|
1957
|
+
setSearch("");
|
|
1958
|
+
setSelectedUser(null);
|
|
1959
|
+
setError(null);
|
|
1960
|
+
setLoading(false);
|
|
1961
|
+
} else {
|
|
1962
|
+
setTimeout(() => searchInputRef.current?.focus(), 100);
|
|
1963
|
+
}
|
|
1964
|
+
}, [open]);
|
|
1965
|
+
const handleCreate = async () => {
|
|
1966
|
+
if (!selectedUser || !config.currentUserId) return;
|
|
1967
|
+
setLoading(true);
|
|
1968
|
+
setError(null);
|
|
1969
|
+
try {
|
|
1970
|
+
const payload = {
|
|
1971
|
+
name: `${config.currentUserName ?? config.currentUserId} \u2194 ${selectedUser.displayName}`,
|
|
1972
|
+
isPrivate: true,
|
|
1973
|
+
members: [{ memberId: selectedUser.email, memberName: selectedUser.displayName, memberType: "user", role: "participant" }]
|
|
1974
|
+
};
|
|
1975
|
+
const ws = await apis.workspaceApi.create(payload);
|
|
1976
|
+
onCreated?.(ws);
|
|
1977
|
+
onClose();
|
|
1978
|
+
} catch (err) {
|
|
1979
|
+
setError(err.message);
|
|
1980
|
+
} finally {
|
|
1981
|
+
setLoading(false);
|
|
1982
|
+
}
|
|
1983
|
+
};
|
|
1984
|
+
return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Modal, { open, onClose, zIndexClass: "z-50", size: "sm", closeOnBackdropClick: false, backdropClassName: "bg-black/40", panelClassName: "bg-surface border-border flex max-h-[min(70dvh,500px)] flex-col overflow-hidden rounded-2xl border shadow-xl", headingId: titleId, ariaLabelledBy: titleId, title: "Conversation Priv\xE9e", description: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-text-muted text-xs", children: "Choisissez un utilisateur pour d\xE9marrer une conversation priv\xE9e." }), headerClassName: "items-center", closeButtonClassName: "text-text-muted hover:text-text transition-colors", titleIcon: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "bg-primary-light text-primary flex h-8 w-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.Lock, { size: 16 }) }), children: [
|
|
1985
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children: [
|
|
1986
|
+
selectedUser ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mb-4", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "bg-primary-light/30 border-primary/20 flex items-center gap-3 rounded-xl border p-3", children: [
|
|
1987
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(MemberAvatar, { memberName: selectedUser.displayName, memberType: "user", avatarUrl: selectedUser.avatarUrl, size: "md", variant: "primary" }),
|
|
1988
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
1989
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-text text-sm font-semibold", children: selectedUser.displayName }),
|
|
1990
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-text-muted text-xs", children: selectedUser.email })
|
|
1991
|
+
] }),
|
|
1992
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "text-text-muted hover:text-danger rounded p-1 transition-colors", onClick: () => setSelectedUser(null), children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.X, { size: 16 }) })
|
|
1993
|
+
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
|
|
1994
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "relative", children: [
|
|
1995
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.Search, { size: 14, className: "text-text-muted absolute top-1/2 left-3 -translate-y-1/2" }),
|
|
1996
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("input", { ref: searchInputRef, type: "search", autoComplete: "off", "data-lpignore": "true", "data-1p-ignore": "true", className: "border-border bg-bg text-text focus:border-primary focus:ring-primary/10 placeholder:text-text-muted/65 w-full rounded-xl border py-2.5 pr-3 pl-9 text-sm transition-all focus:ring-2 focus:outline-none", placeholder: "Rechercher un utilisateur...", value: search, onChange: (e) => setSearch(e.target.value) }),
|
|
1997
|
+
search.length >= 2 && searchingUsers && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "absolute top-1/2 right-3 -translate-y-1/2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.Loader2, { size: 16, className: "text-primary animate-spin" }) })
|
|
1998
|
+
] }),
|
|
1999
|
+
search.length >= 2 && !searchingUsers && filteredUsers.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "border-border bg-surface mt-2 flex max-h-48 flex-col overflow-y-auto rounded-xl border shadow-sm", children: filteredUsers.slice(0, 8).map((u) => /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: "hover:bg-primary-light/50 flex items-center gap-3 px-3.5 py-2.5 text-left transition-colors first:rounded-t-xl last:rounded-b-xl", onClick: () => {
|
|
2000
|
+
setSelectedUser(u);
|
|
2001
|
+
setSearch("");
|
|
2002
|
+
}, children: [
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "bg-primary-light text-primary flex h-8 w-8 items-center justify-center rounded-full text-xs font-semibold", children: getInitials(u.displayName) }),
|
|
2004
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
2005
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-text text-sm font-medium", children: u.displayName }),
|
|
2006
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "text-text-muted text-[11px]", children: u.email })
|
|
2007
|
+
] })
|
|
2008
|
+
] }, u.id)) }),
|
|
2009
|
+
search.length >= 2 && !searchingUsers && filteredUsers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-text-muted mt-3 text-center text-xs italic", children: "Aucun utilisateur trouv\xE9." }),
|
|
2010
|
+
search.length < 2 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "text-text-muted mt-4 text-center text-xs", children: "Saisissez au moins 2 caract\xE8res pour rechercher." })
|
|
2011
|
+
] }),
|
|
2012
|
+
error && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "bg-danger-light text-danger mt-3 rounded-lg px-3 py-2 text-xs", children: error })
|
|
2013
|
+
] }),
|
|
2014
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "border-border flex shrink-0 items-center justify-end gap-2 border-t px-6 py-4", children: [
|
|
2015
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsx)("button", { type: "button", className: "text-text-muted hover:text-text text-sm transition-colors", onClick: onClose, disabled: loading, children: "Annuler" }),
|
|
2016
|
+
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("button", { type: "button", className: cn("flex items-center gap-1.5 rounded-lg px-4 py-2 text-sm font-medium text-white transition-colors disabled:opacity-50", selectedUser ? "bg-primary hover:bg-primary-hover" : "bg-border cursor-not-allowed"), onClick: handleCreate, disabled: !selectedUser || loading, children: [
|
|
2017
|
+
loading ? /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.Loader2, { size: 14, className: "animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_lucide_react9.MessageSquare, { size: 14 }),
|
|
2018
|
+
" D\xE9marrer"
|
|
2019
|
+
] })
|
|
2020
|
+
] })
|
|
2021
|
+
] });
|
|
2022
|
+
};
|
|
2023
|
+
|
|
2024
|
+
// src/components/ContextPanel.tsx
|
|
2025
|
+
var import_react9 = require("react");
|
|
2026
|
+
var import_lucide_react10 = require("lucide-react");
|
|
2027
|
+
var import_jsx_runtime13 = require("react/jsx-runtime");
|
|
2028
|
+
function toEditableEntities(ws) {
|
|
2029
|
+
return (ws.entitiesWithSecrets ?? []).map((e) => ({ key: e.key, value: e.value, isSecret: e.isSecret }));
|
|
2030
|
+
}
|
|
2031
|
+
function getDisplayEntities(ws) {
|
|
2032
|
+
return ws.entitiesWithSecrets ?? [];
|
|
2033
|
+
}
|
|
2034
|
+
var ContextPanel = ({
|
|
2035
|
+
workspace,
|
|
2036
|
+
types = [],
|
|
2037
|
+
onUpdateContext,
|
|
2038
|
+
readonly
|
|
2039
|
+
}) => {
|
|
2040
|
+
const [editing, setEditing] = (0, import_react9.useState)(false);
|
|
2041
|
+
const [editName, setEditName] = (0, import_react9.useState)(workspace.name ?? "");
|
|
2042
|
+
const [editType, setEditType] = (0, import_react9.useState)("");
|
|
2043
|
+
const [editScope, setEditScope] = (0, import_react9.useState)(workspace.scope ?? "");
|
|
2044
|
+
const [editEntities, setEditEntities] = (0, import_react9.useState)(toEditableEntities(workspace));
|
|
2045
|
+
const currentTypeId = typeof workspace.type === "object" && workspace.type !== null ? workspace.type._id : workspace.type;
|
|
2046
|
+
const typeName = typeof workspace.type === "object" && workspace.type !== null ? workspace.type.name : workspace.type;
|
|
2047
|
+
const handleEdit = () => {
|
|
2048
|
+
setEditName(workspace.name ?? "");
|
|
2049
|
+
setEditType(currentTypeId ?? "");
|
|
2050
|
+
setEditScope(workspace.scope ?? "");
|
|
2051
|
+
setEditEntities(toEditableEntities(workspace));
|
|
2052
|
+
setEditing(true);
|
|
2053
|
+
};
|
|
2054
|
+
const handleSave = () => {
|
|
2055
|
+
const entitiesWithSecrets = editEntities.filter((e) => e.key.trim()).map((e) => ({ key: e.key.trim(), value: e.value, isSecret: e.isSecret }));
|
|
2056
|
+
onUpdateContext?.({ name: editName.trim() || void 0, type: editType || void 0, scope: editScope, entitiesWithSecrets });
|
|
2057
|
+
setEditing(false);
|
|
2058
|
+
};
|
|
2059
|
+
const handleCancel = () => {
|
|
2060
|
+
setEditName(workspace.name ?? "");
|
|
2061
|
+
setEditType(currentTypeId ?? "");
|
|
2062
|
+
setEditScope(workspace.scope ?? "");
|
|
2063
|
+
setEditEntities(toEditableEntities(workspace));
|
|
2064
|
+
setEditing(false);
|
|
2065
|
+
};
|
|
2066
|
+
const handleTypeChange = (id) => {
|
|
2067
|
+
setEditType(id);
|
|
2068
|
+
const sel = types.find((t) => t._id === id);
|
|
2069
|
+
if (sel?.entitiesWithSecrets?.length) {
|
|
2070
|
+
setEditEntities(sel.entitiesWithSecrets.map((e) => ({ key: e.key, value: e.value, isSecret: e.isSecret })));
|
|
2071
|
+
}
|
|
2072
|
+
};
|
|
2073
|
+
const displayEntities = getDisplayEntities(workspace);
|
|
2074
|
+
return /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex flex-col gap-3", children: [
|
|
2075
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2076
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs font-semibold tracking-wider uppercase", children: "Contexte" }),
|
|
2077
|
+
!editing && !readonly && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { className: "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-primary,#3b82f6)] flex items-center gap-1 text-xs transition-colors", onClick: handleEdit, children: [
|
|
2078
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Edit2, { size: 11 }),
|
|
2079
|
+
" Modifier"
|
|
2080
|
+
] })
|
|
2081
|
+
] }),
|
|
2082
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "bg-[var(--color-bg,#f8fafc)] flex items-center gap-2 rounded-lg p-2", children: [
|
|
2083
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Nom :" }),
|
|
2084
|
+
editing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2085
|
+
"input",
|
|
2086
|
+
{
|
|
2087
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] focus:border-[var(--color-primary,#3b82f6)] w-48 rounded border px-2 py-1 text-xs focus:outline-none",
|
|
2088
|
+
value: editName,
|
|
2089
|
+
onChange: (e) => setEditName(e.target.value),
|
|
2090
|
+
placeholder: "Nom du workspace"
|
|
2091
|
+
}
|
|
2092
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text,#0f172a)] text-sm font-medium", children: workspace.name ?? "\u2014" })
|
|
2093
|
+
] }),
|
|
2094
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "bg-[var(--color-bg,#f8fafc)] flex items-center gap-2 rounded-lg p-2", children: [
|
|
2095
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Type :" }),
|
|
2096
|
+
editing && types.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2097
|
+
"select",
|
|
2098
|
+
{
|
|
2099
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] focus:border-[var(--color-primary,#3b82f6)] rounded border px-2 py-1 text-xs focus:outline-none",
|
|
2100
|
+
value: editType,
|
|
2101
|
+
onChange: (e) => handleTypeChange(e.target.value),
|
|
2102
|
+
children: [
|
|
2103
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: "", children: "\u2014 Aucun \u2014" }),
|
|
2104
|
+
types.map((t) => /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("option", { value: t._id, children: t.name }, t._id))
|
|
2105
|
+
]
|
|
2106
|
+
}
|
|
2107
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text,#0f172a)] text-sm font-medium", children: typeName ?? "\u2014" })
|
|
2108
|
+
] }),
|
|
2109
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "bg-[var(--color-bg,#f8fafc)] flex items-center gap-2 rounded-lg p-2", children: [
|
|
2110
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Scope :" }),
|
|
2111
|
+
editing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2112
|
+
"input",
|
|
2113
|
+
{
|
|
2114
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] focus:border-[var(--color-primary,#3b82f6)] w-36 rounded border px-2 py-1 text-xs focus:outline-none",
|
|
2115
|
+
value: editScope,
|
|
2116
|
+
onChange: (e) => setEditScope(e.target.value),
|
|
2117
|
+
placeholder: "Saisir..."
|
|
2118
|
+
}
|
|
2119
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text,#0f172a)] text-sm font-medium", children: workspace.scope ?? "\u2014" })
|
|
2120
|
+
] }),
|
|
2121
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { children: [
|
|
2122
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Entit\xE9s" }),
|
|
2123
|
+
editing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-1 flex flex-col gap-1", children: [
|
|
2124
|
+
editEntities.map((e, idx) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex items-center gap-1", children: [
|
|
2125
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2126
|
+
"input",
|
|
2127
|
+
{
|
|
2128
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] focus:border-[var(--color-primary,#3b82f6)] w-20 rounded border px-2 py-1 text-xs focus:outline-none",
|
|
2129
|
+
value: e.key,
|
|
2130
|
+
onChange: (ev) => {
|
|
2131
|
+
const arr = [...editEntities];
|
|
2132
|
+
arr[idx].key = ev.target.value;
|
|
2133
|
+
setEditEntities(arr);
|
|
2134
|
+
},
|
|
2135
|
+
placeholder: "Cl\xE9"
|
|
2136
|
+
}
|
|
2137
|
+
),
|
|
2138
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2139
|
+
"input",
|
|
2140
|
+
{
|
|
2141
|
+
className: "border-[var(--color-border,#e2e8f0)] bg-[var(--color-surface,#fff)] text-[var(--color-text,#0f172a)] focus:border-[var(--color-primary,#3b82f6)] flex-1 rounded border px-2 py-1 text-xs focus:outline-none",
|
|
2142
|
+
value: e.value,
|
|
2143
|
+
onChange: (ev) => {
|
|
2144
|
+
const arr = [...editEntities];
|
|
2145
|
+
arr[idx].value = ev.target.value;
|
|
2146
|
+
setEditEntities(arr);
|
|
2147
|
+
},
|
|
2148
|
+
placeholder: "Valeur"
|
|
2149
|
+
}
|
|
2150
|
+
),
|
|
2151
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2152
|
+
"button",
|
|
2153
|
+
{
|
|
2154
|
+
type: "button",
|
|
2155
|
+
className: `rounded p-0.5 transition-colors ${e.isSecret ? "bg-amber-100 text-amber-600 dark:bg-amber-500/10 dark:text-amber-400" : "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] hover:bg-[var(--color-bg,#f8fafc)]"}`,
|
|
2156
|
+
onClick: () => {
|
|
2157
|
+
const arr = [...editEntities];
|
|
2158
|
+
arr[idx].isSecret = !arr[idx].isSecret;
|
|
2159
|
+
setEditEntities(arr);
|
|
2160
|
+
},
|
|
2161
|
+
title: e.isSecret ? "Secret (cliquer pour rendre public)" : "Public (cliquer pour rendre secret)",
|
|
2162
|
+
children: e.isSecret ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Lock, { size: 12 }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Unlock, { size: 12 })
|
|
2163
|
+
}
|
|
2164
|
+
),
|
|
2165
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
|
|
2166
|
+
"button",
|
|
2167
|
+
{
|
|
2168
|
+
className: "text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10 rounded p-0.5 transition-colors",
|
|
2169
|
+
onClick: () => setEditEntities(editEntities.filter((_, i) => i !== idx)),
|
|
2170
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Trash2, { size: 12 })
|
|
2171
|
+
}
|
|
2172
|
+
)
|
|
2173
|
+
] }, idx)),
|
|
2174
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(
|
|
2175
|
+
"button",
|
|
2176
|
+
{
|
|
2177
|
+
className: "text-[var(--color-primary,#3b82f6)] hover:bg-[var(--color-primary-light,#eff6ff)] mt-1 flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors",
|
|
2178
|
+
onClick: () => setEditEntities([...editEntities, { key: "", value: "", isSecret: false }]),
|
|
2179
|
+
children: [
|
|
2180
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Plus, { size: 12 }),
|
|
2181
|
+
" Ajouter"
|
|
2182
|
+
]
|
|
2183
|
+
}
|
|
2184
|
+
)
|
|
2185
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "mt-1 flex flex-wrap gap-1", children: [
|
|
2186
|
+
displayEntities.map((ent) => /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "bg-[var(--color-bg,#f8fafc)] border-[var(--color-border,#e2e8f0)] inline-flex items-center gap-1 rounded border px-2 py-0.5 text-xs", children: [
|
|
2187
|
+
ent.isSecret && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Lock, { size: 10, className: "text-amber-500 shrink-0" }),
|
|
2188
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("span", { className: "text-[var(--color-text-muted,#94a3b8)] mr-1 font-medium", children: [
|
|
2189
|
+
ent.key,
|
|
2190
|
+
":"
|
|
2191
|
+
] }),
|
|
2192
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text,#0f172a)]", children: ent.isSecret ? "\u2022\u2022\u2022\u2022\u2022\u2022" : ent.value })
|
|
2193
|
+
] }, ent.key)),
|
|
2194
|
+
displayEntities.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime13.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Aucune entit\xE9" })
|
|
2195
|
+
] })
|
|
2196
|
+
] }),
|
|
2197
|
+
editing && /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "flex justify-end gap-2", children: [
|
|
2198
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { className: "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] flex items-center gap-1 text-xs transition-colors", onClick: handleCancel, children: [
|
|
2199
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.X, { size: 11 }),
|
|
2200
|
+
" Annuler"
|
|
2201
|
+
] }),
|
|
2202
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("button", { className: "text-emerald-600 hover:bg-emerald-50 dark:hover:bg-emerald-500/10 flex items-center gap-1 rounded px-2 py-1 text-xs font-medium transition-colors", onClick: handleSave, children: [
|
|
2203
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Check, { size: 11 }),
|
|
2204
|
+
" Enregistrer"
|
|
2205
|
+
] })
|
|
2206
|
+
] }),
|
|
2207
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsxs)("div", { className: "text-[var(--color-text-muted,#94a3b8)] bg-[var(--color-bg,#f8fafc)] mt-2 flex items-center gap-2 rounded-lg p-2 text-xs italic", children: [
|
|
2208
|
+
/* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react10.Brain, { size: 14 }),
|
|
2209
|
+
" Ce contexte est inject\xE9 dans les requ\xEAtes IA (RAG)"
|
|
2210
|
+
] })
|
|
2211
|
+
] });
|
|
2212
|
+
};
|
|
2213
|
+
|
|
2214
|
+
// src/components/MembersPanel.tsx
|
|
2215
|
+
var import_react10 = require("react");
|
|
2216
|
+
var import_lucide_react11 = require("lucide-react");
|
|
2217
|
+
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
2218
|
+
var roleIcon = (role) => {
|
|
2219
|
+
switch (role) {
|
|
2220
|
+
case "owner":
|
|
2221
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Crown, { size: 11, className: "text-warning" });
|
|
2222
|
+
case "viewer":
|
|
2223
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Eye, { size: 11, className: "text-text-muted" });
|
|
2224
|
+
default:
|
|
2225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Users, { size: 11, className: "text-primary" });
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
var MembersPanel = ({
|
|
2229
|
+
members,
|
|
2230
|
+
avatarUsers,
|
|
2231
|
+
onAddMember,
|
|
2232
|
+
onRemoveMember,
|
|
2233
|
+
onUpdateRole,
|
|
2234
|
+
readonly,
|
|
2235
|
+
hideAgents = false
|
|
2236
|
+
}) => {
|
|
2237
|
+
const { config } = useWorkspaceContext();
|
|
2238
|
+
const apis = useWorkspaceApi();
|
|
2239
|
+
const [showUserSearch, setShowUserSearch] = (0, import_react10.useState)(false);
|
|
2240
|
+
const [search, setSearch] = (0, import_react10.useState)("");
|
|
2241
|
+
const [searchResults, setSearchResults] = (0, import_react10.useState)([]);
|
|
2242
|
+
const [searchingUsers, setSearchingUsers] = (0, import_react10.useState)(false);
|
|
2243
|
+
const [allAgents, setAllAgents] = (0, import_react10.useState)([]);
|
|
2244
|
+
(0, import_react10.useEffect)(() => {
|
|
2245
|
+
apis.agentApi.list().then(setAllAgents).catch(() => setAllAgents([]));
|
|
2246
|
+
}, []);
|
|
2247
|
+
(0, import_react10.useEffect)(() => {
|
|
2248
|
+
if (search.length < 2 || !showUserSearch || !config.searchUsers) {
|
|
2249
|
+
setSearchResults([]);
|
|
2250
|
+
return;
|
|
2251
|
+
}
|
|
2252
|
+
let cancelled = false;
|
|
2253
|
+
setSearchingUsers(true);
|
|
2254
|
+
config.searchUsers(search).then((results) => {
|
|
2255
|
+
if (!cancelled) setSearchResults(results);
|
|
2256
|
+
}).catch(() => {
|
|
2257
|
+
if (!cancelled) setSearchResults([]);
|
|
2258
|
+
}).finally(() => {
|
|
2259
|
+
if (!cancelled) setSearchingUsers(false);
|
|
2260
|
+
});
|
|
2261
|
+
return () => {
|
|
2262
|
+
cancelled = true;
|
|
2263
|
+
};
|
|
2264
|
+
}, [search, showUserSearch, config.searchUsers]);
|
|
2265
|
+
const filteredUsers = (0, import_react10.useMemo)(() => {
|
|
2266
|
+
return searchResults.filter(
|
|
2267
|
+
(u) => u.email !== config.currentUserId && !members.some((m) => m.memberId === u.email)
|
|
2268
|
+
);
|
|
2269
|
+
}, [searchResults, members, config.currentUserId]);
|
|
2270
|
+
const userMembers = members.filter((m) => m.memberType === "user");
|
|
2271
|
+
const agentMembers = members.filter((m) => m.memberType === "agent");
|
|
2272
|
+
const agentMemberIds = (0, import_react10.useMemo)(() => new Set(agentMembers.map((m) => m.memberId)), [agentMembers]);
|
|
2273
|
+
const agentMemberIdMap = (0, import_react10.useMemo)(() => new Map(agentMembers.map((m) => [m.memberId, m._id])), [agentMembers]);
|
|
2274
|
+
const handleAddUser = (user) => {
|
|
2275
|
+
onAddMember?.({ memberId: user.email, memberName: user.displayName, memberType: "user", role: "participant" });
|
|
2276
|
+
setSearch("");
|
|
2277
|
+
setShowUserSearch(false);
|
|
2278
|
+
};
|
|
2279
|
+
const handleAddAgent = (agent) => {
|
|
2280
|
+
onAddMember?.({ memberId: agent.agentId, memberName: agent.agentName, memberType: "agent", role: "participant" });
|
|
2281
|
+
};
|
|
2282
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
2283
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2284
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2285
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-text-muted flex items-center gap-1.5 text-xs font-semibold tracking-wider uppercase", children: [
|
|
2286
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Users, { size: 14, className: "text-primary" }),
|
|
2287
|
+
" Utilisateurs"
|
|
2288
|
+
] }),
|
|
2289
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "flex items-center gap-1", children: [
|
|
2290
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "bg-primary-light text-primary rounded-full px-2 py-0.5 text-[10px] font-bold", children: userMembers.length }),
|
|
2291
|
+
!readonly && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("button", { className: "text-primary hover:bg-primary-light flex items-center gap-1 rounded px-2 py-1 text-xs transition-colors", onClick: () => setShowUserSearch(!showUserSearch), children: [
|
|
2292
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.UserPlus, { size: 12 }),
|
|
2293
|
+
" Inviter"
|
|
2294
|
+
] })
|
|
2295
|
+
] })
|
|
2296
|
+
] }),
|
|
2297
|
+
showUserSearch && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
|
|
2298
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "relative mt-2", children: [
|
|
2299
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Search, { size: 12, className: "text-text-muted absolute top-1/2 left-2 -translate-y-1/2" }),
|
|
2300
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("input", { className: "border-border bg-surface text-text focus:border-primary w-full rounded-lg border py-1.5 pr-2 pl-7 text-xs focus:outline-none", placeholder: "Rechercher par nom ou email...", value: search, onChange: (e) => setSearch(e.target.value), autoFocus: true })
|
|
2301
|
+
] }),
|
|
2302
|
+
search.length >= 2 && searchingUsers && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Loader2, { size: 14, className: "text-primary animate-spin" }) }),
|
|
2303
|
+
filteredUsers.length > 0 && !searchingUsers && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "bg-surface border-border mt-1 flex flex-col rounded-lg border shadow-sm", children: filteredUsers.map((u) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "hover:bg-primary-light flex cursor-pointer items-center gap-2 px-3 py-2 text-xs transition-colors first:rounded-t-lg last:rounded-b-lg", onClick: () => handleAddUser(u), children: [
|
|
2304
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "bg-primary-light text-primary flex h-6 w-6 items-center justify-center rounded-full text-[10px] font-semibold", children: getInitials(u.displayName) }),
|
|
2305
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "text-text", children: u.displayName }),
|
|
2306
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Plus, { size: 12, className: "text-primary ml-auto" })
|
|
2307
|
+
] }, u.id)) })
|
|
2308
|
+
] }),
|
|
2309
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "mt-2 flex flex-col gap-1", children: userMembers.map((m) => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "bg-bg group flex items-center gap-2 rounded-lg p-2", children: [
|
|
2310
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(MemberAvatar, { memberName: m.memberName, memberType: m.memberType, avatarUrl: avatarUsers?.find((u) => u.email.toLowerCase() === m.memberId.toLowerCase())?.avatarUrl, size: "sm", variant: m.role === "owner" ? "owner" : "primary" }),
|
|
2311
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
2312
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "text-text truncate text-xs font-medium", children: m.memberName }),
|
|
2313
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "text-text-muted flex items-center gap-1 text-[10px]", children: [
|
|
2314
|
+
roleIcon(m.role),
|
|
2315
|
+
" ",
|
|
2316
|
+
ROLE_LABELS[m.role] ?? m.role
|
|
2317
|
+
] })
|
|
2318
|
+
] }),
|
|
2319
|
+
!(readonly || m.role === "owner" && members.filter((x) => x.role === "owner").length === 1) && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("select", { className: "border-border bg-surface text-text-muted rounded border px-1 py-0.5 text-[10px] opacity-0 transition-opacity group-hover:opacity-100 focus:opacity-100", value: m.role, onChange: (e) => {
|
|
2320
|
+
e.stopPropagation();
|
|
2321
|
+
onUpdateRole?.(m._id, e.target.value);
|
|
2322
|
+
}, disabled: readonly || m.role === "owner" && members.filter((x) => x.role === "owner").length === 1, children: [
|
|
2323
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "owner", children: "Propri\xE9taire" }),
|
|
2324
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "participant", children: "Participant" }),
|
|
2325
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("option", { value: "viewer", children: "Observateur" })
|
|
2326
|
+
] }),
|
|
2327
|
+
m.role !== "owner" && !readonly && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { className: "text-text-muted hover:text-danger rounded p-0.5 opacity-0 transition-all group-hover:opacity-100", onClick: (e) => {
|
|
2328
|
+
e.stopPropagation();
|
|
2329
|
+
onRemoveMember?.(m._id);
|
|
2330
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.X, { size: 14 }) })
|
|
2331
|
+
] }, m._id)) })
|
|
2332
|
+
] }),
|
|
2333
|
+
!hideAgents && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { children: [
|
|
2334
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2335
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "text-text-muted flex items-center gap-1.5 text-xs font-semibold tracking-wider uppercase", children: [
|
|
2336
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Brain, { size: 14, className: "text-primary" }),
|
|
2337
|
+
" Agents IA"
|
|
2338
|
+
] }),
|
|
2339
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("span", { className: "bg-primary-light text-primary rounded-full px-2 py-0.5 text-[10px] font-bold", children: [
|
|
2340
|
+
agentMembers.length,
|
|
2341
|
+
"/",
|
|
2342
|
+
allAgents.length
|
|
2343
|
+
] })
|
|
2344
|
+
] }),
|
|
2345
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "mt-2 flex flex-col gap-1", children: allAgents.map((agent) => {
|
|
2346
|
+
const isMember = agentMemberIds.has(agent.agentId);
|
|
2347
|
+
const memberId = agentMemberIdMap.get(agent.agentId);
|
|
2348
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "bg-bg group flex items-center gap-2 rounded-lg p-2", children: [
|
|
2349
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "bg-surface border-border flex h-7 w-7 items-center justify-center rounded-full border", children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Brain, { size: 14, className: "text-primary" }) }),
|
|
2350
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
2351
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "text-text truncate text-xs font-medium", children: agent.agentName }),
|
|
2352
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("div", { className: "text-text-muted text-[10px]", children: "Agent IA" })
|
|
2353
|
+
] }),
|
|
2354
|
+
!readonly && (isMember ? /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("button", { className: "relative flex h-6 w-6 items-center justify-center rounded-full transition-colors", onClick: (e) => {
|
|
2355
|
+
e.stopPropagation();
|
|
2356
|
+
if (memberId) onRemoveMember?.(memberId);
|
|
2357
|
+
}, children: [
|
|
2358
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Check, { size: 14, className: "text-success absolute transition-opacity group-hover:opacity-0" }),
|
|
2359
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.X, { size: 14, className: "text-danger absolute opacity-0 transition-opacity group-hover:opacity-100" })
|
|
2360
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("button", { className: "text-text-muted hover:text-primary hover:bg-primary-light flex h-6 w-6 items-center justify-center rounded-full transition-colors", onClick: (e) => {
|
|
2361
|
+
e.stopPropagation();
|
|
2362
|
+
handleAddAgent(agent);
|
|
2363
|
+
}, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(import_lucide_react11.Plus, { size: 14 }) }))
|
|
2364
|
+
] }, agent.agentId);
|
|
2365
|
+
}) })
|
|
2366
|
+
] })
|
|
2367
|
+
] });
|
|
2368
|
+
};
|
|
2369
|
+
|
|
2370
|
+
// src/components/SyncPanel.tsx
|
|
2371
|
+
var import_react11 = require("react");
|
|
2372
|
+
var import_lucide_react12 = require("lucide-react");
|
|
2373
|
+
var import_jsx_runtime15 = require("react/jsx-runtime");
|
|
2374
|
+
var SLACK_COLOR = "#4A154B";
|
|
2375
|
+
var SYNC_INFO = {
|
|
2376
|
+
title: "Comment fonctionne la synchronisation ?",
|
|
2377
|
+
description: "La synchronisation bidirectionnelle relie ce workspace \xE0 un canal externe (Slack, Teams\u2026). Les messages, fichiers et membres sont automatiquement r\xE9pliqu\xE9s dans les deux sens.",
|
|
2378
|
+
features: [
|
|
2379
|
+
"Messages envoy\xE9s depuis le workspace sont r\xE9pliqu\xE9s vers Slack",
|
|
2380
|
+
"Messages envoy\xE9s depuis Slack sont r\xE9pliqu\xE9s vers le workspace",
|
|
2381
|
+
"Fichiers et artefacts sont synchronis\xE9s dans les deux sens",
|
|
2382
|
+
"Les nouveaux membres ajout\xE9s au workspace sont invit\xE9s dans Slack",
|
|
2383
|
+
"Les mentions Slack sont converties en mentions workspace",
|
|
2384
|
+
"D\xE9sactiver la synchronisation conserve le canal Slack ouvert (pas d'archivage)"
|
|
2385
|
+
],
|
|
2386
|
+
limitations: [
|
|
2387
|
+
"Les messages initialement post\xE9s par un utilisateur r\xE9el dans Slack ne peuvent pas \xEAtre modifi\xE9s/supprim\xE9s depuis le workspace (limitation API Slack)",
|
|
2388
|
+
"Le bot Slack doit avoir les scopes n\xE9cessaires (channels:*, chat:write, files:read, users:read.email)",
|
|
2389
|
+
"Le listener Socket Mode doit tourner sur une seule instance en production",
|
|
2390
|
+
"Seuls les utilisateurs ayant le m\xEAme email dans Slack et le workspace sont synchronis\xE9s"
|
|
2391
|
+
]
|
|
2392
|
+
};
|
|
2393
|
+
var SyncPanel = ({
|
|
2394
|
+
workspaceId,
|
|
2395
|
+
syncWithSlack,
|
|
2396
|
+
isOwner,
|
|
2397
|
+
slackConfigured = true,
|
|
2398
|
+
onToggleSync
|
|
2399
|
+
}) => {
|
|
2400
|
+
const [syncStatus, setSyncStatus] = (0, import_react11.useState)(null);
|
|
2401
|
+
const [loading, setLoading] = (0, import_react11.useState)(true);
|
|
2402
|
+
const [toggling, setToggling] = (0, import_react11.useState)(false);
|
|
2403
|
+
const [infoOpen, setInfoOpen] = (0, import_react11.useState)(false);
|
|
2404
|
+
const apis = useWorkspaceApi();
|
|
2405
|
+
const fetchSyncStatus = (0, import_react11.useCallback)(async () => {
|
|
2406
|
+
try {
|
|
2407
|
+
const status = await apis.syncApi.getStatus(workspaceId);
|
|
2408
|
+
setSyncStatus(status);
|
|
2409
|
+
} catch (err) {
|
|
2410
|
+
console.error("Failed to fetch sync status:", err);
|
|
2411
|
+
} finally {
|
|
2412
|
+
setLoading(false);
|
|
2413
|
+
}
|
|
2414
|
+
}, [workspaceId]);
|
|
2415
|
+
(0, import_react11.useEffect)(() => {
|
|
2416
|
+
fetchSyncStatus();
|
|
2417
|
+
}, [fetchSyncStatus]);
|
|
2418
|
+
(0, import_react11.useEffect)(() => {
|
|
2419
|
+
if (syncStatus) {
|
|
2420
|
+
setSyncStatus(
|
|
2421
|
+
(prev) => prev ? { ...prev, syncWithSlack: syncWithSlack ?? false } : prev
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
}, [syncWithSlack]);
|
|
2425
|
+
const handleToggle = async (provider, enabled) => {
|
|
2426
|
+
setToggling(true);
|
|
2427
|
+
try {
|
|
2428
|
+
await onToggleSync?.(provider, enabled);
|
|
2429
|
+
await fetchSyncStatus();
|
|
2430
|
+
} catch (err) {
|
|
2431
|
+
console.error("Failed to toggle sync:", err);
|
|
2432
|
+
} finally {
|
|
2433
|
+
setToggling(false);
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
const slackLink = syncStatus?.links?.find((l) => l.provider === "slack");
|
|
2437
|
+
const isSlackActive = syncStatus?.syncWithSlack ?? syncWithSlack ?? false;
|
|
2438
|
+
if (loading) {
|
|
2439
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.Loader2, { size: 20, className: "text-primary animate-spin" }) });
|
|
2440
|
+
}
|
|
2441
|
+
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
2442
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2443
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text-muted text-xs font-semibold tracking-wider uppercase", children: "Synchronisation" }),
|
|
2444
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2445
|
+
"button",
|
|
2446
|
+
{
|
|
2447
|
+
className: "text-text-muted hover:text-primary flex items-center gap-1 text-xs transition-colors",
|
|
2448
|
+
onClick: () => fetchSyncStatus(),
|
|
2449
|
+
title: "Rafra\xEEchir",
|
|
2450
|
+
children: [
|
|
2451
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.RefreshCw, { size: 11 }),
|
|
2452
|
+
" Rafra\xEEchir"
|
|
2453
|
+
]
|
|
2454
|
+
}
|
|
2455
|
+
)
|
|
2456
|
+
] }),
|
|
2457
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: cn("bg-bg border-border rounded-lg border", !slackConfigured && "opacity-60"), children: [
|
|
2458
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between p-3", children: [
|
|
2459
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2.5", children: [
|
|
2460
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2461
|
+
"div",
|
|
2462
|
+
{
|
|
2463
|
+
className: "flex h-8 w-8 items-center justify-center rounded-lg",
|
|
2464
|
+
style: { backgroundColor: SLACK_COLOR },
|
|
2465
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.Hash, { size: 16, className: "text-white" })
|
|
2466
|
+
}
|
|
2467
|
+
),
|
|
2468
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
2469
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-text flex items-center gap-1.5 text-sm font-medium", children: [
|
|
2470
|
+
"Slack",
|
|
2471
|
+
!slackConfigured ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.XCircle, { size: 14, className: "text-text-muted" }) : isSlackActive ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.CheckCircle2, { size: 14, className: "text-success" }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.XCircle, { size: 14, className: "text-text-muted" })
|
|
2472
|
+
] }),
|
|
2473
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text-muted text-[10px]", children: !slackConfigured ? "Non configur\xE9" : isSlackActive ? "Synchronisation active" : "Non synchronis\xE9" })
|
|
2474
|
+
] })
|
|
2475
|
+
] }),
|
|
2476
|
+
slackConfigured && isOwner && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2477
|
+
"button",
|
|
2478
|
+
{
|
|
2479
|
+
className: `rounded-lg px-3 py-1.5 text-xs font-medium transition-colors ${isSlackActive ? "bg-danger-light text-danger hover:bg-danger/20" : "bg-success-light text-success hover:bg-success/20"}`,
|
|
2480
|
+
onClick: () => handleToggle("slack", !isSlackActive),
|
|
2481
|
+
disabled: toggling,
|
|
2482
|
+
children: toggling ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.Loader2, { size: 12, className: "animate-spin" }) : isSlackActive ? "D\xE9sactiver" : "Activer"
|
|
2483
|
+
}
|
|
2484
|
+
),
|
|
2485
|
+
!slackConfigured && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "bg-bg border-border rounded border px-2 py-1 text-[10px] font-medium text-text-muted", children: "Non disponible" })
|
|
2486
|
+
] }),
|
|
2487
|
+
!slackConfigured && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-border border-t px-3 py-2.5", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-text-muted flex items-center gap-2 text-xs", children: [
|
|
2488
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.AlertTriangle, { size: 14, className: "flex-shrink-0 text-warning" }),
|
|
2489
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "La synchronisation Slack n'est pas configur\xE9e sur le serveur. Contactez un administrateur pour l'activer." })
|
|
2490
|
+
] }) }),
|
|
2491
|
+
slackConfigured && slackLink && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-border border-t px-3 py-2.5", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex flex-col gap-1.5", children: [
|
|
2492
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between", children: [
|
|
2493
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text-muted text-[10px] font-medium uppercase tracking-wider", children: "Canal externe" }),
|
|
2494
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.ExternalLink, { size: 10, className: "text-text-muted" })
|
|
2495
|
+
] }),
|
|
2496
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "bg-surface border-border rounded border p-2", children: [
|
|
2497
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2 text-xs", children: [
|
|
2498
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.Hash, { size: 12, className: "text-text-muted flex-shrink-0" }),
|
|
2499
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text font-medium", children: slackLink.externalName || slackLink.externalId })
|
|
2500
|
+
] }),
|
|
2501
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-text-muted mt-1 flex items-center gap-2 text-[10px]", children: [
|
|
2502
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
2503
|
+
"ID: ",
|
|
2504
|
+
slackLink.externalId
|
|
2505
|
+
] }),
|
|
2506
|
+
slackLink.externalTeamId && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_jsx_runtime15.Fragment, { children: [
|
|
2507
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "\u2022" }),
|
|
2508
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
2509
|
+
"Team: ",
|
|
2510
|
+
slackLink.externalTeamId
|
|
2511
|
+
] })
|
|
2512
|
+
] })
|
|
2513
|
+
] }),
|
|
2514
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-text-muted mt-1 flex items-center gap-2 text-[10px]", children: [
|
|
2515
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2516
|
+
"span",
|
|
2517
|
+
{
|
|
2518
|
+
className: `inline-flex items-center gap-1 rounded-full px-1.5 py-0.5 text-[9px] font-medium ${isSlackActive ? "bg-success-light text-success" : "bg-warning-light text-warning"}`,
|
|
2519
|
+
children: isSlackActive ? "Sync active" : "Sync en pause"
|
|
2520
|
+
}
|
|
2521
|
+
),
|
|
2522
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("span", { children: [
|
|
2523
|
+
"Cr\xE9\xE9 le",
|
|
2524
|
+
" ",
|
|
2525
|
+
new Date(slackLink.createdAt).toLocaleDateString("fr-FR")
|
|
2526
|
+
] })
|
|
2527
|
+
] }),
|
|
2528
|
+
!isSlackActive && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "text-text-muted mt-1.5 text-[10px] italic", children: "Le canal Slack reste ouvert \u2014 seule la synchronisation est en pause." })
|
|
2529
|
+
] })
|
|
2530
|
+
] }) }),
|
|
2531
|
+
slackConfigured && isSlackActive && !slackLink && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "border-border border-t px-3 py-2.5", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-warning bg-warning-light flex items-center gap-2 rounded-lg p-2 text-xs", children: [
|
|
2532
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.AlertTriangle, { size: 14, className: "flex-shrink-0" }),
|
|
2533
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { children: "La synchronisation est activ\xE9e mais aucun canal Slack n'est encore li\xE9. Le canal sera cr\xE9\xE9 automatiquement." })
|
|
2534
|
+
] }) })
|
|
2535
|
+
] }),
|
|
2536
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "bg-bg border-border rounded-lg border opacity-50", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center justify-between p-3", children: [
|
|
2537
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "flex items-center gap-2.5", children: [
|
|
2538
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("div", { className: "bg-info flex h-8 w-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-xs font-bold text-white", children: "T" }) }),
|
|
2539
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
2540
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "text-text flex items-center gap-1.5 text-sm font-medium", children: [
|
|
2541
|
+
"Microsoft Teams",
|
|
2542
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.XCircle, { size: 14, className: "text-text-muted" })
|
|
2543
|
+
] }),
|
|
2544
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text-muted text-[10px]", children: "Bient\xF4t disponible" })
|
|
2545
|
+
] })
|
|
2546
|
+
] }),
|
|
2547
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "bg-bg border-border rounded border px-2 py-1 text-[10px] font-medium text-text-muted", children: "Prochainement" })
|
|
2548
|
+
] }) }),
|
|
2549
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "bg-bg border-border rounded-lg border", children: [
|
|
2550
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2551
|
+
"button",
|
|
2552
|
+
{
|
|
2553
|
+
className: "flex w-full items-center gap-2 p-3 text-left",
|
|
2554
|
+
onClick: () => setInfoOpen(!infoOpen),
|
|
2555
|
+
children: [
|
|
2556
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react12.Info, { size: 14, className: "text-info flex-shrink-0" }),
|
|
2557
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text text-xs font-medium", children: SYNC_INFO.title }),
|
|
2558
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2559
|
+
"span",
|
|
2560
|
+
{
|
|
2561
|
+
className: `text-text-muted ml-auto text-[10px] transition-transform ${infoOpen ? "rotate-180" : ""}`,
|
|
2562
|
+
children: "\u25BE"
|
|
2563
|
+
}
|
|
2564
|
+
)
|
|
2565
|
+
]
|
|
2566
|
+
}
|
|
2567
|
+
),
|
|
2568
|
+
infoOpen && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "border-border border-t px-3 pb-3 pt-2", children: [
|
|
2569
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("p", { className: "text-text-muted mb-3 text-xs leading-relaxed", children: SYNC_INFO.description }),
|
|
2570
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { className: "mb-3", children: [
|
|
2571
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text mb-1.5 block text-[10px] font-semibold uppercase tracking-wider", children: "Fonctionnalit\xE9s" }),
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ul", { className: "flex flex-col gap-1", children: SYNC_INFO.features.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2573
|
+
"li",
|
|
2574
|
+
{
|
|
2575
|
+
className: "text-text-muted flex items-start gap-1.5 text-xs",
|
|
2576
|
+
children: [
|
|
2577
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2578
|
+
import_lucide_react12.CheckCircle2,
|
|
2579
|
+
{
|
|
2580
|
+
size: 12,
|
|
2581
|
+
className: "text-success mt-0.5 flex-shrink-0"
|
|
2582
|
+
}
|
|
2583
|
+
),
|
|
2584
|
+
f
|
|
2585
|
+
]
|
|
2586
|
+
},
|
|
2587
|
+
i
|
|
2588
|
+
)) })
|
|
2589
|
+
] }),
|
|
2590
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)("div", { children: [
|
|
2591
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("span", { className: "text-text mb-1.5 block text-[10px] font-semibold uppercase tracking-wider", children: "Limitations connues" }),
|
|
2592
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)("ul", { className: "flex flex-col gap-1", children: SYNC_INFO.limitations.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
2593
|
+
"li",
|
|
2594
|
+
{
|
|
2595
|
+
className: "text-text-muted flex items-start gap-1.5 text-xs",
|
|
2596
|
+
children: [
|
|
2597
|
+
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
|
|
2598
|
+
import_lucide_react12.AlertTriangle,
|
|
2599
|
+
{
|
|
2600
|
+
size: 12,
|
|
2601
|
+
className: "text-warning mt-0.5 flex-shrink-0"
|
|
2602
|
+
}
|
|
2603
|
+
),
|
|
2604
|
+
l
|
|
2605
|
+
]
|
|
2606
|
+
},
|
|
2607
|
+
i
|
|
2608
|
+
)) })
|
|
2609
|
+
] })
|
|
2610
|
+
] })
|
|
2611
|
+
] })
|
|
2612
|
+
] });
|
|
2613
|
+
};
|
|
2614
|
+
|
|
2615
|
+
// src/components/MessageFeed.tsx
|
|
2616
|
+
var import_react17 = require("react");
|
|
2617
|
+
var import_lucide_react15 = require("lucide-react");
|
|
2618
|
+
var import_date_fns = require("date-fns");
|
|
2619
|
+
var import_locale = require("date-fns/locale");
|
|
2620
|
+
|
|
2621
|
+
// src/components/MessageItem.tsx
|
|
2622
|
+
var import_react16 = __toESM(require("react"));
|
|
2623
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2624
|
+
|
|
2625
|
+
// src/components/ArtefactViewerDialog.tsx
|
|
2626
|
+
var import_react12 = require("react");
|
|
2627
|
+
var import_lucide_react13 = require("lucide-react");
|
|
2628
|
+
var import_streamdown = require("streamdown");
|
|
2629
|
+
var import_styles = require("streamdown/styles.css");
|
|
2630
|
+
var import_jsx_runtime16 = require("react/jsx-runtime");
|
|
2631
|
+
function ArtefactViewerDialog({ artefact, onClose }) {
|
|
2632
|
+
const { artefactApi } = useWorkspaceApi();
|
|
2633
|
+
const [url, setUrl] = (0, import_react12.useState)(null);
|
|
2634
|
+
const [textContent, setTextContent] = (0, import_react12.useState)(null);
|
|
2635
|
+
const [htmlContent, setHtmlContent] = (0, import_react12.useState)(null);
|
|
2636
|
+
const [loading, setLoading] = (0, import_react12.useState)(true);
|
|
2637
|
+
const mimetype = artefact.file?.mimetype ?? "";
|
|
2638
|
+
const fileName = fixMojibake(artefact.file?.originalName ?? "");
|
|
2639
|
+
const isImage = mimetype.startsWith("image/");
|
|
2640
|
+
const isMarkdown = fileName.endsWith(".md") || mimetype === "text/markdown";
|
|
2641
|
+
const isJson = fileName.endsWith(".json") || mimetype === "application/json";
|
|
2642
|
+
const isDocx = fileName.endsWith(".docx") || mimetype === "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
2643
|
+
const isText = !isMarkdown && !isJson && !isDocx && (mimetype === "text/plain" || mimetype.startsWith("text/"));
|
|
2644
|
+
(0, import_react12.useEffect)(() => {
|
|
2645
|
+
if (!artefact.file?._id) {
|
|
2646
|
+
setLoading(false);
|
|
2647
|
+
return;
|
|
2648
|
+
}
|
|
2649
|
+
let cancelled = false;
|
|
2650
|
+
setLoading(true);
|
|
2651
|
+
(async () => {
|
|
2652
|
+
try {
|
|
2653
|
+
if (isImage) {
|
|
2654
|
+
const { url: signedUrl } = await artefactApi.getSignedUrl(artefact.file._id);
|
|
2655
|
+
if (!cancelled) setUrl(signedUrl);
|
|
2656
|
+
} else if (isText || isMarkdown || isJson) {
|
|
2657
|
+
const blob = await artefactApi.getDownloadBlob(artefact.file._id);
|
|
2658
|
+
const text = await blob.text();
|
|
2659
|
+
if (!cancelled) setTextContent(text);
|
|
2660
|
+
} else if (isDocx) {
|
|
2661
|
+
const blob = await artefactApi.getDownloadBlob(artefact.file._id);
|
|
2662
|
+
const arrayBuffer = await blob.arrayBuffer();
|
|
2663
|
+
const mammoth = await import("mammoth");
|
|
2664
|
+
const result = await mammoth.convertToHtml({ arrayBuffer });
|
|
2665
|
+
if (!cancelled) setHtmlContent(result.value);
|
|
2666
|
+
}
|
|
2667
|
+
} catch (err) {
|
|
2668
|
+
console.error("Failed to load artefact preview:", err);
|
|
2669
|
+
} finally {
|
|
2670
|
+
if (!cancelled) setLoading(false);
|
|
2671
|
+
}
|
|
2672
|
+
})();
|
|
2673
|
+
return () => {
|
|
2674
|
+
cancelled = true;
|
|
2675
|
+
};
|
|
2676
|
+
}, [artefact.file?._id, isImage, isText, isMarkdown, isJson, isDocx, artefactApi]);
|
|
2677
|
+
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2678
|
+
Modal,
|
|
2679
|
+
{
|
|
2680
|
+
open: true,
|
|
2681
|
+
onClose,
|
|
2682
|
+
size: "full",
|
|
2683
|
+
panelClassName: "bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] flex max-h-[95vh] h-[95vh] w-full flex-col overflow-hidden rounded-2xl border shadow-2xl",
|
|
2684
|
+
className: "p-2 sm:p-4",
|
|
2685
|
+
closeOnBackdropClick: true,
|
|
2686
|
+
title: fileName,
|
|
2687
|
+
titleClassName: "truncate text-sm font-semibold",
|
|
2688
|
+
headerClassName: "items-center px-5 py-3",
|
|
2689
|
+
closeIconSize: 18,
|
|
2690
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex min-h-0 flex-1 flex-col overflow-auto p-4 sm:p-6", children: loading ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex w-full flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react13.Loader2, { size: 28, className: "text-[var(--color-primary,#3b82f6)] animate-spin" }) }) : isImage && url ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "flex h-full w-full flex-1 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("img", { src: url, alt: fileName, className: "max-h-full max-w-full rounded-lg object-contain" }) }) : isMarkdown && textContent !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { className: "w-full overflow-auto", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_streamdown.Streamdown, { mode: "static", children: textContent }) }) : isJson && textContent !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("pre", { className: "bg-[var(--color-bg,#f8fafc)] text-[var(--color-text,#0f172a)] w-full flex-1 overflow-auto rounded-lg p-6 text-sm leading-relaxed whitespace-pre-wrap", children: (() => {
|
|
2691
|
+
try {
|
|
2692
|
+
return JSON.stringify(JSON.parse(textContent), null, 2);
|
|
2693
|
+
} catch {
|
|
2694
|
+
return textContent;
|
|
2695
|
+
}
|
|
2696
|
+
})() }) : isDocx && htmlContent !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2697
|
+
"div",
|
|
2698
|
+
{
|
|
2699
|
+
className: "bg-[var(--color-bg,#f8fafc)] text-[var(--color-text,#0f172a)] w-full flex-1 overflow-auto rounded-lg p-6 text-sm leading-relaxed",
|
|
2700
|
+
dangerouslySetInnerHTML: { __html: htmlContent }
|
|
2701
|
+
}
|
|
2702
|
+
) : isText && textContent !== null ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("pre", { className: "bg-[var(--color-bg,#f8fafc)] text-[var(--color-text,#0f172a)] w-full flex-1 overflow-auto rounded-lg p-6 text-sm leading-relaxed whitespace-pre-wrap", children: textContent }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: "text-[var(--color-text-muted,#94a3b8)] flex w-full flex-1 flex-col items-center justify-center gap-2 py-12 text-center", children: [
|
|
2703
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react13.FileIcon, { size: 48, className: "opacity-30" }),
|
|
2704
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-sm", children: "Aper\xE7u non disponible pour ce type de fichier" }),
|
|
2705
|
+
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("p", { className: "text-[10px]", children: mimetype })
|
|
2706
|
+
] }) })
|
|
2707
|
+
}
|
|
2708
|
+
);
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
// src/components/MessageItem.tsx
|
|
2712
|
+
var import_streamdown2 = require("streamdown");
|
|
2713
|
+
var import_cjk = require("@streamdown/cjk");
|
|
2714
|
+
var import_code = require("@streamdown/code");
|
|
2715
|
+
var import_math = require("@streamdown/math");
|
|
2716
|
+
var import_mermaid = require("@streamdown/mermaid");
|
|
2717
|
+
var import_katex_min = require("katex/dist/katex.min.css");
|
|
2718
|
+
var import_styles2 = require("streamdown/styles.css");
|
|
2719
|
+
|
|
2720
|
+
// src/components/streamdownCustomComponents.tsx
|
|
2721
|
+
var import_react15 = __toESM(require("react"));
|
|
2722
|
+
|
|
2723
|
+
// src/components/ChartRenderer.tsx
|
|
2724
|
+
var import_react13 = __toESM(require("react"));
|
|
2725
|
+
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
2726
|
+
var ChartJS = null;
|
|
2727
|
+
var loadChartJS = async () => {
|
|
2728
|
+
if (ChartJS) return ChartJS;
|
|
2729
|
+
const mod = await import("chart.js");
|
|
2730
|
+
ChartJS = mod.Chart;
|
|
2731
|
+
ChartJS.register(...mod.registerables);
|
|
2732
|
+
return ChartJS;
|
|
2733
|
+
};
|
|
2734
|
+
var ChartRenderer = import_react13.default.memo(({ chartConfig }) => {
|
|
2735
|
+
const containerRef = (0, import_react13.useRef)(null);
|
|
2736
|
+
const chartInstanceRef = (0, import_react13.useRef)(null);
|
|
2737
|
+
const [isLoading, setIsLoading] = (0, import_react13.useState)(true);
|
|
2738
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
2739
|
+
const [canvasKey, setCanvasKey] = (0, import_react13.useState)(() => `canvas-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
2740
|
+
const chartData = (0, import_react13.useMemo)(() => chartConfig.chartConfig.data, [chartConfig]);
|
|
2741
|
+
const chartOptions = (0, import_react13.useMemo)(() => ({
|
|
2742
|
+
...chartConfig.chartConfig.options,
|
|
2743
|
+
responsive: true,
|
|
2744
|
+
maintainAspectRatio: false,
|
|
2745
|
+
animation: { duration: 0 },
|
|
2746
|
+
interaction: { intersect: false, mode: "index" },
|
|
2747
|
+
plugins: {
|
|
2748
|
+
...chartConfig.chartConfig.options?.plugins,
|
|
2749
|
+
legend: { ...chartConfig.chartConfig.options?.plugins?.legend, labels: { usePointStyle: true, padding: 15, font: { size: 12 } } }
|
|
2750
|
+
}
|
|
2751
|
+
}), [chartConfig]);
|
|
2752
|
+
(0, import_react13.useEffect)(() => {
|
|
2753
|
+
const timer = setTimeout(async () => {
|
|
2754
|
+
try {
|
|
2755
|
+
setIsLoading(true);
|
|
2756
|
+
setError(null);
|
|
2757
|
+
if (chartInstanceRef.current) {
|
|
2758
|
+
try {
|
|
2759
|
+
chartInstanceRef.current.destroy();
|
|
2760
|
+
} catch {
|
|
2761
|
+
}
|
|
2762
|
+
chartInstanceRef.current = null;
|
|
2763
|
+
}
|
|
2764
|
+
setCanvasKey(`canvas-${Date.now()}-${Math.random().toString(16).slice(2)}`);
|
|
2765
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
2766
|
+
const CJS = await loadChartJS();
|
|
2767
|
+
if (containerRef.current) {
|
|
2768
|
+
const canvas = containerRef.current.querySelector("canvas");
|
|
2769
|
+
if (canvas) {
|
|
2770
|
+
const existing = CJS.getChart(canvas);
|
|
2771
|
+
if (existing) existing.destroy();
|
|
2772
|
+
const ctx = canvas.getContext("2d");
|
|
2773
|
+
if (ctx) {
|
|
2774
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2775
|
+
chartInstanceRef.current = new CJS(ctx, { type: chartConfig.chartConfig.type, data: chartData, options: chartOptions });
|
|
2776
|
+
setIsLoading(false);
|
|
2777
|
+
}
|
|
2778
|
+
}
|
|
2779
|
+
}
|
|
2780
|
+
} catch (err) {
|
|
2781
|
+
setError("\xC9chec du graphique : " + (err.message || "Erreur inconnue"));
|
|
2782
|
+
setIsLoading(false);
|
|
2783
|
+
}
|
|
2784
|
+
}, 10);
|
|
2785
|
+
return () => {
|
|
2786
|
+
clearTimeout(timer);
|
|
2787
|
+
if (chartInstanceRef.current) {
|
|
2788
|
+
try {
|
|
2789
|
+
chartInstanceRef.current.destroy();
|
|
2790
|
+
} catch {
|
|
2791
|
+
}
|
|
2792
|
+
chartInstanceRef.current = null;
|
|
2793
|
+
}
|
|
2794
|
+
};
|
|
2795
|
+
}, [chartConfig, chartData, chartOptions]);
|
|
2796
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { className: "my-4 rounded-lg border px-4 py-3 text-sm", style: { borderColor: "#ef4444", backgroundColor: "rgba(239,68,68,0.08)", color: "#b91c1c" }, children: [
|
|
2797
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("strong", { className: "mb-1 block", children: "Erreur de graphique" }),
|
|
2798
|
+
error
|
|
2799
|
+
] });
|
|
2800
|
+
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "my-4 w-full", style: { height: 500, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)("div", { ref: containerRef, className: "relative h-[calc(100%-40px)] p-2", children: [
|
|
2801
|
+
isLoading && /* @__PURE__ */ (0, import_jsx_runtime17.jsx)("div", { className: "absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 text-sm", style: { color: "var(--color-text-secondary,#64748b)" }, children: "Chargement du graphique..." }),
|
|
2802
|
+
/* @__PURE__ */ (0, import_jsx_runtime17.jsx)("canvas", { style: { width: "100%", height: "100%", display: isLoading ? "none" : "block" } }, canvasKey)
|
|
2803
|
+
] }) });
|
|
2804
|
+
});
|
|
2805
|
+
ChartRenderer.displayName = "ChartRenderer";
|
|
2806
|
+
|
|
2807
|
+
// src/components/TableRenderer.tsx
|
|
2808
|
+
var import_react14 = __toESM(require("react"));
|
|
2809
|
+
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
2810
|
+
var TableRenderer = import_react14.default.memo(({ tableConfig }) => {
|
|
2811
|
+
const [error, setError] = (0, import_react14.useState)(null);
|
|
2812
|
+
(0, import_react14.useEffect)(() => {
|
|
2813
|
+
try {
|
|
2814
|
+
if (!tableConfig.headers || !Array.isArray(tableConfig.headers)) throw new Error("Headers must be an array");
|
|
2815
|
+
if (!tableConfig.rows || !Array.isArray(tableConfig.rows)) throw new Error("Rows must be an array");
|
|
2816
|
+
setError(null);
|
|
2817
|
+
} catch (err) {
|
|
2818
|
+
setError(err.message || "Configuration de tableau invalide");
|
|
2819
|
+
}
|
|
2820
|
+
}, [tableConfig]);
|
|
2821
|
+
if (error) return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "my-4 rounded-lg border px-4 py-3 text-sm", style: { borderColor: "#ef4444", backgroundColor: "rgba(239,68,68,0.08)", color: "#b91c1c" }, children: [
|
|
2822
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("strong", { className: "mb-1 block", children: "Erreur de tableau" }),
|
|
2823
|
+
error
|
|
2824
|
+
] });
|
|
2825
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "my-4 w-full overflow-x-auto rounded-lg border", style: { backgroundColor: "var(--color-surface,#fff)", borderColor: "var(--color-border,#e2e8f0)" }, children: [
|
|
2826
|
+
tableConfig.caption && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { className: "mb-3 text-center text-sm font-semibold", style: { color: "var(--color-text,#0f172a)" }, children: tableConfig.caption }),
|
|
2827
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("table", { className: "min-w-full border-collapse text-sm", children: [
|
|
2828
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("thead", { children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("tr", { children: tableConfig.headers.map((header, i) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2829
|
+
"th",
|
|
2830
|
+
{
|
|
2831
|
+
className: "border-b px-3 py-2 text-left text-xs font-semibold tracking-wide uppercase",
|
|
2832
|
+
style: { backgroundColor: "var(--color-bg,#f8fafc)", borderColor: "var(--color-border,#e2e8f0)", color: "var(--color-text-secondary,#475569)", whiteSpace: "nowrap" },
|
|
2833
|
+
children: header
|
|
2834
|
+
},
|
|
2835
|
+
i
|
|
2836
|
+
)) }) }),
|
|
2837
|
+
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("tbody", { children: tableConfig.rows.map((row, rowIdx) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("tr", { style: { backgroundColor: rowIdx % 2 === 1 ? "var(--color-bg,#f8fafc)" : void 0 }, children: row.map((cell, cellIdx) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
|
|
2838
|
+
"td",
|
|
2839
|
+
{
|
|
2840
|
+
className: "border-b px-3 py-2 align-top",
|
|
2841
|
+
style: { borderColor: "var(--color-border,#e2e8f0)", maxWidth: "260px", wordBreak: "break-word", color: "var(--color-text,#0f172a)" },
|
|
2842
|
+
children: String(cell)
|
|
2843
|
+
},
|
|
2844
|
+
cellIdx
|
|
2845
|
+
)) }, rowIdx)) })
|
|
2846
|
+
] })
|
|
2847
|
+
] });
|
|
2848
|
+
});
|
|
2849
|
+
TableRenderer.displayName = "TableRenderer";
|
|
2850
|
+
|
|
2851
|
+
// src/components/streamdownCustomComponents.tsx
|
|
2852
|
+
var import_jsx_runtime19 = require("react/jsx-runtime");
|
|
2853
|
+
var CUSTOM_LANGUAGES = /* @__PURE__ */ new Set(["chart", "tabledata", "optionsblock", "table"]);
|
|
2854
|
+
var workspaceStreamdownComponents = {
|
|
2855
|
+
pre: ({ children }) => {
|
|
2856
|
+
const codeChild = import_react15.default.Children.toArray(children)[0];
|
|
2857
|
+
if (codeChild && typeof codeChild === "object" && "props" in codeChild) {
|
|
2858
|
+
const codeProps = codeChild.props || {};
|
|
2859
|
+
const className = codeProps.className || "";
|
|
2860
|
+
const match = /language-(\w+)/.exec(className);
|
|
2861
|
+
const language = match ? match[1] : "";
|
|
2862
|
+
const codeContent = codeProps.children;
|
|
2863
|
+
if (language === "chart" && codeContent) {
|
|
2864
|
+
try {
|
|
2865
|
+
const cfg = JSON.parse(String(codeContent).replace(/\n$/, ""));
|
|
2866
|
+
if (cfg.type === "chartjs" && cfg.chartConfig) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(ChartRenderer, { chartConfig: cfg });
|
|
2867
|
+
} catch {
|
|
2868
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { children });
|
|
2869
|
+
}
|
|
2870
|
+
}
|
|
2871
|
+
if (language === "tabledata" && codeContent) {
|
|
2872
|
+
try {
|
|
2873
|
+
const cfg = JSON.parse(String(codeContent).replace(/\n$/, ""));
|
|
2874
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(TableRenderer, { tableConfig: cfg });
|
|
2875
|
+
} catch {
|
|
2876
|
+
return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { children });
|
|
2877
|
+
}
|
|
2878
|
+
}
|
|
2879
|
+
if (CUSTOM_LANGUAGES.has(language)) return /* @__PURE__ */ (0, import_jsx_runtime19.jsx)("pre", { children });
|
|
2880
|
+
if (import_react15.default.isValidElement(codeChild)) {
|
|
2881
|
+
return import_react15.default.cloneElement(codeChild, { "data-block": true });
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
return children;
|
|
2885
|
+
}
|
|
2886
|
+
};
|
|
2887
|
+
|
|
2888
|
+
// src/components/MessageItem.tsx
|
|
2889
|
+
var import_jsx_runtime20 = require("react/jsx-runtime");
|
|
2890
|
+
var MAX_LINES = 12;
|
|
2891
|
+
var COLLAPSED_MAX_HEIGHT = MAX_LINES * 23 + 20;
|
|
2892
|
+
var MAX_VISIBLE_ARTEFACTS = 3;
|
|
2893
|
+
var REACTION_OPTIONS = [
|
|
2894
|
+
{ key: "like", emoji: "\u{1F44D}", label: "J\u2019aime", icon: import_lucide_react14.ThumbsUp },
|
|
2895
|
+
{ key: "dislike", emoji: "\u{1F44E}", label: "Pas d\u2019accord", icon: import_lucide_react14.ThumbsDown },
|
|
2896
|
+
{ key: "heart", emoji: "\u2764\uFE0F", label: "J\u2019adore", icon: import_lucide_react14.Heart },
|
|
2897
|
+
{ key: "laugh", emoji: "\u{1F602}", label: "Dr\xF4le", icon: import_lucide_react14.Laugh },
|
|
2898
|
+
{ key: "sad", emoji: "\u{1F622}", label: "Triste", icon: import_lucide_react14.Frown },
|
|
2899
|
+
{ key: "check", emoji: "\u2705", label: "Fait", icon: import_lucide_react14.CheckCircle2 }
|
|
2900
|
+
];
|
|
2901
|
+
var REACTION_EMOJI_MAP = Object.fromEntries(
|
|
2902
|
+
REACTION_OPTIONS.map((r) => [r.key, r.emoji])
|
|
2903
|
+
);
|
|
2904
|
+
function preprocessMentions(content, members) {
|
|
2905
|
+
let result = content;
|
|
2906
|
+
const globalMentions = ["@Tous le monde et agents", "@Tous le monde"];
|
|
2907
|
+
for (const token of globalMentions) {
|
|
2908
|
+
const escaped = token.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2909
|
+
const regex = new RegExp(`(${escaped})`, "gi");
|
|
2910
|
+
result = result.replace(regex, "**$1**");
|
|
2911
|
+
}
|
|
2912
|
+
if (!members.length) return result;
|
|
2913
|
+
const sorted = [...members].sort(
|
|
2914
|
+
(a, b) => b.memberName.length - a.memberName.length
|
|
2915
|
+
);
|
|
2916
|
+
for (const member of sorted) {
|
|
2917
|
+
const mentionByName = `@${member.memberName}`;
|
|
2918
|
+
const mentionById = `@${member.memberId}`;
|
|
2919
|
+
const escapedName = mentionByName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2920
|
+
const escapedId = mentionById.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2921
|
+
const regex = new RegExp(`(${escapedName}|${escapedId})`, "gi");
|
|
2922
|
+
result = result.replace(regex, "**$1**");
|
|
2923
|
+
}
|
|
2924
|
+
return result;
|
|
2925
|
+
}
|
|
2926
|
+
function formatTime(iso) {
|
|
2927
|
+
return new Date(iso).toLocaleTimeString("fr-FR", {
|
|
2928
|
+
hour: "2-digit",
|
|
2929
|
+
minute: "2-digit"
|
|
2930
|
+
});
|
|
2931
|
+
}
|
|
2932
|
+
function InlineImagePreview({ artefact }) {
|
|
2933
|
+
const { artefactApi } = useWorkspaceApi();
|
|
2934
|
+
const [url, setUrl] = (0, import_react16.useState)(null);
|
|
2935
|
+
const [loading, setLoading] = (0, import_react16.useState)(true);
|
|
2936
|
+
(0, import_react16.useEffect)(() => {
|
|
2937
|
+
let cancelled = false;
|
|
2938
|
+
artefactApi.getSignedUrl(artefact.file._id).then(({ url: signedUrl }) => {
|
|
2939
|
+
if (!cancelled) setUrl(signedUrl);
|
|
2940
|
+
}).catch(() => {
|
|
2941
|
+
}).finally(() => {
|
|
2942
|
+
if (!cancelled) setLoading(false);
|
|
2943
|
+
});
|
|
2944
|
+
return () => {
|
|
2945
|
+
cancelled = true;
|
|
2946
|
+
};
|
|
2947
|
+
}, [artefact.file._id, artefactApi]);
|
|
2948
|
+
if (loading) {
|
|
2949
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "bg-bg flex h-32 w-full items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Loader2, { size: 16, className: "text-text-muted animate-spin" }) });
|
|
2950
|
+
}
|
|
2951
|
+
if (!url) {
|
|
2952
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "bg-bg flex h-24 w-full items-center justify-center gap-2 rounded-lg", children: [
|
|
2953
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.ImageIcon, { size: 16, className: "text-text-muted" }),
|
|
2954
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-muted text-xs", children: "Image indisponible" })
|
|
2955
|
+
] });
|
|
2956
|
+
}
|
|
2957
|
+
return (
|
|
2958
|
+
// eslint-disable-next-line @next/next/no-img-element
|
|
2959
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
2960
|
+
"img",
|
|
2961
|
+
{
|
|
2962
|
+
src: url,
|
|
2963
|
+
alt: fixMojibake(artefact.file.originalName),
|
|
2964
|
+
className: "max-h-48 w-full rounded-lg object-contain"
|
|
2965
|
+
}
|
|
2966
|
+
)
|
|
2967
|
+
);
|
|
2968
|
+
}
|
|
2969
|
+
function ArtefactCard({
|
|
2970
|
+
artefact,
|
|
2971
|
+
isUserMessage,
|
|
2972
|
+
workspaceId,
|
|
2973
|
+
onView,
|
|
2974
|
+
onDeleted,
|
|
2975
|
+
canDelete = false
|
|
2976
|
+
}) {
|
|
2977
|
+
const { artefactApi } = useWorkspaceApi();
|
|
2978
|
+
const isImage = artefact.file.mimetype.startsWith("image/");
|
|
2979
|
+
const [confirmOpen, setConfirmOpen] = (0, import_react16.useState)(false);
|
|
2980
|
+
const handleDownload = async (e) => {
|
|
2981
|
+
e.stopPropagation();
|
|
2982
|
+
try {
|
|
2983
|
+
const blob = await artefactApi.getDownloadBlob(artefact.file._id);
|
|
2984
|
+
const blobUrl = URL.createObjectURL(blob);
|
|
2985
|
+
const a = document.createElement("a");
|
|
2986
|
+
a.href = blobUrl;
|
|
2987
|
+
a.download = fixMojibake(artefact.file.originalName);
|
|
2988
|
+
document.body.appendChild(a);
|
|
2989
|
+
a.click();
|
|
2990
|
+
a.remove();
|
|
2991
|
+
URL.revokeObjectURL(blobUrl);
|
|
2992
|
+
} catch (err) {
|
|
2993
|
+
console.error("Download failed:", err);
|
|
2994
|
+
}
|
|
2995
|
+
};
|
|
2996
|
+
const handleDeleteConfirmed = async () => {
|
|
2997
|
+
if (!workspaceId) return;
|
|
2998
|
+
try {
|
|
2999
|
+
await artefactApi.delete(workspaceId, artefact._id);
|
|
3000
|
+
onDeleted?.(artefact._id);
|
|
3001
|
+
} catch (err) {
|
|
3002
|
+
console.error("Delete failed:", err);
|
|
3003
|
+
}
|
|
3004
|
+
};
|
|
3005
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3006
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3007
|
+
"div",
|
|
3008
|
+
{
|
|
3009
|
+
className: cn(
|
|
3010
|
+
"group/art flex flex-col overflow-hidden rounded-xl border transition-shadow hover:shadow-md",
|
|
3011
|
+
isUserMessage ? "border-white/20 bg-white/10" : "border-border bg-bg"
|
|
3012
|
+
),
|
|
3013
|
+
children: [
|
|
3014
|
+
isImage && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "cursor-pointer", onClick: onView, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(InlineImagePreview, { artefact }) }),
|
|
3015
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 px-3 py-2", children: [
|
|
3016
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3017
|
+
"div",
|
|
3018
|
+
{
|
|
3019
|
+
className: cn(
|
|
3020
|
+
"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg",
|
|
3021
|
+
isUserMessage ? "bg-white/15" : "bg-primary-light"
|
|
3022
|
+
),
|
|
3023
|
+
children: isImage ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3024
|
+
import_lucide_react14.ImageIcon,
|
|
3025
|
+
{
|
|
3026
|
+
size: 14,
|
|
3027
|
+
className: isUserMessage ? "text-white/80" : "text-primary"
|
|
3028
|
+
}
|
|
3029
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3030
|
+
import_lucide_react14.FileIcon,
|
|
3031
|
+
{
|
|
3032
|
+
size: 14,
|
|
3033
|
+
className: isUserMessage ? "text-white/80" : "text-primary"
|
|
3034
|
+
}
|
|
3035
|
+
)
|
|
3036
|
+
}
|
|
3037
|
+
),
|
|
3038
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
3039
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3040
|
+
"div",
|
|
3041
|
+
{
|
|
3042
|
+
className: cn(
|
|
3043
|
+
"truncate text-xs font-medium",
|
|
3044
|
+
isUserMessage ? "text-white" : "text-text"
|
|
3045
|
+
),
|
|
3046
|
+
children: fixMojibake(artefact.file.originalName)
|
|
3047
|
+
}
|
|
3048
|
+
),
|
|
3049
|
+
artefact.file.size > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3050
|
+
"div",
|
|
3051
|
+
{
|
|
3052
|
+
className: cn(
|
|
3053
|
+
"text-[10px]",
|
|
3054
|
+
isUserMessage ? "text-white/60" : "text-text-muted"
|
|
3055
|
+
),
|
|
3056
|
+
children: artefact.file.size > 1024 * 1024 ? `${(artefact.file.size / (1024 * 1024)).toFixed(1)} MB` : `${(artefact.file.size / 1024).toFixed(0)} KB`
|
|
3057
|
+
}
|
|
3058
|
+
)
|
|
3059
|
+
] }),
|
|
3060
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-0.5 opacity-0 transition-opacity group-hover/art:opacity-100", children: [
|
|
3061
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3062
|
+
"button",
|
|
3063
|
+
{
|
|
3064
|
+
onClick: (e) => {
|
|
3065
|
+
e.stopPropagation();
|
|
3066
|
+
onView();
|
|
3067
|
+
},
|
|
3068
|
+
className: cn(
|
|
3069
|
+
"rounded-md p-1.5 transition-colors",
|
|
3070
|
+
isUserMessage ? "text-white/70 hover:bg-white/15 hover:text-white" : "text-info hover:bg-info-light"
|
|
3071
|
+
),
|
|
3072
|
+
title: "Visualiser",
|
|
3073
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Eye, { size: 13 })
|
|
3074
|
+
}
|
|
3075
|
+
),
|
|
3076
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3077
|
+
"button",
|
|
3078
|
+
{
|
|
3079
|
+
onClick: handleDownload,
|
|
3080
|
+
className: cn(
|
|
3081
|
+
"rounded-md p-1.5 transition-colors",
|
|
3082
|
+
isUserMessage ? "text-white/70 hover:bg-white/15 hover:text-white" : "text-primary hover:bg-primary-light"
|
|
3083
|
+
),
|
|
3084
|
+
title: "T\xE9l\xE9charger",
|
|
3085
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Download, { size: 13 })
|
|
3086
|
+
}
|
|
3087
|
+
),
|
|
3088
|
+
workspaceId && canDelete && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3089
|
+
"button",
|
|
3090
|
+
{
|
|
3091
|
+
onClick: (e) => {
|
|
3092
|
+
e.stopPropagation();
|
|
3093
|
+
setConfirmOpen(true);
|
|
3094
|
+
},
|
|
3095
|
+
className: cn(
|
|
3096
|
+
"rounded-md p-1.5 transition-colors",
|
|
3097
|
+
isUserMessage ? "text-white/70 hover:bg-white/15 hover:text-white" : "text-danger hover:bg-danger-light"
|
|
3098
|
+
),
|
|
3099
|
+
title: "Supprimer",
|
|
3100
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Trash2, { size: 13 })
|
|
3101
|
+
}
|
|
3102
|
+
)
|
|
3103
|
+
] })
|
|
3104
|
+
] })
|
|
3105
|
+
]
|
|
3106
|
+
}
|
|
3107
|
+
),
|
|
3108
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3109
|
+
ConfirmModal,
|
|
3110
|
+
{
|
|
3111
|
+
open: confirmOpen,
|
|
3112
|
+
onClose: () => setConfirmOpen(false),
|
|
3113
|
+
onConfirm: handleDeleteConfirmed,
|
|
3114
|
+
title: "Supprimer l'artefact",
|
|
3115
|
+
description: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3116
|
+
"Voulez-vous vraiment supprimer",
|
|
3117
|
+
" ",
|
|
3118
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("strong", { children: fixMojibake(artefact.file.originalName) }),
|
|
3119
|
+
" ? Cette action est irr\xE9versible."
|
|
3120
|
+
] }),
|
|
3121
|
+
confirmText: "Supprimer",
|
|
3122
|
+
danger: true
|
|
3123
|
+
}
|
|
3124
|
+
)
|
|
3125
|
+
] });
|
|
3126
|
+
}
|
|
3127
|
+
function MessageActionsMenu({
|
|
3128
|
+
isUser,
|
|
3129
|
+
onEdit,
|
|
3130
|
+
onDelete
|
|
3131
|
+
}) {
|
|
3132
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
3133
|
+
const menuRef = (0, import_react16.useRef)(null);
|
|
3134
|
+
(0, import_react16.useEffect)(() => {
|
|
3135
|
+
if (!open) return;
|
|
3136
|
+
const handler = (e) => {
|
|
3137
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
3138
|
+
setOpen(false);
|
|
3139
|
+
}
|
|
3140
|
+
};
|
|
3141
|
+
document.addEventListener("mousedown", handler);
|
|
3142
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
3143
|
+
}, [open]);
|
|
3144
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "relative", ref: menuRef, children: [
|
|
3145
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3146
|
+
"button",
|
|
3147
|
+
{
|
|
3148
|
+
onClick: (e) => {
|
|
3149
|
+
e.stopPropagation();
|
|
3150
|
+
setOpen((v) => !v);
|
|
3151
|
+
},
|
|
3152
|
+
className: cn(
|
|
3153
|
+
"rounded-full p-1 shadow-sm transition-all",
|
|
3154
|
+
"bg-surface border-border text-text-muted hover:text-text border"
|
|
3155
|
+
),
|
|
3156
|
+
title: "Actions",
|
|
3157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.MoreVertical, { size: 14 })
|
|
3158
|
+
}
|
|
3159
|
+
),
|
|
3160
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3161
|
+
"div",
|
|
3162
|
+
{
|
|
3163
|
+
className: cn(
|
|
3164
|
+
"bg-surface border-border absolute z-30 min-w-[140px] overflow-hidden rounded-lg border shadow-lg",
|
|
3165
|
+
isUser ? "right-0 bottom-full mb-1" : "bottom-full left-0 mb-1"
|
|
3166
|
+
),
|
|
3167
|
+
children: [
|
|
3168
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3169
|
+
"button",
|
|
3170
|
+
{
|
|
3171
|
+
onClick: (e) => {
|
|
3172
|
+
e.stopPropagation();
|
|
3173
|
+
setOpen(false);
|
|
3174
|
+
onEdit();
|
|
3175
|
+
},
|
|
3176
|
+
className: "text-text hover:bg-primary-light hover:text-primary flex w-full items-center gap-2 px-3 py-2 text-xs transition-colors",
|
|
3177
|
+
children: [
|
|
3178
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Pencil, { size: 13 }),
|
|
3179
|
+
"Modifier"
|
|
3180
|
+
]
|
|
3181
|
+
}
|
|
3182
|
+
),
|
|
3183
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3184
|
+
"button",
|
|
3185
|
+
{
|
|
3186
|
+
onClick: (e) => {
|
|
3187
|
+
e.stopPropagation();
|
|
3188
|
+
setOpen(false);
|
|
3189
|
+
onDelete();
|
|
3190
|
+
},
|
|
3191
|
+
className: "text-danger hover:bg-danger-light flex w-full items-center gap-2 px-3 py-2 text-xs transition-colors",
|
|
3192
|
+
children: [
|
|
3193
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Trash2, { size: 13 }),
|
|
3194
|
+
"Supprimer"
|
|
3195
|
+
]
|
|
3196
|
+
}
|
|
3197
|
+
)
|
|
3198
|
+
]
|
|
3199
|
+
}
|
|
3200
|
+
)
|
|
3201
|
+
] });
|
|
3202
|
+
}
|
|
3203
|
+
function ReactionPicker({
|
|
3204
|
+
isUser,
|
|
3205
|
+
onSelect
|
|
3206
|
+
}) {
|
|
3207
|
+
const [open, setOpen] = (0, import_react16.useState)(false);
|
|
3208
|
+
const pickerRef = (0, import_react16.useRef)(null);
|
|
3209
|
+
(0, import_react16.useEffect)(() => {
|
|
3210
|
+
if (!open) return;
|
|
3211
|
+
const handler = (e) => {
|
|
3212
|
+
if (pickerRef.current && !pickerRef.current.contains(e.target)) {
|
|
3213
|
+
setOpen(false);
|
|
3214
|
+
}
|
|
3215
|
+
};
|
|
3216
|
+
document.addEventListener("mousedown", handler);
|
|
3217
|
+
return () => document.removeEventListener("mousedown", handler);
|
|
3218
|
+
}, [open]);
|
|
3219
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "relative", ref: pickerRef, children: [
|
|
3220
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3221
|
+
"button",
|
|
3222
|
+
{
|
|
3223
|
+
onClick: (e) => {
|
|
3224
|
+
e.stopPropagation();
|
|
3225
|
+
setOpen((v) => !v);
|
|
3226
|
+
},
|
|
3227
|
+
className: cn(
|
|
3228
|
+
"rounded-full p-1 shadow-sm transition-all",
|
|
3229
|
+
"bg-surface border-border text-text-muted hover:text-text border"
|
|
3230
|
+
),
|
|
3231
|
+
title: "R\xE9agir",
|
|
3232
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.SmilePlus, { size: 14 })
|
|
3233
|
+
}
|
|
3234
|
+
),
|
|
3235
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3236
|
+
"div",
|
|
3237
|
+
{
|
|
3238
|
+
className: cn(
|
|
3239
|
+
"bg-surface border-border absolute z-30 flex items-center gap-0.5 rounded-full border px-1.5 py-1 shadow-lg",
|
|
3240
|
+
isUser ? "right-0 bottom-full mb-1" : "bottom-full left-0 mb-1"
|
|
3241
|
+
),
|
|
3242
|
+
children: REACTION_OPTIONS.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3243
|
+
"button",
|
|
3244
|
+
{
|
|
3245
|
+
onClick: (e) => {
|
|
3246
|
+
e.stopPropagation();
|
|
3247
|
+
setOpen(false);
|
|
3248
|
+
onSelect(opt.key);
|
|
3249
|
+
},
|
|
3250
|
+
className: "hover:bg-bg flex h-7 w-7 items-center justify-center rounded-full text-base transition-all hover:scale-125",
|
|
3251
|
+
title: opt.label,
|
|
3252
|
+
children: opt.emoji
|
|
3253
|
+
},
|
|
3254
|
+
opt.key
|
|
3255
|
+
))
|
|
3256
|
+
}
|
|
3257
|
+
)
|
|
3258
|
+
] });
|
|
3259
|
+
}
|
|
3260
|
+
function groupReactions(reactions, currentUserId) {
|
|
3261
|
+
const map = /* @__PURE__ */ new Map();
|
|
3262
|
+
for (const r of reactions) {
|
|
3263
|
+
if (!map.has(r.reaction)) {
|
|
3264
|
+
map.set(r.reaction, { count: 0, members: [], hasMine: false });
|
|
3265
|
+
}
|
|
3266
|
+
const group = map.get(r.reaction);
|
|
3267
|
+
group.count++;
|
|
3268
|
+
if (r.memberName) group.members.push(r.memberName);
|
|
3269
|
+
if (r.memberId === currentUserId) group.hasMine = true;
|
|
3270
|
+
}
|
|
3271
|
+
return Array.from(map.entries()).map(([key, val]) => ({
|
|
3272
|
+
reaction: key,
|
|
3273
|
+
emoji: REACTION_EMOJI_MAP[key] ?? key,
|
|
3274
|
+
count: val.count,
|
|
3275
|
+
members: val.members,
|
|
3276
|
+
hasMine: val.hasMine
|
|
3277
|
+
}));
|
|
3278
|
+
}
|
|
3279
|
+
function ReactionBar({
|
|
3280
|
+
reactions,
|
|
3281
|
+
currentUserId,
|
|
3282
|
+
isUser,
|
|
3283
|
+
onToggle
|
|
3284
|
+
}) {
|
|
3285
|
+
const grouped = groupReactions(reactions, currentUserId);
|
|
3286
|
+
if (grouped.length === 0) return null;
|
|
3287
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3288
|
+
"div",
|
|
3289
|
+
{
|
|
3290
|
+
className: cn(
|
|
3291
|
+
"mt-1 flex flex-wrap gap-1 px-1",
|
|
3292
|
+
isUser ? "justify-end" : "justify-start"
|
|
3293
|
+
),
|
|
3294
|
+
children: grouped.map((g) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3295
|
+
"button",
|
|
3296
|
+
{
|
|
3297
|
+
onClick: () => onToggle(g.reaction),
|
|
3298
|
+
className: cn(
|
|
3299
|
+
"group/reaction relative flex items-center gap-1 rounded-full border px-2 py-0.5 text-xs transition-all hover:shadow-sm",
|
|
3300
|
+
g.hasMine ? "border-primary bg-primary-light text-primary font-medium" : "border-border bg-surface text-text-secondary hover:border-primary/40 hover:bg-primary-light/50"
|
|
3301
|
+
),
|
|
3302
|
+
title: g.members.join(", "),
|
|
3303
|
+
children: [
|
|
3304
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-sm", children: g.emoji }),
|
|
3305
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: g.count }),
|
|
3306
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3307
|
+
"div",
|
|
3308
|
+
{
|
|
3309
|
+
className: cn(
|
|
3310
|
+
"pointer-events-none absolute z-40 min-w-max rounded-lg px-2.5 py-1.5 text-[11px] leading-relaxed opacity-0 shadow-lg transition-opacity group-hover/reaction:opacity-100",
|
|
3311
|
+
"bg-[#1e293b] text-white",
|
|
3312
|
+
isUser ? "right-0 bottom-full mb-2" : "bottom-full left-0 mb-2"
|
|
3313
|
+
),
|
|
3314
|
+
children: g.members.length > 0 ? g.members.join(", ") : "R\xE9action anonyme"
|
|
3315
|
+
}
|
|
3316
|
+
)
|
|
3317
|
+
]
|
|
3318
|
+
},
|
|
3319
|
+
g.reaction
|
|
3320
|
+
))
|
|
3321
|
+
}
|
|
3322
|
+
);
|
|
3323
|
+
}
|
|
3324
|
+
var MessageItem = ({
|
|
3325
|
+
message,
|
|
3326
|
+
role,
|
|
3327
|
+
members = [],
|
|
3328
|
+
workspaceId,
|
|
3329
|
+
currentUserId,
|
|
3330
|
+
isOwner = false,
|
|
3331
|
+
avatarUsers,
|
|
3332
|
+
onArtefactDeleted,
|
|
3333
|
+
onReply,
|
|
3334
|
+
onEditMessage,
|
|
3335
|
+
onDeleteMessage,
|
|
3336
|
+
onToggleReaction,
|
|
3337
|
+
onTogglePin
|
|
3338
|
+
}) => {
|
|
3339
|
+
const isAgent = message.senderType === "agent";
|
|
3340
|
+
const isUser = role === "user";
|
|
3341
|
+
const isDeleted = message.deleted === true;
|
|
3342
|
+
const processedContent = isDeleted ? "" : preprocessMentions(message.content, members);
|
|
3343
|
+
const artefacts = isDeleted ? [] : "artefacts" in message ? message.artefacts : [];
|
|
3344
|
+
const reactions = isDeleted ? [] : "reactions" in message ? message.reactions ?? [] : [];
|
|
3345
|
+
const time = formatTime(message.createdAt);
|
|
3346
|
+
const isPinned = !isDeleted && message.pinned === true;
|
|
3347
|
+
const [isExpanded, setIsExpanded] = (0, import_react16.useState)(false);
|
|
3348
|
+
const [artefactsExpanded, setArtefactsExpanded] = (0, import_react16.useState)(false);
|
|
3349
|
+
const [isLong, setIsLong] = (0, import_react16.useState)(false);
|
|
3350
|
+
const contentRef = (0, import_react16.useRef)(null);
|
|
3351
|
+
(0, import_react16.useEffect)(() => {
|
|
3352
|
+
if (isDeleted) return;
|
|
3353
|
+
const el = contentRef.current;
|
|
3354
|
+
if (!el) return;
|
|
3355
|
+
const checkHeight = () => {
|
|
3356
|
+
if (el.scrollHeight > COLLAPSED_MAX_HEIGHT) {
|
|
3357
|
+
setIsLong(true);
|
|
3358
|
+
} else {
|
|
3359
|
+
setIsLong(false);
|
|
3360
|
+
}
|
|
3361
|
+
};
|
|
3362
|
+
checkHeight();
|
|
3363
|
+
const timer1 = setTimeout(checkHeight, 100);
|
|
3364
|
+
const timer2 = setTimeout(checkHeight, 500);
|
|
3365
|
+
let observer = null;
|
|
3366
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
3367
|
+
observer = new ResizeObserver(checkHeight);
|
|
3368
|
+
observer.observe(el);
|
|
3369
|
+
}
|
|
3370
|
+
return () => {
|
|
3371
|
+
clearTimeout(timer1);
|
|
3372
|
+
clearTimeout(timer2);
|
|
3373
|
+
if (observer) observer.disconnect();
|
|
3374
|
+
};
|
|
3375
|
+
}, [message.content, isDeleted]);
|
|
3376
|
+
const isDark = typeof document !== "undefined" && document.documentElement.getAttribute("data-theme") === "dark";
|
|
3377
|
+
const codePlugin = import_react16.default.useMemo(
|
|
3378
|
+
() => (0, import_code.createCodePlugin)({
|
|
3379
|
+
themes: isDark ? ["min-dark", "min-dark"] : ["min-light", "min-light"]
|
|
3380
|
+
}),
|
|
3381
|
+
[isDark]
|
|
3382
|
+
);
|
|
3383
|
+
const [viewingArtefact, setViewingArtefact] = (0, import_react16.useState)(null);
|
|
3384
|
+
const [confirmDeleteOpen, setConfirmDeleteOpen] = (0, import_react16.useState)(false);
|
|
3385
|
+
const memberName = message.sender?.memberName || message.senderName;
|
|
3386
|
+
if (isDeleted) {
|
|
3387
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3388
|
+
"div",
|
|
3389
|
+
{
|
|
3390
|
+
className: cn(
|
|
3391
|
+
"group flex max-w-[85%] gap-3",
|
|
3392
|
+
isUser ? "flex-row-reverse self-end" : "self-start"
|
|
3393
|
+
),
|
|
3394
|
+
children: [
|
|
3395
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3396
|
+
MemberAvatar,
|
|
3397
|
+
{
|
|
3398
|
+
memberName,
|
|
3399
|
+
memberType: message.senderType,
|
|
3400
|
+
avatarUrl: avatarUsers?.find(
|
|
3401
|
+
(u) => u.email.toLowerCase() === message.senderId.toLowerCase()
|
|
3402
|
+
)?.avatarUrl,
|
|
3403
|
+
size: "md",
|
|
3404
|
+
variant: isUser ? "currentUser" : "primary",
|
|
3405
|
+
className: "opacity-50 shadow-sm"
|
|
3406
|
+
}
|
|
3407
|
+
),
|
|
3408
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3409
|
+
!isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 px-1", children: [
|
|
3410
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-muted text-[11px] font-semibold", children: memberName }),
|
|
3411
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px]", children: time })
|
|
3412
|
+
] }),
|
|
3413
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3414
|
+
"div",
|
|
3415
|
+
{
|
|
3416
|
+
className: cn(
|
|
3417
|
+
"rounded-2xl px-4 py-2.5 italic",
|
|
3418
|
+
isUser ? "bg-primary/80 rounded-tr-sm text-white/80" : "bg-surface border-border text-text-muted rounded-tl-sm border"
|
|
3419
|
+
),
|
|
3420
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-xs", children: "\u{1F5D1}\uFE0F Ce message a \xE9t\xE9 supprim\xE9" })
|
|
3421
|
+
}
|
|
3422
|
+
),
|
|
3423
|
+
isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("div", { className: "flex items-center gap-1 self-end px-1", children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px]", children: time }) })
|
|
3424
|
+
] })
|
|
3425
|
+
]
|
|
3426
|
+
}
|
|
3427
|
+
);
|
|
3428
|
+
}
|
|
3429
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3430
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3431
|
+
"div",
|
|
3432
|
+
{
|
|
3433
|
+
className: cn(
|
|
3434
|
+
"group flex max-w-[85%] gap-3",
|
|
3435
|
+
isUser ? "flex-row-reverse self-end" : "self-start"
|
|
3436
|
+
),
|
|
3437
|
+
children: [
|
|
3438
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3439
|
+
MemberAvatar,
|
|
3440
|
+
{
|
|
3441
|
+
memberName,
|
|
3442
|
+
memberType: message.senderType,
|
|
3443
|
+
avatarUrl: avatarUsers?.find(
|
|
3444
|
+
(u) => u.email.toLowerCase() === message.senderId.toLowerCase()
|
|
3445
|
+
)?.avatarUrl,
|
|
3446
|
+
size: "md",
|
|
3447
|
+
variant: isUser ? "currentUser" : "primary",
|
|
3448
|
+
className: "mt-1 flex-shrink-0 self-start shadow-sm"
|
|
3449
|
+
}
|
|
3450
|
+
),
|
|
3451
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex min-w-0 flex-col gap-0.5", children: [
|
|
3452
|
+
!isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-2 px-1", children: [
|
|
3453
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3454
|
+
"span",
|
|
3455
|
+
{
|
|
3456
|
+
className: cn(
|
|
3457
|
+
"text-[11px] font-semibold",
|
|
3458
|
+
isAgent ? "text-indigo-600" : "text-text-secondary"
|
|
3459
|
+
),
|
|
3460
|
+
children: memberName
|
|
3461
|
+
}
|
|
3462
|
+
),
|
|
3463
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px]", children: time }),
|
|
3464
|
+
message.editedAt && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px] italic", children: "(modifi\xE9)" }),
|
|
3465
|
+
onReply && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3466
|
+
"button",
|
|
3467
|
+
{
|
|
3468
|
+
onClick: () => onReply(message),
|
|
3469
|
+
className: "text-text-muted hover:text-primary ml-auto rounded p-0.5 opacity-0 transition-all group-hover:opacity-100",
|
|
3470
|
+
title: "R\xE9pondre",
|
|
3471
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Reply, { size: 13 })
|
|
3472
|
+
}
|
|
3473
|
+
)
|
|
3474
|
+
] }),
|
|
3475
|
+
isPinned && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3476
|
+
"div",
|
|
3477
|
+
{
|
|
3478
|
+
className: cn(
|
|
3479
|
+
"flex items-center gap-1 px-2 py-0.5 text-[10px] font-medium",
|
|
3480
|
+
isUser ? "self-end text-amber-200" : "text-amber-600 dark:text-amber-400"
|
|
3481
|
+
),
|
|
3482
|
+
children: [
|
|
3483
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Pin, { size: 10, className: "rotate-45" }),
|
|
3484
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { children: "\xC9pingl\xE9" })
|
|
3485
|
+
]
|
|
3486
|
+
}
|
|
3487
|
+
),
|
|
3488
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-start gap-1.5", children: [
|
|
3489
|
+
isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-shrink-0 items-center gap-0.5 self-center opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
3490
|
+
onTogglePin && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3491
|
+
"button",
|
|
3492
|
+
{
|
|
3493
|
+
onClick: (e) => {
|
|
3494
|
+
e.stopPropagation();
|
|
3495
|
+
onTogglePin(message._id);
|
|
3496
|
+
},
|
|
3497
|
+
className: cn(
|
|
3498
|
+
"rounded-full p-1 shadow-sm transition-all",
|
|
3499
|
+
"bg-surface border-border border",
|
|
3500
|
+
isPinned ? "text-amber-500 hover:text-amber-600" : "text-text-muted hover:text-text"
|
|
3501
|
+
),
|
|
3502
|
+
title: isPinned ? "D\xE9s\xE9pingler" : "\xC9pingler",
|
|
3503
|
+
children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.PinOff, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Pin, { size: 14 })
|
|
3504
|
+
}
|
|
3505
|
+
),
|
|
3506
|
+
onToggleReaction && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3507
|
+
ReactionPicker,
|
|
3508
|
+
{
|
|
3509
|
+
isUser,
|
|
3510
|
+
onSelect: (r) => onToggleReaction(message._id, r)
|
|
3511
|
+
}
|
|
3512
|
+
),
|
|
3513
|
+
onEditMessage && onDeleteMessage && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3514
|
+
MessageActionsMenu,
|
|
3515
|
+
{
|
|
3516
|
+
isUser,
|
|
3517
|
+
onEdit: () => onEditMessage(message),
|
|
3518
|
+
onDelete: () => setConfirmDeleteOpen(true)
|
|
3519
|
+
}
|
|
3520
|
+
)
|
|
3521
|
+
] }),
|
|
3522
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3523
|
+
"div",
|
|
3524
|
+
{
|
|
3525
|
+
className: cn(
|
|
3526
|
+
"relative rounded-2xl px-4 py-2.5 shadow-sm transition-shadow group-hover:shadow-md",
|
|
3527
|
+
isUser ? "bg-primary rounded-tr-sm text-white" : isAgent ? "rounded-tl-sm border border-indigo-100 bg-gradient-to-br from-white to-indigo-50/40 dark:border-indigo-800/60 dark:from-indigo-900/20 dark:to-indigo-800/10" : "bg-surface border-border rounded-tl-sm border"
|
|
3528
|
+
),
|
|
3529
|
+
children: [
|
|
3530
|
+
message.replyToMessage && typeof message.replyToMessage === "object" && "content" in message.replyToMessage && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3531
|
+
"div",
|
|
3532
|
+
{
|
|
3533
|
+
className: cn(
|
|
3534
|
+
"mb-2 rounded-lg border-l-3 px-3 py-1.5",
|
|
3535
|
+
isUser ? "border-l-white/50 bg-white/15" : "border-l-[var(--color-primary)] bg-[var(--color-primary-light)]/60"
|
|
3536
|
+
),
|
|
3537
|
+
children: [
|
|
3538
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3539
|
+
"div",
|
|
3540
|
+
{
|
|
3541
|
+
className: cn(
|
|
3542
|
+
"text-[10px] font-semibold",
|
|
3543
|
+
isUser ? "text-white/80" : "text-primary"
|
|
3544
|
+
),
|
|
3545
|
+
children: message.replyToMessage.senderName
|
|
3546
|
+
}
|
|
3547
|
+
),
|
|
3548
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3549
|
+
"div",
|
|
3550
|
+
{
|
|
3551
|
+
className: cn(
|
|
3552
|
+
"truncate text-[11px]",
|
|
3553
|
+
isUser ? "text-white/60" : "text-text-muted",
|
|
3554
|
+
message.replyToMessage.deleted && "italic"
|
|
3555
|
+
),
|
|
3556
|
+
children: message.replyToMessage.deleted ? "\u{1F5D1}\uFE0F Ce message a \xE9t\xE9 supprim\xE9" : message.replyToMessage.content.length > 100 ? message.replyToMessage.content.slice(0, 100) + "\u2026" : message.replyToMessage.content
|
|
3557
|
+
}
|
|
3558
|
+
)
|
|
3559
|
+
]
|
|
3560
|
+
}
|
|
3561
|
+
),
|
|
3562
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3563
|
+
"div",
|
|
3564
|
+
{
|
|
3565
|
+
ref: contentRef,
|
|
3566
|
+
className: cn(
|
|
3567
|
+
"streamdown-message overflow-hidden text-sm leading-relaxed transition-all duration-300 ease-in-out",
|
|
3568
|
+
isUser && "text-white [&_a]:text-white/80 [&_a]:underline [&_code]:bg-white/15 [&_code]:text-white [&_strong]:text-white/90"
|
|
3569
|
+
),
|
|
3570
|
+
style: !isExpanded && isLong ? {
|
|
3571
|
+
maxHeight: `${COLLAPSED_MAX_HEIGHT}px`,
|
|
3572
|
+
maskImage: "linear-gradient(to bottom, black 70%, transparent 100%)",
|
|
3573
|
+
WebkitMaskImage: "linear-gradient(to bottom, black 70%, transparent 100%)"
|
|
3574
|
+
} : void 0,
|
|
3575
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3576
|
+
import_streamdown2.Streamdown,
|
|
3577
|
+
{
|
|
3578
|
+
mode: "static",
|
|
3579
|
+
plugins: { code: codePlugin, mermaid: import_mermaid.mermaid, math: import_math.math, cjk: import_cjk.cjk },
|
|
3580
|
+
components: workspaceStreamdownComponents,
|
|
3581
|
+
children: processedContent
|
|
3582
|
+
}
|
|
3583
|
+
)
|
|
3584
|
+
}
|
|
3585
|
+
),
|
|
3586
|
+
isLong && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3587
|
+
"button",
|
|
3588
|
+
{
|
|
3589
|
+
onClick: () => setIsExpanded((prev) => !prev),
|
|
3590
|
+
className: cn(
|
|
3591
|
+
"mt-2 flex cursor-pointer items-center gap-0.5 self-start text-[10px] font-semibold transition-colors select-none",
|
|
3592
|
+
isUser ? "text-white/85 hover:text-white" : "text-primary hover:text-primary-hover"
|
|
3593
|
+
),
|
|
3594
|
+
type: "button",
|
|
3595
|
+
children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3596
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.ChevronUp, { size: 12 }),
|
|
3597
|
+
"Voir moins"
|
|
3598
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3599
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.ChevronDown, { size: 12 }),
|
|
3600
|
+
"Voir plus"
|
|
3601
|
+
] })
|
|
3602
|
+
}
|
|
3603
|
+
),
|
|
3604
|
+
artefacts.length > 0 && (() => {
|
|
3605
|
+
const hasMany = artefacts.length > MAX_VISIBLE_ARTEFACTS;
|
|
3606
|
+
const visibleArtefacts = artefactsExpanded || !hasMany ? artefacts : artefacts.slice(0, MAX_VISIBLE_ARTEFACTS);
|
|
3607
|
+
const hiddenCount = artefacts.length - MAX_VISIBLE_ARTEFACTS;
|
|
3608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3609
|
+
"div",
|
|
3610
|
+
{
|
|
3611
|
+
className: cn(
|
|
3612
|
+
"mt-3 flex flex-col gap-2 border-t pt-2.5",
|
|
3613
|
+
isUser ? "border-white/20" : "border-border-light"
|
|
3614
|
+
),
|
|
3615
|
+
children: [
|
|
3616
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
|
|
3617
|
+
"div",
|
|
3618
|
+
{
|
|
3619
|
+
className: cn(
|
|
3620
|
+
"flex items-center gap-1 text-[10px] font-medium",
|
|
3621
|
+
isUser ? "text-white/60" : "text-text-muted"
|
|
3622
|
+
),
|
|
3623
|
+
children: [
|
|
3624
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Paperclip, { size: 10 }),
|
|
3625
|
+
artefacts.length,
|
|
3626
|
+
" pi\xE8ce",
|
|
3627
|
+
artefacts.length > 1 ? "s" : "",
|
|
3628
|
+
" jointe",
|
|
3629
|
+
artefacts.length > 1 ? "s" : ""
|
|
3630
|
+
]
|
|
3631
|
+
}
|
|
3632
|
+
),
|
|
3633
|
+
visibleArtefacts.map((art) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3634
|
+
ArtefactCard,
|
|
3635
|
+
{
|
|
3636
|
+
artefact: art,
|
|
3637
|
+
isUserMessage: isUser,
|
|
3638
|
+
workspaceId,
|
|
3639
|
+
onView: () => setViewingArtefact(art),
|
|
3640
|
+
onDeleted: onArtefactDeleted,
|
|
3641
|
+
canDelete: isOwner || art.createdBy === currentUserId
|
|
3642
|
+
},
|
|
3643
|
+
art._id
|
|
3644
|
+
)),
|
|
3645
|
+
hasMany && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3646
|
+
"button",
|
|
3647
|
+
{
|
|
3648
|
+
onClick: () => setArtefactsExpanded((prev) => !prev),
|
|
3649
|
+
className: cn(
|
|
3650
|
+
"mt-0.5 flex cursor-pointer items-center gap-1 self-start rounded-md px-2 py-1 text-[10px] font-semibold transition-all select-none",
|
|
3651
|
+
isUser ? "text-white/80 hover:bg-white/10 hover:text-white" : "text-primary hover:bg-primary-light hover:text-primary-hover"
|
|
3652
|
+
),
|
|
3653
|
+
type: "button",
|
|
3654
|
+
children: artefactsExpanded ? /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3655
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.ChevronUp, { size: 12 }),
|
|
3656
|
+
"Voir moins"
|
|
3657
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_jsx_runtime20.Fragment, { children: [
|
|
3658
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.ChevronDown, { size: 12 }),
|
|
3659
|
+
"+",
|
|
3660
|
+
hiddenCount,
|
|
3661
|
+
" autre",
|
|
3662
|
+
hiddenCount > 1 ? "s" : "",
|
|
3663
|
+
" pi\xE8ce",
|
|
3664
|
+
hiddenCount > 1 ? "s" : "",
|
|
3665
|
+
" jointe",
|
|
3666
|
+
hiddenCount > 1 ? "s" : ""
|
|
3667
|
+
] })
|
|
3668
|
+
}
|
|
3669
|
+
)
|
|
3670
|
+
]
|
|
3671
|
+
}
|
|
3672
|
+
);
|
|
3673
|
+
})()
|
|
3674
|
+
]
|
|
3675
|
+
}
|
|
3676
|
+
),
|
|
3677
|
+
!isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex flex-shrink-0 items-center gap-0.5 self-center opacity-0 transition-opacity group-hover:opacity-100", children: [
|
|
3678
|
+
onToggleReaction && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3679
|
+
ReactionPicker,
|
|
3680
|
+
{
|
|
3681
|
+
isUser,
|
|
3682
|
+
onSelect: (r) => onToggleReaction(message._id, r)
|
|
3683
|
+
}
|
|
3684
|
+
),
|
|
3685
|
+
onTogglePin && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3686
|
+
"button",
|
|
3687
|
+
{
|
|
3688
|
+
onClick: (e) => {
|
|
3689
|
+
e.stopPropagation();
|
|
3690
|
+
onTogglePin(message._id);
|
|
3691
|
+
},
|
|
3692
|
+
className: cn(
|
|
3693
|
+
"rounded-full p-1 shadow-sm transition-all",
|
|
3694
|
+
"bg-surface border-border border",
|
|
3695
|
+
isPinned ? "text-amber-500 hover:text-amber-600" : "text-text-muted hover:text-text"
|
|
3696
|
+
),
|
|
3697
|
+
title: isPinned ? "D\xE9s\xE9pingler" : "\xC9pingler",
|
|
3698
|
+
children: isPinned ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.PinOff, { size: 14 }) : /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Pin, { size: 14 })
|
|
3699
|
+
}
|
|
3700
|
+
)
|
|
3701
|
+
] })
|
|
3702
|
+
] }),
|
|
3703
|
+
onToggleReaction && reactions.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3704
|
+
ReactionBar,
|
|
3705
|
+
{
|
|
3706
|
+
reactions,
|
|
3707
|
+
currentUserId,
|
|
3708
|
+
isUser,
|
|
3709
|
+
onToggle: (r) => onToggleReaction(message._id, r)
|
|
3710
|
+
}
|
|
3711
|
+
),
|
|
3712
|
+
isUser && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)("div", { className: "flex items-center gap-1 self-end px-1", children: [
|
|
3713
|
+
message.editedAt && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px] italic", children: "(modifi\xE9)" }),
|
|
3714
|
+
onReply && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3715
|
+
"button",
|
|
3716
|
+
{
|
|
3717
|
+
onClick: () => onReply(message),
|
|
3718
|
+
className: "text-text-muted hover:text-primary rounded p-0.5 opacity-0 transition-all group-hover:opacity-100",
|
|
3719
|
+
title: "R\xE9pondre",
|
|
3720
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react14.Reply, { size: 13 })
|
|
3721
|
+
}
|
|
3722
|
+
),
|
|
3723
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)("span", { className: "text-text-placeholder text-[10px]", children: time })
|
|
3724
|
+
] })
|
|
3725
|
+
] })
|
|
3726
|
+
]
|
|
3727
|
+
}
|
|
3728
|
+
),
|
|
3729
|
+
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3730
|
+
ConfirmModal,
|
|
3731
|
+
{
|
|
3732
|
+
open: confirmDeleteOpen,
|
|
3733
|
+
onClose: () => setConfirmDeleteOpen(false),
|
|
3734
|
+
onConfirm: () => {
|
|
3735
|
+
onDeleteMessage?.(message._id);
|
|
3736
|
+
setConfirmDeleteOpen(false);
|
|
3737
|
+
},
|
|
3738
|
+
title: "Supprimer le message",
|
|
3739
|
+
description: "Voulez-vous vraiment supprimer ce message ? Cette action est irr\xE9versible.",
|
|
3740
|
+
confirmText: "Supprimer",
|
|
3741
|
+
danger: true
|
|
3742
|
+
}
|
|
3743
|
+
),
|
|
3744
|
+
viewingArtefact && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
|
|
3745
|
+
ArtefactViewerDialog,
|
|
3746
|
+
{
|
|
3747
|
+
artefact: viewingArtefact,
|
|
3748
|
+
onClose: () => setViewingArtefact(null)
|
|
3749
|
+
}
|
|
3750
|
+
)
|
|
3751
|
+
] });
|
|
3752
|
+
};
|
|
3753
|
+
|
|
3754
|
+
// src/components/MessageFeed.tsx
|
|
3755
|
+
var import_jsx_runtime21 = require("react/jsx-runtime");
|
|
3756
|
+
function getRole(msg, currentUserId) {
|
|
3757
|
+
if (msg.senderType === "agent") return "assistant";
|
|
3758
|
+
if (currentUserId && msg.senderId === currentUserId) return "user";
|
|
3759
|
+
return "participant";
|
|
3760
|
+
}
|
|
3761
|
+
function getDateKey(dateStr) {
|
|
3762
|
+
const d = typeof dateStr === "string" ? (0, import_date_fns.parseISO)(dateStr) : new Date(dateStr);
|
|
3763
|
+
return (0, import_date_fns.format)(d, "yyyy-MM-dd");
|
|
3764
|
+
}
|
|
3765
|
+
function formatDateLabel(dateStr) {
|
|
3766
|
+
const d = typeof dateStr === "string" ? (0, import_date_fns.parseISO)(dateStr) : new Date(dateStr);
|
|
3767
|
+
if ((0, import_date_fns.isToday)(d)) return "Aujourd'hui";
|
|
3768
|
+
if ((0, import_date_fns.isYesterday)(d)) return "Hier";
|
|
3769
|
+
return (0, import_date_fns.format)(d, "EEEE d MMMM yyyy", { locale: import_locale.fr });
|
|
3770
|
+
}
|
|
3771
|
+
function DateSeparator({ date, isCollapsed, onToggle, messageCount }) {
|
|
3772
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center gap-3 py-2 select-none", role: "separator", children: [
|
|
3773
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-px flex-1" }),
|
|
3774
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
3775
|
+
"button",
|
|
3776
|
+
{
|
|
3777
|
+
onClick: onToggle,
|
|
3778
|
+
className: "bg-[var(--color-surface,#fff)] border-[var(--color-border,#e2e8f0)] text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] hover:bg-[var(--color-bg,#f8fafc)] flex cursor-pointer items-center gap-1.5 rounded-full border px-3 py-1 text-[11px] font-medium capitalize shadow-sm transition-colors",
|
|
3779
|
+
type: "button",
|
|
3780
|
+
children: [
|
|
3781
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: formatDateLabel(date) }),
|
|
3782
|
+
isCollapsed && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "bg-[var(--color-primary,#3b82f6)] flex h-4 min-w-[16px] items-center justify-center rounded-full px-1 text-[9px] leading-none font-bold text-white", children: messageCount }),
|
|
3783
|
+
isCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react15.ChevronRight, { size: 12, className: "text-[var(--color-text-muted,#94a3b8)] shrink-0" }) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react15.ChevronDown, { size: 12, className: "text-[var(--color-text-muted,#94a3b8)] shrink-0" })
|
|
3784
|
+
]
|
|
3785
|
+
}
|
|
3786
|
+
),
|
|
3787
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "bg-[var(--color-border,#e2e8f0)] h-px flex-1" })
|
|
3788
|
+
] });
|
|
3789
|
+
}
|
|
3790
|
+
var BOTTOM_THRESHOLD = 80;
|
|
3791
|
+
var MessageFeed = ({
|
|
3792
|
+
messages,
|
|
3793
|
+
members = [],
|
|
3794
|
+
currentUserId,
|
|
3795
|
+
workspaceId,
|
|
3796
|
+
avatarUsers,
|
|
3797
|
+
isTyping,
|
|
3798
|
+
typingText,
|
|
3799
|
+
hasMore = false,
|
|
3800
|
+
loadingMore = false,
|
|
3801
|
+
onLoadMore,
|
|
3802
|
+
onArtefactDeleted,
|
|
3803
|
+
onReply,
|
|
3804
|
+
onEditMessage,
|
|
3805
|
+
onDeleteMessage,
|
|
3806
|
+
onToggleReaction,
|
|
3807
|
+
onTogglePin,
|
|
3808
|
+
isOwner = false
|
|
3809
|
+
}) => {
|
|
3810
|
+
const feedRef = (0, import_react17.useRef)(null);
|
|
3811
|
+
const initialScrollDone = (0, import_react17.useRef)(false);
|
|
3812
|
+
const prevScrollHeight = (0, import_react17.useRef)(0);
|
|
3813
|
+
const prevMessageCount = (0, import_react17.useRef)(0);
|
|
3814
|
+
const isAtBottom = (0, import_react17.useRef)(true);
|
|
3815
|
+
const [newMessageCount, setNewMessageCount] = (0, import_react17.useState)(0);
|
|
3816
|
+
const [collapsedDates, setCollapsedDates] = (0, import_react17.useState)({});
|
|
3817
|
+
const toggleDateCollapse = (0, import_react17.useCallback)((dateKey) => {
|
|
3818
|
+
setCollapsedDates((prev) => ({ ...prev, [dateKey]: !prev[dateKey] }));
|
|
3819
|
+
}, []);
|
|
3820
|
+
const checkIsAtBottom = (0, import_react17.useCallback)(() => {
|
|
3821
|
+
const el = feedRef.current;
|
|
3822
|
+
if (!el) return true;
|
|
3823
|
+
return el.scrollHeight - el.scrollTop - el.clientHeight < BOTTOM_THRESHOLD;
|
|
3824
|
+
}, []);
|
|
3825
|
+
const scrollToBottom = (0, import_react17.useCallback)(() => {
|
|
3826
|
+
const el = feedRef.current;
|
|
3827
|
+
if (!el) return;
|
|
3828
|
+
el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
|
|
3829
|
+
isAtBottom.current = true;
|
|
3830
|
+
setNewMessageCount(0);
|
|
3831
|
+
}, []);
|
|
3832
|
+
const feedItems = (0, import_react17.useMemo)(() => {
|
|
3833
|
+
const counts = {};
|
|
3834
|
+
for (const msg of messages) {
|
|
3835
|
+
const k = getDateKey(msg.createdAt);
|
|
3836
|
+
counts[k] = (counts[k] || 0) + 1;
|
|
3837
|
+
}
|
|
3838
|
+
const items = [];
|
|
3839
|
+
let lastDateKey = "";
|
|
3840
|
+
for (const msg of messages) {
|
|
3841
|
+
const dateKey = getDateKey(msg.createdAt);
|
|
3842
|
+
if (dateKey !== lastDateKey) {
|
|
3843
|
+
items.push({ type: "separator", dateKey, date: msg.createdAt, messageCount: counts[dateKey] || 0 });
|
|
3844
|
+
lastDateKey = dateKey;
|
|
3845
|
+
}
|
|
3846
|
+
items.push({ type: "message", dateKey, msg });
|
|
3847
|
+
}
|
|
3848
|
+
return items;
|
|
3849
|
+
}, [messages]);
|
|
3850
|
+
(0, import_react17.useEffect)(() => {
|
|
3851
|
+
const el = feedRef.current;
|
|
3852
|
+
if (!el) return;
|
|
3853
|
+
const messageCountGrew = messages.length > prevMessageCount.current;
|
|
3854
|
+
const prependedOlder = messageCountGrew && prevScrollHeight.current > 0 && initialScrollDone.current;
|
|
3855
|
+
if (!initialScrollDone.current) {
|
|
3856
|
+
el.scrollTop = el.scrollHeight;
|
|
3857
|
+
initialScrollDone.current = true;
|
|
3858
|
+
} else if (prependedOlder) {
|
|
3859
|
+
el.scrollTop = el.scrollHeight - prevScrollHeight.current;
|
|
3860
|
+
} else if (messageCountGrew) {
|
|
3861
|
+
if (isAtBottom.current) {
|
|
3862
|
+
el.scrollTop = el.scrollHeight;
|
|
3863
|
+
} else {
|
|
3864
|
+
setNewMessageCount((prev) => prev + messages.length - prevMessageCount.current);
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
prevMessageCount.current = messages.length;
|
|
3868
|
+
prevScrollHeight.current = 0;
|
|
3869
|
+
}, [messages]);
|
|
3870
|
+
(0, import_react17.useEffect)(() => {
|
|
3871
|
+
if (isTyping && feedRef.current && isAtBottom.current) feedRef.current.scrollTop = feedRef.current.scrollHeight;
|
|
3872
|
+
}, [isTyping]);
|
|
3873
|
+
const handleScroll = (0, import_react17.useCallback)(() => {
|
|
3874
|
+
const el = feedRef.current;
|
|
3875
|
+
if (!el) return;
|
|
3876
|
+
const atBottom = checkIsAtBottom();
|
|
3877
|
+
isAtBottom.current = atBottom;
|
|
3878
|
+
if (atBottom && newMessageCount > 0) setNewMessageCount(0);
|
|
3879
|
+
if (!hasMore || loadingMore || !onLoadMore) return;
|
|
3880
|
+
if (el.scrollTop <= 50) {
|
|
3881
|
+
prevScrollHeight.current = el.scrollHeight;
|
|
3882
|
+
onLoadMore();
|
|
3883
|
+
}
|
|
3884
|
+
}, [hasMore, loadingMore, onLoadMore, checkIsAtBottom, newMessageCount]);
|
|
3885
|
+
if (messages.length === 0) return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-1 flex-col items-center justify-center gap-2 text-center", ref: feedRef, children: [
|
|
3886
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react15.Brain, { size: 32, className: "text-[var(--color-text-muted,#94a3b8)]" }),
|
|
3887
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[var(--color-text-muted,#94a3b8)] text-sm", children: "Aucun message pour le moment." }),
|
|
3888
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("p", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Commencez par envoyer un message pour d\xE9marrer la conversation." })
|
|
3889
|
+
] });
|
|
3890
|
+
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "relative flex flex-1 flex-col overflow-hidden", children: [
|
|
3891
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex flex-1 flex-col gap-4 overflow-y-auto p-4", ref: feedRef, onScroll: handleScroll, children: [
|
|
3892
|
+
loadingMore && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "flex items-center justify-center gap-2 py-3", children: [
|
|
3893
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react15.Loader2, { size: 16, className: "text-[var(--color-primary,#3b82f6)] animate-spin" }),
|
|
3894
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs", children: "Chargement des messages pr\xE9c\xE9dents\u2026" })
|
|
3895
|
+
] }),
|
|
3896
|
+
!hasMore && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex items-center justify-center py-2", children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-[10px] italic", children: "D\xE9but de la conversation" }) }),
|
|
3897
|
+
feedItems.map(
|
|
3898
|
+
(item) => item.type === "separator" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3899
|
+
DateSeparator,
|
|
3900
|
+
{
|
|
3901
|
+
date: item.date,
|
|
3902
|
+
isCollapsed: !!collapsedDates[item.dateKey],
|
|
3903
|
+
onToggle: () => toggleDateCollapse(item.dateKey),
|
|
3904
|
+
messageCount: item.messageCount
|
|
3905
|
+
},
|
|
3906
|
+
`sep-${item.dateKey}`
|
|
3907
|
+
) : !collapsedDates[item.dateKey] ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
|
|
3908
|
+
MessageItem,
|
|
3909
|
+
{
|
|
3910
|
+
message: item.msg,
|
|
3911
|
+
role: getRole(item.msg, currentUserId),
|
|
3912
|
+
members,
|
|
3913
|
+
workspaceId,
|
|
3914
|
+
currentUserId,
|
|
3915
|
+
isOwner,
|
|
3916
|
+
avatarUsers,
|
|
3917
|
+
onArtefactDeleted,
|
|
3918
|
+
onReply,
|
|
3919
|
+
onEditMessage,
|
|
3920
|
+
onDeleteMessage,
|
|
3921
|
+
onToggleReaction,
|
|
3922
|
+
onTogglePin
|
|
3923
|
+
},
|
|
3924
|
+
item.msg._id
|
|
3925
|
+
) : null
|
|
3926
|
+
),
|
|
3927
|
+
isTyping && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("div", { className: "text-[var(--color-text-muted,#94a3b8)] flex items-center gap-2 self-start text-xs", children: [
|
|
3928
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "flex gap-0.5", children: [0, 150, 300].map((delay) => /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "bg-[var(--color-primary,#3b82f6)] h-1.5 w-1.5 animate-bounce rounded-full", style: { animationDelay: `${delay}ms` } }, delay)) }),
|
|
3929
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)("span", { children: typingText || "Assistant IA est en train d'\xE9crire\u2026" })
|
|
3930
|
+
] })
|
|
3931
|
+
] }),
|
|
3932
|
+
newMessageCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
|
|
3933
|
+
"button",
|
|
3934
|
+
{
|
|
3935
|
+
onClick: scrollToBottom,
|
|
3936
|
+
className: "bg-[var(--color-primary,#3b82f6)] hover:bg-[var(--color-primary-hover,#2563eb)] absolute bottom-4 left-1/2 z-10 flex -translate-x-1/2 items-center gap-2 rounded-full px-4 py-2 text-xs font-medium text-white shadow-lg transition-all hover:scale-105 active:scale-95",
|
|
3937
|
+
children: [
|
|
3938
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_lucide_react15.ArrowDown, { size: 14 }),
|
|
3939
|
+
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)("span", { children: [
|
|
3940
|
+
newMessageCount,
|
|
3941
|
+
" nouveau",
|
|
3942
|
+
newMessageCount > 1 ? "x" : "",
|
|
3943
|
+
" message",
|
|
3944
|
+
newMessageCount > 1 ? "s" : ""
|
|
3945
|
+
] })
|
|
3946
|
+
]
|
|
3947
|
+
}
|
|
3948
|
+
)
|
|
3949
|
+
] });
|
|
3950
|
+
};
|
|
3951
|
+
|
|
3952
|
+
// src/components/MessageFilterDialog.tsx
|
|
3953
|
+
var import_react18 = require("react");
|
|
3954
|
+
var import_lucide_react16 = require("lucide-react");
|
|
3955
|
+
var import_jsx_runtime22 = require("react/jsx-runtime");
|
|
3956
|
+
var EMPTY_FILTERS = { concernsMe: false, members: [] };
|
|
3957
|
+
var MessageFilterDialog = ({
|
|
3958
|
+
open,
|
|
3959
|
+
onClose,
|
|
3960
|
+
onApply,
|
|
3961
|
+
currentFilters,
|
|
3962
|
+
members,
|
|
3963
|
+
avatarUsers = [],
|
|
3964
|
+
hideAgents = false
|
|
3965
|
+
}) => {
|
|
3966
|
+
const displayedMembers = hideAgents ? members.filter((m) => m.memberType !== "agent") : members;
|
|
3967
|
+
const [draft, setDraft] = (0, import_react18.useState)({ ...currentFilters });
|
|
3968
|
+
const [applying, setApplying] = (0, import_react18.useState)(false);
|
|
3969
|
+
(0, import_react18.useEffect)(() => {
|
|
3970
|
+
if (open) setDraft({ ...currentFilters });
|
|
3971
|
+
}, [open, currentFilters]);
|
|
3972
|
+
const handleToggleConcernsMe = () => setDraft((prev) => ({
|
|
3973
|
+
concernsMe: !prev.concernsMe,
|
|
3974
|
+
members: !prev.concernsMe ? [] : prev.members
|
|
3975
|
+
}));
|
|
3976
|
+
const handleToggleMember = (memberId) => setDraft((prev) => {
|
|
3977
|
+
const isSelected = prev.members.includes(memberId);
|
|
3978
|
+
return { ...prev, members: isSelected ? prev.members.filter((id) => id !== memberId) : [...prev.members, memberId] };
|
|
3979
|
+
});
|
|
3980
|
+
const handleApply = async () => {
|
|
3981
|
+
setApplying(true);
|
|
3982
|
+
try {
|
|
3983
|
+
await onApply(draft);
|
|
3984
|
+
} finally {
|
|
3985
|
+
setApplying(false);
|
|
3986
|
+
onClose();
|
|
3987
|
+
}
|
|
3988
|
+
};
|
|
3989
|
+
const getAvatarForMember = (m) => avatarUsers.find((u) => u.email === m.memberId);
|
|
3990
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
3991
|
+
Modal,
|
|
3992
|
+
{
|
|
3993
|
+
open,
|
|
3994
|
+
onClose,
|
|
3995
|
+
size: "md",
|
|
3996
|
+
title: "Filtrer les messages",
|
|
3997
|
+
description: "Affinez les messages affich\xE9s dans la discussion.",
|
|
3998
|
+
titleIcon: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "bg-[var(--color-primary-light,#eff6ff)] text-[var(--color-primary,#3b82f6)] flex h-8 w-8 items-center justify-center rounded-lg", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react16.Filter, { size: 16 }) }),
|
|
3999
|
+
closeOnBackdropClick: true,
|
|
4000
|
+
closeOnEscape: !applying,
|
|
4001
|
+
closeButtonDisabled: applying,
|
|
4002
|
+
children: [
|
|
4003
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-5 px-6 py-5", children: [
|
|
4004
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
4005
|
+
"button",
|
|
4006
|
+
{
|
|
4007
|
+
type: "button",
|
|
4008
|
+
disabled: applying,
|
|
4009
|
+
onClick: handleToggleConcernsMe,
|
|
4010
|
+
className: cn(
|
|
4011
|
+
"flex items-center justify-between rounded-xl border px-4 py-3 text-left transition-all",
|
|
4012
|
+
draft.concernsMe ? "border-[var(--color-primary,#3b82f6)] bg-[var(--color-primary-light,#eff6ff)]" : "border-[var(--color-border,#e2e8f0)] hover:border-[var(--color-primary,#3b82f6)]/40"
|
|
4013
|
+
),
|
|
4014
|
+
children: [
|
|
4015
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col", children: [
|
|
4016
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: cn("text-sm font-medium", draft.concernsMe ? "text-[var(--color-primary,#3b82f6)]" : "text-[var(--color-text,#0f172a)]"), children: "Messages qui me concernent" }),
|
|
4017
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-[11px]", children: "Messages envoy\xE9s par moi ou dans lesquels je suis mentionn\xE9" })
|
|
4018
|
+
] }),
|
|
4019
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(
|
|
4020
|
+
"relative h-6 w-11 flex-shrink-0 rounded-full transition-colors",
|
|
4021
|
+
draft.concernsMe ? "bg-[var(--color-primary,#3b82f6)]" : "bg-[var(--color-border,#e2e8f0)]"
|
|
4022
|
+
), children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(
|
|
4023
|
+
"absolute top-0.5 h-5 w-5 rounded-full bg-white shadow-sm transition-transform",
|
|
4024
|
+
draft.concernsMe ? "translate-x-[22px]" : "translate-x-0.5"
|
|
4025
|
+
) }) })
|
|
4026
|
+
]
|
|
4027
|
+
}
|
|
4028
|
+
),
|
|
4029
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
4030
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("span", { className: "text-[var(--color-text-muted,#94a3b8)] text-xs font-semibold tracking-wider uppercase", children: "Filtrer par membres" }),
|
|
4031
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: cn(
|
|
4032
|
+
"flex max-h-52 flex-col gap-1 overflow-y-auto rounded-xl border p-2 transition-opacity",
|
|
4033
|
+
draft.concernsMe ? "pointer-events-none border-[var(--color-border,#e2e8f0)]/50 opacity-50" : "border-[var(--color-border,#e2e8f0)]"
|
|
4034
|
+
), children: [
|
|
4035
|
+
displayedMembers.map((member) => {
|
|
4036
|
+
const isSelected = draft.members.includes(member.memberId);
|
|
4037
|
+
const avatar = getAvatarForMember(member);
|
|
4038
|
+
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
4039
|
+
"button",
|
|
4040
|
+
{
|
|
4041
|
+
type: "button",
|
|
4042
|
+
disabled: draft.concernsMe || applying,
|
|
4043
|
+
onClick: () => handleToggleMember(member.memberId),
|
|
4044
|
+
className: cn(
|
|
4045
|
+
"flex items-center gap-2.5 rounded-lg px-3 py-2 text-left transition-all",
|
|
4046
|
+
isSelected ? "border-[var(--color-primary,#3b82f6)] bg-[var(--color-primary-light,#eff6ff)] border" : "border border-transparent hover:bg-[var(--color-bg,#f8fafc)]"
|
|
4047
|
+
),
|
|
4048
|
+
children: [
|
|
4049
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn(
|
|
4050
|
+
"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-[10px] font-semibold overflow-hidden",
|
|
4051
|
+
isSelected ? "bg-[var(--color-primary,#3b82f6)] text-white" : "bg-[var(--color-bg,#f8fafc)] text-[var(--color-text-muted,#94a3b8)]"
|
|
4052
|
+
), children: avatar?.avatarUrl ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("img", { src: avatar.avatarUrl, alt: member.memberName, className: "h-full w-full object-cover" }) : getInitials(member.memberName) }),
|
|
4053
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "min-w-0 flex-1", children: [
|
|
4054
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: cn("truncate text-xs font-medium", isSelected ? "text-[var(--color-primary,#3b82f6)]" : "text-[var(--color-text,#0f172a)]"), children: member.memberName }),
|
|
4055
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "text-[var(--color-text-muted,#94a3b8)] text-[10px]", children: member.memberType === "agent" ? "Agent IA" : "Utilisateur" })
|
|
4056
|
+
] }),
|
|
4057
|
+
isSelected && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("svg", { className: "text-[var(--color-primary,#3b82f6)] flex-shrink-0", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("path", { d: "M3 8.5L6 11.5L13 4.5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
4058
|
+
]
|
|
4059
|
+
},
|
|
4060
|
+
member._id
|
|
4061
|
+
);
|
|
4062
|
+
}),
|
|
4063
|
+
displayedMembers.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)("div", { className: "text-[var(--color-text-muted,#94a3b8)] py-4 text-center text-xs italic", children: "Aucun membre dans ce workspace" })
|
|
4064
|
+
] })
|
|
4065
|
+
] })
|
|
4066
|
+
] }),
|
|
4067
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "border-[var(--color-border,#e2e8f0)] flex items-center justify-between border-t px-6 py-3", children: [
|
|
4068
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
4069
|
+
"button",
|
|
4070
|
+
{
|
|
4071
|
+
type: "button",
|
|
4072
|
+
onClick: () => setDraft({ ...EMPTY_FILTERS }),
|
|
4073
|
+
disabled: applying,
|
|
4074
|
+
className: "text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] flex items-center gap-1.5 text-xs transition-colors disabled:opacity-50",
|
|
4075
|
+
children: [
|
|
4076
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react16.RotateCcw, { size: 12 }),
|
|
4077
|
+
" R\xE9initialiser"
|
|
4078
|
+
]
|
|
4079
|
+
}
|
|
4080
|
+
),
|
|
4081
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)("div", { className: "flex gap-2", children: [
|
|
4082
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
|
|
4083
|
+
"button",
|
|
4084
|
+
{
|
|
4085
|
+
type: "button",
|
|
4086
|
+
onClick: onClose,
|
|
4087
|
+
disabled: applying,
|
|
4088
|
+
className: "border-[var(--color-border,#e2e8f0)] text-[var(--color-text-muted,#94a3b8)] hover:text-[var(--color-text,#0f172a)] rounded-lg border px-4 py-1.5 text-xs font-medium transition-colors disabled:opacity-50",
|
|
4089
|
+
children: "Annuler"
|
|
4090
|
+
}
|
|
4091
|
+
),
|
|
4092
|
+
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
|
|
4093
|
+
"button",
|
|
4094
|
+
{
|
|
4095
|
+
type: "button",
|
|
4096
|
+
onClick: handleApply,
|
|
4097
|
+
disabled: applying,
|
|
4098
|
+
className: "bg-[var(--color-primary,#3b82f6)] hover:bg-[var(--color-primary-hover,#2563eb)] flex items-center gap-1.5 rounded-lg px-4 py-1.5 text-xs font-medium text-white transition-colors disabled:opacity-70",
|
|
4099
|
+
children: [
|
|
4100
|
+
applying && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_lucide_react16.Loader2, { size: 12, className: "animate-spin" }),
|
|
4101
|
+
applying ? "Application..." : "Appliquer"
|
|
4102
|
+
]
|
|
4103
|
+
}
|
|
4104
|
+
)
|
|
4105
|
+
] })
|
|
4106
|
+
] })
|
|
4107
|
+
]
|
|
4108
|
+
}
|
|
4109
|
+
);
|
|
4110
|
+
};
|
|
4111
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
4112
|
+
0 && (module.exports = {
|
|
4113
|
+
AVAILABLE_AGENTS,
|
|
4114
|
+
ArtefactViewerDialog,
|
|
4115
|
+
Badge,
|
|
4116
|
+
CATEGORY_STYLES,
|
|
4117
|
+
ChartRenderer,
|
|
4118
|
+
ConfirmModal,
|
|
4119
|
+
ContextPanel,
|
|
4120
|
+
CreatePrivateWorkspaceModal,
|
|
4121
|
+
CreateWorkspaceModal,
|
|
4122
|
+
EMPTY_FILTERS,
|
|
4123
|
+
FILTER_PILLS,
|
|
4124
|
+
ICON_COLOR,
|
|
4125
|
+
IGNORED_FIELDS,
|
|
4126
|
+
MEMBER_ROLE_LABELS,
|
|
4127
|
+
MemberAvatar,
|
|
4128
|
+
MembersPanel,
|
|
4129
|
+
MessageFeed,
|
|
4130
|
+
MessageFilterDialog,
|
|
4131
|
+
MessageItem,
|
|
4132
|
+
Modal,
|
|
4133
|
+
Pagination,
|
|
4134
|
+
ROLE_LABELS,
|
|
4135
|
+
STATUS_LABELS,
|
|
4136
|
+
SyncPanel,
|
|
4137
|
+
TableRenderer,
|
|
4138
|
+
Tooltip,
|
|
4139
|
+
WORKSPACE_FIELD_LABELS,
|
|
4140
|
+
WORKSPACE_STATUS_LABELS,
|
|
4141
|
+
WorkspaceCard,
|
|
4142
|
+
WorkspaceEvent,
|
|
4143
|
+
WorkspaceList,
|
|
4144
|
+
WorkspaceProvider,
|
|
4145
|
+
WorkspaceRow,
|
|
4146
|
+
cn,
|
|
4147
|
+
createWorkspaceApis,
|
|
4148
|
+
createWorkspaceClient,
|
|
4149
|
+
createWorkspaceListStore,
|
|
4150
|
+
createWorkspaceSocket,
|
|
4151
|
+
fixMojibake,
|
|
4152
|
+
formatWorkspaceExport,
|
|
4153
|
+
getInitials,
|
|
4154
|
+
useWorkspaceApi,
|
|
4155
|
+
useWorkspaceContext,
|
|
4156
|
+
workspaceStreamdownComponents
|
|
4157
|
+
});
|
|
4158
|
+
//# sourceMappingURL=index.js.map
|