helix-lang 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +168 -0
  2. package/dist/architect.d.ts +14 -0
  3. package/dist/architect.d.ts.map +1 -0
  4. package/dist/architect.js +127 -0
  5. package/dist/architect.js.map +1 -0
  6. package/dist/bin/helix.d.ts +20 -0
  7. package/dist/bin/helix.d.ts.map +1 -0
  8. package/dist/bin/helix.js +921 -0
  9. package/dist/bin/helix.js.map +1 -0
  10. package/dist/commands/collaborate/index.d.ts +2 -0
  11. package/dist/commands/collaborate/index.d.ts.map +1 -0
  12. package/dist/commands/collaborate/index.js +129 -0
  13. package/dist/commands/collaborate/index.js.map +1 -0
  14. package/dist/commands/collaborate/server.d.ts +31 -0
  15. package/dist/commands/collaborate/server.d.ts.map +1 -0
  16. package/dist/commands/collaborate/server.js +159 -0
  17. package/dist/commands/collaborate/server.js.map +1 -0
  18. package/dist/commands/deploy/index.d.ts +25 -0
  19. package/dist/commands/deploy/index.d.ts.map +1 -0
  20. package/dist/commands/deploy/index.js +130 -0
  21. package/dist/commands/deploy/index.js.map +1 -0
  22. package/dist/commands/deploy/platforms/fly.d.ts +9 -0
  23. package/dist/commands/deploy/platforms/fly.d.ts.map +1 -0
  24. package/dist/commands/deploy/platforms/fly.js +68 -0
  25. package/dist/commands/deploy/platforms/fly.js.map +1 -0
  26. package/dist/commands/deploy/platforms/railway.d.ts +9 -0
  27. package/dist/commands/deploy/platforms/railway.d.ts.map +1 -0
  28. package/dist/commands/deploy/platforms/railway.js +115 -0
  29. package/dist/commands/deploy/platforms/railway.js.map +1 -0
  30. package/dist/commands/deploy/platforms/vercel.d.ts +10 -0
  31. package/dist/commands/deploy/platforms/vercel.d.ts.map +1 -0
  32. package/dist/commands/deploy/platforms/vercel.js +126 -0
  33. package/dist/commands/deploy/platforms/vercel.js.map +1 -0
  34. package/dist/commands/deploy.d.ts +6 -0
  35. package/dist/commands/deploy.d.ts.map +1 -0
  36. package/dist/commands/deploy.js +56 -0
  37. package/dist/commands/deploy.js.map +1 -0
  38. package/dist/commands/evolve/analyzers/performance.d.ts +13 -0
  39. package/dist/commands/evolve/analyzers/performance.d.ts.map +1 -0
  40. package/dist/commands/evolve/analyzers/performance.js +591 -0
  41. package/dist/commands/evolve/analyzers/performance.js.map +1 -0
  42. package/dist/commands/evolve/analyzers/security.d.ts +21 -0
  43. package/dist/commands/evolve/analyzers/security.d.ts.map +1 -0
  44. package/dist/commands/evolve/analyzers/security.js +280 -0
  45. package/dist/commands/evolve/analyzers/security.js.map +1 -0
  46. package/dist/commands/evolve/index.d.ts +2 -0
  47. package/dist/commands/evolve/index.d.ts.map +1 -0
  48. package/dist/commands/evolve/index.js +122 -0
  49. package/dist/commands/evolve/index.js.map +1 -0
  50. package/dist/commands/generate.d.ts +6 -0
  51. package/dist/commands/generate.d.ts.map +1 -0
  52. package/dist/commands/generate.js +277 -0
  53. package/dist/commands/generate.js.map +1 -0
  54. package/dist/commands/install.d.ts +2 -0
  55. package/dist/commands/install.d.ts.map +1 -0
  56. package/dist/commands/install.js +176 -0
  57. package/dist/commands/install.js.map +1 -0
  58. package/dist/commands/library/index.d.ts +27 -0
  59. package/dist/commands/library/index.d.ts.map +1 -0
  60. package/dist/commands/library/index.js +126 -0
  61. package/dist/commands/library/index.js.map +1 -0
  62. package/dist/commands/migrate.d.ts +5 -0
  63. package/dist/commands/migrate.d.ts.map +1 -0
  64. package/dist/commands/migrate.js +258 -0
  65. package/dist/commands/migrate.js.map +1 -0
  66. package/dist/commands/new.d.ts +6 -0
  67. package/dist/commands/new.d.ts.map +1 -0
  68. package/dist/commands/new.js +195 -0
  69. package/dist/commands/new.js.map +1 -0
  70. package/dist/commands/preflight.d.ts +20 -0
  71. package/dist/commands/preflight.d.ts.map +1 -0
  72. package/dist/commands/preflight.js +182 -0
  73. package/dist/commands/preflight.js.map +1 -0
  74. package/dist/commands/preview.d.ts +13 -0
  75. package/dist/commands/preview.d.ts.map +1 -0
  76. package/dist/commands/preview.js +260 -0
  77. package/dist/commands/preview.js.map +1 -0
  78. package/dist/commands/run.d.ts +6 -0
  79. package/dist/commands/run.d.ts.map +1 -0
  80. package/dist/commands/run.js +96 -0
  81. package/dist/commands/run.js.map +1 -0
  82. package/dist/commands/spawn.d.ts +11 -0
  83. package/dist/commands/spawn.d.ts.map +1 -0
  84. package/dist/commands/spawn.js +916 -0
  85. package/dist/commands/spawn.js.map +1 -0
  86. package/dist/compiler.d.ts +12 -0
  87. package/dist/compiler.d.ts.map +1 -0
  88. package/dist/compiler.js +92 -0
  89. package/dist/compiler.js.map +1 -0
  90. package/dist/core/file-writer.d.ts +36 -0
  91. package/dist/core/file-writer.d.ts.map +1 -0
  92. package/dist/core/file-writer.js +268 -0
  93. package/dist/core/file-writer.js.map +1 -0
  94. package/dist/core/registry.d.ts +57 -0
  95. package/dist/core/registry.d.ts.map +1 -0
  96. package/dist/core/registry.js +222 -0
  97. package/dist/core/registry.js.map +1 -0
  98. package/dist/core/self-healing.d.ts +47 -0
  99. package/dist/core/self-healing.d.ts.map +1 -0
  100. package/dist/core/self-healing.js +250 -0
  101. package/dist/core/self-healing.js.map +1 -0
  102. package/dist/core/types.d.ts +126 -0
  103. package/dist/core/types.d.ts.map +1 -0
  104. package/dist/core/types.js +7 -0
  105. package/dist/core/types.js.map +1 -0
  106. package/dist/generators/databases/mongodb.d.ts +10 -0
  107. package/dist/generators/databases/mongodb.d.ts.map +1 -0
  108. package/dist/generators/databases/mongodb.js +83 -0
  109. package/dist/generators/databases/mongodb.js.map +1 -0
  110. package/dist/generators/databases/redis.d.ts +2 -0
  111. package/dist/generators/databases/redis.d.ts.map +1 -0
  112. package/dist/generators/databases/redis.js +140 -0
  113. package/dist/generators/databases/redis.js.map +1 -0
  114. package/dist/generators/flutter.d.ts +32 -0
  115. package/dist/generators/flutter.d.ts.map +1 -0
  116. package/dist/generators/flutter.js +628 -0
  117. package/dist/generators/flutter.js.map +1 -0
  118. package/dist/openrouter.d.ts +68 -0
  119. package/dist/openrouter.d.ts.map +1 -0
  120. package/dist/openrouter.js +241 -0
  121. package/dist/openrouter.js.map +1 -0
  122. package/dist/page-generator.d.ts +22 -0
  123. package/dist/page-generator.d.ts.map +1 -0
  124. package/dist/page-generator.js +192 -0
  125. package/dist/page-generator.js.map +1 -0
  126. package/dist/parser.d.ts +76 -0
  127. package/dist/parser.d.ts.map +1 -0
  128. package/dist/parser.js +691 -0
  129. package/dist/parser.js.map +1 -0
  130. package/dist/prompts/master-architect.d.ts +9 -0
  131. package/dist/prompts/master-architect.d.ts.map +1 -0
  132. package/dist/prompts/master-architect.js +150 -0
  133. package/dist/prompts/master-architect.js.map +1 -0
  134. package/dist/researcher.d.ts +12 -0
  135. package/dist/researcher.d.ts.map +1 -0
  136. package/dist/researcher.js +85 -0
  137. package/dist/researcher.js.map +1 -0
  138. package/dist/self-heal.d.ts +29 -0
  139. package/dist/self-heal.d.ts.map +1 -0
  140. package/dist/self-heal.js +260 -0
  141. package/dist/self-heal.js.map +1 -0
  142. package/dist/services/SupabaseDeployer.d.ts +9 -0
  143. package/dist/services/SupabaseDeployer.d.ts.map +1 -0
  144. package/dist/services/SupabaseDeployer.js +50 -0
  145. package/dist/services/SupabaseDeployer.js.map +1 -0
  146. package/dist/test-generator.d.ts +18 -0
  147. package/dist/test-generator.d.ts.map +1 -0
  148. package/dist/test-generator.js +180 -0
  149. package/dist/test-generator.js.map +1 -0
  150. package/dist/themes/index.d.ts +52 -0
  151. package/dist/themes/index.d.ts.map +1 -0
  152. package/dist/themes/index.js +273 -0
  153. package/dist/themes/index.js.map +1 -0
  154. package/dist/types.d.ts +9 -0
  155. package/dist/types.d.ts.map +1 -0
  156. package/dist/types.js +81 -0
  157. package/dist/types.js.map +1 -0
  158. package/dist/utils/constitutional-validator.d.ts +73 -0
  159. package/dist/utils/constitutional-validator.d.ts.map +1 -0
  160. package/dist/utils/constitutional-validator.js +249 -0
  161. package/dist/utils/constitutional-validator.js.map +1 -0
  162. package/library/auth-flow/app/api/auth/[...nextauth]/route.ts +31 -0
  163. package/library/auth-flow/app/api/auth/login/route.ts +90 -0
  164. package/library/auth-flow/app/api/auth/register/route.ts +91 -0
  165. package/library/auth-flow/components/auth/AuthMiddleware.tsx +139 -0
  166. package/library/auth-flow/components/auth/LoginForm.tsx +125 -0
  167. package/library/auth-flow/components/auth/RegisterForm.tsx +168 -0
  168. package/library/auth-flow/components/auth/nextauth-config.ts +99 -0
  169. package/library/auth-flow/manifest.json +29 -0
  170. package/library/auth-flow/schema.prisma +45 -0
  171. package/library/dashboard-analytics/components/dashboard/ActivityFeed.tsx +109 -0
  172. package/library/dashboard-analytics/components/dashboard/LineChart.tsx +180 -0
  173. package/library/dashboard-analytics/components/dashboard/StatsCard.tsx +47 -0
  174. package/library/dashboard-analytics/components/dashboard/SummaryGrid.tsx +39 -0
  175. package/library/dashboard-analytics/manifest.json +19 -0
  176. package/library/data-table/components/table/BulkActions.tsx +59 -0
  177. package/library/data-table/components/table/ColumnToggle.tsx +65 -0
  178. package/library/data-table/components/table/DataTable.tsx +318 -0
  179. package/library/data-table/components/table/ExportCSV.tsx +52 -0
  180. package/library/data-table/components/table/SearchFilter.tsx +48 -0
  181. package/library/data-table/manifest.json +20 -0
  182. package/library/file-upload/app/api/upload/route.ts +107 -0
  183. package/library/file-upload/components/upload/DropZone.tsx +268 -0
  184. package/library/file-upload/components/upload/FilePreview.tsx +82 -0
  185. package/library/file-upload/components/upload/UploadProgress.tsx +92 -0
  186. package/library/file-upload/components/upload/fileStorage.ts +142 -0
  187. package/library/file-upload/manifest.json +21 -0
  188. package/library/notification-system/app/api/notifications/route.ts +121 -0
  189. package/library/notification-system/components/notifications/NotificationBell.tsx +154 -0
  190. package/library/notification-system/components/notifications/NotificationProvider.tsx +161 -0
  191. package/library/notification-system/components/notifications/Toast.tsx +112 -0
  192. package/library/notification-system/manifest.json +20 -0
  193. package/package.json +66 -0
@@ -0,0 +1,318 @@
1
+ 'use client';
2
+
3
+ import { useState, useMemo, useCallback } from 'react';
4
+ import SearchFilter from './SearchFilter';
5
+ import ColumnToggle from './ColumnToggle';
6
+ import ExportCSV from './ExportCSV';
7
+ import BulkActions from './BulkActions';
8
+
9
+ export interface Column<T> {
10
+ key: string;
11
+ label: string;
12
+ sortable?: boolean;
13
+ render?: (value: unknown, row: T) => React.ReactNode;
14
+ }
15
+
16
+ interface DataTableProps<T extends Record<string, unknown>> {
17
+ data: T[];
18
+ columns: Column<T>[];
19
+ pageSize?: number;
20
+ searchable?: boolean;
21
+ exportable?: boolean;
22
+ selectable?: boolean;
23
+ onBulkDelete?: (ids: string[]) => void;
24
+ rowKey?: string;
25
+ exportFilename?: string;
26
+ }
27
+
28
+ type SortDirection = 'asc' | 'desc' | null;
29
+
30
+ export default function DataTable<T extends Record<string, unknown>>({
31
+ data,
32
+ columns,
33
+ pageSize = 10,
34
+ searchable = true,
35
+ exportable = true,
36
+ selectable = true,
37
+ onBulkDelete,
38
+ rowKey = 'id',
39
+ exportFilename = 'data',
40
+ }: DataTableProps<T>) {
41
+ const [search, setSearch] = useState('');
42
+ const [sortKey, setSortKey] = useState<string | null>(null);
43
+ const [sortDir, setSortDir] = useState<SortDirection>(null);
44
+ const [page, setPage] = useState(0);
45
+ const [visibleCols, setVisibleCols] = useState<Set<string>>(
46
+ () => new Set(columns.map((c) => c.key))
47
+ );
48
+ const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set());
49
+
50
+ // Filter
51
+ const filtered = useMemo(() => {
52
+ if (!search.trim()) return data;
53
+ const q = search.toLowerCase();
54
+ return data.filter((row) =>
55
+ columns.some((col) => {
56
+ const val = row[col.key];
57
+ return val !== null && val !== undefined && String(val).toLowerCase().includes(q);
58
+ })
59
+ );
60
+ }, [data, search, columns]);
61
+
62
+ // Sort
63
+ const sorted = useMemo(() => {
64
+ if (!sortKey || !sortDir) return filtered;
65
+ return [...filtered].sort((a, b) => {
66
+ const aVal = a[sortKey];
67
+ const bVal = b[sortKey];
68
+ if (aVal === bVal) return 0;
69
+ if (aVal === null || aVal === undefined) return 1;
70
+ if (bVal === null || bVal === undefined) return -1;
71
+ const cmp = String(aVal).localeCompare(String(bVal), undefined, { numeric: true });
72
+ return sortDir === 'asc' ? cmp : -cmp;
73
+ });
74
+ }, [filtered, sortKey, sortDir]);
75
+
76
+ // Paginate
77
+ const totalPages = Math.max(1, Math.ceil(sorted.length / pageSize));
78
+ const currentPage = Math.min(page, totalPages - 1);
79
+ const paginated = sorted.slice(currentPage * pageSize, (currentPage + 1) * pageSize);
80
+
81
+ const activeColumns = columns.filter((c) => visibleCols.has(c.key));
82
+
83
+ const handleSort = useCallback(
84
+ (key: string) => {
85
+ if (sortKey === key) {
86
+ if (sortDir === 'asc') setSortDir('desc');
87
+ else if (sortDir === 'desc') {
88
+ setSortKey(null);
89
+ setSortDir(null);
90
+ }
91
+ } else {
92
+ setSortKey(key);
93
+ setSortDir('asc');
94
+ }
95
+ setPage(0);
96
+ },
97
+ [sortKey, sortDir]
98
+ );
99
+
100
+ const toggleRow = (id: string) => {
101
+ setSelectedRows((prev) => {
102
+ const next = new Set(prev);
103
+ if (next.has(id)) next.delete(id);
104
+ else next.add(id);
105
+ return next;
106
+ });
107
+ };
108
+
109
+ const selectAll = () => {
110
+ setSelectedRows(new Set(sorted.map((r) => String(r[rowKey]))));
111
+ };
112
+
113
+ const deselectAll = () => {
114
+ setSelectedRows(new Set());
115
+ };
116
+
117
+ const handleBulkDelete = () => {
118
+ if (onBulkDelete) {
119
+ onBulkDelete(Array.from(selectedRows));
120
+ setSelectedRows(new Set());
121
+ }
122
+ };
123
+
124
+ const toggleColumn = (key: string) => {
125
+ setVisibleCols((prev) => {
126
+ const next = new Set(prev);
127
+ if (next.has(key) && next.size > 1) next.delete(key);
128
+ else next.add(key);
129
+ return next;
130
+ });
131
+ };
132
+
133
+ return (
134
+ <div className="backdrop-blur-xl bg-white/5 border border-white/10 rounded-2xl overflow-hidden">
135
+ {/* Toolbar */}
136
+ <div className="p-4 border-b border-white/5 flex flex-col sm:flex-row gap-3">
137
+ {searchable && (
138
+ <div className="flex-1 max-w-sm">
139
+ <SearchFilter
140
+ value={search}
141
+ onChange={(v) => {
142
+ setSearch(v);
143
+ setPage(0);
144
+ }}
145
+ />
146
+ </div>
147
+ )}
148
+ <div className="flex items-center gap-2 ml-auto">
149
+ <ColumnToggle
150
+ columns={columns}
151
+ visibleColumns={visibleCols}
152
+ onToggle={toggleColumn}
153
+ />
154
+ {exportable && (
155
+ <ExportCSV
156
+ data={sorted as Record<string, unknown>[]}
157
+ columns={activeColumns}
158
+ filename={exportFilename}
159
+ />
160
+ )}
161
+ </div>
162
+ </div>
163
+
164
+ {/* Bulk actions */}
165
+ {selectable && selectedRows.size > 0 && (
166
+ <div className="px-4 pt-3">
167
+ <BulkActions
168
+ selectedCount={selectedRows.size}
169
+ totalCount={sorted.length}
170
+ onSelectAll={selectAll}
171
+ onDeselectAll={deselectAll}
172
+ onDeleteSelected={handleBulkDelete}
173
+ allSelected={selectedRows.size === sorted.length}
174
+ />
175
+ </div>
176
+ )}
177
+
178
+ {/* Table */}
179
+ <div className="overflow-x-auto">
180
+ <table className="w-full">
181
+ <thead>
182
+ <tr className="border-b border-white/5">
183
+ {selectable && (
184
+ <th className="w-12 px-4 py-3">
185
+ <input
186
+ type="checkbox"
187
+ checked={paginated.length > 0 && paginated.every((r) => selectedRows.has(String(r[rowKey])))}
188
+ onChange={() => {
189
+ const pageIds = paginated.map((r) => String(r[rowKey]));
190
+ const allChecked = pageIds.every((id) => selectedRows.has(id));
191
+ setSelectedRows((prev) => {
192
+ const next = new Set(prev);
193
+ pageIds.forEach((id) => {
194
+ if (allChecked) next.delete(id);
195
+ else next.add(id);
196
+ });
197
+ return next;
198
+ });
199
+ }}
200
+ className="w-4 h-4 rounded border-white/20 bg-white/5 text-cyan-500 focus:ring-cyan-500/50 focus:ring-offset-0"
201
+ />
202
+ </th>
203
+ )}
204
+ {activeColumns.map((col) => (
205
+ <th
206
+ key={col.key}
207
+ className="px-4 py-3 text-left text-xs font-medium text-white/40 uppercase tracking-wider"
208
+ >
209
+ {col.sortable !== false ? (
210
+ <button
211
+ onClick={() => handleSort(col.key)}
212
+ className="flex items-center gap-1 hover:text-white/70 transition-colors"
213
+ >
214
+ {col.label}
215
+ <span className="text-[10px]">
216
+ {sortKey === col.key
217
+ ? sortDir === 'asc'
218
+ ? '\u25B2'
219
+ : '\u25BC'
220
+ : '\u25B4\u25BE'}
221
+ </span>
222
+ </button>
223
+ ) : (
224
+ col.label
225
+ )}
226
+ </th>
227
+ ))}
228
+ </tr>
229
+ </thead>
230
+ <tbody>
231
+ {paginated.length === 0 ? (
232
+ <tr>
233
+ <td
234
+ colSpan={activeColumns.length + (selectable ? 1 : 0)}
235
+ className="px-4 py-12 text-center text-white/30 text-sm"
236
+ >
237
+ No results found
238
+ </td>
239
+ </tr>
240
+ ) : (
241
+ paginated.map((row, idx) => {
242
+ const id = String(row[rowKey]);
243
+ const isSelected = selectedRows.has(id);
244
+ return (
245
+ <tr
246
+ key={id || idx}
247
+ className={`border-b border-white/5 hover:bg-white/[0.03] transition-colors ${
248
+ isSelected ? 'bg-cyan-500/5' : ''
249
+ }`}
250
+ >
251
+ {selectable && (
252
+ <td className="px-4 py-3">
253
+ <input
254
+ type="checkbox"
255
+ checked={isSelected}
256
+ onChange={() => toggleRow(id)}
257
+ className="w-4 h-4 rounded border-white/20 bg-white/5 text-cyan-500 focus:ring-cyan-500/50 focus:ring-offset-0"
258
+ />
259
+ </td>
260
+ )}
261
+ {activeColumns.map((col) => (
262
+ <td key={col.key} className="px-4 py-3 text-sm text-white/70">
263
+ {col.render
264
+ ? col.render(row[col.key], row)
265
+ : String(row[col.key] ?? '')}
266
+ </td>
267
+ ))}
268
+ </tr>
269
+ );
270
+ })
271
+ )}
272
+ </tbody>
273
+ </table>
274
+ </div>
275
+
276
+ {/* Pagination */}
277
+ <div className="px-4 py-3 border-t border-white/5 flex items-center justify-between">
278
+ <p className="text-sm text-white/40">
279
+ Showing {currentPage * pageSize + 1} to{' '}
280
+ {Math.min((currentPage + 1) * pageSize, sorted.length)} of {sorted.length} results
281
+ </p>
282
+ <div className="flex items-center gap-1">
283
+ <button
284
+ onClick={() => setPage(0)}
285
+ disabled={currentPage === 0}
286
+ className="px-2 py-1 text-sm text-white/50 hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
287
+ >
288
+ First
289
+ </button>
290
+ <button
291
+ onClick={() => setPage((p) => Math.max(0, p - 1))}
292
+ disabled={currentPage === 0}
293
+ className="px-3 py-1.5 bg-white/5 border border-white/10 rounded text-sm text-white/70 hover:bg-white/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
294
+ >
295
+ Prev
296
+ </button>
297
+ <span className="px-3 py-1.5 text-sm text-white/50">
298
+ {currentPage + 1} / {totalPages}
299
+ </span>
300
+ <button
301
+ onClick={() => setPage((p) => Math.min(totalPages - 1, p + 1))}
302
+ disabled={currentPage >= totalPages - 1}
303
+ className="px-3 py-1.5 bg-white/5 border border-white/10 rounded text-sm text-white/70 hover:bg-white/10 disabled:opacity-30 disabled:cursor-not-allowed transition-all"
304
+ >
305
+ Next
306
+ </button>
307
+ <button
308
+ onClick={() => setPage(totalPages - 1)}
309
+ disabled={currentPage >= totalPages - 1}
310
+ className="px-2 py-1 text-sm text-white/50 hover:text-white disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
311
+ >
312
+ Last
313
+ </button>
314
+ </div>
315
+ </div>
316
+ </div>
317
+ );
318
+ }
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ interface ExportCSVProps<T extends Record<string, unknown>> {
4
+ data: T[];
5
+ columns: { key: string; label: string }[];
6
+ filename?: string;
7
+ }
8
+
9
+ function escapeCSVField(value: unknown): string {
10
+ const str = String(value ?? '');
11
+ if (str.includes(',') || str.includes('"') || str.includes('\n')) {
12
+ return `"${str.replace(/"/g, '""')}"`;
13
+ }
14
+ return str;
15
+ }
16
+
17
+ export default function ExportCSV<T extends Record<string, unknown>>({
18
+ data,
19
+ columns,
20
+ filename = 'export',
21
+ }: ExportCSVProps<T>) {
22
+ const handleExport = () => {
23
+ const header = columns.map((col) => escapeCSVField(col.label)).join(',');
24
+ const rows = data.map((row) =>
25
+ columns.map((col) => escapeCSVField(row[col.key])).join(',')
26
+ );
27
+ const csv = [header, ...rows].join('\n');
28
+
29
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
30
+ const url = URL.createObjectURL(blob);
31
+ const link = document.createElement('a');
32
+ link.href = url;
33
+ link.download = `${filename}-${new Date().toISOString().slice(0, 10)}.csv`;
34
+ document.body.appendChild(link);
35
+ link.click();
36
+ document.body.removeChild(link);
37
+ URL.revokeObjectURL(url);
38
+ };
39
+
40
+ return (
41
+ <button
42
+ onClick={handleExport}
43
+ disabled={data.length === 0}
44
+ className="flex items-center gap-2 px-3 py-2 bg-white/5 border border-white/10 rounded-lg text-sm text-white/70 hover:bg-white/10 hover:text-white disabled:opacity-40 disabled:cursor-not-allowed transition-all"
45
+ >
46
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
47
+ <path strokeLinecap="round" strokeLinejoin="round" d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
48
+ </svg>
49
+ Export CSV
50
+ </button>
51
+ );
52
+ }
@@ -0,0 +1,48 @@
1
+ 'use client';
2
+
3
+ interface SearchFilterProps {
4
+ value: string;
5
+ onChange: (value: string) => void;
6
+ placeholder?: string;
7
+ }
8
+
9
+ export default function SearchFilter({
10
+ value,
11
+ onChange,
12
+ placeholder = 'Search...',
13
+ }: SearchFilterProps) {
14
+ return (
15
+ <div className="relative">
16
+ <svg
17
+ className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-white/30"
18
+ fill="none"
19
+ viewBox="0 0 24 24"
20
+ stroke="currentColor"
21
+ strokeWidth={2}
22
+ >
23
+ <path
24
+ strokeLinecap="round"
25
+ strokeLinejoin="round"
26
+ d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
27
+ />
28
+ </svg>
29
+ <input
30
+ type="text"
31
+ value={value}
32
+ onChange={(e) => onChange(e.target.value)}
33
+ placeholder={placeholder}
34
+ className="w-full pl-10 pr-4 py-2 bg-white/5 border border-white/10 rounded-lg text-white text-sm placeholder-white/30 focus:outline-none focus:ring-2 focus:ring-cyan-500/50 focus:border-cyan-500/50 transition-all"
35
+ />
36
+ {value && (
37
+ <button
38
+ onClick={() => onChange('')}
39
+ className="absolute right-3 top-1/2 -translate-y-1/2 text-white/30 hover:text-white/60 transition-colors"
40
+ >
41
+ <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
42
+ <path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
43
+ </svg>
44
+ </button>
45
+ )}
46
+ </div>
47
+ );
48
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "data-table",
3
+ "version": "1.0.0",
4
+ "description": "Full-featured data table with sorting, pagination, search, column visibility, CSV export, and bulk actions",
5
+ "author": "AD AI Engine",
6
+ "category": "data",
7
+ "dependencies": [],
8
+ "files": {
9
+ "components": [
10
+ "components/table/DataTable.tsx",
11
+ "components/table/SearchFilter.tsx",
12
+ "components/table/ColumnToggle.tsx",
13
+ "components/table/ExportCSV.tsx",
14
+ "components/table/BulkActions.tsx"
15
+ ],
16
+ "api": []
17
+ },
18
+ "schema": null,
19
+ "instructions": "1. Import DataTable and pass your columns and data\n2. DataTable integrates SearchFilter, ColumnToggle, ExportCSV, and BulkActions automatically\n3. Define columns with { key, label, sortable?, render? }\n4. Provide onBulkDelete callback for bulk delete functionality"
20
+ }
@@ -0,0 +1,107 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as crypto from 'crypto';
5
+
6
+ const UPLOAD_DIR = path.join(process.cwd(), 'public', 'uploads');
7
+ const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
8
+ const ALLOWED_TYPES = new Set([
9
+ 'image/jpeg',
10
+ 'image/png',
11
+ 'image/gif',
12
+ 'image/webp',
13
+ 'application/pdf',
14
+ 'text/csv',
15
+ ]);
16
+
17
+ function ensureUploadDir() {
18
+ if (!fs.existsSync(UPLOAD_DIR)) {
19
+ fs.mkdirSync(UPLOAD_DIR, { recursive: true });
20
+ }
21
+ }
22
+
23
+ export async function POST(request: NextRequest) {
24
+ try {
25
+ const formData = await request.formData();
26
+ const file = formData.get('file');
27
+
28
+ if (!file || !(file instanceof Blob)) {
29
+ return NextResponse.json(
30
+ { error: 'No file provided' },
31
+ { status: 400 }
32
+ );
33
+ }
34
+
35
+ // Validate size
36
+ if (file.size > MAX_FILE_SIZE) {
37
+ return NextResponse.json(
38
+ { error: `File too large. Maximum size: ${MAX_FILE_SIZE / 1024 / 1024}MB` },
39
+ { status: 400 }
40
+ );
41
+ }
42
+
43
+ // Validate type
44
+ if (ALLOWED_TYPES.size > 0 && !ALLOWED_TYPES.has(file.type)) {
45
+ return NextResponse.json(
46
+ { error: `File type ${file.type} is not allowed` },
47
+ { status: 400 }
48
+ );
49
+ }
50
+
51
+ ensureUploadDir();
52
+
53
+ // Generate safe filename
54
+ const originalName = file instanceof File ? file.name : 'upload';
55
+ const ext = path.extname(originalName);
56
+ const hash = crypto.randomUUID().replace(/-/g, '').slice(0, 16);
57
+ const filename = `${Date.now()}-${hash}${ext}`;
58
+ const filePath = path.join(UPLOAD_DIR, filename);
59
+
60
+ // Write file
61
+ const buffer = Buffer.from(await file.arrayBuffer());
62
+ await fs.promises.writeFile(filePath, buffer);
63
+
64
+ return NextResponse.json({
65
+ message: 'File uploaded successfully',
66
+ filename,
67
+ originalName,
68
+ size: file.size,
69
+ type: file.type,
70
+ url: `/uploads/${filename}`,
71
+ });
72
+ } catch (error: unknown) {
73
+ const message = error instanceof Error ? error.message : 'Upload failed';
74
+ return NextResponse.json({ error: message }, { status: 500 });
75
+ }
76
+ }
77
+
78
+ export async function DELETE(request: NextRequest) {
79
+ try {
80
+ const { filename } = await request.json();
81
+
82
+ if (!filename || typeof filename !== 'string') {
83
+ return NextResponse.json(
84
+ { error: 'Filename is required' },
85
+ { status: 400 }
86
+ );
87
+ }
88
+
89
+ // Prevent path traversal
90
+ const safeName = path.basename(filename);
91
+ const filePath = path.join(UPLOAD_DIR, safeName);
92
+
93
+ if (!fs.existsSync(filePath)) {
94
+ return NextResponse.json(
95
+ { error: 'File not found' },
96
+ { status: 404 }
97
+ );
98
+ }
99
+
100
+ await fs.promises.unlink(filePath);
101
+
102
+ return NextResponse.json({ message: 'File deleted successfully' });
103
+ } catch (error: unknown) {
104
+ const message = error instanceof Error ? error.message : 'Delete failed';
105
+ return NextResponse.json({ error: message }, { status: 500 });
106
+ }
107
+ }