@consilioweb/payload-support 0.5.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.
Files changed (189) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +525 -0
  3. package/dist/client.cjs +7 -0
  4. package/dist/client.d.cts +3 -0
  5. package/dist/client.d.ts +3 -0
  6. package/dist/client.js +5 -0
  7. package/dist/index.cjs +7766 -0
  8. package/dist/index.d.cts +384 -0
  9. package/dist/index.d.ts +384 -0
  10. package/dist/index.js +7730 -0
  11. package/dist/views.d.cts +30 -0
  12. package/dist/views.d.ts +30 -0
  13. package/package.json +131 -0
  14. package/src/client.ts +1 -0
  15. package/src/collections/AuthLogs.ts +65 -0
  16. package/src/collections/CannedResponses.ts +69 -0
  17. package/src/collections/ChatMessages.ts +98 -0
  18. package/src/collections/EmailLogs.ts +94 -0
  19. package/src/collections/KnowledgeBase.ts +99 -0
  20. package/src/collections/Macros.ts +98 -0
  21. package/src/collections/PendingEmails.ts +122 -0
  22. package/src/collections/SatisfactionSurveys.ts +98 -0
  23. package/src/collections/SlaPolicies.ts +123 -0
  24. package/src/collections/SupportClients.ts +210 -0
  25. package/src/collections/TicketActivityLog.ts +81 -0
  26. package/src/collections/TicketMessages.ts +364 -0
  27. package/src/collections/TicketStatuses.ts +108 -0
  28. package/src/collections/Tickets.ts +704 -0
  29. package/src/collections/TimeEntries.ts +105 -0
  30. package/src/collections/WebhookEndpoints.ts +96 -0
  31. package/src/collections/index.ts +16 -0
  32. package/src/components/TicketConversation/components/AISummaryPanel.tsx +85 -0
  33. package/src/components/TicketConversation/components/ActionPanels.tsx +140 -0
  34. package/src/components/TicketConversation/components/ActivityLog.tsx +39 -0
  35. package/src/components/TicketConversation/components/ClientBar.tsx +37 -0
  36. package/src/components/TicketConversation/components/ClientHistory.tsx +117 -0
  37. package/src/components/TicketConversation/components/CodeBlock.tsx +186 -0
  38. package/src/components/TicketConversation/components/CodeBlockInserter.tsx +166 -0
  39. package/src/components/TicketConversation/components/QuickActions.tsx +82 -0
  40. package/src/components/TicketConversation/components/TicketHeader.tsx +91 -0
  41. package/src/components/TicketConversation/components/TimeTrackingPanel.tsx +161 -0
  42. package/src/components/TicketConversation/config.ts +82 -0
  43. package/src/components/TicketConversation/constants.ts +74 -0
  44. package/src/components/TicketConversation/context.ts +63 -0
  45. package/src/components/TicketConversation/hooks/useAI.ts +180 -0
  46. package/src/components/TicketConversation/hooks/useMessageActions.ts +131 -0
  47. package/src/components/TicketConversation/hooks/useReply.ts +190 -0
  48. package/src/components/TicketConversation/hooks/useTicketActions.ts +205 -0
  49. package/src/components/TicketConversation/hooks/useTimeTracking.ts +107 -0
  50. package/src/components/TicketConversation/hooks/useTranslation.ts +116 -0
  51. package/src/components/TicketConversation/index.tsx +1110 -0
  52. package/src/components/TicketConversation/locales/en.json +878 -0
  53. package/src/components/TicketConversation/locales/fr.json +878 -0
  54. package/src/components/TicketConversation/types.ts +54 -0
  55. package/src/components/TicketConversation/utils.ts +25 -0
  56. package/src/endpoints/admin-chat-stream.ts +238 -0
  57. package/src/endpoints/admin-chat.ts +263 -0
  58. package/src/endpoints/admin-stats.ts +200 -0
  59. package/src/endpoints/ai.ts +199 -0
  60. package/src/endpoints/apply-macro.ts +144 -0
  61. package/src/endpoints/auth-2fa.ts +163 -0
  62. package/src/endpoints/auto-close.ts +175 -0
  63. package/src/endpoints/billing.ts +167 -0
  64. package/src/endpoints/bulk-action.ts +103 -0
  65. package/src/endpoints/chat-stream.ts +127 -0
  66. package/src/endpoints/chat.ts +188 -0
  67. package/src/endpoints/chatbot.ts +113 -0
  68. package/src/endpoints/delete-account.ts +129 -0
  69. package/src/endpoints/email-stats.ts +109 -0
  70. package/src/endpoints/export-csv.ts +84 -0
  71. package/src/endpoints/export-data.ts +104 -0
  72. package/src/endpoints/import-conversation.ts +307 -0
  73. package/src/endpoints/index.ts +154 -0
  74. package/src/endpoints/login.ts +92 -0
  75. package/src/endpoints/merge-clients.ts +132 -0
  76. package/src/endpoints/merge-tickets.ts +137 -0
  77. package/src/endpoints/oauth-google.ts +179 -0
  78. package/src/endpoints/pending-emails-process.ts +224 -0
  79. package/src/endpoints/presence.ts +104 -0
  80. package/src/endpoints/process-scheduled.ts +144 -0
  81. package/src/endpoints/purge-logs.ts +58 -0
  82. package/src/endpoints/resend-notification.ts +99 -0
  83. package/src/endpoints/round-robin-config.ts +92 -0
  84. package/src/endpoints/satisfaction.ts +93 -0
  85. package/src/endpoints/search.ts +106 -0
  86. package/src/endpoints/seed-kb.ts +153 -0
  87. package/src/endpoints/settings.ts +144 -0
  88. package/src/endpoints/signature.ts +93 -0
  89. package/src/endpoints/sla-check.ts +124 -0
  90. package/src/endpoints/split-ticket.ts +131 -0
  91. package/src/endpoints/statuses.ts +45 -0
  92. package/src/endpoints/track-open.ts +154 -0
  93. package/src/endpoints/typing.ts +101 -0
  94. package/src/endpoints/user-prefs.ts +125 -0
  95. package/src/hooks/checkSLA.ts +414 -0
  96. package/src/hooks/ticketStatusEmail.ts +182 -0
  97. package/src/index.ts +51 -0
  98. package/src/plugin.ts +157 -0
  99. package/src/portal/LiveChat.tsx +1353 -0
  100. package/src/portal/auth/ChatWidget.tsx +350 -0
  101. package/src/portal/auth/ChatbotWidget.tsx +285 -0
  102. package/src/portal/auth/SupportHeader.tsx +409 -0
  103. package/src/portal/auth/dashboard/DashboardClient.tsx +650 -0
  104. package/src/portal/auth/dashboard/page.tsx +84 -0
  105. package/src/portal/auth/faq/FAQSearch.tsx +117 -0
  106. package/src/portal/auth/faq/page.tsx +199 -0
  107. package/src/portal/auth/layout.tsx +61 -0
  108. package/src/portal/auth/profile/page.tsx +705 -0
  109. package/src/portal/auth/tickets/detail/CloseTicketButton.tsx +74 -0
  110. package/src/portal/auth/tickets/detail/CollapsibleMessages.tsx +46 -0
  111. package/src/portal/auth/tickets/detail/MarkSolutionButton.tsx +50 -0
  112. package/src/portal/auth/tickets/detail/MessageActions.tsx +158 -0
  113. package/src/portal/auth/tickets/detail/PrintButton.tsx +16 -0
  114. package/src/portal/auth/tickets/detail/ReadReceipt.tsx +34 -0
  115. package/src/portal/auth/tickets/detail/ReopenTicketButton.tsx +74 -0
  116. package/src/portal/auth/tickets/detail/SatisfactionForm.tsx +156 -0
  117. package/src/portal/auth/tickets/detail/TicketPolling.tsx +57 -0
  118. package/src/portal/auth/tickets/detail/TicketReplyForm.tsx +294 -0
  119. package/src/portal/auth/tickets/detail/TypingIndicator.tsx +58 -0
  120. package/src/portal/auth/tickets/detail/page.tsx +738 -0
  121. package/src/portal/auth/tickets/new/page.tsx +515 -0
  122. package/src/portal/forgot-password/page.tsx +114 -0
  123. package/src/portal/layout.tsx +26 -0
  124. package/src/portal/locales/en.json +374 -0
  125. package/src/portal/locales/fr.json +374 -0
  126. package/src/portal/login/page.tsx +351 -0
  127. package/src/portal/page.tsx +162 -0
  128. package/src/portal/register/page.tsx +281 -0
  129. package/src/portal/reset-password/page.tsx +152 -0
  130. package/src/styles/BillingView.module.scss +311 -0
  131. package/src/styles/ChatView.module.scss +438 -0
  132. package/src/styles/CommandPalette.module.scss +160 -0
  133. package/src/styles/CrmView.module.scss +554 -0
  134. package/src/styles/EmailTracking.module.scss +238 -0
  135. package/src/styles/ImportConversation.module.scss +267 -0
  136. package/src/styles/Layout.module.scss +55 -0
  137. package/src/styles/Logs.module.scss +164 -0
  138. package/src/styles/NewTicket.module.scss +143 -0
  139. package/src/styles/PendingEmails.module.scss +629 -0
  140. package/src/styles/SupportDashboard.module.scss +649 -0
  141. package/src/styles/TicketDetail.module.scss +1043 -0
  142. package/src/styles/TicketInbox.module.scss +296 -0
  143. package/src/styles/TicketingSettings.module.scss +358 -0
  144. package/src/styles/TimeDashboard.module.scss +287 -0
  145. package/src/styles/_tokens.scss +78 -0
  146. package/src/styles/theme.css +633 -0
  147. package/src/types.ts +255 -0
  148. package/src/utils/adminNotification.ts +38 -0
  149. package/src/utils/auth.ts +46 -0
  150. package/src/utils/emailTemplate.ts +343 -0
  151. package/src/utils/fireWebhooks.ts +84 -0
  152. package/src/utils/index.ts +22 -0
  153. package/src/utils/rateLimiter.ts +52 -0
  154. package/src/utils/readSettings.ts +67 -0
  155. package/src/utils/slugs.ts +54 -0
  156. package/src/utils/webhookDispatcher.ts +120 -0
  157. package/src/views/BillingView/client.tsx +137 -0
  158. package/src/views/BillingView/index.tsx +33 -0
  159. package/src/views/ChatView/client.tsx +294 -0
  160. package/src/views/ChatView/index.tsx +33 -0
  161. package/src/views/CrmView/client.tsx +206 -0
  162. package/src/views/CrmView/index.tsx +33 -0
  163. package/src/views/EmailTrackingView/client.tsx +124 -0
  164. package/src/views/EmailTrackingView/index.tsx +33 -0
  165. package/src/views/ImportConversationView/client.tsx +133 -0
  166. package/src/views/ImportConversationView/index.tsx +33 -0
  167. package/src/views/LogsView/client.tsx +151 -0
  168. package/src/views/LogsView/index.tsx +30 -0
  169. package/src/views/NewTicketView/client.tsx +227 -0
  170. package/src/views/NewTicketView/index.tsx +30 -0
  171. package/src/views/PendingEmailsView/client.tsx +177 -0
  172. package/src/views/PendingEmailsView/index.tsx +33 -0
  173. package/src/views/SupportDashboardView/client.tsx +424 -0
  174. package/src/views/SupportDashboardView/index.tsx +33 -0
  175. package/src/views/TicketDetailView/client.tsx +775 -0
  176. package/src/views/TicketDetailView/index.tsx +33 -0
  177. package/src/views/TicketInboxView/client.tsx +313 -0
  178. package/src/views/TicketInboxView/index.tsx +30 -0
  179. package/src/views/TicketingSettingsView/client.tsx +866 -0
  180. package/src/views/TicketingSettingsView/index.tsx +33 -0
  181. package/src/views/TimeDashboardView/client.tsx +144 -0
  182. package/src/views/TimeDashboardView/index.tsx +33 -0
  183. package/src/views/shared/AdminViewHeader.tsx +69 -0
  184. package/src/views/shared/ErrorBoundary.tsx +68 -0
  185. package/src/views/shared/Skeleton.tsx +125 -0
  186. package/src/views/shared/adminTokens.ts +37 -0
  187. package/src/views/shared/config.ts +82 -0
  188. package/src/views/shared/index.ts +6 -0
  189. package/src/views.ts +16 -0
@@ -0,0 +1,705 @@
1
+ 'use client'
2
+
3
+ import React, { useState, useEffect } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+
6
+ interface ProfileData {
7
+ firstName: string
8
+ lastName: string
9
+ company: string
10
+ phone: string
11
+ email: string
12
+ notifyOnReply: boolean
13
+ notifyOnStatusChange: boolean
14
+ twoFactorEnabled: boolean
15
+ }
16
+
17
+ export default function ProfilePage() {
18
+ const router = useRouter()
19
+ const [profile, setProfile] = useState<ProfileData | null>(null)
20
+ const [loading, setLoading] = useState(true)
21
+ const [saving, setSaving] = useState(false)
22
+ const [success, setSuccess] = useState('')
23
+ const [error, setError] = useState('')
24
+
25
+ // Account deletion
26
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false)
27
+ const [deletePassword, setDeletePassword] = useState('')
28
+ const [deleting, setDeleting] = useState(false)
29
+ const [deleteError, setDeleteError] = useState('')
30
+
31
+ // Password change
32
+ const [currentPassword, setCurrentPassword] = useState('')
33
+ const [newPassword, setNewPassword] = useState('')
34
+ const [confirmPassword, setConfirmPassword] = useState('')
35
+ const [passwordError, setPasswordError] = useState('')
36
+ const [passwordSuccess, setPasswordSuccess] = useState('')
37
+ const [changingPassword, setChangingPassword] = useState(false)
38
+
39
+ useEffect(() => {
40
+ const fetchProfile = async () => {
41
+ try {
42
+ const res = await fetch('/api/support-clients/me', { credentials: 'include' })
43
+ if (res.ok) {
44
+ const data = await res.json()
45
+ setProfile({
46
+ firstName: data.user?.firstName || '',
47
+ lastName: data.user?.lastName || '',
48
+ company: data.user?.company || '',
49
+ phone: data.user?.phone || '',
50
+ email: data.user?.email || '',
51
+ notifyOnReply: data.user?.notifyOnReply !== false,
52
+ notifyOnStatusChange: data.user?.notifyOnStatusChange !== false,
53
+ twoFactorEnabled: data.user?.twoFactorEnabled === true,
54
+ })
55
+ }
56
+ } catch {
57
+ setError('Impossible de charger le profil.')
58
+ } finally {
59
+ setLoading(false)
60
+ }
61
+ }
62
+ fetchProfile()
63
+ }, [])
64
+
65
+ const handleSaveProfile = async (e: React.FormEvent) => {
66
+ e.preventDefault()
67
+ if (!profile) return
68
+ setError('')
69
+ setSuccess('')
70
+ setSaving(true)
71
+
72
+ try {
73
+ // Get user ID first
74
+ const meRes = await fetch('/api/support-clients/me', { credentials: 'include' })
75
+ const meData = await meRes.json()
76
+ const userId = meData.user?.id
77
+
78
+ if (!userId) {
79
+ setError('Session expirée. Veuillez vous reconnecter.')
80
+ return
81
+ }
82
+
83
+ const res = await fetch(`/api/support-clients/${userId}`, {
84
+ method: 'PATCH',
85
+ headers: { 'Content-Type': 'application/json' },
86
+ credentials: 'include',
87
+ body: JSON.stringify({
88
+ firstName: profile.firstName,
89
+ lastName: profile.lastName,
90
+ company: profile.company,
91
+ phone: profile.phone,
92
+ notifyOnReply: profile.notifyOnReply,
93
+ notifyOnStatusChange: profile.notifyOnStatusChange,
94
+ twoFactorEnabled: profile.twoFactorEnabled,
95
+ }),
96
+ })
97
+
98
+ if (res.ok) {
99
+ setSuccess('Profil mis a jour avec succes.')
100
+ router.refresh()
101
+ } else {
102
+ const data = await res.json()
103
+ setError(data.errors?.[0]?.message || 'Erreur lors de la mise a jour.')
104
+ }
105
+ } catch {
106
+ setError('Erreur de connexion.')
107
+ } finally {
108
+ setSaving(false)
109
+ }
110
+ }
111
+
112
+ const handleChangePassword = async (e: React.FormEvent) => {
113
+ e.preventDefault()
114
+ setPasswordError('')
115
+ setPasswordSuccess('')
116
+
117
+ if (newPassword.length < 8) {
118
+ setPasswordError('Le mot de passe doit faire au moins 8 caracteres.')
119
+ return
120
+ }
121
+
122
+ if (newPassword !== confirmPassword) {
123
+ setPasswordError('Les mots de passe ne correspondent pas.')
124
+ return
125
+ }
126
+
127
+ setChangingPassword(true)
128
+
129
+ try {
130
+ // Verify current password by logging in
131
+ const loginRes = await fetch('/api/support-clients/login', {
132
+ method: 'POST',
133
+ headers: { 'Content-Type': 'application/json' },
134
+ credentials: 'include',
135
+ body: JSON.stringify({ email: profile?.email, password: currentPassword }),
136
+ })
137
+
138
+ if (!loginRes.ok) {
139
+ setPasswordError('Mot de passe actuel incorrect.')
140
+ return
141
+ }
142
+
143
+ // Get user ID
144
+ const meRes = await fetch('/api/support-clients/me', { credentials: 'include' })
145
+ const meData = await meRes.json()
146
+ const userId = meData.user?.id
147
+
148
+ // Update password
149
+ const res = await fetch(`/api/support-clients/${userId}`, {
150
+ method: 'PATCH',
151
+ headers: { 'Content-Type': 'application/json' },
152
+ credentials: 'include',
153
+ body: JSON.stringify({ password: newPassword }),
154
+ })
155
+
156
+ if (res.ok) {
157
+ setPasswordSuccess('Mot de passe modifie avec succes.')
158
+ setCurrentPassword('')
159
+ setNewPassword('')
160
+ setConfirmPassword('')
161
+ } else {
162
+ setPasswordError('Erreur lors du changement de mot de passe.')
163
+ }
164
+ } catch {
165
+ setPasswordError('Erreur de connexion.')
166
+ } finally {
167
+ setChangingPassword(false)
168
+ }
169
+ }
170
+
171
+ if (loading) {
172
+ return (
173
+ <div className="flex flex-col items-center justify-center py-24">
174
+ <div className="h-8 w-8 animate-spin rounded-full border-2 border-slate-200 border-t-blue-600 dark:border-slate-700 dark:border-t-blue-400" />
175
+ <p className="mt-4 text-sm font-medium text-slate-400 dark:text-slate-500">Chargement du profil...</p>
176
+ </div>
177
+ )
178
+ }
179
+
180
+ if (!profile) return null
181
+
182
+ const initials = `${profile.firstName.charAt(0)}${profile.lastName.charAt(0)}`.toUpperCase()
183
+
184
+ return (
185
+ <div className="mx-auto max-w-3xl px-4 pb-12">
186
+ {/* Back navigation */}
187
+ <button
188
+ onClick={() => router.push('/support/dashboard')}
189
+ className="group mb-8 inline-flex items-center gap-2 text-sm font-medium text-slate-500 dark:text-slate-400 hover:text-blue-600 dark:hover:text-blue-400 transition-colors"
190
+ >
191
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 transition-transform group-hover:-translate-x-0.5">
192
+ <path fillRule="evenodd" d="M17 10a.75.75 0 01-.75.75H5.612l4.158 3.96a.75.75 0 11-1.04 1.08l-5.5-5.25a.75.75 0 010-1.08l5.5-5.25a.75.75 0 111.04 1.08L5.612 9.25H16.25A.75.75 0 0117 10z" clipRule="evenodd" />
193
+ </svg>
194
+ Retour aux tickets
195
+ </button>
196
+
197
+ {/* Profile header */}
198
+ <div className="mb-8 flex items-center gap-5">
199
+ <div className="flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-blue-500 to-blue-600 text-xl font-bold text-white shadow-lg shadow-blue-500/20">
200
+ {initials}
201
+ </div>
202
+ <div>
203
+ <h1 className="text-2xl font-bold tracking-tight text-slate-900 dark:text-white sm:text-3xl">
204
+ Mon profil
205
+ </h1>
206
+ <p className="mt-0.5 text-sm text-slate-500 dark:text-slate-400">
207
+ Gerez vos informations personnelles et vos preferences
208
+ </p>
209
+ </div>
210
+ </div>
211
+
212
+ <div className="space-y-6">
213
+ {/* ── Personal info card ── */}
214
+ <div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 shadow-sm backdrop-blur-sm">
215
+ <div className="border-b border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
216
+ <div className="flex items-center gap-3">
217
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-blue-50 dark:bg-blue-900/30">
218
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-blue-600 dark:text-blue-400">
219
+ <path d="M10 8a3 3 0 100-6 3 3 0 000 6zM3.465 14.493a1.23 1.23 0 00.41 1.412A9.957 9.957 0 0010 18c2.31 0 4.438-.784 6.131-2.1.43-.333.604-.903.408-1.41a7.002 7.002 0 00-13.074.003z" />
220
+ </svg>
221
+ </div>
222
+ <h2 className="text-base font-semibold text-slate-900 dark:text-white">Informations personnelles</h2>
223
+ </div>
224
+ </div>
225
+
226
+ <form onSubmit={handleSaveProfile} className="p-6 sm:p-8">
227
+ {/* Alerts */}
228
+ {error && (
229
+ <div className="mb-6 flex items-start gap-3 rounded-xl border border-red-200 dark:border-red-800/50 bg-red-50 dark:bg-red-900/20 px-4 py-3.5">
230
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="mt-0.5 h-5 w-5 flex-shrink-0 text-red-500">
231
+ <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
232
+ </svg>
233
+ <p className="text-sm font-medium text-red-700 dark:text-red-400">{error}</p>
234
+ </div>
235
+ )}
236
+ {success && (
237
+ <div className="mb-6 flex items-start gap-3 rounded-xl border border-emerald-200 dark:border-emerald-800/50 bg-emerald-50 dark:bg-emerald-900/20 px-4 py-3.5">
238
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="mt-0.5 h-5 w-5 flex-shrink-0 text-emerald-500">
239
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clipRule="evenodd" />
240
+ </svg>
241
+ <p className="text-sm font-medium text-emerald-700 dark:text-emerald-400">{success}</p>
242
+ </div>
243
+ )}
244
+
245
+ {/* Name row */}
246
+ <div className="mb-5 grid grid-cols-1 gap-5 sm:grid-cols-2">
247
+ <div>
248
+ <label htmlFor="firstName" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
249
+ Prenom
250
+ </label>
251
+ <input
252
+ id="firstName"
253
+ type="text"
254
+ required
255
+ value={profile.firstName}
256
+ onChange={(e) => setProfile({ ...profile, firstName: e.target.value })}
257
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
258
+ />
259
+ </div>
260
+ <div>
261
+ <label htmlFor="lastName" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
262
+ Nom
263
+ </label>
264
+ <input
265
+ id="lastName"
266
+ type="text"
267
+ required
268
+ value={profile.lastName}
269
+ onChange={(e) => setProfile({ ...profile, lastName: e.target.value })}
270
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
271
+ />
272
+ </div>
273
+ </div>
274
+
275
+ {/* Company */}
276
+ <div className="mb-5">
277
+ <label htmlFor="company" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
278
+ Entreprise
279
+ </label>
280
+ <input
281
+ id="company"
282
+ type="text"
283
+ required
284
+ value={profile.company}
285
+ onChange={(e) => setProfile({ ...profile, company: e.target.value })}
286
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
287
+ />
288
+ </div>
289
+
290
+ {/* Phone */}
291
+ <div className="mb-5">
292
+ <label htmlFor="phone" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
293
+ Telephone
294
+ </label>
295
+ <input
296
+ id="phone"
297
+ type="tel"
298
+ value={profile.phone}
299
+ onChange={(e) => setProfile({ ...profile, phone: e.target.value })}
300
+ placeholder="+33 6 00 00 00 00"
301
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white placeholder:text-slate-400 dark:placeholder:text-slate-500 outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
302
+ />
303
+ </div>
304
+
305
+ {/* Email (read-only) */}
306
+ <div className="mb-6">
307
+ <label className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">Email</label>
308
+ <div className="flex items-center gap-3 rounded-xl border border-slate-100 dark:border-slate-700 bg-slate-50 dark:bg-slate-900/50 px-4 py-3">
309
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 flex-shrink-0 text-slate-400">
310
+ <path fillRule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clipRule="evenodd" />
311
+ </svg>
312
+ <span className="text-sm text-slate-500 dark:text-slate-400">{profile.email}</span>
313
+ </div>
314
+ <p className="mt-1.5 text-xs text-slate-400 dark:text-slate-500">L&apos;email ne peut pas etre modifie. Contactez le support si besoin.</p>
315
+ </div>
316
+
317
+ <button
318
+ type="submit"
319
+ disabled={saving}
320
+ className="inline-flex items-center justify-center gap-2 rounded-xl bg-blue-600 px-6 py-3 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:bg-blue-700 hover:shadow-md active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
321
+ >
322
+ {saving ? (
323
+ <>
324
+ <svg className="h-4 w-4 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
325
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
326
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
327
+ </svg>
328
+ Enregistrement...
329
+ </>
330
+ ) : (
331
+ 'Enregistrer les modifications'
332
+ )}
333
+ </button>
334
+ </form>
335
+ </div>
336
+
337
+ {/* ── Security & 2FA card ── */}
338
+ <div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 shadow-sm backdrop-blur-sm">
339
+ <div className="border-b border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
340
+ <div className="flex items-center gap-3">
341
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-amber-50 dark:bg-amber-900/30">
342
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-amber-600 dark:text-amber-400">
343
+ <path fillRule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" clipRule="evenodd" />
344
+ </svg>
345
+ </div>
346
+ <h2 className="text-base font-semibold text-slate-900 dark:text-white">Securite</h2>
347
+ </div>
348
+ </div>
349
+
350
+ <div className="p-6 sm:p-8">
351
+ {/* 2FA toggle */}
352
+ <div className="flex items-start justify-between gap-4 rounded-xl border border-slate-100 dark:border-slate-700/50 bg-slate-50/50 dark:bg-slate-900/30 p-4">
353
+ <div className="flex items-start gap-3">
354
+ <div className="mt-0.5 flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-50 dark:bg-blue-900/30">
355
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-blue-600 dark:text-blue-400">
356
+ <path fillRule="evenodd" d="M10 2.5c-1.31 0-2.526.386-3.546 1.051a.75.75 0 01-.908-1.194A8.459 8.459 0 0110 1c1.51 0 2.934.398 4.16 1.094.71.404.607 1.258-.146 1.258-.017 0-.22-.109-.268-.132A6.947 6.947 0 0010 2.5zM5.25 10c0-.98.12-1.933.347-2.845a.75.75 0 10-1.452-.376A10.477 10.477 0 003.75 10c0 .98.12 1.933.347 2.845a.75.75 0 101.452-.376A8.953 8.953 0 015.25 10zm9.5 0c0 .98-.12 1.933-.347 2.845a.75.75 0 101.452.376c.246-.98.395-2 .395-3.221s-.149-2.241-.395-3.221a.75.75 0 10-1.452.376c.227.912.347 1.865.347 2.845zM10 17.5c1.31 0 2.526-.386 3.546-1.051a.75.75 0 01.908 1.194A8.459 8.459 0 0110 19c-1.51 0-2.934-.398-4.16-1.094-.71-.404-.607-1.258.146-1.258.017 0 .22.109.268.132A6.947 6.947 0 0010 17.5zM10 13a3 3 0 100-6 3 3 0 000 6z" clipRule="evenodd" />
357
+ </svg>
358
+ </div>
359
+ <div>
360
+ <p className="text-sm font-semibold text-slate-900 dark:text-white">Verification en deux etapes (2FA)</p>
361
+ <p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">Recevoir un code par email a chaque connexion pour plus de securite</p>
362
+ </div>
363
+ </div>
364
+ <button
365
+ type="button"
366
+ onClick={() => setProfile({ ...profile, twoFactorEnabled: !profile.twoFactorEnabled })}
367
+ className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-500/20 ${
368
+ profile.twoFactorEnabled ? 'bg-blue-600' : 'bg-slate-200 dark:bg-slate-600'
369
+ }`}
370
+ role="switch"
371
+ aria-checked={profile.twoFactorEnabled}
372
+ >
373
+ <span
374
+ className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
375
+ profile.twoFactorEnabled ? 'translate-x-5' : 'translate-x-0'
376
+ }`}
377
+ />
378
+ </button>
379
+ </div>
380
+ </div>
381
+ </div>
382
+
383
+ {/* ── Notifications card ── */}
384
+ <div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 shadow-sm backdrop-blur-sm">
385
+ <div className="border-b border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
386
+ <div className="flex items-center gap-3">
387
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-violet-50 dark:bg-violet-900/30">
388
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-violet-600 dark:text-violet-400">
389
+ <path fillRule="evenodd" d="M10 2a6 6 0 00-6 6c0 1.887-.454 3.665-1.257 5.234a.75.75 0 00.515 1.076 32.91 32.91 0 003.256.508 3.5 3.5 0 006.972 0 32.903 32.903 0 003.256-.508.75.75 0 00.515-1.076A11.448 11.448 0 0116 8a6 6 0 00-6-6zM8.05 14.943a33.54 33.54 0 003.9 0 2 2 0 01-3.9 0z" clipRule="evenodd" />
390
+ </svg>
391
+ </div>
392
+ <h2 className="text-base font-semibold text-slate-900 dark:text-white">Notifications par email</h2>
393
+ </div>
394
+ </div>
395
+
396
+ <div className="divide-y divide-slate-100 dark:divide-slate-700/50">
397
+ {/* Reply notifications */}
398
+ <div className="flex items-center justify-between gap-4 px-6 py-4 sm:px-8">
399
+ <div>
400
+ <p className="text-sm font-semibold text-slate-900 dark:text-white">Reponses du support</p>
401
+ <p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">Recevoir un email a chaque reponse sur mes tickets</p>
402
+ </div>
403
+ <button
404
+ type="button"
405
+ onClick={() => setProfile({ ...profile, notifyOnReply: !profile.notifyOnReply })}
406
+ className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-500/20 ${
407
+ profile.notifyOnReply ? 'bg-blue-600' : 'bg-slate-200 dark:bg-slate-600'
408
+ }`}
409
+ role="switch"
410
+ aria-checked={profile.notifyOnReply}
411
+ >
412
+ <span className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
413
+ profile.notifyOnReply ? 'translate-x-5' : 'translate-x-0'
414
+ }`} />
415
+ </button>
416
+ </div>
417
+
418
+ {/* Status change notifications */}
419
+ <div className="flex items-center justify-between gap-4 px-6 py-4 sm:px-8">
420
+ <div>
421
+ <p className="text-sm font-semibold text-slate-900 dark:text-white">Changements de statut</p>
422
+ <p className="mt-0.5 text-xs text-slate-500 dark:text-slate-400">Recevoir un email quand un ticket est resolu ou ferme</p>
423
+ </div>
424
+ <button
425
+ type="button"
426
+ onClick={() => setProfile({ ...profile, notifyOnStatusChange: !profile.notifyOnStatusChange })}
427
+ className={`relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-4 focus:ring-blue-500/20 ${
428
+ profile.notifyOnStatusChange ? 'bg-blue-600' : 'bg-slate-200 dark:bg-slate-600'
429
+ }`}
430
+ role="switch"
431
+ aria-checked={profile.notifyOnStatusChange}
432
+ >
433
+ <span className={`pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${
434
+ profile.notifyOnStatusChange ? 'translate-x-5' : 'translate-x-0'
435
+ }`} />
436
+ </button>
437
+ </div>
438
+ </div>
439
+
440
+ {/* Save button for notifications + 2FA */}
441
+ <div className="border-t border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
442
+ <button
443
+ type="button"
444
+ onClick={handleSaveProfile}
445
+ disabled={saving}
446
+ className="inline-flex items-center justify-center gap-2 rounded-xl bg-blue-600 px-6 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:bg-blue-700 hover:shadow-md active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
447
+ >
448
+ {saving ? 'Enregistrement...' : 'Sauvegarder les preferences'}
449
+ </button>
450
+ </div>
451
+ </div>
452
+
453
+ {/* ── Password change card ── */}
454
+ <div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 shadow-sm backdrop-blur-sm">
455
+ <div className="border-b border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
456
+ <div className="flex items-center gap-3">
457
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-slate-100 dark:bg-slate-700">
458
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-slate-600 dark:text-slate-400">
459
+ <path fillRule="evenodd" d="M8 7a5 5 0 113.61 4.804l-1.903 1.903A1 1 0 019 14H8v1a1 1 0 01-1 1H6v1a1 1 0 01-1 1H3a1 1 0 01-1-1v-2a1 1 0 01.293-.707L8.196 8.39A5.002 5.002 0 018 7zm5-3a.75.75 0 000 1.5A1.5 1.5 0 0114.5 7 .75.75 0 0016 7a3 3 0 00-3-3z" clipRule="evenodd" />
460
+ </svg>
461
+ </div>
462
+ <h2 className="text-base font-semibold text-slate-900 dark:text-white">Changer le mot de passe</h2>
463
+ </div>
464
+ </div>
465
+
466
+ <form onSubmit={handleChangePassword} className="p-6 sm:p-8">
467
+ {passwordError && (
468
+ <div className="mb-6 flex items-start gap-3 rounded-xl border border-red-200 dark:border-red-800/50 bg-red-50 dark:bg-red-900/20 px-4 py-3.5">
469
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="mt-0.5 h-5 w-5 flex-shrink-0 text-red-500">
470
+ <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-8-5a.75.75 0 01.75.75v4.5a.75.75 0 01-1.5 0v-4.5A.75.75 0 0110 5zm0 10a1 1 0 100-2 1 1 0 000 2z" clipRule="evenodd" />
471
+ </svg>
472
+ <p className="text-sm font-medium text-red-700 dark:text-red-400">{passwordError}</p>
473
+ </div>
474
+ )}
475
+ {passwordSuccess && (
476
+ <div className="mb-6 flex items-start gap-3 rounded-xl border border-emerald-200 dark:border-emerald-800/50 bg-emerald-50 dark:bg-emerald-900/20 px-4 py-3.5">
477
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="mt-0.5 h-5 w-5 flex-shrink-0 text-emerald-500">
478
+ <path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.857-9.809a.75.75 0 00-1.214-.882l-3.483 4.79-1.88-1.88a.75.75 0 10-1.06 1.061l2.5 2.5a.75.75 0 001.137-.089l4-5.5z" clipRule="evenodd" />
479
+ </svg>
480
+ <p className="text-sm font-medium text-emerald-700 dark:text-emerald-400">{passwordSuccess}</p>
481
+ </div>
482
+ )}
483
+
484
+ <div className="mb-5">
485
+ <label htmlFor="currentPassword" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
486
+ Mot de passe actuel
487
+ </label>
488
+ <input
489
+ id="currentPassword"
490
+ type="password"
491
+ required
492
+ value={currentPassword}
493
+ onChange={(e) => setCurrentPassword(e.target.value)}
494
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
495
+ />
496
+ </div>
497
+
498
+ <div className="mb-5">
499
+ <label htmlFor="newPassword" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
500
+ Nouveau mot de passe
501
+ </label>
502
+ <input
503
+ id="newPassword"
504
+ type="password"
505
+ required
506
+ minLength={8}
507
+ value={newPassword}
508
+ onChange={(e) => setNewPassword(e.target.value)}
509
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
510
+ />
511
+ <p className="mt-1.5 text-xs text-slate-400 dark:text-slate-500">Minimum 8 caracteres</p>
512
+ </div>
513
+
514
+ <div className="mb-6">
515
+ <label htmlFor="confirmPassword" className="mb-2 block text-sm font-semibold text-slate-700 dark:text-slate-200">
516
+ Confirmer le mot de passe
517
+ </label>
518
+ <input
519
+ id="confirmPassword"
520
+ type="password"
521
+ required
522
+ value={confirmPassword}
523
+ onChange={(e) => setConfirmPassword(e.target.value)}
524
+ className="w-full rounded-xl border border-slate-200 dark:border-slate-600 bg-slate-50 dark:bg-slate-900/50 px-4 py-3 text-sm text-slate-900 dark:text-white outline-none transition-all duration-200 focus:border-blue-500 focus:bg-white dark:focus:bg-slate-900 focus:ring-4 focus:ring-blue-500/10"
525
+ />
526
+ </div>
527
+
528
+ <button
529
+ type="submit"
530
+ disabled={changingPassword}
531
+ className="inline-flex items-center justify-center gap-2 rounded-xl bg-slate-900 dark:bg-slate-100 px-6 py-3 text-sm font-semibold text-white dark:text-slate-900 shadow-sm transition-all duration-200 hover:bg-slate-800 dark:hover:bg-white hover:shadow-md active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
532
+ >
533
+ {changingPassword ? (
534
+ <>
535
+ <svg className="h-4 w-4 animate-spin" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
536
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
537
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
538
+ </svg>
539
+ Modification...
540
+ </>
541
+ ) : (
542
+ 'Changer le mot de passe'
543
+ )}
544
+ </button>
545
+ </form>
546
+ </div>
547
+
548
+ {/* ── RGPD Data Export card ── */}
549
+ <div className="rounded-2xl border border-slate-200 dark:border-slate-700/50 bg-white dark:bg-slate-800/50 shadow-sm backdrop-blur-sm">
550
+ <div className="border-b border-slate-100 dark:border-slate-700/50 px-6 py-4 sm:px-8">
551
+ <div className="flex items-center gap-3">
552
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-emerald-50 dark:bg-emerald-900/30">
553
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-emerald-600 dark:text-emerald-400">
554
+ <path d="M10.75 2.75a.75.75 0 00-1.5 0v8.614L6.295 8.235a.75.75 0 10-1.09 1.03l4.25 4.5a.75.75 0 001.09 0l4.25-4.5a.75.75 0 00-1.09-1.03l-2.955 3.129V2.75z" />
555
+ <path d="M3.5 12.75a.75.75 0 00-1.5 0v2.5A2.75 2.75 0 004.75 18h10.5A2.75 2.75 0 0018 15.25v-2.5a.75.75 0 00-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5z" />
556
+ </svg>
557
+ </div>
558
+ <h2 className="text-base font-semibold text-slate-900 dark:text-white">Mes données personnelles</h2>
559
+ </div>
560
+ </div>
561
+
562
+ <div className="p-6 sm:p-8">
563
+ <p className="mb-4 text-sm text-slate-500 dark:text-slate-400 leading-relaxed">
564
+ Conformement au RGPD, vous pouvez télécharger l&apos;ensemble de vos données personnelles
565
+ (profil, tickets, messages, enquetes de satisfaction).
566
+ </p>
567
+ <a
568
+ href="/api/support/export-data"
569
+ download
570
+ className="inline-flex items-center gap-2 rounded-xl border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 px-5 py-2.5 text-sm font-semibold text-slate-700 dark:text-slate-300 transition-all duration-200 hover:border-emerald-300 dark:hover:border-emerald-600 hover:bg-emerald-50 dark:hover:bg-emerald-900/20 hover:text-emerald-700 dark:hover:text-emerald-400"
571
+ >
572
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4">
573
+ <path d="M10.75 2.75a.75.75 0 00-1.5 0v8.614L6.295 8.235a.75.75 0 10-1.09 1.03l4.25 4.5a.75.75 0 001.09 0l4.25-4.5a.75.75 0 00-1.09-1.03l-2.955 3.129V2.75z" />
574
+ <path d="M3.5 12.75a.75.75 0 00-1.5 0v2.5A2.75 2.75 0 004.75 18h10.5A2.75 2.75 0 0018 15.25v-2.5a.75.75 0 00-1.5 0v2.5c0 .69-.56 1.25-1.25 1.25H4.75c-.69 0-1.25-.56-1.25-1.25v-2.5z" />
575
+ </svg>
576
+ Exporter mes données (JSON)
577
+ </a>
578
+ </div>
579
+ </div>
580
+
581
+ {/* ── Danger zone — Account Deletion ── */}
582
+ <div className="rounded-2xl border border-red-200 dark:border-red-800/30 bg-white dark:bg-slate-800/50 shadow-sm">
583
+ <div className="border-b border-red-100 dark:border-red-800/20 px-6 py-4 sm:px-8">
584
+ <div className="flex items-center gap-3">
585
+ <div className="flex h-8 w-8 items-center justify-center rounded-lg bg-red-50 dark:bg-red-900/30">
586
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4 text-red-500">
587
+ <path fillRule="evenodd" d="M8.75 1A2.75 2.75 0 006 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 10.23 1.482l.149-.022.841 10.518A2.75 2.75 0 007.596 19h4.807a2.75 2.75 0 002.742-2.53l.841-10.52.149.023a.75.75 0 00.23-1.482A41.03 41.03 0 0014 4.193V3.75A2.75 2.75 0 0011.25 1h-2.5zM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4zM8.58 7.72a.75.75 0 00-1.5.06l.3 7.5a.75.75 0 101.5-.06l-.3-7.5zm4.34.06a.75.75 0 10-1.5-.06l-.3 7.5a.75.75 0 101.5.06l.3-7.5z" clipRule="evenodd" />
588
+ </svg>
589
+ </div>
590
+ <div>
591
+ <h2 className="text-base font-semibold text-red-700 dark:text-red-400">Zone de danger</h2>
592
+ <p className="text-xs text-slate-500 dark:text-slate-400">Actions irréversibles</p>
593
+ </div>
594
+ </div>
595
+ </div>
596
+
597
+ <div className="p-6 sm:p-8">
598
+ <p className="mb-4 text-sm text-slate-500 dark:text-slate-400 leading-relaxed">
599
+ Conformement au RGPD (Article 17 — Droit a l&apos;effacement), vous pouvez demander la suppression
600
+ définitive de votre compte et de toutes vos données. Cette action est irréversible.
601
+ </p>
602
+
603
+ {!showDeleteConfirm ? (
604
+ <button
605
+ onClick={() => setShowDeleteConfirm(true)}
606
+ className="inline-flex items-center gap-2 rounded-xl border border-red-200 dark:border-red-800/50 bg-white dark:bg-slate-800 px-5 py-2.5 text-sm font-semibold text-red-600 dark:text-red-400 transition-all duration-200 hover:bg-red-50 dark:hover:bg-red-900/20 hover:border-red-300"
607
+ >
608
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" className="h-4 w-4">
609
+ <path fillRule="evenodd" d="M8.75 1A2.75 2.75 0 006 3.75v.443c-.795.077-1.584.176-2.365.298a.75.75 0 10.23 1.482l.149-.022.841 10.518A2.75 2.75 0 007.596 19h4.807a2.75 2.75 0 002.742-2.53l.841-10.52.149.023a.75.75 0 00.23-1.482A41.03 41.03 0 0014 4.193V3.75A2.75 2.75 0 0011.25 1h-2.5zM10 4c.84 0 1.673.025 2.5.075V3.75c0-.69-.56-1.25-1.25-1.25h-2.5c-.69 0-1.25.56-1.25 1.25v.325C8.327 4.025 9.16 4 10 4zM8.58 7.72a.75.75 0 00-1.5.06l.3 7.5a.75.75 0 101.5-.06l-.3-7.5zm4.34.06a.75.75 0 10-1.5-.06l-.3 7.5a.75.75 0 101.5.06l.3-7.5z" clipRule="evenodd" />
610
+ </svg>
611
+ Supprimer mon compte
612
+ </button>
613
+ ) : (
614
+ <div className="rounded-xl border border-red-200 dark:border-red-800/30 bg-red-50/50 dark:bg-red-900/10 p-5">
615
+ <p className="mb-3 text-sm font-semibold text-red-700 dark:text-red-400">
616
+ Cette action supprimera definitivement :
617
+ </p>
618
+ <ul className="mb-4 space-y-1.5 text-sm text-red-600 dark:text-red-400">
619
+ <li className="flex items-center gap-2">
620
+ <span className="h-1 w-1 flex-shrink-0 rounded-full bg-red-400" />
621
+ Votre compte et profil
622
+ </li>
623
+ <li className="flex items-center gap-2">
624
+ <span className="h-1 w-1 flex-shrink-0 rounded-full bg-red-400" />
625
+ Tous vos tickets et messages
626
+ </li>
627
+ <li className="flex items-center gap-2">
628
+ <span className="h-1 w-1 flex-shrink-0 rounded-full bg-red-400" />
629
+ Vos enquetes de satisfaction
630
+ </li>
631
+ <li className="flex items-center gap-2">
632
+ <span className="h-1 w-1 flex-shrink-0 rounded-full bg-red-400" />
633
+ Vos conversations de chat
634
+ </li>
635
+ </ul>
636
+
637
+ {deleteError && (
638
+ <div className="mb-4 flex items-start gap-3 rounded-lg border border-red-200 dark:border-red-800/50 bg-red-50 dark:bg-red-900/20 px-4 py-2.5">
639
+ <p className="text-sm font-medium text-red-700 dark:text-red-400">{deleteError}</p>
640
+ </div>
641
+ )}
642
+
643
+ <div className="mb-4">
644
+ <label htmlFor="deletePassword" className="mb-2 block text-sm font-semibold text-red-700 dark:text-red-400">
645
+ Confirmez avec votre mot de passe
646
+ </label>
647
+ <input
648
+ id="deletePassword"
649
+ type="password"
650
+ required
651
+ value={deletePassword}
652
+ onChange={(e) => setDeletePassword(e.target.value)}
653
+ placeholder="Votre mot de passe"
654
+ className="w-full rounded-xl border border-red-200 dark:border-red-700 bg-white dark:bg-slate-900 px-4 py-3 text-sm text-slate-900 dark:text-white placeholder:text-slate-400 outline-none transition-all focus:border-red-500 focus:ring-4 focus:ring-red-500/10"
655
+ />
656
+ </div>
657
+
658
+ <div className="flex gap-3">
659
+ <button
660
+ onClick={async () => {
661
+ if (!deletePassword) {
662
+ setDeleteError('Veuillez entrer votre mot de passe.')
663
+ return
664
+ }
665
+ setDeleting(true)
666
+ setDeleteError('')
667
+ try {
668
+ const res = await fetch('/api/support/delete-account', {
669
+ method: 'POST',
670
+ headers: { 'Content-Type': 'application/json' },
671
+ credentials: 'include',
672
+ body: JSON.stringify({ confirmPassword: deletePassword }),
673
+ })
674
+ const data = await res.json()
675
+ if (res.ok && data.deleted) {
676
+ router.push('/support/login?deleted=true')
677
+ } else {
678
+ setDeleteError(data.error || 'Erreur lors de la suppression.')
679
+ }
680
+ } catch {
681
+ setDeleteError('Erreur de connexion.')
682
+ } finally {
683
+ setDeleting(false)
684
+ }
685
+ }}
686
+ disabled={deleting || !deletePassword}
687
+ className="rounded-xl bg-red-600 px-5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-200 hover:bg-red-700 hover:shadow-md active:scale-[0.98] disabled:opacity-50 disabled:cursor-not-allowed"
688
+ >
689
+ {deleting ? 'Suppression...' : 'Confirmer la suppression'}
690
+ </button>
691
+ <button
692
+ onClick={() => { setShowDeleteConfirm(false); setDeletePassword(''); setDeleteError('') }}
693
+ className="rounded-xl border border-slate-200 dark:border-slate-600 bg-white dark:bg-slate-800 px-5 py-2.5 text-sm font-semibold text-slate-600 dark:text-slate-300 transition-all duration-200 hover:bg-slate-50 dark:hover:bg-slate-700"
694
+ >
695
+ Annuler
696
+ </button>
697
+ </div>
698
+ </div>
699
+ )}
700
+ </div>
701
+ </div>
702
+ </div>
703
+ </div>
704
+ )
705
+ }