@consilioweb/payload-support 2.0.1 → 3.0.0

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.
Files changed (215) hide show
  1. package/README.md +636 -402
  2. package/dist/components/RichTextEditor/index.d.ts +19 -0
  3. package/dist/components/TicketConversation/RewriteDropdown.d.ts +6 -0
  4. package/dist/components/TicketConversation/SkeletonText.d.ts +4 -0
  5. package/dist/components/TicketConversation/components/AISummaryPanel.d.ts +14 -0
  6. package/dist/components/TicketConversation/components/ActionPanels.d.ts +47 -0
  7. package/dist/components/TicketConversation/components/ActivityLog.d.ts +5 -0
  8. package/dist/components/TicketConversation/components/ClientBar.d.ts +5 -0
  9. package/dist/components/TicketConversation/components/ClientHistory.d.ts +24 -0
  10. package/dist/components/TicketConversation/components/CodeBlock.d.ts +26 -0
  11. package/dist/components/TicketConversation/components/CodeBlockInserter.d.ts +12 -0
  12. package/dist/components/TicketConversation/components/QuickActions.d.ts +32 -0
  13. package/dist/components/TicketConversation/components/TicketHeader.d.ts +19 -0
  14. package/dist/components/TicketConversation/components/TimeTrackingPanel.d.ts +24 -0
  15. package/dist/components/TicketConversation/config.d.ts +10 -0
  16. package/dist/components/TicketConversation/config.js +1 -40
  17. package/dist/components/TicketConversation/constants.d.ts +56 -0
  18. package/dist/components/TicketConversation/context.d.ts +53 -0
  19. package/dist/components/TicketConversation/hooks/useAI.d.ts +21 -0
  20. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.d.ts +3 -0
  21. package/dist/components/TicketConversation/hooks/useFeatures.d.ts +23 -0
  22. package/dist/components/TicketConversation/hooks/useFeatures.js +61 -0
  23. package/dist/components/TicketConversation/hooks/useMessageActions.d.ts +20 -0
  24. package/dist/components/TicketConversation/hooks/useReply.d.ts +27 -0
  25. package/dist/components/TicketConversation/hooks/useTicketActions.d.ts +58 -0
  26. package/dist/components/TicketConversation/hooks/useTimeTracking.d.ts +18 -0
  27. package/dist/components/TicketConversation/hooks/useTranslation.d.ts +33 -0
  28. package/dist/components/TicketConversation/index.d.ts +4 -0
  29. package/dist/components/TicketConversation/index.js +2 -2
  30. package/dist/components/TicketConversation/types.d.ts +56 -0
  31. package/dist/components/TicketConversation/utils.d.ts +2 -0
  32. package/dist/index.cjs +392 -320
  33. package/dist/index.d.cts +100 -3
  34. package/dist/index.d.ts +100 -3
  35. package/dist/index.js +388 -321
  36. package/dist/utils/features.d.ts +123 -0
  37. package/dist/utils/features.js +147 -0
  38. package/dist/views/BillingView/client.d.ts +2 -0
  39. package/dist/views/BillingView/index.d.ts +4 -0
  40. package/dist/views/ChatView/client.d.ts +2 -0
  41. package/dist/views/ChatView/client.js +9 -9
  42. package/dist/views/ChatView/index.d.ts +4 -0
  43. package/dist/views/CrmView/client.d.ts +2 -0
  44. package/dist/views/CrmView/client.js +6 -9
  45. package/dist/views/CrmView/index.d.ts +4 -0
  46. package/dist/views/EmailTrackingView/client.d.ts +2 -0
  47. package/dist/views/EmailTrackingView/index.d.ts +4 -0
  48. package/dist/views/ImportConversationView/client.d.ts +2 -0
  49. package/dist/views/ImportConversationView/index.d.ts +4 -0
  50. package/dist/views/LogsView/client.d.ts +2 -0
  51. package/dist/views/LogsView/index.d.ts +4 -0
  52. package/dist/views/NewTicketView/client.d.ts +2 -0
  53. package/dist/views/NewTicketView/index.d.ts +4 -0
  54. package/dist/views/PendingEmailsView/client.d.ts +2 -0
  55. package/dist/views/PendingEmailsView/index.d.ts +4 -0
  56. package/dist/views/SupportDashboardView/client.d.ts +2 -0
  57. package/dist/views/SupportDashboardView/index.d.ts +4 -0
  58. package/dist/views/TicketDetailView/NextActionItem.d.ts +6 -0
  59. package/dist/views/TicketDetailView/RewriteDropdown.d.ts +7 -0
  60. package/dist/views/TicketDetailView/client.d.ts +2 -0
  61. package/dist/views/TicketDetailView/client.js +2 -2
  62. package/dist/views/TicketDetailView/constants.d.ts +13 -0
  63. package/dist/views/TicketDetailView/helpers.d.ts +2 -0
  64. package/dist/views/TicketDetailView/index.d.ts +4 -0
  65. package/dist/views/TicketDetailView/types.d.ts +40 -0
  66. package/dist/views/TicketInboxView/client.d.ts +2 -0
  67. package/dist/views/TicketInboxView/client.js +5 -5
  68. package/dist/views/TicketInboxView/index.d.ts +4 -0
  69. package/dist/views/TicketingSettingsView/client.d.ts +2 -0
  70. package/dist/views/TicketingSettingsView/client.js +5 -4
  71. package/dist/views/TicketingSettingsView/index.d.ts +4 -0
  72. package/dist/views/TimeDashboardView/client.d.ts +2 -0
  73. package/dist/views/TimeDashboardView/index.d.ts +4 -0
  74. package/dist/views/shared/AdminViewHeader.d.ts +13 -0
  75. package/dist/views/shared/ErrorBoundary.d.ts +17 -0
  76. package/dist/views/shared/Skeleton.d.ts +21 -0
  77. package/dist/views/shared/StatusPill.d.ts +8 -0
  78. package/dist/views/shared/adminTokens.d.ts +19 -0
  79. package/dist/views/shared/config.d.ts +8 -0
  80. package/dist/views/shared/config.js +1 -40
  81. package/dist/views/shared/dateLocale.d.ts +5 -0
  82. package/dist/views/shared/index.d.ts +12 -0
  83. package/dist/views/shared/index.js +2 -1
  84. package/dist/views/shared/sla.d.ts +14 -0
  85. package/dist/views.d.ts +13 -13
  86. package/package.json +16 -31
  87. package/src/collections/SupportClients.ts +29 -0
  88. package/src/collections/TicketCollaborators.ts +6 -1
  89. package/src/collections/TicketMessages.ts +46 -24
  90. package/src/collections/Tickets.ts +117 -79
  91. package/src/collections/TimeEntries.ts +57 -32
  92. package/src/components/TicketConversation/config.ts +16 -80
  93. package/src/components/TicketConversation/hooks/useFeatures.ts +109 -0
  94. package/src/components/TicketConversation/index.tsx +2 -2
  95. package/src/endpoints/ai.ts +2 -1
  96. package/src/endpoints/client-intelligence.ts +2 -1
  97. package/src/endpoints/process-scheduled.ts +13 -8
  98. package/src/endpoints/round-robin-config.ts +30 -20
  99. package/src/endpoints/settings.ts +28 -77
  100. package/src/index.ts +6 -2
  101. package/src/portal/LiveChat.tsx +33 -22
  102. package/src/portal/auth/tickets/detail/TicketPolling.tsx +5 -1
  103. package/src/types/lucide-react.d.ts +10 -2
  104. package/src/utils/aiAgent.ts +2 -1
  105. package/src/utils/aiProvider.ts +21 -0
  106. package/src/utils/features.ts +297 -0
  107. package/src/utils/fireWebhooks.ts +6 -0
  108. package/src/utils/generateTicketSynthesis.ts +2 -1
  109. package/src/utils/index.ts +4 -2
  110. package/src/utils/readSettings.ts +82 -9
  111. package/src/utils/webhookDispatcher.ts +19 -7
  112. package/src/views/ChatView/client.tsx +13 -9
  113. package/src/views/CrmView/client.tsx +10 -11
  114. package/src/views/TicketDetailView/client.tsx +2 -2
  115. package/src/views/TicketInboxView/client.tsx +10 -5
  116. package/src/views/TicketingSettingsView/client.tsx +8 -6
  117. package/src/views/shared/config.ts +14 -80
  118. package/src/views/shared/index.ts +3 -1
  119. package/dist/components/RichTextEditor/index.cjs +0 -279
  120. package/dist/components/TicketConversation/RewriteDropdown.cjs +0 -98
  121. package/dist/components/TicketConversation/SkeletonText.cjs +0 -21
  122. package/dist/components/TicketConversation/components/AISummaryPanel.cjs +0 -85
  123. package/dist/components/TicketConversation/components/ActionPanels.cjs +0 -193
  124. package/dist/components/TicketConversation/components/ActivityLog.cjs +0 -24
  125. package/dist/components/TicketConversation/components/ClientBar.cjs +0 -43
  126. package/dist/components/TicketConversation/components/ClientHistory.cjs +0 -138
  127. package/dist/components/TicketConversation/components/CodeBlock.cjs +0 -171
  128. package/dist/components/TicketConversation/components/CodeBlockInserter.cjs +0 -156
  129. package/dist/components/TicketConversation/components/QuickActions.cjs +0 -87
  130. package/dist/components/TicketConversation/components/TicketHeader.cjs +0 -93
  131. package/dist/components/TicketConversation/components/TimeTrackingPanel.cjs +0 -134
  132. package/dist/components/TicketConversation/config.cjs +0 -44
  133. package/dist/components/TicketConversation/constants.cjs +0 -127
  134. package/dist/components/TicketConversation/context.cjs +0 -13
  135. package/dist/components/TicketConversation/hooks/useAI.cjs +0 -196
  136. package/dist/components/TicketConversation/hooks/useDocumentIdFromUrl.cjs +0 -20
  137. package/dist/components/TicketConversation/hooks/useMessageActions.cjs +0 -136
  138. package/dist/components/TicketConversation/hooks/useReply.cjs +0 -188
  139. package/dist/components/TicketConversation/hooks/useTicketActions.cjs +0 -290
  140. package/dist/components/TicketConversation/hooks/useTimeTracking.cjs +0 -123
  141. package/dist/components/TicketConversation/hooks/useTranslation.cjs +0 -101
  142. package/dist/components/TicketConversation/index.cjs +0 -1114
  143. package/dist/components/TicketConversation/types.cjs +0 -2
  144. package/dist/components/TicketConversation/utils.cjs +0 -27
  145. package/dist/views/BillingView/client.cjs +0 -469
  146. package/dist/views/BillingView/index.cjs +0 -34
  147. package/dist/views/ChatView/client.cjs +0 -301
  148. package/dist/views/ChatView/index.cjs +0 -34
  149. package/dist/views/CrmView/client.cjs +0 -458
  150. package/dist/views/CrmView/index.cjs +0 -34
  151. package/dist/views/EmailTrackingView/client.cjs +0 -171
  152. package/dist/views/EmailTrackingView/index.cjs +0 -34
  153. package/dist/views/ImportConversationView/client.cjs +0 -238
  154. package/dist/views/ImportConversationView/index.cjs +0 -34
  155. package/dist/views/LogsView/client.cjs +0 -148
  156. package/dist/views/LogsView/index.cjs +0 -32
  157. package/dist/views/NewTicketView/client.cjs +0 -213
  158. package/dist/views/NewTicketView/index.cjs +0 -32
  159. package/dist/views/PendingEmailsView/client.cjs +0 -471
  160. package/dist/views/PendingEmailsView/index.cjs +0 -34
  161. package/dist/views/SupportDashboardView/client.cjs +0 -437
  162. package/dist/views/SupportDashboardView/index.cjs +0 -34
  163. package/dist/views/TicketDetailView/NextActionItem.cjs +0 -40
  164. package/dist/views/TicketDetailView/RewriteDropdown.cjs +0 -80
  165. package/dist/views/TicketDetailView/client.cjs +0 -1412
  166. package/dist/views/TicketDetailView/constants.cjs +0 -20
  167. package/dist/views/TicketDetailView/helpers.cjs +0 -23
  168. package/dist/views/TicketDetailView/index.cjs +0 -34
  169. package/dist/views/TicketDetailView/types.cjs +0 -2
  170. package/dist/views/TicketInboxView/client.cjs +0 -359
  171. package/dist/views/TicketInboxView/index.cjs +0 -32
  172. package/dist/views/TicketingSettingsView/client.cjs +0 -740
  173. package/dist/views/TicketingSettingsView/index.cjs +0 -34
  174. package/dist/views/TimeDashboardView/client.cjs +0 -180
  175. package/dist/views/TimeDashboardView/index.cjs +0 -34
  176. package/dist/views/shared/AdminViewHeader.cjs +0 -68
  177. package/dist/views/shared/ErrorBoundary.cjs +0 -55
  178. package/dist/views/shared/Skeleton.cjs +0 -77
  179. package/dist/views/shared/StatusPill.cjs +0 -47
  180. package/dist/views/shared/adminTokens.cjs +0 -34
  181. package/dist/views/shared/config.cjs +0 -44
  182. package/dist/views/shared/dateLocale.cjs +0 -5
  183. package/dist/views/shared/index.cjs +0 -72
  184. package/dist/views/shared/sla.cjs +0 -37
  185. package/dist/views.cjs +0 -86
  186. package/dist/views.d.cts +0 -13
  187. package/src/__tests__/aiSummaryRendering.test.ts +0 -21
  188. package/src/__tests__/authResponses.test.ts +0 -69
  189. package/src/__tests__/capabilities.test.ts +0 -71
  190. package/src/__tests__/generateTrackingToken.test.ts +0 -67
  191. package/src/__tests__/integration/ai-agent.test.ts +0 -46
  192. package/src/__tests__/integration/auth.test.ts +0 -43
  193. package/src/__tests__/integration/automation-rules.test.ts +0 -68
  194. package/src/__tests__/integration/buildTestPayload.ts +0 -52
  195. package/src/__tests__/integration/channels.test.ts +0 -52
  196. package/src/__tests__/integration/core-behaviors.test.ts +0 -81
  197. package/src/__tests__/integration/dashboard-volume.test.ts +0 -31
  198. package/src/__tests__/integration/digest.test.ts +0 -48
  199. package/src/__tests__/integration/invoice-pdf.test.ts +0 -30
  200. package/src/__tests__/integration/isolation.test.ts +0 -81
  201. package/src/__tests__/integration/mentions-invoice.test.ts +0 -50
  202. package/src/__tests__/integration/nps.test.ts +0 -37
  203. package/src/__tests__/integration/per-team-sla.test.ts +0 -44
  204. package/src/__tests__/integration/push.test.ts +0 -75
  205. package/src/__tests__/integration/sanitization.test.ts +0 -35
  206. package/src/__tests__/integration/sla-pause.test.ts +0 -39
  207. package/src/__tests__/integration/smoke.test.ts +0 -26
  208. package/src/__tests__/integration/snooze.test.ts +0 -68
  209. package/src/__tests__/integration/teams.test.ts +0 -38
  210. package/src/__tests__/rateLimiter.test.ts +0 -83
  211. package/src/__tests__/sanitizeHtml.test.ts +0 -165
  212. package/src/__tests__/sla.test.ts +0 -258
  213. package/src/__tests__/trackOpenEndpoint.test.ts +0 -54
  214. package/src/__tests__/translations.test.ts +0 -67
  215. package/src/__tests__/webhookSecurity.test.ts +0 -58
@@ -4,7 +4,8 @@ import React, { useState, useEffect } from 'react'
4
4
  import { Settings, Mail, Bot, Clock, Timer, Globe, FileSignature } from 'lucide-react'
5
5
  import { V, btnStyle } from '../shared/adminTokens'
6
6
  import { AdminViewHeader } from '../shared/AdminViewHeader'
7
- import { getFeatures, saveFeatures, DEFAULT_FEATURES, type TicketingFeatures } from '../../components/TicketConversation/config'
7
+ import { DEFAULT_FEATURES, type TicketingFeatures } from '../../components/TicketConversation/config'
8
+ import { useFeatures } from '../../components/TicketConversation/hooks/useFeatures'
8
9
  import { useTranslation } from '../../components/TicketConversation/hooks/useTranslation'
9
10
  import ts from '../../styles/TicketingSettings.module.scss'
10
11
 
@@ -301,7 +302,7 @@ export const TicketingSettingsClient: React.FC = () => {
301
302
  const { t } = useTranslation()
302
303
  const FEATURE_LIST = React.useMemo(() => getFeatureList(t), [t])
303
304
  const CATEGORIES = React.useMemo(() => getCategories(t), [t])
304
- const [features, setFeatures] = useState<TicketingFeatures>(() => getFeatures())
305
+ const { features, setFeatures, save: saveFeatures } = useFeatures()
305
306
  const [settings, setSettings] = useState<AllSettings>(DEFAULT_SETTINGS)
306
307
  const [signature, setSignature] = useState('')
307
308
  const [saved, setSaved] = useState(false)
@@ -355,15 +356,16 @@ export const TicketingSettingsClient: React.FC = () => {
355
356
 
356
357
  const handleSave = async () => {
357
358
  setSaving(true)
358
- // Save features to localStorage (UI-only flags)
359
- saveFeatures(features)
360
- // Save global settings + per-user prefs
359
+ // Feature flags and the other blocks share one preference row, so the two
360
+ // writes are sequenced: running them in parallel would make the second
361
+ // request merge onto settings read before the first one landed.
362
+ const featuresOk = await saveFeatures(features)
361
363
  const [settingsOk, prefsOk] = await Promise.all([
362
364
  saveSettingsToAPI(settings),
363
365
  saveUserPrefs({ locale: settings.locale.language, signature }),
364
366
  ])
365
367
  setSaving(false)
366
- if (settingsOk && prefsOk) {
368
+ if (featuresOk && settingsOk && prefsOk) {
367
369
  setSaved(true)
368
370
  setTimeout(() => setSaved(false), 3000)
369
371
  }
@@ -1,82 +1,16 @@
1
1
  /**
2
- * Feature flags for the ticketing module.
3
- * Each feature can be enabled/disabled by the admin.
4
- * When disabled, the corresponding UI section is hidden entirely.
2
+ * Ticketing feature flags re-export of the shared implementation.
3
+ *
4
+ * See `utils/features.ts`. This module is kept so that
5
+ * `views/shared/index.ts` and existing view imports keep resolving.
5
6
  */
6
- export interface TicketingFeatures {
7
- /** Time tracking: timer, manual entries, billing */
8
- timeTracking: boolean
9
- /** AI features: sentiment, synthesis, suggestion, rewrite */
10
- ai: boolean
11
- /** Satisfaction surveys: CSAT rating after resolution */
12
- satisfaction: boolean
13
- /** Live chat integration: chat -> ticket conversion */
14
- chat: boolean
15
- /** Email tracking: pixel tracking, open/sent status per message */
16
- emailTracking: boolean
17
- /** Canned responses: quick reply templates */
18
- canned: boolean
19
- /** Ticket merge: combine two tickets into one */
20
- merge: boolean
21
- /** Snooze: temporarily hide a ticket */
22
- snooze: boolean
23
- /** External messages: add messages received outside the system */
24
- externalMessages: boolean
25
- /** Client history: past tickets, projects, notes sidebar */
26
- clientHistory: boolean
27
- /** Activity log: audit trail of actions on the ticket */
28
- activityLog: boolean
29
- /** Split ticket: extract a message into a new ticket */
30
- splitTicket: boolean
31
- /** Scheduled replies: send a message at a future date */
32
- scheduledReplies: boolean
33
- /** Auto-close: automatically resolve inactive tickets */
34
- autoClose: boolean
35
- /** Auto-close delay in days (used by auto-close cron) */
36
- autoCloseDays: number
37
- /** Round-robin: distribute new tickets evenly among agents */
38
- roundRobin: boolean
39
- }
40
-
41
- /** Default features -- all enabled */
42
- export const DEFAULT_FEATURES: TicketingFeatures = {
43
- timeTracking: true,
44
- ai: true,
45
- satisfaction: true,
46
- chat: true,
47
- emailTracking: true,
48
- canned: true,
49
- merge: true,
50
- snooze: true,
51
- externalMessages: true,
52
- clientHistory: true,
53
- activityLog: true,
54
- splitTicket: true,
55
- scheduledReplies: true,
56
- autoClose: true,
57
- autoCloseDays: 7,
58
- roundRobin: false,
59
- }
60
-
61
- const STORAGE_KEY = 'ticketing_features'
62
-
63
- /** Read features from localStorage (falls back to defaults) */
64
- export function getFeatures(): TicketingFeatures {
65
- if (typeof window === 'undefined') return DEFAULT_FEATURES
66
- try {
67
- const stored = localStorage.getItem(STORAGE_KEY)
68
- if (stored) {
69
- const parsed = JSON.parse(stored)
70
- return { ...DEFAULT_FEATURES, ...parsed }
71
- }
72
- } catch { /* ignore */ }
73
- return DEFAULT_FEATURES
74
- }
75
-
76
- /** Save features to localStorage */
77
- export function saveFeatures(features: TicketingFeatures): void {
78
- if (typeof window === 'undefined') return
79
- try {
80
- localStorage.setItem(STORAGE_KEY, JSON.stringify(features))
81
- } catch { /* ignore */ }
82
- }
7
+ export {
8
+ DEFAULT_TICKETING_FEATURES,
9
+ DEFAULT_TICKETING_FEATURES as DEFAULT_FEATURES,
10
+ FEATURES_CACHE_KEY,
11
+ fetchFeatures,
12
+ getFeatures,
13
+ normalizeFeatures,
14
+ saveFeatures,
15
+ } from '../../utils/features'
16
+ export type { TicketingFeatures } from '../../utils/features'
@@ -2,8 +2,10 @@ export { AdminErrorBoundary } from './ErrorBoundary'
2
2
  export { V, btnStyle } from './adminTokens'
3
3
  export { AdminViewHeader } from './AdminViewHeader'
4
4
  export { Skeleton, SkeletonText, SkeletonCard, SkeletonTable, SkeletonDashboard } from './Skeleton'
5
- export { getFeatures, saveFeatures, DEFAULT_FEATURES } from './config'
5
+ export { getFeatures, saveFeatures, fetchFeatures, normalizeFeatures, DEFAULT_FEATURES } from './config'
6
6
  export type { TicketingFeatures } from './config'
7
+ export { useFeatures } from '../../components/TicketConversation/hooks/useFeatures'
8
+ export type { UseFeaturesResult } from '../../components/TicketConversation/hooks/useFeatures'
7
9
  export { StatusPill } from './StatusPill'
8
10
  export type { StatusPillProps, TicketStatusVariant } from './StatusPill'
9
11
  export { computeSlaState, formatSlaRemaining } from './sla'
@@ -1,279 +0,0 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var react = require('react');
5
-
6
- const RichTextEditor = react.forwardRef(function RichTextEditor2({
7
- initialValue = "",
8
- onChange,
9
- placeholder = "\xC9crivez votre message...",
10
- minHeight = 150,
11
- onFileUpload,
12
- borderColor = "#000",
13
- focusBorderColor = "#00E5FF"
14
- }, ref) {
15
- const editorRef = react.useRef(null);
16
- const fileInputRef = react.useRef(null);
17
- const [focused, setFocused] = react.useState(false);
18
- const [isEmpty, setIsEmpty] = react.useState(!initialValue);
19
- react.useEffect(() => {
20
- if (editorRef.current && initialValue) {
21
- editorRef.current.innerHTML = initialValue;
22
- const text = editorRef.current.innerText?.trim() || "";
23
- setIsEmpty(!text && !editorRef.current.querySelector("img"));
24
- }
25
- }, []);
26
- const emitChange = react.useCallback(() => {
27
- if (!editorRef.current) return;
28
- const html = editorRef.current.innerHTML;
29
- const text = editorRef.current.innerText?.trim() || "";
30
- const empty = !text && !editorRef.current.querySelector("img");
31
- setIsEmpty(empty);
32
- onChange(empty ? "" : html, text);
33
- }, [onChange]);
34
- const exec = react.useCallback((command, value) => {
35
- document.execCommand(command, false, value);
36
- editorRef.current?.focus();
37
- setTimeout(emitChange, 0);
38
- }, [emitChange]);
39
- react.useImperativeHandle(ref, () => ({
40
- clear: () => {
41
- if (editorRef.current) {
42
- editorRef.current.innerHTML = "";
43
- setIsEmpty(true);
44
- onChange("", "");
45
- }
46
- },
47
- setContent: (html) => {
48
- if (editorRef.current) {
49
- editorRef.current.innerHTML = html;
50
- const text = editorRef.current.innerText?.trim() || "";
51
- setIsEmpty(!text && !editorRef.current.querySelector("img"));
52
- onChange(html, text);
53
- }
54
- },
55
- getHtml: () => editorRef.current?.innerHTML || "",
56
- getPlainText: () => editorRef.current?.innerText?.trim() || "",
57
- focus: () => editorRef.current?.focus()
58
- }));
59
- const handleInsertLink = react.useCallback(() => {
60
- const sel = window.getSelection();
61
- if (!sel || sel.isCollapsed) {
62
- const url = prompt("URL du lien :");
63
- if (!url) return;
64
- const text = prompt("Texte du lien :", url) || url;
65
- const safeUrl = url.replace(/"/g, "&quot;");
66
- const safeText = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
67
- document.execCommand("insertHTML", false, `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${safeText}</a>`);
68
- editorRef.current?.focus();
69
- setTimeout(emitChange, 0);
70
- } else {
71
- const url = prompt("URL du lien :");
72
- if (url) exec("createLink", url);
73
- }
74
- }, [exec, emitChange]);
75
- const handleImageClick = react.useCallback(() => {
76
- if (onFileUpload) {
77
- fileInputRef.current?.click();
78
- } else {
79
- const url = prompt("URL de l'image :");
80
- if (url) {
81
- const safeUrl = url.replace(/"/g, "&quot;");
82
- document.execCommand("insertHTML", false, `<img src="${safeUrl}" alt="Image" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
83
- editorRef.current?.focus();
84
- setTimeout(emitChange, 0);
85
- }
86
- }
87
- }, [onFileUpload, emitChange]);
88
- const handleFileChange = react.useCallback(async (e) => {
89
- if (!e.target.files || !onFileUpload) return;
90
- for (const file of Array.from(e.target.files)) {
91
- if (!file.type.startsWith("image/")) continue;
92
- const url = await onFileUpload(file);
93
- if (url) {
94
- const safeName = file.name.replace(/"/g, "&quot;");
95
- document.execCommand("insertHTML", false, `<img src="${url}" alt="${safeName}" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
96
- editorRef.current?.focus();
97
- setTimeout(emitChange, 0);
98
- }
99
- }
100
- if (fileInputRef.current) fileInputRef.current.value = "";
101
- }, [onFileUpload, emitChange]);
102
- const handleClearFormat = react.useCallback(() => {
103
- document.execCommand("removeFormat");
104
- document.execCommand("formatBlock", false, "p");
105
- editorRef.current?.focus();
106
- setTimeout(emitChange, 0);
107
- }, [emitChange]);
108
- const handleKeyDown = react.useCallback((e) => {
109
- if (e.key !== "Enter" || e.shiftKey) return;
110
- const sel = window.getSelection();
111
- if (!sel || sel.rangeCount === 0) return;
112
- let node = sel.anchorNode;
113
- let blockquote = null;
114
- while (node && node !== editorRef.current) {
115
- if (node.nodeName === "BLOCKQUOTE") {
116
- blockquote = node;
117
- break;
118
- }
119
- node = node.parentNode;
120
- }
121
- if (!blockquote) return;
122
- const text = blockquote.innerText || "";
123
- if (text.replace(/\n+$/, "").length === 0 || text.endsWith("\n\n") || text.endsWith("\n")) {
124
- e.preventDefault();
125
- const p = document.createElement("p");
126
- p.innerHTML = "<br>";
127
- blockquote.after(p);
128
- if (blockquote.lastChild && blockquote.lastChild.nodeName === "BR") {
129
- blockquote.removeChild(blockquote.lastChild);
130
- }
131
- const range = document.createRange();
132
- range.setStart(p, 0);
133
- range.collapse(true);
134
- sel.removeAllRanges();
135
- sel.addRange(range);
136
- setTimeout(emitChange, 0);
137
- }
138
- }, [emitChange]);
139
- const handlePaste = react.useCallback(async (e) => {
140
- if (!onFileUpload) return;
141
- const items = e.clipboardData?.items;
142
- if (!items) return;
143
- for (const item of Array.from(items)) {
144
- if (item.type.startsWith("image/")) {
145
- e.preventDefault();
146
- const file = item.getAsFile();
147
- if (file) {
148
- const url = await onFileUpload(file);
149
- if (url) {
150
- document.execCommand("insertHTML", false, `<img src="${url}" alt="Image coll\xE9e" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
151
- editorRef.current?.focus();
152
- setTimeout(emitChange, 0);
153
- }
154
- }
155
- return;
156
- }
157
- }
158
- }, [onFileUpload, emitChange]);
159
- const btn = {
160
- border: "none",
161
- background: "transparent",
162
- cursor: "pointer",
163
- padding: "8px 10px",
164
- fontSize: "13px",
165
- fontWeight: 700,
166
- color: "#555",
167
- borderRadius: "5px",
168
- lineHeight: 1,
169
- minHeight: "44px",
170
- minWidth: "44px",
171
- display: "inline-flex",
172
- alignItems: "center",
173
- justifyContent: "center"
174
- };
175
- const sep = {
176
- width: "1px",
177
- height: "18px",
178
- background: "#d1d5db",
179
- margin: "0 4px",
180
- alignSelf: "center"
181
- };
182
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: `3px solid ${focused ? focusBorderColor : borderColor}`, borderRadius: "12px", overflow: "hidden", transition: "border-color 0.15s", background: "#fff" }, children: [
183
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
184
- .rte-toolbar button:hover { background: #e5e7eb !important; }
185
- .rte-editor blockquote { border-left: 4px solid #00E5FF; margin: 8px 0; padding: 8px 16px; background: #f0f9ff; border-radius: 0 6px 6px 0; }
186
- .rte-editor img { max-width: 100%; height: auto; border-radius: 8px; margin: 8px 0; }
187
- .rte-editor a { color: #00838f; text-decoration: underline; }
188
- .rte-editor ul, .rte-editor ol { margin: 8px 0; padding-left: 24px; }
189
- .rte-editor li { margin: 2px 0; }
190
- .rte-editor p { margin: 0 0 6px 0; }
191
- ` }),
192
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rte-toolbar", style: { display: "flex", alignItems: "center", gap: "2px", padding: "6px 10px", borderBottom: "2px solid #e5e7eb", backgroundColor: "#f9fafb", flexWrap: "wrap" }, children: [
193
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
194
- e.preventDefault();
195
- exec("bold");
196
- }, title: "Gras (Ctrl+B)", children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" }) }),
197
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
198
- e.preventDefault();
199
- exec("italic");
200
- }, title: "Italique (Ctrl+I)", children: /* @__PURE__ */ jsxRuntime.jsx("em", { style: { fontStyle: "italic" }, children: "I" }) }),
201
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
202
- e.preventDefault();
203
- exec("underline");
204
- }, title: "Soulign\xE9 (Ctrl+U)", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textDecoration: "underline" }, children: "S" }) }),
205
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
206
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "16px" }, onMouseDown: (e) => {
207
- e.preventDefault();
208
- exec("formatBlock", "blockquote");
209
- }, title: "Citation", children: "\u201C\u201D" }),
210
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
211
- e.preventDefault();
212
- exec("insertUnorderedList");
213
- }, title: "Liste \xE0 puces", children: "\u2022 Liste" }),
214
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
215
- e.preventDefault();
216
- exec("insertOrderedList");
217
- }, title: "Liste num\xE9rot\xE9e", children: "1. Liste" }),
218
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
219
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
220
- e.preventDefault();
221
- handleInsertLink();
222
- }, title: "Ins\xE9rer un lien", children: "\u{1F517}" }),
223
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
224
- e.preventDefault();
225
- handleImageClick();
226
- }, title: "Ins\xE9rer une image", children: "\u{1F5BC}\uFE0F" }),
227
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
228
- /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", style: { ...btn, fontSize: "13px" }, onMouseDown: (e) => {
229
- e.preventDefault();
230
- handleClearFormat();
231
- }, title: "Effacer la mise en forme", children: [
232
- "T",
233
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: "10px", verticalAlign: "super" }, children: "\xD7" })
234
- ] })
235
- ] }),
236
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
237
- isEmpty && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, padding: "14px 16px", color: "#9ca3af", fontSize: "14px", pointerEvents: "none", userSelect: "none" }, children: placeholder }),
238
- /* @__PURE__ */ jsxRuntime.jsx(
239
- "div",
240
- {
241
- ref: editorRef,
242
- className: "rte-editor",
243
- contentEditable: true,
244
- suppressContentEditableWarning: true,
245
- onInput: emitChange,
246
- onFocus: () => setFocused(true),
247
- onBlur: () => {
248
- setFocused(false);
249
- emitChange();
250
- },
251
- onPaste: handlePaste,
252
- onKeyDown: handleKeyDown,
253
- style: {
254
- minHeight: `${minHeight}px`,
255
- padding: "14px 16px",
256
- fontSize: "14px",
257
- lineHeight: 1.6,
258
- color: "#1f2937",
259
- outline: "none",
260
- overflowY: "auto"
261
- }
262
- }
263
- )
264
- ] }),
265
- onFileUpload && /* @__PURE__ */ jsxRuntime.jsx(
266
- "input",
267
- {
268
- ref: fileInputRef,
269
- type: "file",
270
- accept: "image/*",
271
- multiple: true,
272
- onChange: handleFileChange,
273
- style: { display: "none" }
274
- }
275
- )
276
- ] });
277
- });
278
-
279
- exports.RichTextEditor = RichTextEditor;
@@ -1,98 +0,0 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var React = require('react');
5
- var constants = require('./constants');
6
-
7
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
8
-
9
- var React__default = /*#__PURE__*/_interopDefault(React);
10
-
11
- const RewriteDropdown = ({ disabled, loading, onSelect }) => {
12
- const [open, setOpen] = React.useState(false);
13
- const ref = React__default.default.useRef(null);
14
- React__default.default.useEffect(() => {
15
- if (!open) return;
16
- const close = (e) => {
17
- if (ref.current && !ref.current.contains(e.target)) setOpen(false);
18
- };
19
- document.addEventListener("mousedown", close);
20
- return () => document.removeEventListener("mousedown", close);
21
- }, [open]);
22
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref, style: { position: "relative", display: "inline-block" }, children: [
23
- /* @__PURE__ */ jsxRuntime.jsxs(
24
- "button",
25
- {
26
- type: "button",
27
- onClick: () => setOpen(!open),
28
- disabled,
29
- style: {
30
- ...constants.s.outlineBtn("#0891b2", disabled),
31
- fontSize: "11px",
32
- padding: "3px 10px",
33
- borderRadius: "14px",
34
- display: "flex",
35
- alignItems: "center",
36
- gap: "4px"
37
- },
38
- children: [
39
- loading ? "Reformulation..." : "\u270F\uFE0F Reformuler",
40
- !loading && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 9, opacity: 0.7 }, children: "\u25BC" })
41
- ]
42
- }
43
- ),
44
- open && !disabled && !loading && /* @__PURE__ */ jsxRuntime.jsx(
45
- "div",
46
- {
47
- style: {
48
- position: "absolute",
49
- bottom: "100%",
50
- left: 0,
51
- marginBottom: 4,
52
- background: "var(--theme-elevation-0, #fff)",
53
- border: "1px solid var(--theme-elevation-200, #e5e7eb)",
54
- borderRadius: 8,
55
- boxShadow: "0 4px 16px rgba(0,0,0,0.12)",
56
- zIndex: 100,
57
- minWidth: 180,
58
- overflow: "hidden"
59
- },
60
- children: constants.REWRITE_STYLES.map((style) => /* @__PURE__ */ jsxRuntime.jsxs(
61
- "button",
62
- {
63
- type: "button",
64
- onClick: () => {
65
- setOpen(false);
66
- onSelect(style.id);
67
- },
68
- style: {
69
- display: "flex",
70
- flexDirection: "column",
71
- width: "100%",
72
- padding: "8px 12px",
73
- border: "none",
74
- background: "transparent",
75
- cursor: "pointer",
76
- textAlign: "left",
77
- borderBottom: "1px solid var(--theme-elevation-100, #f3f4f6)",
78
- transition: "background 120ms"
79
- },
80
- onMouseEnter: (e) => {
81
- e.target.style.background = "var(--theme-elevation-50, #f9fafb)";
82
- },
83
- onMouseLeave: (e) => {
84
- e.target.style.background = "transparent";
85
- },
86
- children: [
87
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 12, fontWeight: 600, color: "var(--theme-text, #111)" }, children: style.label }),
88
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, color: "var(--theme-elevation-500, #6b7280)" }, children: style.desc })
89
- ]
90
- },
91
- style.id
92
- ))
93
- }
94
- )
95
- ] });
96
- };
97
-
98
- exports.RewriteDropdown = RewriteDropdown;
@@ -1,21 +0,0 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
-
5
- function SkeletonText({ lines = 3 }) {
6
- return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexDirection: "column", gap: "8px", padding: "12px 0" }, children: Array.from({ length: lines }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
7
- "div",
8
- {
9
- style: {
10
- height: "14px",
11
- borderRadius: "4px",
12
- backgroundColor: "#e2e8f0",
13
- width: i === lines - 1 ? "60%" : "100%",
14
- animation: "pulse 1.5s ease-in-out infinite"
15
- }
16
- },
17
- i
18
- )) });
19
- }
20
-
21
- exports.SkeletonText = SkeletonText;
@@ -1,85 +0,0 @@
1
- 'use strict';
2
-
3
- var jsxRuntime = require('react/jsx-runtime');
4
- var constants = require('../constants');
5
-
6
- function renderAiSummary(summary) {
7
- const lines = summary.split("\n");
8
- const result = [];
9
- lines.forEach((line, lineIndex) => {
10
- if (lineIndex > 0) result.push(/* @__PURE__ */ jsxRuntime.jsx("br", {}, `break-${lineIndex}`));
11
- line.split(/(\*\*.*?\*\*)/g).filter(Boolean).forEach((part, partIndex) => {
12
- if (part.startsWith("**") && part.endsWith("**")) {
13
- result.push(/* @__PURE__ */ jsxRuntime.jsx("strong", { children: part.slice(2, -2) }, `${lineIndex}-${partIndex}`));
14
- } else {
15
- result.push(part);
16
- }
17
- });
18
- });
19
- return result;
20
- }
21
- function AISummaryPanel({
22
- showAiSummary,
23
- setShowAiSummary,
24
- aiSummary,
25
- aiGenerating,
26
- aiSaving,
27
- aiSaved,
28
- handleAiGenerate,
29
- handleAiSave
30
- }) {
31
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "14px" }, children: [
32
- /* @__PURE__ */ jsxRuntime.jsx(
33
- "button",
34
- {
35
- onClick: () => {
36
- setShowAiSummary(!showAiSummary);
37
- if (!showAiSummary && !aiSummary) handleAiGenerate();
38
- },
39
- style: {
40
- ...constants.s.ghostBtn("#17807c", false),
41
- fontSize: "12px",
42
- display: "inline-flex",
43
- alignItems: "center",
44
- gap: "6px"
45
- },
46
- children: showAiSummary ? "Masquer la synth\xE8se IA" : "Synth\xE8se IA"
47
- }
48
- ),
49
- showAiSummary && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: {
50
- marginTop: "10px",
51
- padding: "14px 18px",
52
- borderRadius: "8px",
53
- backgroundColor: "#eef8f7",
54
- border: "1px solid #b8dcda"
55
- }, children: [
56
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "10px" }, children: [
57
- /* @__PURE__ */ jsxRuntime.jsx("h4", { style: { fontSize: "13px", fontWeight: 600, color: "#17807c", margin: 0 }, children: "Synth\xE8se IA" }),
58
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
59
- /* @__PURE__ */ jsxRuntime.jsx(
60
- "button",
61
- {
62
- onClick: handleAiGenerate,
63
- disabled: aiGenerating,
64
- style: { ...constants.s.outlineBtn("#17807c", aiGenerating), fontSize: "11px", padding: "4px 10px" },
65
- children: aiGenerating ? "G\xE9n\xE9ration..." : "R\xE9g\xE9n\xE9rer"
66
- }
67
- ),
68
- aiSummary && !aiGenerating && /* @__PURE__ */ jsxRuntime.jsx(
69
- "button",
70
- {
71
- onClick: handleAiSave,
72
- disabled: aiSaving || aiSaved,
73
- style: { ...constants.s.btn(aiSaved ? "#16a34a" : "#2563eb", aiSaving), fontSize: "11px", padding: "4px 10px" },
74
- children: aiSaved ? "Sauvegard\xE9" : aiSaving ? "Sauvegarde..." : "Sauvegarder (note interne)"
75
- }
76
- )
77
- ] })
78
- ] }),
79
- aiGenerating ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", alignItems: "center", gap: "10px", color: "#17807c", fontSize: "13px" }, children: "Analyse de la conversation en cours..." }) : aiSummary ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: "13px", lineHeight: "1.7", color: "#1e1b4b", whiteSpace: "pre-wrap" }, children: renderAiSummary(aiSummary) }) : /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#999", fontStyle: "italic", fontSize: "13px", margin: 0 }, children: `Cliquez sur "R\xE9g\xE9n\xE9rer" pour lancer l'analyse` })
80
- ] })
81
- ] });
82
- }
83
-
84
- exports.AISummaryPanel = AISummaryPanel;
85
- exports.renderAiSummary = renderAiSummary;