@wakastellar/ui 2.0.0 → 2.1.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 (290) hide show
  1. package/dist/cli/commands/add.d.ts +7 -0
  2. package/dist/cli/commands/init.d.ts +6 -0
  3. package/dist/cli/commands/list.d.ts +5 -0
  4. package/dist/cli/commands/search.d.ts +1 -0
  5. package/dist/cli/index.cjs +4844 -0
  6. package/dist/cli/index.d.ts +1 -0
  7. package/dist/cli/utils/config.d.ts +29 -0
  8. package/dist/cli/utils/logger.d.ts +20 -0
  9. package/dist/cli/utils/registry.d.ts +23 -0
  10. package/package.json +14 -3
  11. package/src/blocks/activity-timeline/index.tsx +586 -0
  12. package/src/blocks/calendar-view/index.tsx +756 -0
  13. package/src/blocks/chat/index.tsx +1018 -0
  14. package/src/blocks/chat/widget.tsx +504 -0
  15. package/src/blocks/dashboard/index.tsx +522 -0
  16. package/src/blocks/empty-states/index.tsx +452 -0
  17. package/src/blocks/error-pages/index.tsx +426 -0
  18. package/src/blocks/faq/index.tsx +479 -0
  19. package/src/blocks/file-manager/index.tsx +890 -0
  20. package/src/blocks/footer/index.tsx +133 -0
  21. package/src/blocks/header/index.tsx +357 -0
  22. package/src/blocks/headtab/index.tsx +139 -0
  23. package/src/blocks/i18n-editor/index.tsx +1016 -0
  24. package/src/blocks/index.ts +80 -0
  25. package/src/blocks/kanban-board/index.tsx +779 -0
  26. package/src/blocks/landing/index.tsx +677 -0
  27. package/src/blocks/language-selector/index.tsx +88 -0
  28. package/src/blocks/layout/index.tsx +159 -0
  29. package/src/blocks/login/index.tsx +339 -0
  30. package/src/blocks/login/types.ts +131 -0
  31. package/src/blocks/pricing/index.tsx +564 -0
  32. package/src/blocks/profile/index.tsx +746 -0
  33. package/src/blocks/settings/index.tsx +558 -0
  34. package/src/blocks/sidebar/index.tsx +713 -0
  35. package/src/blocks/theme-creator-block/index.tsx +835 -0
  36. package/src/blocks/user-management/index.tsx +1037 -0
  37. package/src/blocks/wizard/index.tsx +719 -0
  38. package/src/components/DataTable/DataTable.tsx +406 -0
  39. package/src/components/DataTable/DataTableAdvanced.tsx +720 -0
  40. package/src/components/DataTable/DataTableBody.tsx +216 -0
  41. package/src/components/DataTable/DataTableCell.tsx +172 -0
  42. package/src/components/DataTable/DataTableColumnResizer.tsx +62 -0
  43. package/src/components/DataTable/DataTableConflictResolver.tsx +478 -0
  44. package/src/components/DataTable/DataTableContextMenu.tsx +219 -0
  45. package/src/components/DataTable/DataTableEditCell.tsx +279 -0
  46. package/src/components/DataTable/DataTableFilterBuilder.tsx +519 -0
  47. package/src/components/DataTable/DataTableFilters.tsx +535 -0
  48. package/src/components/DataTable/DataTableGrouping.tsx +147 -0
  49. package/src/components/DataTable/DataTableHeader.tsx +172 -0
  50. package/src/components/DataTable/DataTablePagination.tsx +125 -0
  51. package/src/components/DataTable/DataTableSelection.tsx +269 -0
  52. package/src/components/DataTable/DataTableSyncStatus.tsx +281 -0
  53. package/src/components/DataTable/DataTableToolbar.tsx +262 -0
  54. package/src/components/DataTable/README.md +446 -0
  55. package/src/components/DataTable/__tests__/DataTableAdvanced.test.tsx +426 -0
  56. package/src/components/DataTable/__tests__/DataTableEdit.test.tsx +329 -0
  57. package/src/components/DataTable/__tests__/useDataTableAdvanced.test.ts +455 -0
  58. package/src/components/DataTable/examples/EditExample.tsx +166 -0
  59. package/src/components/DataTable/formatters/index.ts +335 -0
  60. package/src/components/DataTable/hooks/__tests__/useDataTableEdit.test.ts +239 -0
  61. package/src/components/DataTable/hooks/useDataTable.ts +145 -0
  62. package/src/components/DataTable/hooks/useDataTableAdvanced.ts +342 -0
  63. package/src/components/DataTable/hooks/useDataTableAdvancedFilters.ts +637 -0
  64. package/src/components/DataTable/hooks/useDataTableColumnTemplates.ts +186 -0
  65. package/src/components/DataTable/hooks/useDataTableEdit.ts +167 -0
  66. package/src/components/DataTable/hooks/useDataTableExport.ts +227 -0
  67. package/src/components/DataTable/hooks/useDataTableImport.ts +216 -0
  68. package/src/components/DataTable/hooks/useDataTableOffline.ts +481 -0
  69. package/src/components/DataTable/hooks/useDataTableTheme.ts +213 -0
  70. package/src/components/DataTable/hooks/useDataTableVirtualization.ts +99 -0
  71. package/src/components/DataTable/hooks/useTableLayout.ts +85 -0
  72. package/src/components/DataTable/index.ts +81 -0
  73. package/src/components/DataTable/services/IndexedDBService.ts +504 -0
  74. package/src/components/DataTable/templates/index.tsx +803 -0
  75. package/src/components/DataTable/types.ts +504 -0
  76. package/src/components/DataTable/utils.ts +164 -0
  77. package/src/components/DataTable/workers/exportWorker.ts +213 -0
  78. package/src/components/accordion/index.tsx +61 -0
  79. package/src/components/alert/index.tsx +61 -0
  80. package/src/components/alert-dialog/index.tsx +146 -0
  81. package/src/components/aspect-ratio/index.tsx +12 -0
  82. package/src/components/avatar/index.tsx +54 -0
  83. package/src/components/badge/Badge.stories.tsx +64 -0
  84. package/src/components/badge/index.tsx +38 -0
  85. package/src/components/button/Button.stories.tsx +173 -0
  86. package/src/components/button/index.tsx +56 -0
  87. package/src/components/calendar/index.tsx +73 -0
  88. package/src/components/card/index.tsx +78 -0
  89. package/src/components/checkbox/index.tsx +34 -0
  90. package/src/components/code/index.tsx +229 -0
  91. package/src/components/collapsible/index.tsx +16 -0
  92. package/src/components/command/index.tsx +162 -0
  93. package/src/components/context-menu/index.tsx +204 -0
  94. package/src/components/dialog/index.tsx +126 -0
  95. package/src/components/dropdown-menu/index.tsx +204 -0
  96. package/src/components/error-boundary/ErrorBoundary.tsx +281 -0
  97. package/src/components/error-boundary/index.ts +7 -0
  98. package/src/components/form/index.tsx +183 -0
  99. package/src/components/hover-card/index.tsx +33 -0
  100. package/src/components/index.ts +368 -0
  101. package/src/components/input/Input.stories.tsx +100 -0
  102. package/src/components/input/index.tsx +27 -0
  103. package/src/components/input-otp/index.tsx +277 -0
  104. package/src/components/label/index.tsx +30 -0
  105. package/src/components/language-selector/index.tsx +341 -0
  106. package/src/components/menubar/index.tsx +240 -0
  107. package/src/components/navigation-menu/index.tsx +134 -0
  108. package/src/components/popover/index.tsx +35 -0
  109. package/src/components/progress/index.tsx +32 -0
  110. package/src/components/radio-group/index.tsx +48 -0
  111. package/src/components/scroll-area/index.tsx +52 -0
  112. package/src/components/select/index.tsx +164 -0
  113. package/src/components/separator/index.tsx +35 -0
  114. package/src/components/sheet/index.tsx +147 -0
  115. package/src/components/skeleton/index.tsx +22 -0
  116. package/src/components/slider/index.tsx +32 -0
  117. package/src/components/switch/index.tsx +33 -0
  118. package/src/components/table/index.tsx +117 -0
  119. package/src/components/tabs/index.tsx +59 -0
  120. package/src/components/textarea/index.tsx +30 -0
  121. package/src/components/theme-selector/index.tsx +327 -0
  122. package/src/components/toast/index.tsx +133 -0
  123. package/src/components/toaster/index.tsx +34 -0
  124. package/src/components/toggle/index.tsx +49 -0
  125. package/src/components/tooltip/index.tsx +34 -0
  126. package/src/components/typography/index.tsx +276 -0
  127. package/src/components/waka-3d-pie-chart/index.tsx +486 -0
  128. package/src/components/waka-achievement-unlock/index.tsx +716 -0
  129. package/src/components/waka-activity-feed/index.tsx +686 -0
  130. package/src/components/waka-address-autocomplete/index.tsx +1202 -0
  131. package/src/components/waka-admincrumb/index.tsx +349 -0
  132. package/src/components/waka-alert-stack/index.tsx +827 -0
  133. package/src/components/waka-allocation-matrix/index.tsx +1278 -0
  134. package/src/components/waka-approval-chain/index.tsx +766 -0
  135. package/src/components/waka-audit-log/index.tsx +1475 -0
  136. package/src/components/waka-autocomplete/index.tsx +358 -0
  137. package/src/components/waka-badge-showcase/index.tsx +704 -0
  138. package/src/components/waka-barcode/index.tsx +260 -0
  139. package/src/components/waka-biometric-prompt/index.tsx +765 -0
  140. package/src/components/waka-bottom-sheet/index.tsx +495 -0
  141. package/src/components/waka-breadcrumb/index.tsx +376 -0
  142. package/src/components/waka-breadcrumb-path/index.tsx +513 -0
  143. package/src/components/waka-budget-burn/index.tsx +1234 -0
  144. package/src/components/waka-capacity-planner/index.tsx +1107 -0
  145. package/src/components/waka-carousel/index.tsx +893 -0
  146. package/src/components/waka-cart-summary/index.tsx +1055 -0
  147. package/src/components/waka-challenge-timer/index.tsx +1044 -0
  148. package/src/components/waka-charts/WakaAreaChart.tsx +251 -0
  149. package/src/components/waka-charts/WakaBarChart.tsx +222 -0
  150. package/src/components/waka-charts/WakaChart.tsx +124 -0
  151. package/src/components/waka-charts/WakaLineChart.tsx +219 -0
  152. package/src/components/waka-charts/WakaMiniChart.tsx +133 -0
  153. package/src/components/waka-charts/WakaPieChart.tsx +214 -0
  154. package/src/components/waka-charts/WakaSparkline.tsx +229 -0
  155. package/src/components/waka-charts/dataTableHelpers.ts +109 -0
  156. package/src/components/waka-charts/hooks/useChartTheme.ts +123 -0
  157. package/src/components/waka-charts/hooks/useRechartsLoader.ts +234 -0
  158. package/src/components/waka-charts/index.ts +90 -0
  159. package/src/components/waka-charts/types.ts +330 -0
  160. package/src/components/waka-chat-bubble/index.tsx +1060 -0
  161. package/src/components/waka-checklist/index.tsx +1067 -0
  162. package/src/components/waka-checkout-stepper/index.tsx +976 -0
  163. package/src/components/waka-cohort-table/index.tsx +1011 -0
  164. package/src/components/waka-color-picker/index.tsx +447 -0
  165. package/src/components/waka-combo-counter/index.tsx +864 -0
  166. package/src/components/waka-combobox/index.tsx +497 -0
  167. package/src/components/waka-command-bar/index.tsx +403 -0
  168. package/src/components/waka-compare-period/index.tsx +1230 -0
  169. package/src/components/waka-connection-matrix/index.tsx +1053 -0
  170. package/src/components/waka-contribution-graph/index.tsx +552 -0
  171. package/src/components/waka-cost-breakdown/index.tsx +1065 -0
  172. package/src/components/waka-coupon-input/index.tsx +592 -0
  173. package/src/components/waka-credit-card-input/index.tsx +982 -0
  174. package/src/components/waka-daily-reward/index.tsx +762 -0
  175. package/src/components/waka-date-range-picker/index.tsx +378 -0
  176. package/src/components/waka-datetime-picker/index.tsx +793 -0
  177. package/src/components/waka-datetime-picker.form-integration/index.tsx +402 -0
  178. package/src/components/waka-deployment-lane/index.tsx +673 -0
  179. package/src/components/waka-device-trust/index.tsx +1259 -0
  180. package/src/components/waka-dock/index.tsx +285 -0
  181. package/src/components/waka-drawer/index.tsx +319 -0
  182. package/src/components/waka-empty-state/index.tsx +545 -0
  183. package/src/components/waka-error-shake/index.tsx +398 -0
  184. package/src/components/waka-feature-announcement/index.tsx +991 -0
  185. package/src/components/waka-file-upload/index.tsx +437 -0
  186. package/src/components/waka-floating-nav/index.tsx +413 -0
  187. package/src/components/waka-flow-diagram/index.tsx +508 -0
  188. package/src/components/waka-funnel-chart/index.tsx +823 -0
  189. package/src/components/waka-glow-card/index.tsx +246 -0
  190. package/src/components/waka-goal-progress/index.tsx +1025 -0
  191. package/src/components/waka-haptic-button/index.tsx +388 -0
  192. package/src/components/waka-health-pulse/index.tsx +451 -0
  193. package/src/components/waka-heatmap/index.tsx +1026 -0
  194. package/src/components/waka-hotspot/index.tsx +682 -0
  195. package/src/components/waka-image/index.tsx +373 -0
  196. package/src/components/waka-incident-timeline/index.tsx +686 -0
  197. package/src/components/waka-invoice-preview/index.tsx +829 -0
  198. package/src/components/waka-kanban/index.tsx +646 -0
  199. package/src/components/waka-kpi-dashboard/index.tsx +755 -0
  200. package/src/components/waka-leaderboard/index.tsx +746 -0
  201. package/src/components/waka-level-progress/index.tsx +665 -0
  202. package/src/components/waka-liquid-button/index.tsx +520 -0
  203. package/src/components/waka-loading-orbit/index.tsx +478 -0
  204. package/src/components/waka-loot-box/index.tsx +1091 -0
  205. package/src/components/waka-magic-link/index.tsx +321 -0
  206. package/src/components/waka-magnetic-button/index.tsx +567 -0
  207. package/src/components/waka-mention-input/index.tsx +953 -0
  208. package/src/components/waka-metric-sparkline/index.tsx +627 -0
  209. package/src/components/waka-milestone-road/index.tsx +1064 -0
  210. package/src/components/waka-modal/index.tsx +374 -0
  211. package/src/components/waka-morph-button/index.tsx +495 -0
  212. package/src/components/waka-network-topology/index.tsx +801 -0
  213. package/src/components/waka-notifications/index.tsx +414 -0
  214. package/src/components/waka-number-input/index.tsx +373 -0
  215. package/src/components/waka-orbital-menu/index.tsx +445 -0
  216. package/src/components/waka-order-tracker/index.tsx +1041 -0
  217. package/src/components/waka-pagination/index.tsx +393 -0
  218. package/src/components/waka-password-strength/index.tsx +824 -0
  219. package/src/components/waka-payment-method-picker/index.tsx +715 -0
  220. package/src/components/waka-permission-matrix/index.tsx +1302 -0
  221. package/src/components/waka-phone-input/index.tsx +801 -0
  222. package/src/components/waka-pipeline-view/index.tsx +604 -0
  223. package/src/components/waka-player-card/index.tsx +691 -0
  224. package/src/components/waka-points-popup/index.tsx +366 -0
  225. package/src/components/waka-power-up/index.tsx +1155 -0
  226. package/src/components/waka-presence-indicator/index.tsx +1181 -0
  227. package/src/components/waka-pricing-table/index.tsx +755 -0
  228. package/src/components/waka-product-card/index.tsx +786 -0
  229. package/src/components/waka-progress-onboarding/index.tsx +878 -0
  230. package/src/components/waka-pull-to-refresh/index.tsx +451 -0
  231. package/src/components/waka-qrcode/index.tsx +232 -0
  232. package/src/components/waka-quest-card/index.tsx +1275 -0
  233. package/src/components/waka-quota-bar/index.tsx +693 -0
  234. package/src/components/waka-radar-score/index.tsx +512 -0
  235. package/src/components/waka-rank-badge/index.tsx +813 -0
  236. package/src/components/waka-rating-input/index.tsx +560 -0
  237. package/src/components/waka-reaction-picker/index.tsx +1062 -0
  238. package/src/components/waka-region-map/index.tsx +730 -0
  239. package/src/components/waka-resource-gauge/index.tsx +654 -0
  240. package/src/components/waka-resource-pool/index.tsx +1035 -0
  241. package/src/components/waka-rich-text-editor/index.tsx +594 -0
  242. package/src/components/waka-rollback-slider/index.tsx +891 -0
  243. package/src/components/waka-sankey-diagram/index.tsx +1032 -0
  244. package/src/components/waka-schedule-picker/index.tsx +1060 -0
  245. package/src/components/waka-scratch-card/index.tsx +914 -0
  246. package/src/components/waka-season-pass/index.tsx +886 -0
  247. package/src/components/waka-security-score/index.tsx +1126 -0
  248. package/src/components/waka-segmented-control/index.tsx +238 -0
  249. package/src/components/waka-server-rack/index.tsx +764 -0
  250. package/src/components/waka-session-manager/index.tsx +815 -0
  251. package/src/components/waka-signature-pad/index.tsx +744 -0
  252. package/src/components/waka-skeleton-wave/index.tsx +454 -0
  253. package/src/components/waka-skill-tree/index.tsx +1031 -0
  254. package/src/components/waka-sla-tracker/index.tsx +798 -0
  255. package/src/components/waka-slider-range/index.tsx +765 -0
  256. package/src/components/waka-spin-wheel/index.tsx +671 -0
  257. package/src/components/waka-spinner/index.tsx +284 -0
  258. package/src/components/waka-spotlight/index.tsx +410 -0
  259. package/src/components/waka-stat/index.tsx +428 -0
  260. package/src/components/waka-stats-hexagon/index.tsx +824 -0
  261. package/src/components/waka-status-matrix/index.tsx +565 -0
  262. package/src/components/waka-stepper/index.tsx +489 -0
  263. package/src/components/waka-streak-counter/index.tsx +334 -0
  264. package/src/components/waka-success-explosion/index.tsx +453 -0
  265. package/src/components/waka-swipe-card/index.tsx +574 -0
  266. package/src/components/waka-tabs-morph/index.tsx +509 -0
  267. package/src/components/waka-tag-input/index.tsx +877 -0
  268. package/src/components/waka-team-banner/index.tsx +1183 -0
  269. package/src/components/waka-terminal-output/index.tsx +836 -0
  270. package/src/components/waka-theme-creator/index.tsx +762 -0
  271. package/src/components/waka-theme-manager/index.tsx +654 -0
  272. package/src/components/waka-thread-view/index.tsx +874 -0
  273. package/src/components/waka-tilt-card/index.tsx +250 -0
  274. package/src/components/waka-time-picker/index.tsx +479 -0
  275. package/src/components/waka-timeline/index.tsx +385 -0
  276. package/src/components/waka-tooltip-tour/index.tsx +855 -0
  277. package/src/components/waka-tour-guide/index.tsx +920 -0
  278. package/src/components/waka-tournament-bracket/index.tsx +1276 -0
  279. package/src/components/waka-tree/index.tsx +557 -0
  280. package/src/components/waka-treemap-chart/index.tsx +1031 -0
  281. package/src/components/waka-two-factor-setup/index.tsx +995 -0
  282. package/src/components/waka-typewriter/index.tsx +566 -0
  283. package/src/components/waka-typing-indicator/index.tsx +649 -0
  284. package/src/components/waka-versus-card/index.tsx +1026 -0
  285. package/src/components/waka-video/index.tsx +557 -0
  286. package/src/components/waka-video-call/index.tsx +1087 -0
  287. package/src/components/waka-virtual-list/index.tsx +327 -0
  288. package/src/components/waka-voice-message/index.tsx +1019 -0
  289. package/src/components/waka-welcome-modal/index.tsx +790 -0
  290. package/src/components/waka-xp-bar/index.tsx +799 -0
@@ -0,0 +1,779 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cn } from "../../utils"
5
+ import { Button } from "../../components/button"
6
+ import { Input } from "../../components/input"
7
+ import { Badge } from "../../components/badge"
8
+ import { Avatar, AvatarFallback, AvatarImage } from "../../components/avatar"
9
+ import { Card, CardContent, CardHeader } from "../../components/card"
10
+ import { Textarea } from "../../components/textarea"
11
+ import {
12
+ DropdownMenu,
13
+ DropdownMenuContent,
14
+ DropdownMenuItem,
15
+ DropdownMenuSeparator,
16
+ DropdownMenuTrigger,
17
+ } from "../../components/dropdown-menu"
18
+ import {
19
+ Dialog,
20
+ DialogContent,
21
+ DialogDescription,
22
+ DialogFooter,
23
+ DialogHeader,
24
+ DialogTitle,
25
+ } from "../../components/dialog"
26
+ import {
27
+ Plus,
28
+ MoreHorizontal,
29
+ Edit,
30
+ Trash2,
31
+ Calendar,
32
+ Tag,
33
+ User,
34
+ GripVertical,
35
+ X,
36
+ Check,
37
+ Clock,
38
+ AlertCircle,
39
+ } from "lucide-react"
40
+
41
+ // ============================================
42
+ // TYPES
43
+ // ============================================
44
+
45
+ export type KanbanCardPriority = "low" | "medium" | "high" | "urgent"
46
+
47
+ export interface KanbanCardLabel {
48
+ id: string
49
+ name: string
50
+ color: string
51
+ }
52
+
53
+ export interface KanbanCardAssignee {
54
+ id: string
55
+ name: string
56
+ avatar?: string
57
+ }
58
+
59
+ export interface KanbanCard {
60
+ id: string
61
+ title: string
62
+ description?: string
63
+ priority?: KanbanCardPriority
64
+ labels?: KanbanCardLabel[]
65
+ assignees?: KanbanCardAssignee[]
66
+ dueDate?: Date | string
67
+ createdAt?: Date | string
68
+ metadata?: Record<string, unknown>
69
+ }
70
+
71
+ export interface KanbanColumn {
72
+ id: string
73
+ title: string
74
+ cards: KanbanCard[]
75
+ color?: string
76
+ limit?: number
77
+ }
78
+
79
+ export interface WakaKanbanBoardProps {
80
+ /** Colonnes du tableau */
81
+ columns: KanbanColumn[]
82
+ /** Callback lors du changement des colonnes */
83
+ onColumnsChange?: (columns: KanbanColumn[]) => void
84
+ /** Callback lors du clic sur une carte */
85
+ onCardClick?: (card: KanbanCard, columnId: string) => void
86
+ /** Callback lors de l'ajout d'une carte */
87
+ onCardAdd?: (columnId: string, card: Partial<KanbanCard>) => void
88
+ /** Callback lors de la modification d'une carte */
89
+ onCardEdit?: (cardId: string, columnId: string, data: Partial<KanbanCard>) => void
90
+ /** Callback lors de la suppression d'une carte */
91
+ onCardDelete?: (cardId: string, columnId: string) => void
92
+ /** Callback lors du déplacement d'une carte */
93
+ onCardMove?: (cardId: string, fromColumnId: string, toColumnId: string, index: number) => void
94
+ /** Callback lors de l'ajout d'une colonne */
95
+ onColumnAdd?: (column: Partial<KanbanColumn>) => void
96
+ /** Callback lors de la modification d'une colonne */
97
+ onColumnEdit?: (columnId: string, data: Partial<KanbanColumn>) => void
98
+ /** Callback lors de la suppression d'une colonne */
99
+ onColumnDelete?: (columnId: string) => void
100
+ /** Afficher le bouton d'ajout de carte */
101
+ showAddCard?: boolean
102
+ /** Afficher le bouton d'ajout de colonne */
103
+ showAddColumn?: boolean
104
+ /** Afficher le nombre de cartes */
105
+ showCardCount?: boolean
106
+ /** Afficher les assignés */
107
+ showAssignees?: boolean
108
+ /** Afficher les labels */
109
+ showLabels?: boolean
110
+ /** Afficher la priorité */
111
+ showPriority?: boolean
112
+ /** Afficher la date d'échéance */
113
+ showDueDate?: boolean
114
+ /** Autoriser le drag & drop */
115
+ draggable?: boolean
116
+ /** Labels disponibles */
117
+ availableLabels?: KanbanCardLabel[]
118
+ /** Assignés disponibles */
119
+ availableAssignees?: KanbanCardAssignee[]
120
+ /** Classes CSS additionnelles */
121
+ className?: string
122
+ }
123
+
124
+ // ============================================
125
+ // PRIORITY CONFIG
126
+ // ============================================
127
+
128
+ const priorityConfig: Record<KanbanCardPriority, { label: string; color: string; icon: React.ElementType }> = {
129
+ low: { label: "Basse", color: "bg-gray-500", icon: Clock },
130
+ medium: { label: "Moyenne", color: "bg-blue-500", icon: Clock },
131
+ high: { label: "Haute", color: "bg-orange-500", icon: AlertCircle },
132
+ urgent: { label: "Urgente", color: "bg-red-500", icon: AlertCircle },
133
+ }
134
+
135
+ // ============================================
136
+ // SUB-COMPONENTS
137
+ // ============================================
138
+
139
+ interface KanbanCardComponentProps {
140
+ card: KanbanCard
141
+ columnId: string
142
+ showAssignees: boolean
143
+ showLabels: boolean
144
+ showPriority: boolean
145
+ showDueDate: boolean
146
+ draggable: boolean
147
+ onCardClick?: (card: KanbanCard, columnId: string) => void
148
+ onCardEdit?: (cardId: string, columnId: string, data: Partial<KanbanCard>) => void
149
+ onCardDelete?: (cardId: string, columnId: string) => void
150
+ onDragStart?: (e: React.DragEvent, cardId: string, columnId: string) => void
151
+ }
152
+
153
+ function KanbanCardComponent({
154
+ card,
155
+ columnId,
156
+ showAssignees,
157
+ showLabels,
158
+ showPriority,
159
+ showDueDate,
160
+ draggable,
161
+ onCardClick,
162
+ onCardEdit,
163
+ onCardDelete,
164
+ onDragStart,
165
+ }: KanbanCardComponentProps) {
166
+ const isOverdue = React.useMemo(() => {
167
+ if (!card.dueDate) return false
168
+ const due = typeof card.dueDate === "string" ? new Date(card.dueDate) : card.dueDate
169
+ return due < new Date()
170
+ }, [card.dueDate])
171
+
172
+ const formatDueDate = (date: Date | string): string => {
173
+ const d = typeof date === "string" ? new Date(date) : date
174
+ return d.toLocaleDateString("fr-FR", { day: "numeric", month: "short" })
175
+ }
176
+
177
+ return (
178
+ <Card
179
+ className={cn(
180
+ "cursor-pointer hover:shadow-md transition-shadow",
181
+ draggable && "cursor-grab active:cursor-grabbing"
182
+ )}
183
+ draggable={draggable}
184
+ onDragStart={(e) => onDragStart?.(e, card.id, columnId)}
185
+ onClick={() => onCardClick?.(card, columnId)}
186
+ >
187
+ <CardContent className="p-3 space-y-2">
188
+ {/* Labels */}
189
+ {showLabels && card.labels && card.labels.length > 0 && (
190
+ <div className="flex flex-wrap gap-1">
191
+ {card.labels.map((label) => (
192
+ <div
193
+ key={label.id}
194
+ className="h-1.5 w-8 rounded-full"
195
+ style={{ backgroundColor: label.color }}
196
+ title={label.name}
197
+ />
198
+ ))}
199
+ </div>
200
+ )}
201
+
202
+ {/* Title */}
203
+ <div className="flex items-start justify-between gap-2">
204
+ <h4 className="text-sm font-medium leading-tight">{card.title}</h4>
205
+ <DropdownMenu>
206
+ <DropdownMenuTrigger asChild onClick={(e) => e.stopPropagation()}>
207
+ <Button variant="ghost" size="icon" className="h-6 w-6 flex-shrink-0">
208
+ <MoreHorizontal className="h-3 w-3" />
209
+ </Button>
210
+ </DropdownMenuTrigger>
211
+ <DropdownMenuContent align="end">
212
+ <DropdownMenuItem onClick={(e) => { e.stopPropagation(); onCardClick?.(card, columnId); }}>
213
+ <Edit className="mr-2 h-4 w-4" />
214
+ Modifier
215
+ </DropdownMenuItem>
216
+ <DropdownMenuSeparator />
217
+ <DropdownMenuItem
218
+ onClick={(e) => { e.stopPropagation(); onCardDelete?.(card.id, columnId); }}
219
+ className="text-destructive"
220
+ >
221
+ <Trash2 className="mr-2 h-4 w-4" />
222
+ Supprimer
223
+ </DropdownMenuItem>
224
+ </DropdownMenuContent>
225
+ </DropdownMenu>
226
+ </div>
227
+
228
+ {/* Description preview */}
229
+ {card.description && (
230
+ <p className="text-xs text-muted-foreground line-clamp-2">
231
+ {card.description}
232
+ </p>
233
+ )}
234
+
235
+ {/* Footer */}
236
+ <div className="flex items-center justify-between pt-1">
237
+ <div className="flex items-center gap-2">
238
+ {/* Priority */}
239
+ {showPriority && card.priority && (
240
+ <div
241
+ className={cn(
242
+ "h-2 w-2 rounded-full",
243
+ priorityConfig[card.priority].color
244
+ )}
245
+ title={priorityConfig[card.priority].label}
246
+ />
247
+ )}
248
+
249
+ {/* Due date */}
250
+ {showDueDate && card.dueDate && (
251
+ <Badge
252
+ variant={isOverdue ? "destructive" : "outline"}
253
+ className="text-xs px-1.5 py-0"
254
+ >
255
+ <Calendar className="h-3 w-3 mr-1" />
256
+ {formatDueDate(card.dueDate)}
257
+ </Badge>
258
+ )}
259
+ </div>
260
+
261
+ {/* Assignees */}
262
+ {showAssignees && card.assignees && card.assignees.length > 0 && (
263
+ <div className="flex -space-x-1">
264
+ {card.assignees.slice(0, 3).map((assignee) => (
265
+ <Avatar key={assignee.id} className="h-5 w-5 border-2 border-background">
266
+ <AvatarImage src={assignee.avatar} />
267
+ <AvatarFallback className="text-[8px]">
268
+ {assignee.name.charAt(0)}
269
+ </AvatarFallback>
270
+ </Avatar>
271
+ ))}
272
+ {card.assignees.length > 3 && (
273
+ <div className="h-5 w-5 rounded-full bg-muted flex items-center justify-center text-[8px] border-2 border-background">
274
+ +{card.assignees.length - 3}
275
+ </div>
276
+ )}
277
+ </div>
278
+ )}
279
+ </div>
280
+ </CardContent>
281
+ </Card>
282
+ )
283
+ }
284
+
285
+ interface KanbanColumnComponentProps {
286
+ column: KanbanColumn
287
+ showCardCount: boolean
288
+ showAddCard: boolean
289
+ showAssignees: boolean
290
+ showLabels: boolean
291
+ showPriority: boolean
292
+ showDueDate: boolean
293
+ draggable: boolean
294
+ onCardClick?: (card: KanbanCard, columnId: string) => void
295
+ onCardAdd?: (columnId: string, card: Partial<KanbanCard>) => void
296
+ onCardEdit?: (cardId: string, columnId: string, data: Partial<KanbanCard>) => void
297
+ onCardDelete?: (cardId: string, columnId: string) => void
298
+ onColumnEdit?: (columnId: string, data: Partial<KanbanColumn>) => void
299
+ onColumnDelete?: (columnId: string) => void
300
+ onDragStart?: (e: React.DragEvent, cardId: string, columnId: string) => void
301
+ onDragOver?: (e: React.DragEvent) => void
302
+ onDrop?: (e: React.DragEvent, columnId: string) => void
303
+ }
304
+
305
+ function KanbanColumnComponent({
306
+ column,
307
+ showCardCount,
308
+ showAddCard,
309
+ showAssignees,
310
+ showLabels,
311
+ showPriority,
312
+ showDueDate,
313
+ draggable,
314
+ onCardClick,
315
+ onCardAdd,
316
+ onCardEdit,
317
+ onCardDelete,
318
+ onColumnEdit,
319
+ onColumnDelete,
320
+ onDragStart,
321
+ onDragOver,
322
+ onDrop,
323
+ }: KanbanColumnComponentProps) {
324
+ const [isAddingCard, setIsAddingCard] = React.useState(false)
325
+ const [newCardTitle, setNewCardTitle] = React.useState("")
326
+ const [isEditingTitle, setIsEditingTitle] = React.useState(false)
327
+ const [editedTitle, setEditedTitle] = React.useState(column.title)
328
+
329
+ const handleAddCard = () => {
330
+ if (newCardTitle.trim()) {
331
+ onCardAdd?.(column.id, { title: newCardTitle.trim() })
332
+ setNewCardTitle("")
333
+ setIsAddingCard(false)
334
+ }
335
+ }
336
+
337
+ const handleSaveTitle = () => {
338
+ if (editedTitle.trim() && editedTitle !== column.title) {
339
+ onColumnEdit?.(column.id, { title: editedTitle.trim() })
340
+ }
341
+ setIsEditingTitle(false)
342
+ }
343
+
344
+ const isOverLimit = column.limit && column.cards.length >= column.limit
345
+
346
+ return (
347
+ <div
348
+ className="flex flex-col w-72 flex-shrink-0 bg-muted/50 rounded-lg"
349
+ onDragOver={onDragOver}
350
+ onDrop={(e) => onDrop?.(e, column.id)}
351
+ >
352
+ {/* Header */}
353
+ <div className="flex items-center justify-between p-3 border-b">
354
+ <div className="flex items-center gap-2">
355
+ {column.color && (
356
+ <div
357
+ className="h-3 w-3 rounded-full"
358
+ style={{ backgroundColor: column.color }}
359
+ />
360
+ )}
361
+ {isEditingTitle ? (
362
+ <div className="flex items-center gap-1">
363
+ <Input
364
+ value={editedTitle}
365
+ onChange={(e) => setEditedTitle(e.target.value)}
366
+ className="h-7 text-sm font-semibold"
367
+ autoFocus
368
+ onBlur={handleSaveTitle}
369
+ onKeyDown={(e) => {
370
+ if (e.key === "Enter") handleSaveTitle()
371
+ if (e.key === "Escape") setIsEditingTitle(false)
372
+ }}
373
+ />
374
+ </div>
375
+ ) : (
376
+ <h3
377
+ className="font-semibold text-sm cursor-pointer"
378
+ onClick={() => setIsEditingTitle(true)}
379
+ >
380
+ {column.title}
381
+ </h3>
382
+ )}
383
+ {showCardCount && (
384
+ <Badge variant="secondary" className="text-xs">
385
+ {column.cards.length}
386
+ {column.limit && `/${column.limit}`}
387
+ </Badge>
388
+ )}
389
+ </div>
390
+ <DropdownMenu>
391
+ <DropdownMenuTrigger asChild>
392
+ <Button variant="ghost" size="icon" className="h-7 w-7">
393
+ <MoreHorizontal className="h-4 w-4" />
394
+ </Button>
395
+ </DropdownMenuTrigger>
396
+ <DropdownMenuContent align="end">
397
+ <DropdownMenuItem onClick={() => setIsEditingTitle(true)}>
398
+ <Edit className="mr-2 h-4 w-4" />
399
+ Renommer
400
+ </DropdownMenuItem>
401
+ <DropdownMenuSeparator />
402
+ <DropdownMenuItem
403
+ onClick={() => onColumnDelete?.(column.id)}
404
+ className="text-destructive"
405
+ >
406
+ <Trash2 className="mr-2 h-4 w-4" />
407
+ Supprimer
408
+ </DropdownMenuItem>
409
+ </DropdownMenuContent>
410
+ </DropdownMenu>
411
+ </div>
412
+
413
+ {/* Cards */}
414
+ <div className="flex-1 p-2 space-y-2 min-h-[200px] overflow-y-auto">
415
+ {column.cards.map((card) => (
416
+ <KanbanCardComponent
417
+ key={card.id}
418
+ card={card}
419
+ columnId={column.id}
420
+ showAssignees={showAssignees}
421
+ showLabels={showLabels}
422
+ showPriority={showPriority}
423
+ showDueDate={showDueDate}
424
+ draggable={draggable}
425
+ onCardClick={onCardClick}
426
+ onCardEdit={onCardEdit}
427
+ onCardDelete={onCardDelete}
428
+ onDragStart={onDragStart}
429
+ />
430
+ ))}
431
+
432
+ {/* Add card form */}
433
+ {isAddingCard && (
434
+ <Card className="p-2">
435
+ <Textarea
436
+ placeholder="Titre de la carte..."
437
+ value={newCardTitle}
438
+ onChange={(e) => setNewCardTitle(e.target.value)}
439
+ className="min-h-[60px] text-sm"
440
+ autoFocus
441
+ />
442
+ <div className="flex gap-2 mt-2">
443
+ <Button size="sm" onClick={handleAddCard}>
444
+ Ajouter
445
+ </Button>
446
+ <Button
447
+ size="sm"
448
+ variant="ghost"
449
+ onClick={() => {
450
+ setIsAddingCard(false)
451
+ setNewCardTitle("")
452
+ }}
453
+ >
454
+ <X className="h-4 w-4" />
455
+ </Button>
456
+ </div>
457
+ </Card>
458
+ )}
459
+ </div>
460
+
461
+ {/* Add card button */}
462
+ {showAddCard && !isAddingCard && !isOverLimit && (
463
+ <div className="p-2 border-t">
464
+ <Button
465
+ variant="ghost"
466
+ size="sm"
467
+ className="w-full justify-start"
468
+ onClick={() => setIsAddingCard(true)}
469
+ >
470
+ <Plus className="h-4 w-4 mr-2" />
471
+ Ajouter une carte
472
+ </Button>
473
+ </div>
474
+ )}
475
+
476
+ {isOverLimit && (
477
+ <div className="p-2 border-t text-center text-xs text-muted-foreground">
478
+ Limite atteinte
479
+ </div>
480
+ )}
481
+ </div>
482
+ )
483
+ }
484
+
485
+ // ============================================
486
+ // MAIN COMPONENT
487
+ // ============================================
488
+
489
+ export function WakaKanbanBoard({
490
+ columns: externalColumns,
491
+ onColumnsChange,
492
+ onCardClick,
493
+ onCardAdd,
494
+ onCardEdit,
495
+ onCardDelete,
496
+ onCardMove,
497
+ onColumnAdd,
498
+ onColumnEdit,
499
+ onColumnDelete,
500
+ showAddCard = true,
501
+ showAddColumn = true,
502
+ showCardCount = true,
503
+ showAssignees = true,
504
+ showLabels = true,
505
+ showPriority = true,
506
+ showDueDate = true,
507
+ draggable = true,
508
+ availableLabels = [],
509
+ availableAssignees = [],
510
+ className,
511
+ }: WakaKanbanBoardProps) {
512
+ const [columns, setColumns] = React.useState(externalColumns)
513
+ const [draggedCard, setDraggedCard] = React.useState<{ cardId: string; columnId: string } | null>(null)
514
+ const [isAddingColumn, setIsAddingColumn] = React.useState(false)
515
+ const [newColumnTitle, setNewColumnTitle] = React.useState("")
516
+
517
+ React.useEffect(() => {
518
+ setColumns(externalColumns)
519
+ }, [externalColumns])
520
+
521
+ const handleDragStart = (e: React.DragEvent, cardId: string, columnId: string) => {
522
+ setDraggedCard({ cardId, columnId })
523
+ e.dataTransfer.effectAllowed = "move"
524
+ }
525
+
526
+ const handleDragOver = (e: React.DragEvent) => {
527
+ e.preventDefault()
528
+ e.dataTransfer.dropEffect = "move"
529
+ }
530
+
531
+ const handleDrop = (e: React.DragEvent, toColumnId: string) => {
532
+ e.preventDefault()
533
+ if (!draggedCard) return
534
+
535
+ const { cardId, columnId: fromColumnId } = draggedCard
536
+
537
+ if (fromColumnId === toColumnId) {
538
+ setDraggedCard(null)
539
+ return
540
+ }
541
+
542
+ if (onCardMove) {
543
+ onCardMove(cardId, fromColumnId, toColumnId, 0)
544
+ } else {
545
+ // Internal state management
546
+ const newColumns = columns.map((col) => {
547
+ if (col.id === fromColumnId) {
548
+ return {
549
+ ...col,
550
+ cards: col.cards.filter((card) => card.id !== cardId),
551
+ }
552
+ }
553
+ if (col.id === toColumnId) {
554
+ const card = columns
555
+ .find((c) => c.id === fromColumnId)
556
+ ?.cards.find((c) => c.id === cardId)
557
+ if (card) {
558
+ return {
559
+ ...col,
560
+ cards: [...col.cards, card],
561
+ }
562
+ }
563
+ }
564
+ return col
565
+ })
566
+ setColumns(newColumns)
567
+ onColumnsChange?.(newColumns)
568
+ }
569
+
570
+ setDraggedCard(null)
571
+ }
572
+
573
+ const handleCardAdd = (columnId: string, card: Partial<KanbanCard>) => {
574
+ if (onCardAdd) {
575
+ onCardAdd(columnId, card)
576
+ } else {
577
+ const newCard: KanbanCard = {
578
+ id: `card-${Date.now()}`,
579
+ title: card.title || "Nouvelle carte",
580
+ ...card,
581
+ }
582
+ const newColumns = columns.map((col) => {
583
+ if (col.id === columnId) {
584
+ return { ...col, cards: [...col.cards, newCard] }
585
+ }
586
+ return col
587
+ })
588
+ setColumns(newColumns)
589
+ onColumnsChange?.(newColumns)
590
+ }
591
+ }
592
+
593
+ const handleCardDelete = (cardId: string, columnId: string) => {
594
+ if (onCardDelete) {
595
+ onCardDelete(cardId, columnId)
596
+ } else {
597
+ const newColumns = columns.map((col) => {
598
+ if (col.id === columnId) {
599
+ return { ...col, cards: col.cards.filter((card) => card.id !== cardId) }
600
+ }
601
+ return col
602
+ })
603
+ setColumns(newColumns)
604
+ onColumnsChange?.(newColumns)
605
+ }
606
+ }
607
+
608
+ const handleColumnDelete = (columnId: string) => {
609
+ if (onColumnDelete) {
610
+ onColumnDelete(columnId)
611
+ } else {
612
+ const newColumns = columns.filter((col) => col.id !== columnId)
613
+ setColumns(newColumns)
614
+ onColumnsChange?.(newColumns)
615
+ }
616
+ }
617
+
618
+ const handleAddColumn = () => {
619
+ if (newColumnTitle.trim()) {
620
+ const newColumn: KanbanColumn = {
621
+ id: `column-${Date.now()}`,
622
+ title: newColumnTitle.trim(),
623
+ cards: [],
624
+ }
625
+ if (onColumnAdd) {
626
+ onColumnAdd(newColumn)
627
+ } else {
628
+ const newColumns = [...columns, newColumn]
629
+ setColumns(newColumns)
630
+ onColumnsChange?.(newColumns)
631
+ }
632
+ setNewColumnTitle("")
633
+ setIsAddingColumn(false)
634
+ }
635
+ }
636
+
637
+ return (
638
+ <div className={cn("overflow-x-auto", className)}>
639
+ <div className="flex gap-4 p-4 min-h-[500px]">
640
+ {columns.map((column) => (
641
+ <KanbanColumnComponent
642
+ key={column.id}
643
+ column={column}
644
+ showCardCount={showCardCount}
645
+ showAddCard={showAddCard}
646
+ showAssignees={showAssignees}
647
+ showLabels={showLabels}
648
+ showPriority={showPriority}
649
+ showDueDate={showDueDate}
650
+ draggable={draggable}
651
+ onCardClick={onCardClick}
652
+ onCardAdd={handleCardAdd}
653
+ onCardEdit={onCardEdit}
654
+ onCardDelete={handleCardDelete}
655
+ onColumnEdit={onColumnEdit}
656
+ onColumnDelete={handleColumnDelete}
657
+ onDragStart={handleDragStart}
658
+ onDragOver={handleDragOver}
659
+ onDrop={handleDrop}
660
+ />
661
+ ))}
662
+
663
+ {/* Add column */}
664
+ {showAddColumn && (
665
+ <div className="w-72 flex-shrink-0">
666
+ {isAddingColumn ? (
667
+ <Card className="p-3">
668
+ <Input
669
+ placeholder="Titre de la colonne..."
670
+ value={newColumnTitle}
671
+ onChange={(e) => setNewColumnTitle(e.target.value)}
672
+ autoFocus
673
+ onKeyDown={(e) => {
674
+ if (e.key === "Enter") handleAddColumn()
675
+ if (e.key === "Escape") setIsAddingColumn(false)
676
+ }}
677
+ />
678
+ <div className="flex gap-2 mt-2">
679
+ <Button size="sm" onClick={handleAddColumn}>
680
+ Ajouter
681
+ </Button>
682
+ <Button
683
+ size="sm"
684
+ variant="ghost"
685
+ onClick={() => {
686
+ setIsAddingColumn(false)
687
+ setNewColumnTitle("")
688
+ }}
689
+ >
690
+ <X className="h-4 w-4" />
691
+ </Button>
692
+ </div>
693
+ </Card>
694
+ ) : (
695
+ <Button
696
+ variant="outline"
697
+ className="w-full justify-start h-12"
698
+ onClick={() => setIsAddingColumn(true)}
699
+ >
700
+ <Plus className="h-4 w-4 mr-2" />
701
+ Ajouter une colonne
702
+ </Button>
703
+ )}
704
+ </div>
705
+ )}
706
+ </div>
707
+ </div>
708
+ )
709
+ }
710
+
711
+ // ============================================
712
+ // PRESETS
713
+ // ============================================
714
+
715
+ export const defaultKanbanColumns: KanbanColumn[] = [
716
+ {
717
+ id: "todo",
718
+ title: "À faire",
719
+ color: "#6b7280",
720
+ cards: [
721
+ {
722
+ id: "1",
723
+ title: "Implémenter l'authentification",
724
+ description: "Ajouter le login/logout avec JWT",
725
+ priority: "high",
726
+ labels: [{ id: "dev", name: "Dev", color: "#3b82f6" }],
727
+ assignees: [{ id: "1", name: "Marie D." }],
728
+ },
729
+ {
730
+ id: "2",
731
+ title: "Créer les maquettes",
732
+ priority: "medium",
733
+ labels: [{ id: "design", name: "Design", color: "#8b5cf6" }],
734
+ },
735
+ ],
736
+ },
737
+ {
738
+ id: "in-progress",
739
+ title: "En cours",
740
+ color: "#3b82f6",
741
+ limit: 3,
742
+ cards: [
743
+ {
744
+ id: "3",
745
+ title: "Refactoring du dashboard",
746
+ priority: "medium",
747
+ assignees: [{ id: "2", name: "Pierre M." }],
748
+ dueDate: new Date(Date.now() + 1000 * 60 * 60 * 24 * 2),
749
+ },
750
+ ],
751
+ },
752
+ {
753
+ id: "review",
754
+ title: "En revue",
755
+ color: "#f59e0b",
756
+ cards: [],
757
+ },
758
+ {
759
+ id: "done",
760
+ title: "Terminé",
761
+ color: "#10b981",
762
+ cards: [
763
+ {
764
+ id: "4",
765
+ title: "Setup du projet",
766
+ priority: "low",
767
+ },
768
+ ],
769
+ },
770
+ ]
771
+
772
+ export const defaultKanbanLabels: KanbanCardLabel[] = [
773
+ { id: "dev", name: "Dev", color: "#3b82f6" },
774
+ { id: "design", name: "Design", color: "#8b5cf6" },
775
+ { id: "bug", name: "Bug", color: "#ef4444" },
776
+ { id: "feature", name: "Feature", color: "#10b981" },
777
+ ]
778
+
779
+ export default WakaKanbanBoard