@wakastellar/ui 2.0.0 → 2.1.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 (291) hide show
  1. package/README.md +71 -8
  2. package/dist/cli/commands/add.d.ts +7 -0
  3. package/dist/cli/commands/init.d.ts +6 -0
  4. package/dist/cli/commands/list.d.ts +5 -0
  5. package/dist/cli/commands/search.d.ts +1 -0
  6. package/dist/cli/index.cjs +6014 -0
  7. package/dist/cli/index.d.ts +1 -0
  8. package/dist/cli/utils/config.d.ts +29 -0
  9. package/dist/cli/utils/logger.d.ts +20 -0
  10. package/dist/cli/utils/registry.d.ts +23 -0
  11. package/package.json +14 -3
  12. package/src/blocks/activity-timeline/index.tsx +586 -0
  13. package/src/blocks/calendar-view/index.tsx +756 -0
  14. package/src/blocks/chat/index.tsx +1018 -0
  15. package/src/blocks/chat/widget.tsx +504 -0
  16. package/src/blocks/dashboard/index.tsx +522 -0
  17. package/src/blocks/empty-states/index.tsx +452 -0
  18. package/src/blocks/error-pages/index.tsx +426 -0
  19. package/src/blocks/faq/index.tsx +479 -0
  20. package/src/blocks/file-manager/index.tsx +890 -0
  21. package/src/blocks/footer/index.tsx +133 -0
  22. package/src/blocks/header/index.tsx +357 -0
  23. package/src/blocks/headtab/index.tsx +139 -0
  24. package/src/blocks/i18n-editor/index.tsx +1016 -0
  25. package/src/blocks/index.ts +80 -0
  26. package/src/blocks/kanban-board/index.tsx +779 -0
  27. package/src/blocks/landing/index.tsx +677 -0
  28. package/src/blocks/language-selector/index.tsx +88 -0
  29. package/src/blocks/layout/index.tsx +159 -0
  30. package/src/blocks/login/index.tsx +339 -0
  31. package/src/blocks/login/types.ts +131 -0
  32. package/src/blocks/pricing/index.tsx +564 -0
  33. package/src/blocks/profile/index.tsx +746 -0
  34. package/src/blocks/settings/index.tsx +558 -0
  35. package/src/blocks/sidebar/index.tsx +713 -0
  36. package/src/blocks/theme-creator-block/index.tsx +835 -0
  37. package/src/blocks/user-management/index.tsx +1037 -0
  38. package/src/blocks/wizard/index.tsx +719 -0
  39. package/src/components/DataTable/DataTable.tsx +406 -0
  40. package/src/components/DataTable/DataTableAdvanced.tsx +720 -0
  41. package/src/components/DataTable/DataTableBody.tsx +216 -0
  42. package/src/components/DataTable/DataTableCell.tsx +172 -0
  43. package/src/components/DataTable/DataTableColumnResizer.tsx +62 -0
  44. package/src/components/DataTable/DataTableConflictResolver.tsx +478 -0
  45. package/src/components/DataTable/DataTableContextMenu.tsx +219 -0
  46. package/src/components/DataTable/DataTableEditCell.tsx +279 -0
  47. package/src/components/DataTable/DataTableFilterBuilder.tsx +519 -0
  48. package/src/components/DataTable/DataTableFilters.tsx +535 -0
  49. package/src/components/DataTable/DataTableGrouping.tsx +147 -0
  50. package/src/components/DataTable/DataTableHeader.tsx +172 -0
  51. package/src/components/DataTable/DataTablePagination.tsx +125 -0
  52. package/src/components/DataTable/DataTableSelection.tsx +269 -0
  53. package/src/components/DataTable/DataTableSyncStatus.tsx +281 -0
  54. package/src/components/DataTable/DataTableToolbar.tsx +262 -0
  55. package/src/components/DataTable/README.md +446 -0
  56. package/src/components/DataTable/__tests__/DataTableAdvanced.test.tsx +426 -0
  57. package/src/components/DataTable/__tests__/DataTableEdit.test.tsx +329 -0
  58. package/src/components/DataTable/__tests__/useDataTableAdvanced.test.ts +455 -0
  59. package/src/components/DataTable/examples/EditExample.tsx +166 -0
  60. package/src/components/DataTable/formatters/index.ts +335 -0
  61. package/src/components/DataTable/hooks/__tests__/useDataTableEdit.test.ts +239 -0
  62. package/src/components/DataTable/hooks/useDataTable.ts +145 -0
  63. package/src/components/DataTable/hooks/useDataTableAdvanced.ts +342 -0
  64. package/src/components/DataTable/hooks/useDataTableAdvancedFilters.ts +637 -0
  65. package/src/components/DataTable/hooks/useDataTableColumnTemplates.ts +186 -0
  66. package/src/components/DataTable/hooks/useDataTableEdit.ts +167 -0
  67. package/src/components/DataTable/hooks/useDataTableExport.ts +227 -0
  68. package/src/components/DataTable/hooks/useDataTableImport.ts +216 -0
  69. package/src/components/DataTable/hooks/useDataTableOffline.ts +481 -0
  70. package/src/components/DataTable/hooks/useDataTableTheme.ts +213 -0
  71. package/src/components/DataTable/hooks/useDataTableVirtualization.ts +99 -0
  72. package/src/components/DataTable/hooks/useTableLayout.ts +85 -0
  73. package/src/components/DataTable/index.ts +81 -0
  74. package/src/components/DataTable/services/IndexedDBService.ts +504 -0
  75. package/src/components/DataTable/templates/index.tsx +803 -0
  76. package/src/components/DataTable/types.ts +504 -0
  77. package/src/components/DataTable/utils.ts +164 -0
  78. package/src/components/DataTable/workers/exportWorker.ts +213 -0
  79. package/src/components/accordion/index.tsx +61 -0
  80. package/src/components/alert/index.tsx +61 -0
  81. package/src/components/alert-dialog/index.tsx +146 -0
  82. package/src/components/aspect-ratio/index.tsx +12 -0
  83. package/src/components/avatar/index.tsx +54 -0
  84. package/src/components/badge/Badge.stories.tsx +64 -0
  85. package/src/components/badge/index.tsx +38 -0
  86. package/src/components/button/Button.stories.tsx +173 -0
  87. package/src/components/button/index.tsx +56 -0
  88. package/src/components/calendar/index.tsx +73 -0
  89. package/src/components/card/index.tsx +78 -0
  90. package/src/components/checkbox/index.tsx +34 -0
  91. package/src/components/code/index.tsx +229 -0
  92. package/src/components/collapsible/index.tsx +16 -0
  93. package/src/components/command/index.tsx +162 -0
  94. package/src/components/context-menu/index.tsx +204 -0
  95. package/src/components/dialog/index.tsx +126 -0
  96. package/src/components/dropdown-menu/index.tsx +204 -0
  97. package/src/components/error-boundary/ErrorBoundary.tsx +281 -0
  98. package/src/components/error-boundary/index.ts +7 -0
  99. package/src/components/form/index.tsx +183 -0
  100. package/src/components/hover-card/index.tsx +33 -0
  101. package/src/components/index.ts +368 -0
  102. package/src/components/input/Input.stories.tsx +100 -0
  103. package/src/components/input/index.tsx +27 -0
  104. package/src/components/input-otp/index.tsx +277 -0
  105. package/src/components/label/index.tsx +30 -0
  106. package/src/components/language-selector/index.tsx +341 -0
  107. package/src/components/menubar/index.tsx +240 -0
  108. package/src/components/navigation-menu/index.tsx +134 -0
  109. package/src/components/popover/index.tsx +35 -0
  110. package/src/components/progress/index.tsx +32 -0
  111. package/src/components/radio-group/index.tsx +48 -0
  112. package/src/components/scroll-area/index.tsx +52 -0
  113. package/src/components/select/index.tsx +164 -0
  114. package/src/components/separator/index.tsx +35 -0
  115. package/src/components/sheet/index.tsx +147 -0
  116. package/src/components/skeleton/index.tsx +22 -0
  117. package/src/components/slider/index.tsx +32 -0
  118. package/src/components/switch/index.tsx +33 -0
  119. package/src/components/table/index.tsx +117 -0
  120. package/src/components/tabs/index.tsx +59 -0
  121. package/src/components/textarea/index.tsx +30 -0
  122. package/src/components/theme-selector/index.tsx +327 -0
  123. package/src/components/toast/index.tsx +133 -0
  124. package/src/components/toaster/index.tsx +34 -0
  125. package/src/components/toggle/index.tsx +49 -0
  126. package/src/components/tooltip/index.tsx +34 -0
  127. package/src/components/typography/index.tsx +276 -0
  128. package/src/components/waka-3d-pie-chart/index.tsx +486 -0
  129. package/src/components/waka-achievement-unlock/index.tsx +716 -0
  130. package/src/components/waka-activity-feed/index.tsx +686 -0
  131. package/src/components/waka-address-autocomplete/index.tsx +1202 -0
  132. package/src/components/waka-admincrumb/index.tsx +349 -0
  133. package/src/components/waka-alert-stack/index.tsx +827 -0
  134. package/src/components/waka-allocation-matrix/index.tsx +1278 -0
  135. package/src/components/waka-approval-chain/index.tsx +766 -0
  136. package/src/components/waka-audit-log/index.tsx +1475 -0
  137. package/src/components/waka-autocomplete/index.tsx +358 -0
  138. package/src/components/waka-badge-showcase/index.tsx +704 -0
  139. package/src/components/waka-barcode/index.tsx +260 -0
  140. package/src/components/waka-biometric-prompt/index.tsx +765 -0
  141. package/src/components/waka-bottom-sheet/index.tsx +495 -0
  142. package/src/components/waka-breadcrumb/index.tsx +376 -0
  143. package/src/components/waka-breadcrumb-path/index.tsx +513 -0
  144. package/src/components/waka-budget-burn/index.tsx +1234 -0
  145. package/src/components/waka-capacity-planner/index.tsx +1107 -0
  146. package/src/components/waka-carousel/index.tsx +893 -0
  147. package/src/components/waka-cart-summary/index.tsx +1055 -0
  148. package/src/components/waka-challenge-timer/index.tsx +1044 -0
  149. package/src/components/waka-charts/WakaAreaChart.tsx +251 -0
  150. package/src/components/waka-charts/WakaBarChart.tsx +222 -0
  151. package/src/components/waka-charts/WakaChart.tsx +124 -0
  152. package/src/components/waka-charts/WakaLineChart.tsx +219 -0
  153. package/src/components/waka-charts/WakaMiniChart.tsx +133 -0
  154. package/src/components/waka-charts/WakaPieChart.tsx +214 -0
  155. package/src/components/waka-charts/WakaSparkline.tsx +229 -0
  156. package/src/components/waka-charts/dataTableHelpers.ts +109 -0
  157. package/src/components/waka-charts/hooks/useChartTheme.ts +123 -0
  158. package/src/components/waka-charts/hooks/useRechartsLoader.ts +234 -0
  159. package/src/components/waka-charts/index.ts +90 -0
  160. package/src/components/waka-charts/types.ts +330 -0
  161. package/src/components/waka-chat-bubble/index.tsx +1060 -0
  162. package/src/components/waka-checklist/index.tsx +1067 -0
  163. package/src/components/waka-checkout-stepper/index.tsx +976 -0
  164. package/src/components/waka-cohort-table/index.tsx +1011 -0
  165. package/src/components/waka-color-picker/index.tsx +447 -0
  166. package/src/components/waka-combo-counter/index.tsx +864 -0
  167. package/src/components/waka-combobox/index.tsx +497 -0
  168. package/src/components/waka-command-bar/index.tsx +403 -0
  169. package/src/components/waka-compare-period/index.tsx +1230 -0
  170. package/src/components/waka-connection-matrix/index.tsx +1053 -0
  171. package/src/components/waka-contribution-graph/index.tsx +552 -0
  172. package/src/components/waka-cost-breakdown/index.tsx +1065 -0
  173. package/src/components/waka-coupon-input/index.tsx +592 -0
  174. package/src/components/waka-credit-card-input/index.tsx +982 -0
  175. package/src/components/waka-daily-reward/index.tsx +762 -0
  176. package/src/components/waka-date-range-picker/index.tsx +378 -0
  177. package/src/components/waka-datetime-picker/index.tsx +793 -0
  178. package/src/components/waka-datetime-picker.form-integration/index.tsx +402 -0
  179. package/src/components/waka-deployment-lane/index.tsx +673 -0
  180. package/src/components/waka-device-trust/index.tsx +1259 -0
  181. package/src/components/waka-dock/index.tsx +285 -0
  182. package/src/components/waka-drawer/index.tsx +319 -0
  183. package/src/components/waka-empty-state/index.tsx +545 -0
  184. package/src/components/waka-error-shake/index.tsx +398 -0
  185. package/src/components/waka-feature-announcement/index.tsx +991 -0
  186. package/src/components/waka-file-upload/index.tsx +437 -0
  187. package/src/components/waka-floating-nav/index.tsx +413 -0
  188. package/src/components/waka-flow-diagram/index.tsx +508 -0
  189. package/src/components/waka-funnel-chart/index.tsx +823 -0
  190. package/src/components/waka-glow-card/index.tsx +246 -0
  191. package/src/components/waka-goal-progress/index.tsx +1025 -0
  192. package/src/components/waka-haptic-button/index.tsx +388 -0
  193. package/src/components/waka-health-pulse/index.tsx +451 -0
  194. package/src/components/waka-heatmap/index.tsx +1026 -0
  195. package/src/components/waka-hotspot/index.tsx +682 -0
  196. package/src/components/waka-image/index.tsx +373 -0
  197. package/src/components/waka-incident-timeline/index.tsx +686 -0
  198. package/src/components/waka-invoice-preview/index.tsx +829 -0
  199. package/src/components/waka-kanban/index.tsx +646 -0
  200. package/src/components/waka-kpi-dashboard/index.tsx +755 -0
  201. package/src/components/waka-leaderboard/index.tsx +746 -0
  202. package/src/components/waka-level-progress/index.tsx +665 -0
  203. package/src/components/waka-liquid-button/index.tsx +520 -0
  204. package/src/components/waka-loading-orbit/index.tsx +478 -0
  205. package/src/components/waka-loot-box/index.tsx +1091 -0
  206. package/src/components/waka-magic-link/index.tsx +321 -0
  207. package/src/components/waka-magnetic-button/index.tsx +567 -0
  208. package/src/components/waka-mention-input/index.tsx +953 -0
  209. package/src/components/waka-metric-sparkline/index.tsx +627 -0
  210. package/src/components/waka-milestone-road/index.tsx +1064 -0
  211. package/src/components/waka-modal/index.tsx +374 -0
  212. package/src/components/waka-morph-button/index.tsx +495 -0
  213. package/src/components/waka-network-topology/index.tsx +801 -0
  214. package/src/components/waka-notifications/index.tsx +414 -0
  215. package/src/components/waka-number-input/index.tsx +373 -0
  216. package/src/components/waka-orbital-menu/index.tsx +445 -0
  217. package/src/components/waka-order-tracker/index.tsx +1041 -0
  218. package/src/components/waka-pagination/index.tsx +393 -0
  219. package/src/components/waka-password-strength/index.tsx +824 -0
  220. package/src/components/waka-payment-method-picker/index.tsx +715 -0
  221. package/src/components/waka-permission-matrix/index.tsx +1302 -0
  222. package/src/components/waka-phone-input/index.tsx +801 -0
  223. package/src/components/waka-pipeline-view/index.tsx +604 -0
  224. package/src/components/waka-player-card/index.tsx +691 -0
  225. package/src/components/waka-points-popup/index.tsx +366 -0
  226. package/src/components/waka-power-up/index.tsx +1155 -0
  227. package/src/components/waka-presence-indicator/index.tsx +1181 -0
  228. package/src/components/waka-pricing-table/index.tsx +755 -0
  229. package/src/components/waka-product-card/index.tsx +786 -0
  230. package/src/components/waka-progress-onboarding/index.tsx +878 -0
  231. package/src/components/waka-pull-to-refresh/index.tsx +451 -0
  232. package/src/components/waka-qrcode/index.tsx +232 -0
  233. package/src/components/waka-quest-card/index.tsx +1275 -0
  234. package/src/components/waka-quota-bar/index.tsx +693 -0
  235. package/src/components/waka-radar-score/index.tsx +512 -0
  236. package/src/components/waka-rank-badge/index.tsx +813 -0
  237. package/src/components/waka-rating-input/index.tsx +560 -0
  238. package/src/components/waka-reaction-picker/index.tsx +1062 -0
  239. package/src/components/waka-region-map/index.tsx +730 -0
  240. package/src/components/waka-resource-gauge/index.tsx +654 -0
  241. package/src/components/waka-resource-pool/index.tsx +1035 -0
  242. package/src/components/waka-rich-text-editor/index.tsx +594 -0
  243. package/src/components/waka-rollback-slider/index.tsx +891 -0
  244. package/src/components/waka-sankey-diagram/index.tsx +1032 -0
  245. package/src/components/waka-schedule-picker/index.tsx +1060 -0
  246. package/src/components/waka-scratch-card/index.tsx +914 -0
  247. package/src/components/waka-season-pass/index.tsx +886 -0
  248. package/src/components/waka-security-score/index.tsx +1126 -0
  249. package/src/components/waka-segmented-control/index.tsx +238 -0
  250. package/src/components/waka-server-rack/index.tsx +764 -0
  251. package/src/components/waka-session-manager/index.tsx +815 -0
  252. package/src/components/waka-signature-pad/index.tsx +744 -0
  253. package/src/components/waka-skeleton-wave/index.tsx +454 -0
  254. package/src/components/waka-skill-tree/index.tsx +1031 -0
  255. package/src/components/waka-sla-tracker/index.tsx +798 -0
  256. package/src/components/waka-slider-range/index.tsx +765 -0
  257. package/src/components/waka-spin-wheel/index.tsx +671 -0
  258. package/src/components/waka-spinner/index.tsx +284 -0
  259. package/src/components/waka-spotlight/index.tsx +410 -0
  260. package/src/components/waka-stat/index.tsx +428 -0
  261. package/src/components/waka-stats-hexagon/index.tsx +824 -0
  262. package/src/components/waka-status-matrix/index.tsx +565 -0
  263. package/src/components/waka-stepper/index.tsx +489 -0
  264. package/src/components/waka-streak-counter/index.tsx +334 -0
  265. package/src/components/waka-success-explosion/index.tsx +453 -0
  266. package/src/components/waka-swipe-card/index.tsx +574 -0
  267. package/src/components/waka-tabs-morph/index.tsx +509 -0
  268. package/src/components/waka-tag-input/index.tsx +877 -0
  269. package/src/components/waka-team-banner/index.tsx +1183 -0
  270. package/src/components/waka-terminal-output/index.tsx +836 -0
  271. package/src/components/waka-theme-creator/index.tsx +762 -0
  272. package/src/components/waka-theme-manager/index.tsx +654 -0
  273. package/src/components/waka-thread-view/index.tsx +874 -0
  274. package/src/components/waka-tilt-card/index.tsx +250 -0
  275. package/src/components/waka-time-picker/index.tsx +479 -0
  276. package/src/components/waka-timeline/index.tsx +385 -0
  277. package/src/components/waka-tooltip-tour/index.tsx +855 -0
  278. package/src/components/waka-tour-guide/index.tsx +920 -0
  279. package/src/components/waka-tournament-bracket/index.tsx +1276 -0
  280. package/src/components/waka-tree/index.tsx +557 -0
  281. package/src/components/waka-treemap-chart/index.tsx +1031 -0
  282. package/src/components/waka-two-factor-setup/index.tsx +995 -0
  283. package/src/components/waka-typewriter/index.tsx +566 -0
  284. package/src/components/waka-typing-indicator/index.tsx +649 -0
  285. package/src/components/waka-versus-card/index.tsx +1026 -0
  286. package/src/components/waka-video/index.tsx +557 -0
  287. package/src/components/waka-video-call/index.tsx +1087 -0
  288. package/src/components/waka-virtual-list/index.tsx +327 -0
  289. package/src/components/waka-voice-message/index.tsx +1019 -0
  290. package/src/components/waka-welcome-modal/index.tsx +790 -0
  291. package/src/components/waka-xp-bar/index.tsx +799 -0
@@ -0,0 +1,586 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils"
5
+ import { Button } from "../../components/button"
6
+ import { Badge } from "../../components/badge"
7
+ import { Avatar, AvatarFallback, AvatarImage } from "../../components/avatar"
8
+ import { Card, CardContent, CardHeader, CardTitle } from "../../components/card"
9
+ import {
10
+ Select,
11
+ SelectContent,
12
+ SelectItem,
13
+ SelectTrigger,
14
+ SelectValue,
15
+ } from "../../components/select"
16
+ import { Input } from "../../components/input"
17
+ import {
18
+ Search,
19
+ Filter,
20
+ RefreshCw,
21
+ ChevronDown,
22
+ Clock,
23
+ User,
24
+ FileText,
25
+ MessageSquare,
26
+ Settings,
27
+ Upload,
28
+ Download,
29
+ Trash2,
30
+ Edit,
31
+ Plus,
32
+ Check,
33
+ X,
34
+ AlertCircle,
35
+ Bell,
36
+ } from "lucide-react"
37
+
38
+ // ============================================
39
+ // TYPES
40
+ // ============================================
41
+
42
+ export type ActivityType =
43
+ | "created"
44
+ | "updated"
45
+ | "deleted"
46
+ | "commented"
47
+ | "uploaded"
48
+ | "downloaded"
49
+ | "shared"
50
+ | "approved"
51
+ | "rejected"
52
+ | "mentioned"
53
+ | "assigned"
54
+ | "completed"
55
+ | "custom"
56
+
57
+ export interface ActivityUser {
58
+ id: string
59
+ name: string
60
+ avatar?: string
61
+ email?: string
62
+ }
63
+
64
+ export interface ActivityItem {
65
+ id: string
66
+ type: ActivityType
67
+ title: string
68
+ description?: string
69
+ user: ActivityUser
70
+ timestamp: Date | string
71
+ metadata?: {
72
+ targetName?: string
73
+ targetType?: string
74
+ oldValue?: string
75
+ newValue?: string
76
+ comment?: string
77
+ [key: string]: unknown
78
+ }
79
+ icon?: React.ReactNode
80
+ color?: string
81
+ }
82
+
83
+ export interface ActivityFilter {
84
+ id: string
85
+ label: string
86
+ types: ActivityType[]
87
+ }
88
+
89
+ export interface WakaActivityTimelineProps {
90
+ /** Activités */
91
+ activities: ActivityItem[]
92
+ /** Titre */
93
+ title?: string
94
+ /** Afficher la recherche */
95
+ showSearch?: boolean
96
+ /** Afficher les filtres */
97
+ showFilters?: boolean
98
+ /** Filtres disponibles */
99
+ filters?: ActivityFilter[]
100
+ /** Filtre actif */
101
+ activeFilter?: string
102
+ /** Callback changement de filtre */
103
+ onFilterChange?: (filterId: string | null) => void
104
+ /** Afficher le bouton rafraîchir */
105
+ showRefresh?: boolean
106
+ /** Callback rafraîchissement */
107
+ onRefresh?: () => void
108
+ /** En cours de chargement */
109
+ loading?: boolean
110
+ /** Regrouper par jour */
111
+ groupByDay?: boolean
112
+ /** Afficher plus */
113
+ showLoadMore?: boolean
114
+ /** Callback charger plus */
115
+ onLoadMore?: () => void
116
+ /** En cours de chargement plus */
117
+ loadingMore?: boolean
118
+ /** Afficher les avatars */
119
+ showAvatars?: boolean
120
+ /** Afficher les timestamps relatifs */
121
+ relativeTime?: boolean
122
+ /** Layout */
123
+ layout?: "timeline" | "feed" | "compact"
124
+ /** Classes CSS additionnelles */
125
+ className?: string
126
+ }
127
+
128
+ // ============================================
129
+ // ACTIVITY CONFIG
130
+ // ============================================
131
+
132
+ const activityConfig: Record<ActivityType, { icon: React.ElementType; color: string; label: string }> = {
133
+ created: { icon: Plus, color: "text-green-500", label: "créé" },
134
+ updated: { icon: Edit, color: "text-blue-500", label: "modifié" },
135
+ deleted: { icon: Trash2, color: "text-red-500", label: "supprimé" },
136
+ commented: { icon: MessageSquare, color: "text-purple-500", label: "commenté" },
137
+ uploaded: { icon: Upload, color: "text-cyan-500", label: "uploadé" },
138
+ downloaded: { icon: Download, color: "text-gray-500", label: "téléchargé" },
139
+ shared: { icon: User, color: "text-orange-500", label: "partagé" },
140
+ approved: { icon: Check, color: "text-green-500", label: "approuvé" },
141
+ rejected: { icon: X, color: "text-red-500", label: "rejeté" },
142
+ mentioned: { icon: Bell, color: "text-yellow-500", label: "mentionné" },
143
+ assigned: { icon: User, color: "text-indigo-500", label: "assigné" },
144
+ completed: { icon: Check, color: "text-green-500", label: "terminé" },
145
+ custom: { icon: AlertCircle, color: "text-gray-500", label: "" },
146
+ }
147
+
148
+ // ============================================
149
+ // SUB-COMPONENTS
150
+ // ============================================
151
+
152
+ interface ActivityAvatarProps {
153
+ user: ActivityUser
154
+ size?: "sm" | "md"
155
+ }
156
+
157
+ function ActivityAvatar({ user, size = "sm" }: ActivityAvatarProps) {
158
+ const sizeClasses = {
159
+ sm: "h-8 w-8",
160
+ md: "h-10 w-10",
161
+ }
162
+
163
+ const initials = user.name
164
+ .split(" ")
165
+ .map((n) => n[0])
166
+ .join("")
167
+ .toUpperCase()
168
+ .slice(0, 2)
169
+
170
+ return (
171
+ <Avatar className={sizeClasses[size]}>
172
+ <AvatarImage src={user.avatar} alt={user.name} />
173
+ <AvatarFallback className="text-xs">{initials}</AvatarFallback>
174
+ </Avatar>
175
+ )
176
+ }
177
+
178
+ interface ActivityIconProps {
179
+ type: ActivityType
180
+ customIcon?: React.ReactNode
181
+ customColor?: string
182
+ }
183
+
184
+ function ActivityIcon({ type, customIcon, customColor }: ActivityIconProps) {
185
+ const config = activityConfig[type]
186
+ const Icon = config.icon
187
+
188
+ return (
189
+ <div
190
+ className={cn(
191
+ "flex h-8 w-8 items-center justify-center rounded-full bg-background border-2 shadow-sm",
192
+ customColor || config.color
193
+ )}
194
+ >
195
+ {customIcon || <Icon className="h-4 w-4" />}
196
+ </div>
197
+ )
198
+ }
199
+
200
+ interface ActivityItemComponentProps {
201
+ activity: ActivityItem
202
+ showAvatar: boolean
203
+ relativeTime: boolean
204
+ layout: "timeline" | "feed" | "compact"
205
+ isLast: boolean
206
+ }
207
+
208
+ function ActivityItemComponent({
209
+ activity,
210
+ showAvatar,
211
+ relativeTime,
212
+ layout,
213
+ isLast,
214
+ }: ActivityItemComponentProps) {
215
+ const formatTime = (timestamp: Date | string): string => {
216
+ const date = typeof timestamp === "string" ? new Date(timestamp) : timestamp
217
+
218
+ if (relativeTime) {
219
+ const now = new Date()
220
+ const diff = now.getTime() - date.getTime()
221
+ const minutes = Math.floor(diff / 60000)
222
+ const hours = Math.floor(minutes / 60)
223
+ const days = Math.floor(hours / 24)
224
+
225
+ if (minutes < 1) return "À l'instant"
226
+ if (minutes < 60) return `Il y a ${minutes} min`
227
+ if (hours < 24) return `Il y a ${hours}h`
228
+ if (days < 7) return `Il y a ${days}j`
229
+ }
230
+
231
+ return date.toLocaleDateString("fr-FR", {
232
+ day: "numeric",
233
+ month: "short",
234
+ hour: "2-digit",
235
+ minute: "2-digit",
236
+ })
237
+ }
238
+
239
+ const config = activityConfig[activity.type]
240
+
241
+ if (layout === "compact") {
242
+ return (
243
+ <div className="flex items-center gap-3 py-2">
244
+ <ActivityIcon
245
+ type={activity.type}
246
+ customIcon={activity.icon}
247
+ customColor={activity.color}
248
+ />
249
+ <div className="flex-1 min-w-0">
250
+ <p className="text-sm truncate">
251
+ <span className="font-medium">{activity.user.name}</span>{" "}
252
+ <span className="text-muted-foreground">{activity.title}</span>
253
+ </p>
254
+ </div>
255
+ <span className="text-xs text-muted-foreground flex-shrink-0">
256
+ {formatTime(activity.timestamp)}
257
+ </span>
258
+ </div>
259
+ )
260
+ }
261
+
262
+ if (layout === "feed") {
263
+ return (
264
+ <Card className="mb-4">
265
+ <CardContent className="pt-4">
266
+ <div className="flex gap-3">
267
+ {showAvatar && <ActivityAvatar user={activity.user} size="md" />}
268
+ <div className="flex-1">
269
+ <div className="flex items-center gap-2 mb-1">
270
+ <span className="font-medium">{activity.user.name}</span>
271
+ <Badge variant="secondary" className="text-xs">
272
+ {config.label || activity.type}
273
+ </Badge>
274
+ <span className="text-xs text-muted-foreground">
275
+ {formatTime(activity.timestamp)}
276
+ </span>
277
+ </div>
278
+ <p className="text-sm">{activity.title}</p>
279
+ {activity.description && (
280
+ <p className="text-sm text-muted-foreground mt-1">
281
+ {activity.description}
282
+ </p>
283
+ )}
284
+ {activity.metadata?.comment && (
285
+ <div className="mt-2 p-3 bg-muted rounded-lg text-sm">
286
+ {activity.metadata.comment}
287
+ </div>
288
+ )}
289
+ </div>
290
+ </div>
291
+ </CardContent>
292
+ </Card>
293
+ )
294
+ }
295
+
296
+ // Timeline layout
297
+ return (
298
+ <div className="flex gap-4">
299
+ {/* Timeline line and icon */}
300
+ <div className="flex flex-col items-center">
301
+ <ActivityIcon
302
+ type={activity.type}
303
+ customIcon={activity.icon}
304
+ customColor={activity.color}
305
+ />
306
+ {!isLast && <div className="w-0.5 flex-1 bg-border mt-2" />}
307
+ </div>
308
+
309
+ {/* Content */}
310
+ <div className="flex-1 pb-8">
311
+ <div className="flex items-start gap-3">
312
+ {showAvatar && <ActivityAvatar user={activity.user} />}
313
+ <div className="flex-1">
314
+ <div className="flex items-center gap-2 flex-wrap">
315
+ <span className="font-medium">{activity.user.name}</span>
316
+ <span className="text-muted-foreground">{activity.title}</span>
317
+ </div>
318
+ {activity.description && (
319
+ <p className="text-sm text-muted-foreground mt-1">
320
+ {activity.description}
321
+ </p>
322
+ )}
323
+ {activity.metadata?.comment && (
324
+ <div className="mt-2 p-3 bg-muted rounded-lg text-sm">
325
+ {activity.metadata.comment}
326
+ </div>
327
+ )}
328
+ <span className="text-xs text-muted-foreground mt-2 block">
329
+ {formatTime(activity.timestamp)}
330
+ </span>
331
+ </div>
332
+ </div>
333
+ </div>
334
+ </div>
335
+ )
336
+ }
337
+
338
+ // ============================================
339
+ // MAIN COMPONENT
340
+ // ============================================
341
+
342
+ export function WakaActivityTimeline({
343
+ activities,
344
+ title = "Activité",
345
+ showSearch = true,
346
+ showFilters = true,
347
+ filters = [],
348
+ activeFilter: externalActiveFilter,
349
+ onFilterChange,
350
+ showRefresh = true,
351
+ onRefresh,
352
+ loading = false,
353
+ groupByDay = true,
354
+ showLoadMore = false,
355
+ onLoadMore,
356
+ loadingMore = false,
357
+ showAvatars = true,
358
+ relativeTime = true,
359
+ layout = "timeline",
360
+ className,
361
+ }: WakaActivityTimelineProps) {
362
+ const [searchQuery, setSearchQuery] = React.useState("")
363
+ const [internalActiveFilter, setInternalActiveFilter] = React.useState<string | null>(null)
364
+
365
+ const activeFilter = externalActiveFilter ?? internalActiveFilter
366
+
367
+ const handleFilterChange = (filterId: string | null) => {
368
+ if (onFilterChange) {
369
+ onFilterChange(filterId)
370
+ } else {
371
+ setInternalActiveFilter(filterId)
372
+ }
373
+ }
374
+
375
+ // Filter activities
376
+ const filteredActivities = React.useMemo(() => {
377
+ let result = activities
378
+
379
+ // Search filter
380
+ if (searchQuery) {
381
+ const query = searchQuery.toLowerCase()
382
+ result = result.filter(
383
+ (activity) =>
384
+ activity.title.toLowerCase().includes(query) ||
385
+ activity.user.name.toLowerCase().includes(query) ||
386
+ activity.description?.toLowerCase().includes(query)
387
+ )
388
+ }
389
+
390
+ // Type filter
391
+ if (activeFilter) {
392
+ const filter = filters.find((f) => f.id === activeFilter)
393
+ if (filter) {
394
+ result = result.filter((activity) => filter.types.includes(activity.type))
395
+ }
396
+ }
397
+
398
+ return result
399
+ }, [activities, searchQuery, activeFilter, filters])
400
+
401
+ // Group by day
402
+ const groupedActivities = React.useMemo(() => {
403
+ if (!groupByDay) return null
404
+
405
+ const groups: Record<string, ActivityItem[]> = {}
406
+
407
+ filteredActivities.forEach((activity) => {
408
+ const date = typeof activity.timestamp === "string"
409
+ ? new Date(activity.timestamp)
410
+ : activity.timestamp
411
+ const dateKey = date.toLocaleDateString("fr-FR", {
412
+ weekday: "long",
413
+ day: "numeric",
414
+ month: "long",
415
+ year: "numeric",
416
+ })
417
+
418
+ if (!groups[dateKey]) {
419
+ groups[dateKey] = []
420
+ }
421
+ groups[dateKey].push(activity)
422
+ })
423
+
424
+ return groups
425
+ }, [filteredActivities, groupByDay])
426
+
427
+ const renderActivities = (items: ActivityItem[]) => (
428
+ <div>
429
+ {items.map((activity, index) => (
430
+ <ActivityItemComponent
431
+ key={activity.id}
432
+ activity={activity}
433
+ showAvatar={showAvatars}
434
+ relativeTime={relativeTime}
435
+ layout={layout}
436
+ isLast={index === items.length - 1}
437
+ />
438
+ ))}
439
+ </div>
440
+ )
441
+
442
+ return (
443
+ <div className={cn("space-y-4", className)}>
444
+ {/* Header */}
445
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
446
+ <h2 className="text-xl font-semibold">{title}</h2>
447
+ <div className="flex items-center gap-2">
448
+ {showRefresh && onRefresh && (
449
+ <Button
450
+ variant="outline"
451
+ size="sm"
452
+ onClick={onRefresh}
453
+ disabled={loading}
454
+ >
455
+ <RefreshCw className={cn("h-4 w-4", loading && "animate-spin")} />
456
+ </Button>
457
+ )}
458
+ </div>
459
+ </div>
460
+
461
+ {/* Search and Filters */}
462
+ {(showSearch || showFilters) && (
463
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center">
464
+ {showSearch && (
465
+ <div className="relative flex-1 max-w-sm">
466
+ <Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
467
+ <Input
468
+ placeholder="Rechercher..."
469
+ value={searchQuery}
470
+ onChange={(e) => setSearchQuery(e.target.value)}
471
+ className="pl-9"
472
+ />
473
+ </div>
474
+ )}
475
+ {showFilters && filters.length > 0 && (
476
+ <Select
477
+ value={activeFilter || "all"}
478
+ onValueChange={(value) => handleFilterChange(value === "all" ? null : value)}
479
+ >
480
+ <SelectTrigger className="w-[180px]">
481
+ <Filter className="h-4 w-4 mr-2" />
482
+ <SelectValue placeholder="Filtrer" />
483
+ </SelectTrigger>
484
+ <SelectContent>
485
+ <SelectItem value="all">Toutes les activités</SelectItem>
486
+ {filters.map((filter) => (
487
+ <SelectItem key={filter.id} value={filter.id}>
488
+ {filter.label}
489
+ </SelectItem>
490
+ ))}
491
+ </SelectContent>
492
+ </Select>
493
+ )}
494
+ </div>
495
+ )}
496
+
497
+ {/* Activities */}
498
+ {loading ? (
499
+ <div className="flex items-center justify-center py-12">
500
+ <RefreshCw className="h-6 w-6 animate-spin text-muted-foreground" />
501
+ </div>
502
+ ) : filteredActivities.length === 0 ? (
503
+ <div className="text-center py-12">
504
+ <Clock className="h-12 w-12 text-muted-foreground mx-auto mb-4" />
505
+ <p className="text-muted-foreground">Aucune activité</p>
506
+ </div>
507
+ ) : groupByDay && groupedActivities ? (
508
+ <div className="space-y-6">
509
+ {Object.entries(groupedActivities).map(([date, items]) => (
510
+ <div key={date}>
511
+ <h3 className="text-sm font-medium text-muted-foreground mb-4 capitalize">
512
+ {date}
513
+ </h3>
514
+ {renderActivities(items)}
515
+ </div>
516
+ ))}
517
+ </div>
518
+ ) : (
519
+ renderActivities(filteredActivities)
520
+ )}
521
+
522
+ {/* Load More */}
523
+ {showLoadMore && onLoadMore && (
524
+ <div className="flex justify-center pt-4">
525
+ <Button
526
+ variant="outline"
527
+ onClick={onLoadMore}
528
+ disabled={loadingMore}
529
+ >
530
+ {loadingMore ? (
531
+ <RefreshCw className="h-4 w-4 animate-spin mr-2" />
532
+ ) : (
533
+ <ChevronDown className="h-4 w-4 mr-2" />
534
+ )}
535
+ Charger plus
536
+ </Button>
537
+ </div>
538
+ )}
539
+ </div>
540
+ )
541
+ }
542
+
543
+ // ============================================
544
+ // PRESETS
545
+ // ============================================
546
+
547
+ export const defaultActivityFilters: ActivityFilter[] = [
548
+ { id: "edits", label: "Modifications", types: ["created", "updated", "deleted"] },
549
+ { id: "comments", label: "Commentaires", types: ["commented", "mentioned"] },
550
+ { id: "files", label: "Fichiers", types: ["uploaded", "downloaded", "shared"] },
551
+ { id: "approvals", label: "Approbations", types: ["approved", "rejected", "completed"] },
552
+ ]
553
+
554
+ export const defaultActivities: ActivityItem[] = [
555
+ {
556
+ id: "1",
557
+ type: "created",
558
+ title: "a créé le document 'Rapport Q4'",
559
+ user: { id: "1", name: "Marie Dupont" },
560
+ timestamp: new Date(Date.now() - 1000 * 60 * 5),
561
+ },
562
+ {
563
+ id: "2",
564
+ type: "commented",
565
+ title: "a commenté sur 'Projet Alpha'",
566
+ user: { id: "2", name: "Pierre Martin" },
567
+ timestamp: new Date(Date.now() - 1000 * 60 * 30),
568
+ metadata: { comment: "Super travail sur cette partie !" },
569
+ },
570
+ {
571
+ id: "3",
572
+ type: "uploaded",
573
+ title: "a uploadé 3 fichiers",
574
+ user: { id: "3", name: "Sophie Bernard" },
575
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 2),
576
+ },
577
+ {
578
+ id: "4",
579
+ type: "approved",
580
+ title: "a approuvé la demande de congés",
581
+ user: { id: "1", name: "Marie Dupont" },
582
+ timestamp: new Date(Date.now() - 1000 * 60 * 60 * 24),
583
+ },
584
+ ]
585
+
586
+ export default WakaActivityTimeline