jbilcke-hf HF staff commited on
Commit
43dd1ac
β€’
1 Parent(s): e5c2fd4

ready for release (sort of)

Browse files
Files changed (43) hide show
  1. README.md +5 -0
  2. next.config.js +10 -0
  3. package-lock.json +292 -0
  4. package.json +9 -0
  5. src/api/index.ts +0 -16
  6. src/app/api/tasks/[uuid]/route.ts +18 -0
  7. src/app/api/tasks/route.ts +24 -0
  8. src/app/layout.tsx +5 -4
  9. src/app/main.tsx +0 -66
  10. src/app/page.tsx +4 -15
  11. src/app/studio/[ownerId]/main.tsx +61 -0
  12. src/app/{pending β†’ studio/[ownerId]}/page.tsx +5 -8
  13. src/app/types.ts +6 -6
  14. src/components/business/tasks/columns.tsx +39 -37
  15. src/components/business/tasks/data-table-faceted-filter.tsx +0 -147
  16. src/components/business/tasks/data-table-pagination.tsx +0 -97
  17. src/components/business/tasks/data-table-row-actions.tsx +7 -30
  18. src/components/business/tasks/data-table-toolbar.tsx +0 -61
  19. src/components/business/tasks/data-table-view-options.tsx +0 -59
  20. src/components/business/tasks/user-nav.tsx +0 -62
  21. src/components/business/tasks/{data-table.tsx β†’ video-tasks-queue.tsx} +57 -65
  22. src/components/ui/alert.tsx +2 -2
  23. src/components/ui/avatar.tsx +1 -1
  24. src/components/ui/badge.tsx +5 -5
  25. src/components/ui/button.tsx +7 -7
  26. src/components/ui/calendar.tsx +7 -7
  27. src/components/ui/card.tsx +2 -2
  28. src/components/ui/checkbox.tsx +1 -1
  29. src/components/ui/command.tsx +7 -7
  30. src/components/ui/dialog.tsx +4 -4
  31. src/components/ui/dropdown-menu.tsx +7 -7
  32. src/components/ui/input.tsx +1 -1
  33. src/components/ui/menubar.tsx +10 -10
  34. src/components/ui/popover.tsx +1 -1
  35. src/components/ui/select.tsx +4 -4
  36. src/components/ui/separator.tsx +1 -1
  37. src/components/ui/table.tsx +4 -4
  38. src/components/ui/textarea.tsx +24 -0
  39. src/server/README.md +1 -0
  40. src/server/actions.ts +14 -0
  41. src/{api β†’ server}/base.ts +41 -2
  42. src/server/index.ts +39 -0
  43. tsconfig.json +1 -1
README.md CHANGED
@@ -23,3 +23,8 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
23
  You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
24
 
25
  This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
 
 
 
 
 
 
23
  You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
24
 
25
  This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
26
+
27
+ ## Things to know
28
+
29
+ Next will cache the API calls!
30
+ So be careful about this.
next.config.js CHANGED
@@ -3,6 +3,16 @@ const nextConfig = {
3
  experimental: {
4
  serverActions: true,
5
  },
 
 
 
 
 
 
 
 
 
 
6
  }
7
 
8
  module.exports = nextConfig
 
3
  experimental: {
4
  serverActions: true,
5
  },
6
+
7
+ async redirects() {
8
+ return [
9
+ {
10
+ source: '/api/download/:id*',
11
+ destination: `${process.env.VC_VIDEOCHAIN_API_URL}/download/:id*`,
12
+ permanent: true,
13
+ },
14
+ ]
15
+ },
16
  }
17
 
18
  module.exports = nextConfig
package-lock.json CHANGED
@@ -9,13 +9,20 @@
9
  "version": "0.1.0",
10
  "dependencies": {
11
  "@radix-ui/react-accordion": "^1.1.2",
 
12
  "@radix-ui/react-checkbox": "^1.0.4",
13
  "@radix-ui/react-collapsible": "^1.0.3",
14
  "@radix-ui/react-dialog": "^1.0.4",
 
15
  "@radix-ui/react-icons": "^1.3.0",
16
  "@radix-ui/react-menubar": "^1.0.3",
 
 
 
17
  "@radix-ui/react-slot": "^1.0.2",
18
  "@silevis/reactgrid": "^4.0.5",
 
 
19
  "@types/fluent-ffmpeg": "^2.1.21",
20
  "@types/node": "20.4.2",
21
  "@types/react": "18.2.15",
@@ -25,6 +32,7 @@
25
  "class-variance-authority": "^0.6.1",
26
  "clsx": "^2.0.0",
27
  "cmdk": "^0.2.0",
 
28
  "date-fns": "^2.30.0",
29
  "eslint": "8.45.0",
30
  "eslint-config-next": "13.4.10",
@@ -44,6 +52,7 @@
44
  "ts-node": "^10.9.1",
45
  "typescript": "5.1.6",
46
  "uuid": "^9.0.0",
 
47
  "zustand": "^4.3.9"
48
  }
49
  },
@@ -578,6 +587,14 @@
578
  }
579
  }
580
  },
 
 
 
 
 
 
 
 
581
  "node_modules/@radix-ui/primitive": {
582
  "version": "1.0.1",
583
  "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
@@ -640,6 +657,32 @@
640
  }
641
  }
642
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
643
  "node_modules/@radix-ui/react-checkbox": {
644
  "version": "1.0.4",
645
  "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz",
@@ -840,6 +883,35 @@
840
  }
841
  }
842
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
843
  "node_modules/@radix-ui/react-focus-guards": {
844
  "version": "1.0.1",
845
  "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
@@ -980,6 +1052,43 @@
980
  }
981
  }
982
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
983
  "node_modules/@radix-ui/react-popper": {
984
  "version": "1.1.2",
985
  "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz",
@@ -1113,6 +1222,72 @@
1113
  }
1114
  }
1115
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1116
  "node_modules/@radix-ui/react-slot": {
1117
  "version": "1.0.2",
1118
  "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
@@ -1254,6 +1429,29 @@
1254
  }
1255
  }
1256
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1257
  "node_modules/@radix-ui/rect": {
1258
  "version": "1.0.1",
1259
  "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
@@ -1288,6 +1486,72 @@
1288
  "tslib": "^2.4.0"
1289
  }
1290
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1291
  "node_modules/@tsconfig/node10": {
1292
  "version": "1.0.9",
1293
  "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
@@ -1308,6 +1572,11 @@
1308
  "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
1309
  "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
1310
  },
 
 
 
 
 
1311
  "node_modules/@types/fluent-ffmpeg": {
1312
  "version": "2.1.21",
1313
  "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.21.tgz",
@@ -2369,6 +2638,29 @@
2369
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
2370
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
2371
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2372
  "node_modules/cosmiconfig": {
2373
  "version": "8.2.0",
2374
  "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
 
9
  "version": "0.1.0",
10
  "dependencies": {
11
  "@radix-ui/react-accordion": "^1.1.2",
12
+ "@radix-ui/react-avatar": "^1.0.3",
13
  "@radix-ui/react-checkbox": "^1.0.4",
14
  "@radix-ui/react-collapsible": "^1.0.3",
15
  "@radix-ui/react-dialog": "^1.0.4",
16
+ "@radix-ui/react-dropdown-menu": "^2.0.5",
17
  "@radix-ui/react-icons": "^1.3.0",
18
  "@radix-ui/react-menubar": "^1.0.3",
19
+ "@radix-ui/react-popover": "^1.0.6",
20
+ "@radix-ui/react-select": "^1.2.2",
21
+ "@radix-ui/react-separator": "^1.0.3",
22
  "@radix-ui/react-slot": "^1.0.2",
23
  "@silevis/reactgrid": "^4.0.5",
24
+ "@tanstack/react-query": "^4.30.0",
25
+ "@tanstack/react-table": "^8.9.3",
26
  "@types/fluent-ffmpeg": "^2.1.21",
27
  "@types/node": "20.4.2",
28
  "@types/react": "18.2.15",
 
32
  "class-variance-authority": "^0.6.1",
33
  "clsx": "^2.0.0",
34
  "cmdk": "^0.2.0",
35
+ "cookies-next": "^2.1.2",
36
  "date-fns": "^2.30.0",
37
  "eslint": "8.45.0",
38
  "eslint-config-next": "13.4.10",
 
52
  "ts-node": "^10.9.1",
53
  "typescript": "5.1.6",
54
  "uuid": "^9.0.0",
55
+ "zod": "^3.21.4",
56
  "zustand": "^4.3.9"
57
  }
58
  },
 
587
  }
588
  }
589
  },
590
+ "node_modules/@radix-ui/number": {
591
+ "version": "1.0.1",
592
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.0.1.tgz",
593
+ "integrity": "sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==",
594
+ "dependencies": {
595
+ "@babel/runtime": "^7.13.10"
596
+ }
597
+ },
598
  "node_modules/@radix-ui/primitive": {
599
  "version": "1.0.1",
600
  "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.1.tgz",
 
657
  }
658
  }
659
  },
660
+ "node_modules/@radix-ui/react-avatar": {
661
+ "version": "1.0.3",
662
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.0.3.tgz",
663
+ "integrity": "sha512-9ToF7YNex3Ste45LrAeTlKtONI9yVRt/zOS158iilIkW5K/Apeyb/TUQlcEFTEFvWr8Kzdi2ZYrm1/suiXPajQ==",
664
+ "dependencies": {
665
+ "@babel/runtime": "^7.13.10",
666
+ "@radix-ui/react-context": "1.0.1",
667
+ "@radix-ui/react-primitive": "1.0.3",
668
+ "@radix-ui/react-use-callback-ref": "1.0.1",
669
+ "@radix-ui/react-use-layout-effect": "1.0.1"
670
+ },
671
+ "peerDependencies": {
672
+ "@types/react": "*",
673
+ "@types/react-dom": "*",
674
+ "react": "^16.8 || ^17.0 || ^18.0",
675
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
676
+ },
677
+ "peerDependenciesMeta": {
678
+ "@types/react": {
679
+ "optional": true
680
+ },
681
+ "@types/react-dom": {
682
+ "optional": true
683
+ }
684
+ }
685
+ },
686
  "node_modules/@radix-ui/react-checkbox": {
687
  "version": "1.0.4",
688
  "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz",
 
883
  }
884
  }
885
  },
886
+ "node_modules/@radix-ui/react-dropdown-menu": {
887
+ "version": "2.0.5",
888
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.0.5.tgz",
889
+ "integrity": "sha512-xdOrZzOTocqqkCkYo8yRPCib5OkTkqN7lqNCdxwPOdE466DOaNl4N8PkUIlsXthQvW5Wwkd+aEmWpfWlBoDPEw==",
890
+ "dependencies": {
891
+ "@babel/runtime": "^7.13.10",
892
+ "@radix-ui/primitive": "1.0.1",
893
+ "@radix-ui/react-compose-refs": "1.0.1",
894
+ "@radix-ui/react-context": "1.0.1",
895
+ "@radix-ui/react-id": "1.0.1",
896
+ "@radix-ui/react-menu": "2.0.5",
897
+ "@radix-ui/react-primitive": "1.0.3",
898
+ "@radix-ui/react-use-controllable-state": "1.0.1"
899
+ },
900
+ "peerDependencies": {
901
+ "@types/react": "*",
902
+ "@types/react-dom": "*",
903
+ "react": "^16.8 || ^17.0 || ^18.0",
904
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
905
+ },
906
+ "peerDependenciesMeta": {
907
+ "@types/react": {
908
+ "optional": true
909
+ },
910
+ "@types/react-dom": {
911
+ "optional": true
912
+ }
913
+ }
914
+ },
915
  "node_modules/@radix-ui/react-focus-guards": {
916
  "version": "1.0.1",
917
  "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.0.1.tgz",
 
1052
  }
1053
  }
1054
  },
1055
+ "node_modules/@radix-ui/react-popover": {
1056
+ "version": "1.0.6",
1057
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.0.6.tgz",
1058
+ "integrity": "sha512-cZ4defGpkZ0qTRtlIBzJLSzL6ht7ofhhW4i1+pkemjV1IKXm0wgCRnee154qlV6r9Ttunmh2TNZhMfV2bavUyA==",
1059
+ "dependencies": {
1060
+ "@babel/runtime": "^7.13.10",
1061
+ "@radix-ui/primitive": "1.0.1",
1062
+ "@radix-ui/react-compose-refs": "1.0.1",
1063
+ "@radix-ui/react-context": "1.0.1",
1064
+ "@radix-ui/react-dismissable-layer": "1.0.4",
1065
+ "@radix-ui/react-focus-guards": "1.0.1",
1066
+ "@radix-ui/react-focus-scope": "1.0.3",
1067
+ "@radix-ui/react-id": "1.0.1",
1068
+ "@radix-ui/react-popper": "1.1.2",
1069
+ "@radix-ui/react-portal": "1.0.3",
1070
+ "@radix-ui/react-presence": "1.0.1",
1071
+ "@radix-ui/react-primitive": "1.0.3",
1072
+ "@radix-ui/react-slot": "1.0.2",
1073
+ "@radix-ui/react-use-controllable-state": "1.0.1",
1074
+ "aria-hidden": "^1.1.1",
1075
+ "react-remove-scroll": "2.5.5"
1076
+ },
1077
+ "peerDependencies": {
1078
+ "@types/react": "*",
1079
+ "@types/react-dom": "*",
1080
+ "react": "^16.8 || ^17.0 || ^18.0",
1081
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
1082
+ },
1083
+ "peerDependenciesMeta": {
1084
+ "@types/react": {
1085
+ "optional": true
1086
+ },
1087
+ "@types/react-dom": {
1088
+ "optional": true
1089
+ }
1090
+ }
1091
+ },
1092
  "node_modules/@radix-ui/react-popper": {
1093
  "version": "1.1.2",
1094
  "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.1.2.tgz",
 
1222
  }
1223
  }
1224
  },
1225
+ "node_modules/@radix-ui/react-select": {
1226
+ "version": "1.2.2",
1227
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-1.2.2.tgz",
1228
+ "integrity": "sha512-zI7McXr8fNaSrUY9mZe4x/HC0jTLY9fWNhO1oLWYMQGDXuV4UCivIGTxwioSzO0ZCYX9iSLyWmAh/1TOmX3Cnw==",
1229
+ "dependencies": {
1230
+ "@babel/runtime": "^7.13.10",
1231
+ "@radix-ui/number": "1.0.1",
1232
+ "@radix-ui/primitive": "1.0.1",
1233
+ "@radix-ui/react-collection": "1.0.3",
1234
+ "@radix-ui/react-compose-refs": "1.0.1",
1235
+ "@radix-ui/react-context": "1.0.1",
1236
+ "@radix-ui/react-direction": "1.0.1",
1237
+ "@radix-ui/react-dismissable-layer": "1.0.4",
1238
+ "@radix-ui/react-focus-guards": "1.0.1",
1239
+ "@radix-ui/react-focus-scope": "1.0.3",
1240
+ "@radix-ui/react-id": "1.0.1",
1241
+ "@radix-ui/react-popper": "1.1.2",
1242
+ "@radix-ui/react-portal": "1.0.3",
1243
+ "@radix-ui/react-primitive": "1.0.3",
1244
+ "@radix-ui/react-slot": "1.0.2",
1245
+ "@radix-ui/react-use-callback-ref": "1.0.1",
1246
+ "@radix-ui/react-use-controllable-state": "1.0.1",
1247
+ "@radix-ui/react-use-layout-effect": "1.0.1",
1248
+ "@radix-ui/react-use-previous": "1.0.1",
1249
+ "@radix-ui/react-visually-hidden": "1.0.3",
1250
+ "aria-hidden": "^1.1.1",
1251
+ "react-remove-scroll": "2.5.5"
1252
+ },
1253
+ "peerDependencies": {
1254
+ "@types/react": "*",
1255
+ "@types/react-dom": "*",
1256
+ "react": "^16.8 || ^17.0 || ^18.0",
1257
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
1258
+ },
1259
+ "peerDependenciesMeta": {
1260
+ "@types/react": {
1261
+ "optional": true
1262
+ },
1263
+ "@types/react-dom": {
1264
+ "optional": true
1265
+ }
1266
+ }
1267
+ },
1268
+ "node_modules/@radix-ui/react-separator": {
1269
+ "version": "1.0.3",
1270
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.0.3.tgz",
1271
+ "integrity": "sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==",
1272
+ "dependencies": {
1273
+ "@babel/runtime": "^7.13.10",
1274
+ "@radix-ui/react-primitive": "1.0.3"
1275
+ },
1276
+ "peerDependencies": {
1277
+ "@types/react": "*",
1278
+ "@types/react-dom": "*",
1279
+ "react": "^16.8 || ^17.0 || ^18.0",
1280
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
1281
+ },
1282
+ "peerDependenciesMeta": {
1283
+ "@types/react": {
1284
+ "optional": true
1285
+ },
1286
+ "@types/react-dom": {
1287
+ "optional": true
1288
+ }
1289
+ }
1290
+ },
1291
  "node_modules/@radix-ui/react-slot": {
1292
  "version": "1.0.2",
1293
  "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
 
1429
  }
1430
  }
1431
  },
1432
+ "node_modules/@radix-ui/react-visually-hidden": {
1433
+ "version": "1.0.3",
1434
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz",
1435
+ "integrity": "sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==",
1436
+ "dependencies": {
1437
+ "@babel/runtime": "^7.13.10",
1438
+ "@radix-ui/react-primitive": "1.0.3"
1439
+ },
1440
+ "peerDependencies": {
1441
+ "@types/react": "*",
1442
+ "@types/react-dom": "*",
1443
+ "react": "^16.8 || ^17.0 || ^18.0",
1444
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
1445
+ },
1446
+ "peerDependenciesMeta": {
1447
+ "@types/react": {
1448
+ "optional": true
1449
+ },
1450
+ "@types/react-dom": {
1451
+ "optional": true
1452
+ }
1453
+ }
1454
+ },
1455
  "node_modules/@radix-ui/rect": {
1456
  "version": "1.0.1",
1457
  "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.0.1.tgz",
 
1486
  "tslib": "^2.4.0"
1487
  }
1488
  },
1489
+ "node_modules/@tanstack/query-core": {
1490
+ "version": "4.30.0",
1491
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-4.30.0.tgz",
1492
+ "integrity": "sha512-R5ozHCm3CJL9cT3j8cN6ztA0rdjw6pJN9e3zNeCPkwqfp89H1mQsn6W1RSJGuykzydQ+gFhAzteTM7NI3hz32g==",
1493
+ "funding": {
1494
+ "type": "github",
1495
+ "url": "https://github.com/sponsors/tannerlinsley"
1496
+ }
1497
+ },
1498
+ "node_modules/@tanstack/react-query": {
1499
+ "version": "4.30.0",
1500
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-4.30.0.tgz",
1501
+ "integrity": "sha512-uDURvabCVYtEamap4kmyLHCVOtzGSts83OsLpPvyzXMTHNptQwhU+YBH/vrgGAtceAuWn7JQfs9R9jJwPOJXXg==",
1502
+ "dependencies": {
1503
+ "@tanstack/query-core": "4.30.0",
1504
+ "use-sync-external-store": "^1.2.0"
1505
+ },
1506
+ "funding": {
1507
+ "type": "github",
1508
+ "url": "https://github.com/sponsors/tannerlinsley"
1509
+ },
1510
+ "peerDependencies": {
1511
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
1512
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0",
1513
+ "react-native": "*"
1514
+ },
1515
+ "peerDependenciesMeta": {
1516
+ "react-dom": {
1517
+ "optional": true
1518
+ },
1519
+ "react-native": {
1520
+ "optional": true
1521
+ }
1522
+ }
1523
+ },
1524
+ "node_modules/@tanstack/react-table": {
1525
+ "version": "8.9.3",
1526
+ "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.9.3.tgz",
1527
+ "integrity": "sha512-Ng9rdm3JPoSCi6cVZvANsYnF+UoGVRxflMb270tVj0+LjeT/ZtZ9ckxF6oLPLcKesza6VKBqtdF9mQ+vaz24Aw==",
1528
+ "dependencies": {
1529
+ "@tanstack/table-core": "8.9.3"
1530
+ },
1531
+ "engines": {
1532
+ "node": ">=12"
1533
+ },
1534
+ "funding": {
1535
+ "type": "github",
1536
+ "url": "https://github.com/sponsors/tannerlinsley"
1537
+ },
1538
+ "peerDependencies": {
1539
+ "react": ">=16",
1540
+ "react-dom": ">=16"
1541
+ }
1542
+ },
1543
+ "node_modules/@tanstack/table-core": {
1544
+ "version": "8.9.3",
1545
+ "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.9.3.tgz",
1546
+ "integrity": "sha512-NpHZBoHTfqyJk0m/s/+CSuAiwtebhYK90mDuf5eylTvgViNOujiaOaxNDxJkQQAsVvHWZftUGAx1EfO1rkKtLg==",
1547
+ "engines": {
1548
+ "node": ">=12"
1549
+ },
1550
+ "funding": {
1551
+ "type": "github",
1552
+ "url": "https://github.com/sponsors/tannerlinsley"
1553
+ }
1554
+ },
1555
  "node_modules/@tsconfig/node10": {
1556
  "version": "1.0.9",
1557
  "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
 
1572
  "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
1573
  "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
1574
  },
1575
+ "node_modules/@types/cookie": {
1576
+ "version": "0.4.1",
1577
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz",
1578
+ "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q=="
1579
+ },
1580
  "node_modules/@types/fluent-ffmpeg": {
1581
  "version": "2.1.21",
1582
  "resolved": "https://registry.npmjs.org/@types/fluent-ffmpeg/-/fluent-ffmpeg-2.1.21.tgz",
 
2638
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
2639
  "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
2640
  },
2641
+ "node_modules/cookie": {
2642
+ "version": "0.4.2",
2643
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
2644
+ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
2645
+ "engines": {
2646
+ "node": ">= 0.6"
2647
+ }
2648
+ },
2649
+ "node_modules/cookies-next": {
2650
+ "version": "2.1.2",
2651
+ "resolved": "https://registry.npmjs.org/cookies-next/-/cookies-next-2.1.2.tgz",
2652
+ "integrity": "sha512-czxcfqVaQlo0Q/3xMgp/2jpspsuLJrIm6D37wlmibP3DAcYT315c8UxQmDMohhAT/GRWpaHzpDEFANBjzTFQGg==",
2653
+ "dependencies": {
2654
+ "@types/cookie": "^0.4.1",
2655
+ "@types/node": "^16.10.2",
2656
+ "cookie": "^0.4.0"
2657
+ }
2658
+ },
2659
+ "node_modules/cookies-next/node_modules/@types/node": {
2660
+ "version": "16.18.38",
2661
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.38.tgz",
2662
+ "integrity": "sha512-6sfo1qTulpVbkxECP+AVrHV9OoJqhzCsfTNp5NIG+enM4HyM3HvZCO798WShIXBN0+QtDIcutJCjsVYnQP5rIQ=="
2663
+ },
2664
  "node_modules/cosmiconfig": {
2665
  "version": "8.2.0",
2666
  "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.2.0.tgz",
package.json CHANGED
@@ -10,13 +10,20 @@
10
  },
11
  "dependencies": {
12
  "@radix-ui/react-accordion": "^1.1.2",
 
13
  "@radix-ui/react-checkbox": "^1.0.4",
14
  "@radix-ui/react-collapsible": "^1.0.3",
15
  "@radix-ui/react-dialog": "^1.0.4",
 
16
  "@radix-ui/react-icons": "^1.3.0",
17
  "@radix-ui/react-menubar": "^1.0.3",
 
 
 
18
  "@radix-ui/react-slot": "^1.0.2",
19
  "@silevis/reactgrid": "^4.0.5",
 
 
20
  "@types/fluent-ffmpeg": "^2.1.21",
21
  "@types/node": "20.4.2",
22
  "@types/react": "18.2.15",
@@ -26,6 +33,7 @@
26
  "class-variance-authority": "^0.6.1",
27
  "clsx": "^2.0.0",
28
  "cmdk": "^0.2.0",
 
29
  "date-fns": "^2.30.0",
30
  "eslint": "8.45.0",
31
  "eslint-config-next": "13.4.10",
@@ -45,6 +53,7 @@
45
  "ts-node": "^10.9.1",
46
  "typescript": "5.1.6",
47
  "uuid": "^9.0.0",
 
48
  "zustand": "^4.3.9"
49
  }
50
  }
 
10
  },
11
  "dependencies": {
12
  "@radix-ui/react-accordion": "^1.1.2",
13
+ "@radix-ui/react-avatar": "^1.0.3",
14
  "@radix-ui/react-checkbox": "^1.0.4",
15
  "@radix-ui/react-collapsible": "^1.0.3",
16
  "@radix-ui/react-dialog": "^1.0.4",
17
+ "@radix-ui/react-dropdown-menu": "^2.0.5",
18
  "@radix-ui/react-icons": "^1.3.0",
19
  "@radix-ui/react-menubar": "^1.0.3",
20
+ "@radix-ui/react-popover": "^1.0.6",
21
+ "@radix-ui/react-select": "^1.2.2",
22
+ "@radix-ui/react-separator": "^1.0.3",
23
  "@radix-ui/react-slot": "^1.0.2",
24
  "@silevis/reactgrid": "^4.0.5",
25
+ "@tanstack/react-query": "^4.30.0",
26
+ "@tanstack/react-table": "^8.9.3",
27
  "@types/fluent-ffmpeg": "^2.1.21",
28
  "@types/node": "20.4.2",
29
  "@types/react": "18.2.15",
 
33
  "class-variance-authority": "^0.6.1",
34
  "clsx": "^2.0.0",
35
  "cmdk": "^0.2.0",
36
+ "cookies-next": "^2.1.2",
37
  "date-fns": "^2.30.0",
38
  "eslint": "8.45.0",
39
  "eslint-config-next": "13.4.10",
 
53
  "ts-node": "^10.9.1",
54
  "typescript": "5.1.6",
55
  "uuid": "^9.0.0",
56
+ "zod": "^3.21.4",
57
  "zustand": "^4.3.9"
58
  }
59
  }
src/api/index.ts DELETED
@@ -1,16 +0,0 @@
1
-
2
- import { VideoTask } from "@/app/types"
3
-
4
- import { get } from "./base"
5
-
6
- export const getPendingTasks = async () => {
7
- const tasks = await get<VideoTask[]>("", [])
8
-
9
- return tasks
10
- }
11
-
12
- export const getTask = async (id: string) => {
13
- const task = await get<VideoTask>(id, null as unknown as VideoTask)
14
-
15
- return task
16
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/api/tasks/[uuid]/route.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { NextApiRequest, NextApiResponse } from "next"
2
+
3
+ import { VideoTask } from "@/app/types"
4
+ import { NextResponse } from "next/server"
5
+ import { getTask } from "@/server"
6
+
7
+ // TODO: implement some kind of quota system
8
+ export async function GET(
9
+ req: NextApiRequest,
10
+ res: NextApiResponse<{
11
+ task?: VideoTask
12
+ error?: string
13
+ }>
14
+ ) {
15
+ return NextResponse.json({
16
+ task: await getTask(`${req.url?.split('/').pop() || ""}`)
17
+ })
18
+ }
src/app/api/tasks/route.ts ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { VideoTask, VideoTaskRequest } from "@/app/types"
2
+ import { getPendingTasks, submitNewTask } from "@/server"
3
+ import { NextApiRequest, NextApiResponse } from "next"
4
+ import { NextResponse } from "next/server"
5
+
6
+ // TODO: implement some kind of quota system
7
+ export async function GET() {
8
+ return NextResponse.json({
9
+ tasks: await getPendingTasks()
10
+ })
11
+ }
12
+
13
+ // TODO: implement some kind of quota system
14
+ export async function POST(
15
+ req: NextApiRequest,
16
+ res: NextApiResponse<VideoTask | {
17
+ error?: string
18
+ }>
19
+ ) {
20
+ console.log('POST req.body:', req.body)
21
+ const taskRequest = req.body as VideoTaskRequest
22
+ const task = await submitNewTask(taskRequest)
23
+ res.status(200).json(task)
24
+ }
src/app/layout.tsx CHANGED
@@ -1,13 +1,12 @@
1
  import './globals.css'
2
  import type { Metadata } from 'next'
3
  import { Inter } from 'next/font/google'
4
- import Head from 'next/head'
5
 
6
  const inter = Inter({ subsets: ['latin'] })
7
 
8
  export const metadata: Metadata = {
9
- title: 'Create Next App',
10
- description: 'Generated by create next app',
11
  }
12
 
13
  export default function RootLayout({
@@ -17,7 +16,9 @@ export default function RootLayout({
17
  }) {
18
  return (
19
  <html lang="en">
20
- <body className={inter.className}>{children}</body>
 
 
21
  </html>
22
  )
23
  }
 
1
  import './globals.css'
2
  import type { Metadata } from 'next'
3
  import { Inter } from 'next/font/google'
 
4
 
5
  const inter = Inter({ subsets: ['latin'] })
6
 
7
  export const metadata: Metadata = {
8
+ title: 'VideoChain UI',
9
+ description: 'Generate AI videos using this Hugging Face Space!',
10
  }
11
 
12
  export default function RootLayout({
 
16
  }) {
17
  return (
18
  <html lang="en">
19
+ <body className={inter.className}>
20
+ {children}
21
+ </body>
22
  </html>
23
  )
24
  }
src/app/main.tsx DELETED
@@ -1,66 +0,0 @@
1
- import { promises as fs } from "fs"
2
- import path from "path"
3
- import { Metadata } from "next"
4
- import Image from "next/image"
5
- import { z } from "zod"
6
-
7
- import { taskSchema } from "./data/schema"
8
- import { UserNav } from "@/components/business/tasks/user-nav"
9
- import { DataTable } from "@/components/business/tasks/data-table"
10
- import { columns } from "@/components/business/tasks/columns"
11
-
12
-
13
- export const metadata: Metadata = {
14
- title: "Tasks",
15
- description: "A task and issue tracker build using Tanstack Table.",
16
- }
17
-
18
- // Simulate a database read for tasks.
19
- async function getTasks() {
20
- const data = await fs.readFile(
21
- path.join(process.cwd(), "src/app/data/mock.json")
22
- )
23
-
24
- const tasks = JSON.parse(data.toString())
25
-
26
- return z.array(taskSchema).parse(tasks)
27
- }
28
-
29
- export default async function Main() {
30
- const tasks = await getTasks()
31
-
32
- return (
33
- <>
34
- <div className="md:hidden">
35
- <Image
36
- src="/examples/tasks-light.png"
37
- width={1280}
38
- height={998}
39
- alt="Playground"
40
- className="block dark:hidden"
41
- />
42
- <Image
43
- src="/examples/tasks-dark.png"
44
- width={1280}
45
- height={998}
46
- alt="Playground"
47
- className="hidden dark:block"
48
- />
49
- </div>
50
- <div className="hidden h-full flex-1 flex-col space-y-8 p-8 md:flex">
51
- <div className="flex items-center justify-between space-y-2">
52
- <div>
53
- <h2 className="text-2xl font-bold tracking-tight">Welcome back!</h2>
54
- <p className="text-muted-foreground">
55
- Here&apos;s a list of your tasks for this month!
56
- </p>
57
- </div>
58
- <div className="flex items-center space-x-2">
59
- <UserNav />
60
- </div>
61
- </div>
62
- <DataTable data={tasks} columns={columns} />
63
- </div>
64
- </>
65
- )
66
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/app/page.tsx CHANGED
@@ -1,18 +1,7 @@
1
- import Head from "next/head"
2
-
3
- import Main from "./main"
4
 
5
  export default function Index() {
6
- return (
7
- <div>
8
- <Head>
9
- <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86" />
10
- </Head>
11
- <main className="h-screen w-full flex bg-gray-700 text-gray-200">
12
- <div className="flex flex-col">
13
- <Main />
14
- </div>
15
- </main>
16
- </div>
17
- )
18
  }
 
1
+ import { redirect } from 'next/navigation'
2
+ import { v4 as uuidv4 } from "uuid"
 
3
 
4
  export default function Index() {
5
+ const uuid = uuidv4()
6
+ redirect(`/studio/${uuid}`)
 
 
 
 
 
 
 
 
 
 
7
  }
src/app/studio/[ownerId]/main.tsx ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ // import { experimental_useFormStatus as useFormStatus } from "react-dom"
4
+
5
+ import { VideoTasksQueue } from "@/components/business/tasks/video-tasks-queue"
6
+ import { Textarea } from "@/components/ui/textarea"
7
+ import { Button } from "@/components/ui/button"
8
+ import { formSubmit } from "@/server/actions"
9
+ import { getTasks } from "@/server"
10
+
11
+ export default async function Main({ ownerId }: { ownerId: string }) {
12
+ const tasks = await getTasks(ownerId)
13
+
14
+ return (
15
+ <form
16
+ className="h-full flex flex-col space-y-4 max-w-4xl w-full px-4 py-8"
17
+ action={formSubmit}
18
+ >
19
+
20
+ <div className="flex flex-col md:hidden w-full text-center">
21
+ <h2 className="text-4xl font-thin tracking-tight">VideoChain UI</h2>
22
+ <p className="text-md font-thin">
23
+ Powered by <span className="font-normal">Hugging Face πŸ€—</span>
24
+ </p>
25
+ </div>
26
+
27
+ <div className="flex items-center justify-between md:space-x-3 w-full">
28
+ <div className="hidden md:flex flex-col w-54">
29
+ <h2 className="text-4xl font-thin tracking-tight">VideoChain UI</h2>
30
+ <p className="text-md font-thin">
31
+ Powered by <span className="font-normal">Hugging Face πŸ€—</span>
32
+ </p>
33
+ </div>
34
+
35
+ <input
36
+ type="hidden"
37
+ id="ownerId"
38
+ name="ownerId"
39
+ value={ownerId}
40
+ />
41
+
42
+ <Textarea
43
+ id="prompt"
44
+ name="prompt"
45
+ placeholder="Video of llamas playing baseball.."
46
+ className="md:w-3/6 mr-3 md:mr-0"
47
+ />
48
+
49
+ <Button
50
+ variant="secondary"
51
+ size="lg"
52
+ className="text-md md:w-32"
53
+ type="submit"
54
+ >
55
+ Generate
56
+ </Button>
57
+ </div>
58
+ <VideoTasksQueue tasks={tasks} />
59
+ </form>
60
+ )
61
+ }
src/app/{pending β†’ studio/[ownerId]}/page.tsx RENAMED
@@ -1,20 +1,17 @@
1
  import Head from "next/head"
2
 
3
- import { getPendingTasks } from "@/api"
4
 
 
 
5
 
6
- export default async function Pending() {
7
- const tasks = await getPendingTasks()
8
- console.log(`tasks:`, tasks)
9
  return (
10
  <div>
11
  <Head>
12
  <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86" />
13
  </Head>
14
- <main className="h-screen w-full flex bg-gray-700 text-gray-200">
15
- <div className="flex flex-col">
16
- Nb tasks: {tasks.length}
17
- </div>
18
  </main>
19
  </div>
20
  )
 
1
  import Head from "next/head"
2
 
3
+ import Main from "./main"
4
 
5
+ export default async function StudioPage({ params: { ownerId } }: { params: { ownerId: string }}) {
6
+ console.log('ownerId:', ownerId)
7
 
 
 
 
8
  return (
9
  <div>
10
  <Head>
11
  <meta name="viewport" content="width=device-width, initial-scale=0.86, maximum-scale=5.0, minimum-scale=0.86" />
12
  </Head>
13
+ <main className="dark h-screen w-full flex flex-col items-center bg-stone-900 text-stone-100">
14
+ <Main ownerId={`${ownerId || ""}`} />
 
 
15
  </main>
16
  </div>
17
  )
src/app/types.ts CHANGED
@@ -210,7 +210,6 @@ export interface VideoSequenceData {
210
 
211
  hasAssembledVideo: boolean
212
  nbCompletedShots: number
213
- nbTotalShots: number
214
  progressPercent: number
215
  completedAt: string
216
  completed: boolean
@@ -219,11 +218,12 @@ export interface VideoSequenceData {
219
 
220
  export type VideoSequence = VideoSequenceMeta & VideoSequenceData
221
 
222
- export type VideoSequenceRequest = {
223
- token: string
224
- sequence: VideoSequenceMeta
225
- shots: VideoShotMeta[]
226
- }
 
227
 
228
  export type VideoTask = VideoSequence & {
229
  shots: VideoShot[]
 
210
 
211
  hasAssembledVideo: boolean
212
  nbCompletedShots: number
 
213
  progressPercent: number
214
  completedAt: string
215
  completed: boolean
 
218
 
219
  export type VideoSequence = VideoSequenceMeta & VideoSequenceData
220
 
221
+ export type VideoTaskRequest = Partial<{
222
+ prompt: string
223
+ ownerId: string
224
+ sequence: Partial<VideoSequenceMeta>
225
+ shots: Array<Partial<VideoShotMeta>>
226
+ }>
227
 
228
  export type VideoTask = VideoSequence & {
229
  shots: VideoShot[]
src/components/business/tasks/columns.tsx CHANGED
@@ -1,17 +1,16 @@
1
  "use client"
2
 
3
  import { ColumnDef } from "@tanstack/react-table"
4
-
5
- import { Badge } from "@/components/ui/badge"
6
  import { Checkbox } from "@/components/ui/checkbox"
7
 
8
- import { labels, priorities, statuses } from "@/app/data/data"
9
 
10
  import { DataTableColumnHeader } from "./data-table-column-header"
11
  import { DataTableRowActions } from "./data-table-row-actions"
12
- import { Task } from "@/app/data/schema"
13
 
14
- export const columns: ColumnDef<Task>[] = [
 
 
15
  {
16
  id: "select",
17
  header: ({ table }) => (
@@ -26,7 +25,7 @@ export const columns: ColumnDef<Task>[] = [
26
  <Checkbox
27
  checked={row.getIsSelected()}
28
  onCheckedChange={(value) => row.toggleSelected(!!value)}
29
- aria-label="Select row"
30
  className="translate-y-[2px]"
31
  />
32
  ),
@@ -36,25 +35,22 @@ export const columns: ColumnDef<Task>[] = [
36
  {
37
  accessorKey: "id",
38
  header: ({ column }) => (
39
- <DataTableColumnHeader column={column} title="Task" />
40
  ),
41
- cell: ({ row }) => <div className="w-[80px]">{row.getValue("id")}</div>,
42
  enableSorting: false,
43
  enableHiding: false,
44
  },
45
  {
46
- accessorKey: "title",
47
  header: ({ column }) => (
48
- <DataTableColumnHeader column={column} title="Title" />
49
  ),
50
  cell: ({ row }) => {
51
- const label = labels.find((label) => label.value === row.original.label)
52
-
53
  return (
54
  <div className="flex space-x-2">
55
- {label && <Badge variant="outline">{label.label}</Badge>}
56
  <span className="max-w-[500px] truncate font-medium">
57
- {row.getValue("title")}
58
  </span>
59
  </div>
60
  )
@@ -88,34 +84,40 @@ export const columns: ColumnDef<Task>[] = [
88
  },
89
  },
90
  {
91
- accessorKey: "priority",
92
  header: ({ column }) => (
93
- <DataTableColumnHeader column={column} title="Priority" />
94
  ),
95
- cell: ({ row }) => {
96
- const priority = priorities.find(
97
- (priority) => priority.value === row.getValue("priority")
98
- )
99
-
100
- if (!priority) {
101
- return null
102
- }
103
-
104
- return (
105
- <div className="flex items-center">
106
- {priority.icon && (
107
- <priority.icon className="mr-2 h-4 w-4 text-muted-foreground" />
108
- )}
109
- <span>{priority.label}</span>
110
- </div>
111
- )
112
- },
113
- filterFn: (row, id, value) => {
114
- return value.includes(row.getValue(id))
115
- },
 
 
 
116
  },
 
 
117
  {
118
  id: "actions",
119
  cell: ({ row }) => <DataTableRowActions row={row} />,
120
  },
 
121
  ]
 
1
  "use client"
2
 
3
  import { ColumnDef } from "@tanstack/react-table"
 
 
4
  import { Checkbox } from "@/components/ui/checkbox"
5
 
6
+ import { statuses } from "@/app/data/data"
7
 
8
  import { DataTableColumnHeader } from "./data-table-column-header"
9
  import { DataTableRowActions } from "./data-table-row-actions"
 
10
 
11
+ import { VideoTask } from "@/app/types"
12
+
13
+ export const columns: ColumnDef<VideoTask>[] = [
14
  {
15
  id: "select",
16
  header: ({ table }) => (
 
25
  <Checkbox
26
  checked={row.getIsSelected()}
27
  onCheckedChange={(value) => row.toggleSelected(!!value)}
28
+ aria-label="Select video"
29
  className="translate-y-[2px]"
30
  />
31
  ),
 
35
  {
36
  accessorKey: "id",
37
  header: ({ column }) => (
38
+ <DataTableColumnHeader column={column} title="Video ID" />
39
  ),
40
+ cell: ({ row }) => <div className="w-[80px]">{`${row.getValue("id") || ''}`.split("-")[0]}..</div>,
41
  enableSorting: false,
42
  enableHiding: false,
43
  },
44
  {
45
+ accessorKey: "videoPrompt",
46
  header: ({ column }) => (
47
+ <DataTableColumnHeader column={column} title="Prompt" />
48
  ),
49
  cell: ({ row }) => {
 
 
50
  return (
51
  <div className="flex space-x-2">
 
52
  <span className="max-w-[500px] truncate font-medium">
53
+ {row.getValue("videoPrompt")}
54
  </span>
55
  </div>
56
  )
 
84
  },
85
  },
86
  {
87
+ accessorKey: "preview",
88
  header: ({ column }) => (
89
+ null // no header
90
  ),
91
+ cell: ({ row }) => <div className="w-[200px]">
92
+ <a
93
+ className="hover:underline cursor-pointer"
94
+ target="_blank"
95
+ href={`/api/download/${row.getValue("fileName")}`}>
96
+ <video src={`/api/download/${row.getValue("fileName")}`} muted autoPlay />
97
+ </a>
98
+ </div>,
99
+ enableSorting: false,
100
+ enableHiding: false,
101
+ },
102
+ {
103
+ accessorKey: "fileName",
104
+ header: ({ column }) => (
105
+ null // no header
106
+ ),
107
+ cell: ({ row }) => <div className="w-[80px]">
108
+ <a
109
+ className="hover:underline cursor-pointer"
110
+ target="_blank"
111
+ href={`/api/download/${row.getValue("fileName")}`}>Download</a>
112
+ </div>,
113
+ enableSorting: false,
114
+ enableHiding: false,
115
  },
116
+ /*
117
+ action menu (currently disabled)
118
  {
119
  id: "actions",
120
  cell: ({ row }) => <DataTableRowActions row={row} />,
121
  },
122
+ */
123
  ]
src/components/business/tasks/data-table-faceted-filter.tsx DELETED
@@ -1,147 +0,0 @@
1
- import * as React from "react"
2
- import { CheckIcon, PlusCircledIcon } from "@radix-ui/react-icons"
3
- import { Column } from "@tanstack/react-table"
4
-
5
- import { cn } from "@/lib/utils"
6
- import { Badge } from "@/components/ui/badge"
7
- import { Button } from "@/components/ui/button"
8
- import {
9
- Command,
10
- CommandEmpty,
11
- CommandGroup,
12
- CommandInput,
13
- CommandItem,
14
- CommandList,
15
- CommandSeparator,
16
- } from "@/components/ui/command"
17
- import {
18
- Popover,
19
- PopoverContent,
20
- PopoverTrigger,
21
- } from "@/components/ui/popover"
22
- import { Separator } from "@/components/ui/separator"
23
-
24
- interface DataTableFacetedFilter<TData, TValue> {
25
- column?: Column<TData, TValue>
26
- title?: string
27
- options: {
28
- label: string
29
- value: string
30
- icon?: React.ComponentType<{ className?: string }>
31
- }[]
32
- }
33
-
34
- export function DataTableFacetedFilter<TData, TValue>({
35
- column,
36
- title,
37
- options,
38
- }: DataTableFacetedFilter<TData, TValue>) {
39
- const facets = column?.getFacetedUniqueValues()
40
- const selectedValues = new Set(column?.getFilterValue() as string[])
41
-
42
- return (
43
- <Popover>
44
- <PopoverTrigger asChild>
45
- <Button variant="outline" size="sm" className="h-8 border-dashed">
46
- <PlusCircledIcon className="mr-2 h-4 w-4" />
47
- {title}
48
- {selectedValues?.size > 0 && (
49
- <>
50
- <Separator orientation="vertical" className="mx-2 h-4" />
51
- <Badge
52
- variant="secondary"
53
- className="rounded-sm px-1 font-normal lg:hidden"
54
- >
55
- {selectedValues.size}
56
- </Badge>
57
- <div className="hidden space-x-1 lg:flex">
58
- {selectedValues.size > 2 ? (
59
- <Badge
60
- variant="secondary"
61
- className="rounded-sm px-1 font-normal"
62
- >
63
- {selectedValues.size} selected
64
- </Badge>
65
- ) : (
66
- options
67
- .filter((option) => selectedValues.has(option.value))
68
- .map((option) => (
69
- <Badge
70
- variant="secondary"
71
- key={option.value}
72
- className="rounded-sm px-1 font-normal"
73
- >
74
- {option.label}
75
- </Badge>
76
- ))
77
- )}
78
- </div>
79
- </>
80
- )}
81
- </Button>
82
- </PopoverTrigger>
83
- <PopoverContent className="w-[200px] p-0" align="start">
84
- <Command>
85
- <CommandInput placeholder={title} />
86
- <CommandList>
87
- <CommandEmpty>No results found.</CommandEmpty>
88
- <CommandGroup>
89
- {options.map((option) => {
90
- const isSelected = selectedValues.has(option.value)
91
- return (
92
- <CommandItem
93
- key={option.value}
94
- onSelect={() => {
95
- if (isSelected) {
96
- selectedValues.delete(option.value)
97
- } else {
98
- selectedValues.add(option.value)
99
- }
100
- const filterValues = Array.from(selectedValues)
101
- column?.setFilterValue(
102
- filterValues.length ? filterValues : undefined
103
- )
104
- }}
105
- >
106
- <div
107
- className={cn(
108
- "mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
109
- isSelected
110
- ? "bg-primary text-primary-foreground"
111
- : "opacity-50 [&_svg]:invisible"
112
- )}
113
- >
114
- <CheckIcon className={cn("h-4 w-4")} />
115
- </div>
116
- {option.icon && (
117
- <option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
118
- )}
119
- <span>{option.label}</span>
120
- {facets?.get(option.value) && (
121
- <span className="ml-auto flex h-4 w-4 items-center justify-center font-mono text-xs">
122
- {facets.get(option.value)}
123
- </span>
124
- )}
125
- </CommandItem>
126
- )
127
- })}
128
- </CommandGroup>
129
- {selectedValues.size > 0 && (
130
- <>
131
- <CommandSeparator />
132
- <CommandGroup>
133
- <CommandItem
134
- onSelect={() => column?.setFilterValue(undefined)}
135
- className="justify-center text-center"
136
- >
137
- Clear filters
138
- </CommandItem>
139
- </CommandGroup>
140
- </>
141
- )}
142
- </CommandList>
143
- </Command>
144
- </PopoverContent>
145
- </Popover>
146
- )
147
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/business/tasks/data-table-pagination.tsx DELETED
@@ -1,97 +0,0 @@
1
- import {
2
- ChevronLeftIcon,
3
- ChevronRightIcon,
4
- DoubleArrowLeftIcon,
5
- DoubleArrowRightIcon,
6
- } from "@radix-ui/react-icons"
7
- import { Table } from "@tanstack/react-table"
8
-
9
- import { Button } from "@/components/ui/button"
10
- import {
11
- Select,
12
- SelectContent,
13
- SelectItem,
14
- SelectTrigger,
15
- SelectValue,
16
- } from "@/components/ui/select"
17
-
18
- interface DataTablePaginationProps<TData> {
19
- table: Table<TData>
20
- }
21
-
22
- export function DataTablePagination<TData>({
23
- table,
24
- }: DataTablePaginationProps<TData>) {
25
- return (
26
- <div className="flex items-center justify-between px-2">
27
- <div className="flex-1 text-sm text-muted-foreground">
28
- {table.getFilteredSelectedRowModel().rows.length} of{" "}
29
- {table.getFilteredRowModel().rows.length} row(s) selected.
30
- </div>
31
- <div className="flex items-center space-x-6 lg:space-x-8">
32
- <div className="flex items-center space-x-2">
33
- <p className="text-sm font-medium">Rows per page</p>
34
- <Select
35
- value={`${table.getState().pagination.pageSize}`}
36
- onValueChange={(value) => {
37
- table.setPageSize(Number(value))
38
- }}
39
- >
40
- <SelectTrigger className="h-8 w-[70px]">
41
- <SelectValue placeholder={table.getState().pagination.pageSize} />
42
- </SelectTrigger>
43
- <SelectContent side="top">
44
- {[10, 20, 30, 40, 50].map((pageSize) => (
45
- <SelectItem key={pageSize} value={`${pageSize}`}>
46
- {pageSize}
47
- </SelectItem>
48
- ))}
49
- </SelectContent>
50
- </Select>
51
- </div>
52
- <div className="flex w-[100px] items-center justify-center text-sm font-medium">
53
- Page {table.getState().pagination.pageIndex + 1} of{" "}
54
- {table.getPageCount()}
55
- </div>
56
- <div className="flex items-center space-x-2">
57
- <Button
58
- variant="outline"
59
- className="hidden h-8 w-8 p-0 lg:flex"
60
- onClick={() => table.setPageIndex(0)}
61
- disabled={!table.getCanPreviousPage()}
62
- >
63
- <span className="sr-only">Go to first page</span>
64
- <DoubleArrowLeftIcon className="h-4 w-4" />
65
- </Button>
66
- <Button
67
- variant="outline"
68
- className="h-8 w-8 p-0"
69
- onClick={() => table.previousPage()}
70
- disabled={!table.getCanPreviousPage()}
71
- >
72
- <span className="sr-only">Go to previous page</span>
73
- <ChevronLeftIcon className="h-4 w-4" />
74
- </Button>
75
- <Button
76
- variant="outline"
77
- className="h-8 w-8 p-0"
78
- onClick={() => table.nextPage()}
79
- disabled={!table.getCanNextPage()}
80
- >
81
- <span className="sr-only">Go to next page</span>
82
- <ChevronRightIcon className="h-4 w-4" />
83
- </Button>
84
- <Button
85
- variant="outline"
86
- className="hidden h-8 w-8 p-0 lg:flex"
87
- onClick={() => table.setPageIndex(table.getPageCount() - 1)}
88
- disabled={!table.getCanNextPage()}
89
- >
90
- <span className="sr-only">Go to last page</span>
91
- <DoubleArrowRightIcon className="h-4 w-4" />
92
- </Button>
93
- </div>
94
- </div>
95
- </div>
96
- )
97
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/business/tasks/data-table-row-actions.tsx CHANGED
@@ -8,27 +8,19 @@ import {
8
  DropdownMenu,
9
  DropdownMenuContent,
10
  DropdownMenuItem,
11
- DropdownMenuRadioGroup,
12
- DropdownMenuRadioItem,
13
  DropdownMenuSeparator,
14
  DropdownMenuShortcut,
15
- DropdownMenuSub,
16
- DropdownMenuSubContent,
17
- DropdownMenuSubTrigger,
18
  DropdownMenuTrigger,
19
  } from "@/components/ui/dropdown-menu"
20
 
21
- import { labels } from "@/app/data/data"
22
- import { taskSchema } from "@/app/data/schema"
23
 
24
- interface DataTableRowActionsProps<TData> {
25
- row: Row<TData>
26
- }
27
-
28
- export function DataTableRowActions<TData>({
29
  row,
30
- }: DataTableRowActionsProps<TData>) {
31
- const task = taskSchema.parse(row.original)
 
 
32
 
33
  return (
34
  <DropdownMenu>
@@ -42,22 +34,7 @@ export function DataTableRowActions<TData>({
42
  </Button>
43
  </DropdownMenuTrigger>
44
  <DropdownMenuContent align="end" className="w-[160px]">
45
- <DropdownMenuItem>Edit</DropdownMenuItem>
46
- <DropdownMenuItem>Make a copy</DropdownMenuItem>
47
- <DropdownMenuItem>Favorite</DropdownMenuItem>
48
- <DropdownMenuSeparator />
49
- <DropdownMenuSub>
50
- <DropdownMenuSubTrigger>Labels</DropdownMenuSubTrigger>
51
- <DropdownMenuSubContent>
52
- <DropdownMenuRadioGroup value={task.label}>
53
- {labels.map((label) => (
54
- <DropdownMenuRadioItem key={label.value} value={label.value}>
55
- {label.label}
56
- </DropdownMenuRadioItem>
57
- ))}
58
- </DropdownMenuRadioGroup>
59
- </DropdownMenuSubContent>
60
- </DropdownMenuSub>
61
  <DropdownMenuSeparator />
62
  <DropdownMenuItem>
63
  Delete
 
8
  DropdownMenu,
9
  DropdownMenuContent,
10
  DropdownMenuItem,
 
 
11
  DropdownMenuSeparator,
12
  DropdownMenuShortcut,
 
 
 
13
  DropdownMenuTrigger,
14
  } from "@/components/ui/dropdown-menu"
15
 
16
+ import { VideoTask } from "@/app/types"
 
17
 
18
+ export function DataTableRowActions({
 
 
 
 
19
  row,
20
+ }: {
21
+ row: Row<VideoTask>
22
+ }) {
23
+ const task = row.original
24
 
25
  return (
26
  <DropdownMenu>
 
34
  </Button>
35
  </DropdownMenuTrigger>
36
  <DropdownMenuContent align="end" className="w-[160px]">
37
+ <DropdownMenuItem>Download</DropdownMenuItem>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  <DropdownMenuSeparator />
39
  <DropdownMenuItem>
40
  Delete
src/components/business/tasks/data-table-toolbar.tsx DELETED
@@ -1,61 +0,0 @@
1
- "use client"
2
-
3
- import { Cross2Icon } from "@radix-ui/react-icons"
4
- import { Table } from "@tanstack/react-table"
5
-
6
- import { Button } from "@/components/ui/button"
7
- import { Input } from "@/components/ui/input"
8
- import { DataTableViewOptions } from "@/components/business/tasks/data-table-view-options"
9
-
10
- import { DataTableFacetedFilter } from "./data-table-faceted-filter"
11
- import { priorities, statuses } from "@/app/data/data"
12
-
13
- interface DataTableToolbarProps<TData> {
14
- table: Table<TData>
15
- }
16
-
17
- export function DataTableToolbar<TData>({
18
- table,
19
- }: DataTableToolbarProps<TData>) {
20
- const isFiltered = table.getState().columnFilters.length > 0
21
-
22
- return (
23
- <div className="flex items-center justify-between">
24
- <div className="flex flex-1 items-center space-x-2">
25
- <Input
26
- placeholder="Filter tasks..."
27
- value={(table.getColumn("title")?.getFilterValue() as string) ?? ""}
28
- onChange={(event) =>
29
- table.getColumn("title")?.setFilterValue(event.target.value)
30
- }
31
- className="h-8 w-[150px] lg:w-[250px]"
32
- />
33
- {table.getColumn("status") && (
34
- <DataTableFacetedFilter
35
- column={table.getColumn("status")}
36
- title="Status"
37
- options={statuses}
38
- />
39
- )}
40
- {table.getColumn("priority") && (
41
- <DataTableFacetedFilter
42
- column={table.getColumn("priority")}
43
- title="Priority"
44
- options={priorities}
45
- />
46
- )}
47
- {isFiltered && (
48
- <Button
49
- variant="ghost"
50
- onClick={() => table.resetColumnFilters()}
51
- className="h-8 px-2 lg:px-3"
52
- >
53
- Reset
54
- <Cross2Icon className="ml-2 h-4 w-4" />
55
- </Button>
56
- )}
57
- </div>
58
- <DataTableViewOptions table={table} />
59
- </div>
60
- )
61
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/business/tasks/data-table-view-options.tsx DELETED
@@ -1,59 +0,0 @@
1
- "use client"
2
-
3
- import { DropdownMenuTrigger } from "@radix-ui/react-dropdown-menu"
4
- import { MixerHorizontalIcon } from "@radix-ui/react-icons"
5
- import { Table } from "@tanstack/react-table"
6
-
7
- import { Button } from "@/components/ui/button"
8
- import {
9
- DropdownMenu,
10
- DropdownMenuCheckboxItem,
11
- DropdownMenuContent,
12
- DropdownMenuLabel,
13
- DropdownMenuSeparator,
14
- } from "@/components/ui/dropdown-menu"
15
-
16
- interface DataTableViewOptionsProps<TData> {
17
- table: Table<TData>
18
- }
19
-
20
- export function DataTableViewOptions<TData>({
21
- table,
22
- }: DataTableViewOptionsProps<TData>) {
23
- return (
24
- <DropdownMenu>
25
- <DropdownMenuTrigger asChild>
26
- <Button
27
- variant="outline"
28
- size="sm"
29
- className="ml-auto hidden h-8 lg:flex"
30
- >
31
- <MixerHorizontalIcon className="mr-2 h-4 w-4" />
32
- View
33
- </Button>
34
- </DropdownMenuTrigger>
35
- <DropdownMenuContent align="end" className="w-[150px]">
36
- <DropdownMenuLabel>Toggle columns</DropdownMenuLabel>
37
- <DropdownMenuSeparator />
38
- {table
39
- .getAllColumns()
40
- .filter(
41
- (column) =>
42
- typeof column.accessorFn !== "undefined" && column.getCanHide()
43
- )
44
- .map((column) => {
45
- return (
46
- <DropdownMenuCheckboxItem
47
- key={column.id}
48
- className="capitalize"
49
- checked={column.getIsVisible()}
50
- onCheckedChange={(value) => column.toggleVisibility(!!value)}
51
- >
52
- {column.id}
53
- </DropdownMenuCheckboxItem>
54
- )
55
- })}
56
- </DropdownMenuContent>
57
- </DropdownMenu>
58
- )
59
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/business/tasks/user-nav.tsx DELETED
@@ -1,62 +0,0 @@
1
- import {
2
- Avatar,
3
- AvatarFallback,
4
- AvatarImage,
5
- } from "@/components/ui/avatar"
6
- import { Button } from "@/components/ui/button"
7
- import {
8
- DropdownMenu,
9
- DropdownMenuContent,
10
- DropdownMenuGroup,
11
- DropdownMenuItem,
12
- DropdownMenuLabel,
13
- DropdownMenuSeparator,
14
- DropdownMenuShortcut,
15
- DropdownMenuTrigger,
16
- } from "@/components/ui/dropdown-menu"
17
-
18
- export function UserNav() {
19
- return (
20
- <DropdownMenu>
21
- <DropdownMenuTrigger asChild>
22
- <Button variant="ghost" className="relative h-8 w-8 rounded-full">
23
- <Avatar className="h-9 w-9">
24
- <AvatarImage src="/avatars/03.png" alt="@shadcn" />
25
- <AvatarFallback>SC</AvatarFallback>
26
- </Avatar>
27
- </Button>
28
- </DropdownMenuTrigger>
29
- <DropdownMenuContent className="w-56" align="end" forceMount>
30
- <DropdownMenuLabel className="font-normal">
31
- <div className="flex flex-col space-y-1">
32
- <p className="text-sm font-medium leading-none">shadcn</p>
33
- <p className="text-xs leading-none text-muted-foreground">
34
35
- </p>
36
- </div>
37
- </DropdownMenuLabel>
38
- <DropdownMenuSeparator />
39
- <DropdownMenuGroup>
40
- <DropdownMenuItem>
41
- Profile
42
- <DropdownMenuShortcut>β‡§βŒ˜P</DropdownMenuShortcut>
43
- </DropdownMenuItem>
44
- <DropdownMenuItem>
45
- Billing
46
- <DropdownMenuShortcut>⌘B</DropdownMenuShortcut>
47
- </DropdownMenuItem>
48
- <DropdownMenuItem>
49
- Settings
50
- <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
51
- </DropdownMenuItem>
52
- <DropdownMenuItem>New Team</DropdownMenuItem>
53
- </DropdownMenuGroup>
54
- <DropdownMenuSeparator />
55
- <DropdownMenuItem>
56
- Log out
57
- <DropdownMenuShortcut>β‡§βŒ˜Q</DropdownMenuShortcut>
58
- </DropdownMenuItem>
59
- </DropdownMenuContent>
60
- </DropdownMenu>
61
- )
62
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/components/business/tasks/{data-table.tsx β†’ video-tasks-queue.tsx} RENAMED
@@ -25,18 +25,14 @@ import {
25
  TableRow,
26
  } from "@/components/ui/table"
27
 
28
- import { DataTablePagination } from "@/components/business/tasks/data-table-pagination"
29
- import { DataTableToolbar } from "@/components/business/tasks/data-table-toolbar"
30
 
31
- interface DataTableProps<TData, TValue> {
32
- columns: ColumnDef<TData, TValue>[]
33
- data: TData[]
34
- }
35
-
36
- export function DataTable<TData, TValue>({
37
- columns,
38
- data,
39
- }: DataTableProps<TData, TValue>) {
40
  const [rowSelection, setRowSelection] = React.useState({})
41
  const [columnVisibility, setColumnVisibility] =
42
  React.useState<VisibilityState>({})
@@ -46,8 +42,8 @@ export function DataTable<TData, TValue>({
46
  const [sorting, setSorting] = React.useState<SortingState>([])
47
 
48
  const table = useReactTable({
49
- data,
50
- columns,
51
  state: {
52
  sorting,
53
  columnVisibility,
@@ -68,59 +64,55 @@ export function DataTable<TData, TValue>({
68
  })
69
 
70
  return (
71
- <div className="space-y-4">
72
- <DataTableToolbar table={table} />
73
- <div className="rounded-md border">
74
- <Table>
75
- <TableHeader>
76
- {table.getHeaderGroups().map((headerGroup) => (
77
- <TableRow key={headerGroup.id}>
78
- {headerGroup.headers.map((header) => {
79
- return (
80
- <TableHead key={header.id}>
81
- {header.isPlaceholder
82
- ? null
83
- : flexRender(
84
- header.column.columnDef.header,
85
- header.getContext()
86
- )}
87
- </TableHead>
88
- )
89
- })}
90
- </TableRow>
91
- ))}
92
- </TableHeader>
93
- <TableBody>
94
- {table.getRowModel().rows?.length ? (
95
- table.getRowModel().rows.map((row) => (
96
- <TableRow
97
- key={row.id}
98
- data-state={row.getIsSelected() && "selected"}
99
- >
100
- {row.getVisibleCells().map((cell) => (
101
- <TableCell key={cell.id}>
102
- {flexRender(
103
- cell.column.columnDef.cell,
104
- cell.getContext()
105
- )}
106
- </TableCell>
107
- ))}
108
- </TableRow>
109
- ))
110
- ) : (
111
- <TableRow>
112
- <TableCell
113
- colSpan={columns.length}
114
- className="h-24 text-center"
115
- >
116
- No results.
117
- </TableCell>
118
  </TableRow>
119
- )}
120
- </TableBody>
121
- </Table>
122
- </div>
123
- <DataTablePagination table={table} />
 
 
 
 
 
 
 
 
124
  </div>
125
  )
126
  }
 
25
  TableRow,
26
  } from "@/components/ui/table"
27
 
28
+ import { columns } from "@/components/business/tasks/columns"
29
+ import { VideoTask } from "@/app/types"
30
 
31
+ export function VideoTasksQueue({
32
+ tasks
33
+ }: {
34
+ tasks: VideoTask[]
35
+ }) {
 
 
 
 
36
  const [rowSelection, setRowSelection] = React.useState({})
37
  const [columnVisibility, setColumnVisibility] =
38
  React.useState<VisibilityState>({})
 
42
  const [sorting, setSorting] = React.useState<SortingState>([])
43
 
44
  const table = useReactTable({
45
+ data: tasks,
46
+ columns: columns as ColumnDef<VideoTask, any>[],
47
  state: {
48
  sorting,
49
  columnVisibility,
 
64
  })
65
 
66
  return (
67
+ <div className="rounded-lg border overflow-hidden">
68
+ <Table>
69
+ <TableHeader>
70
+ {table.getHeaderGroups().map((headerGroup) => (
71
+ <TableRow key={headerGroup.id}>
72
+ {headerGroup.headers.map((header) => {
73
+ return (
74
+ <TableHead key={header.id}>
75
+ {header.isPlaceholder
76
+ ? null
77
+ : flexRender(
78
+ header.column.columnDef.header,
79
+ header.getContext()
80
+ )}
81
+ </TableHead>
82
+ )
83
+ })}
84
+ </TableRow>
85
+ ))}
86
+ </TableHeader>
87
+ <TableBody>
88
+ {table.getRowModel().rows?.length ? (
89
+ table.getRowModel().rows.map((row) => (
90
+ <TableRow
91
+ key={row.id}
92
+ data-state={row.getIsSelected() && "selected"}
93
+ >
94
+ {row.getVisibleCells().map((cell) => (
95
+ <TableCell key={cell.id}>
96
+ {flexRender(
97
+ cell.column.columnDef.cell,
98
+ cell.getContext()
99
+ )}
100
+ </TableCell>
101
+ ))}
 
 
 
 
 
 
 
 
 
 
 
 
102
  </TableRow>
103
+ ))
104
+ ) : (
105
+ <TableRow>
106
+ <TableCell
107
+ colSpan={columns.length}
108
+ className="h-24 text-center"
109
+ >
110
+ No recent video.
111
+ </TableCell>
112
+ </TableRow>
113
+ )}
114
+ </TableBody>
115
+ </Table>
116
  </div>
117
  )
118
  }
src/components/ui/alert.tsx CHANGED
@@ -4,11 +4,11 @@ import { cva, type VariantProps } from "class-variance-authority"
4
  import { cn } from "@/lib/utils"
5
 
6
  const alertVariants = cva(
7
- "relative w-full rounded-lg border border-zinc-200 p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-zinc-950 dark:border-zinc-800 dark:[&>svg]:text-zinc-50",
8
  {
9
  variants: {
10
  variant: {
11
- default: "bg-white text-zinc-950 dark:bg-zinc-950 dark:text-zinc-50",
12
  destructive:
13
  "border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900",
14
  },
 
4
  import { cn } from "@/lib/utils"
5
 
6
  const alertVariants = cva(
7
+ "relative w-full rounded-lg border border-stone-200 p-4 [&:has(svg)]:pl-11 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-stone-950 dark:border-stone-800 dark:[&>svg]:text-stone-50",
8
  {
9
  variants: {
10
  variant: {
11
+ default: "bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50",
12
  destructive:
13
  "border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900",
14
  },
src/components/ui/avatar.tsx CHANGED
@@ -39,7 +39,7 @@ const AvatarFallback = React.forwardRef<
39
  <AvatarPrimitive.Fallback
40
  ref={ref}
41
  className={cn(
42
- "flex h-full w-full items-center justify-center rounded-full bg-zinc-100 dark:bg-zinc-800",
43
  className
44
  )}
45
  {...props}
 
39
  <AvatarPrimitive.Fallback
40
  ref={ref}
41
  className={cn(
42
+ "flex h-full w-full items-center justify-center rounded-full bg-stone-100 dark:bg-stone-800",
43
  className
44
  )}
45
  {...props}
src/components/ui/badge.tsx CHANGED
@@ -4,17 +4,17 @@ import { cva, type VariantProps } from "class-variance-authority"
4
  import { cn } from "@/lib/utils"
5
 
6
  const badgeVariants = cva(
7
- "inline-flex items-center rounded-full border border-zinc-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:ring-offset-2 dark:border-zinc-800 dark:focus:ring-zinc-800",
8
  {
9
  variants: {
10
  variant: {
11
  default:
12
- "border-transparent bg-zinc-900 text-zinc-50 hover:bg-zinc-900/80 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/80",
13
  secondary:
14
- "border-transparent bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80",
15
  destructive:
16
- "border-transparent bg-red-500 text-zinc-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/80",
17
- outline: "text-zinc-950 dark:text-zinc-50",
18
  },
19
  },
20
  defaultVariants: {
 
4
  import { cn } from "@/lib/utils"
5
 
6
  const badgeVariants = cva(
7
+ "inline-flex items-center rounded-full border border-stone-200 px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 dark:border-stone-800 dark:focus:ring-stone-800",
8
  {
9
  variants: {
10
  variant: {
11
  default:
12
+ "border-transparent bg-stone-900 text-stone-50 hover:bg-stone-900/80 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/80",
13
  secondary:
14
+ "border-transparent bg-stone-100 text-stone-900 hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80",
15
  destructive:
16
+ "border-transparent bg-red-500 text-stone-50 hover:bg-red-500/80 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/80",
17
+ outline: "text-stone-950 dark:text-stone-50",
18
  },
19
  },
20
  defaultVariants: {
src/components/ui/button.tsx CHANGED
@@ -5,19 +5,19 @@ import { cva, type VariantProps } from "class-variance-authority"
5
  import { cn } from "@/lib/utils"
6
 
7
  const buttonVariants = cva(
8
- "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-800",
9
  {
10
  variants: {
11
  variant: {
12
- default: "bg-zinc-900 text-zinc-50 hover:bg-zinc-900/90 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50/90",
13
  destructive:
14
- "bg-red-500 text-zinc-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/90",
15
  outline:
16
- "border border-zinc-200 bg-white hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
17
  secondary:
18
- "bg-zinc-100 text-zinc-900 hover:bg-zinc-100/80 dark:bg-zinc-800 dark:text-zinc-50 dark:hover:bg-zinc-800/80",
19
- ghost: "hover:bg-zinc-100 hover:text-zinc-900 dark:hover:bg-zinc-800 dark:hover:text-zinc-50",
20
- link: "text-zinc-900 underline-offset-4 hover:underline dark:text-zinc-50",
21
  },
22
  size: {
23
  default: "h-10 px-4 py-2",
 
5
  import { cn } from "@/lib/utils"
6
 
7
  const buttonVariants = cva(
8
+ "inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-white transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 dark:ring-offset-stone-950 dark:focus-visible:ring-stone-800",
9
  {
10
  variants: {
11
  variant: {
12
+ default: "bg-stone-900 text-stone-50 hover:bg-stone-900/90 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50/90",
13
  destructive:
14
+ "bg-red-500 text-stone-50 hover:bg-red-500/90 dark:bg-red-900 dark:text-red-50 dark:hover:bg-red-900/90",
15
  outline:
16
+ "border border-stone-200 bg-white hover:bg-stone-100 hover:text-stone-900 dark:border-stone-800 dark:bg-stone-950 dark:hover:bg-stone-800 dark:hover:text-stone-50",
17
  secondary:
18
+ "bg-stone-100 text-stone-900 hover:bg-stone-100/80 dark:bg-stone-800 dark:text-stone-50 dark:hover:bg-stone-800/80",
19
+ ghost: "hover:bg-stone-100 hover:text-stone-900 dark:hover:bg-stone-800 dark:hover:text-stone-50",
20
+ link: "text-stone-900 underline-offset-4 hover:underline dark:text-stone-50",
21
  },
22
  size: {
23
  default: "h-10 px-4 py-2",
src/components/ui/calendar.tsx CHANGED
@@ -34,20 +34,20 @@ function Calendar({
34
  table: "w-full border-collapse space-y-1",
35
  head_row: "flex",
36
  head_cell:
37
- "text-zinc-500 rounded-md w-9 font-normal text-[0.8rem] dark:text-zinc-400",
38
  row: "flex w-full mt-2",
39
- cell: "text-center text-sm p-0 relative [&:has([aria-selected])]:bg-zinc-100 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20 dark:[&:has([aria-selected])]:bg-zinc-800",
40
  day: cn(
41
  buttonVariants({ variant: "ghost" }),
42
  "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
43
  ),
44
  day_selected:
45
- "bg-zinc-900 text-zinc-50 hover:bg-zinc-900 hover:text-zinc-50 focus:bg-zinc-900 focus:text-zinc-50 dark:bg-zinc-50 dark:text-zinc-900 dark:hover:bg-zinc-50 dark:hover:text-zinc-900 dark:focus:bg-zinc-50 dark:focus:text-zinc-900",
46
- day_today: "bg-zinc-100 text-zinc-900 dark:bg-zinc-800 dark:text-zinc-50",
47
- day_outside: "text-zinc-500 opacity-50 dark:text-zinc-400",
48
- day_disabled: "text-zinc-500 opacity-50 dark:text-zinc-400",
49
  day_range_middle:
50
- "aria-selected:bg-zinc-100 aria-selected:text-zinc-900 dark:aria-selected:bg-zinc-800 dark:aria-selected:text-zinc-50",
51
  day_hidden: "invisible",
52
  ...classNames,
53
  }}
 
34
  table: "w-full border-collapse space-y-1",
35
  head_row: "flex",
36
  head_cell:
37
+ "text-stone-500 rounded-md w-9 font-normal text-[0.8rem] dark:text-stone-400",
38
  row: "flex w-full mt-2",
39
+ cell: "text-center text-sm p-0 relative [&:has([aria-selected])]:bg-stone-100 first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md focus-within:relative focus-within:z-20 dark:[&:has([aria-selected])]:bg-stone-800",
40
  day: cn(
41
  buttonVariants({ variant: "ghost" }),
42
  "h-9 w-9 p-0 font-normal aria-selected:opacity-100"
43
  ),
44
  day_selected:
45
+ "bg-stone-900 text-stone-50 hover:bg-stone-900 hover:text-stone-50 focus:bg-stone-900 focus:text-stone-50 dark:bg-stone-50 dark:text-stone-900 dark:hover:bg-stone-50 dark:hover:text-stone-900 dark:focus:bg-stone-50 dark:focus:text-stone-900",
46
+ day_today: "bg-stone-100 text-stone-900 dark:bg-stone-800 dark:text-stone-50",
47
+ day_outside: "text-stone-500 opacity-50 dark:text-stone-400",
48
+ day_disabled: "text-stone-500 opacity-50 dark:text-stone-400",
49
  day_range_middle:
50
+ "aria-selected:bg-stone-100 aria-selected:text-stone-900 dark:aria-selected:bg-stone-800 dark:aria-selected:text-stone-50",
51
  day_hidden: "invisible",
52
  ...classNames,
53
  }}
src/components/ui/card.tsx CHANGED
@@ -9,7 +9,7 @@ const Card = React.forwardRef<
9
  <div
10
  ref={ref}
11
  className={cn(
12
- "rounded-lg border border-zinc-200 bg-white text-zinc-950 shadow-sm dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
13
  className
14
  )}
15
  {...props}
@@ -50,7 +50,7 @@ const CardDescription = React.forwardRef<
50
  >(({ className, ...props }, ref) => (
51
  <p
52
  ref={ref}
53
- className={cn("text-sm text-zinc-500 dark:text-zinc-400", className)}
54
  {...props}
55
  />
56
  ))
 
9
  <div
10
  ref={ref}
11
  className={cn(
12
+ "rounded-lg border border-stone-200 bg-white text-stone-950 shadow-sm dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
13
  className
14
  )}
15
  {...props}
 
50
  >(({ className, ...props }, ref) => (
51
  <p
52
  ref={ref}
53
+ className={cn("text-sm text-stone-500 dark:text-stone-400", className)}
54
  {...props}
55
  />
56
  ))
src/components/ui/checkbox.tsx CHANGED
@@ -13,7 +13,7 @@ const Checkbox = React.forwardRef<
13
  <CheckboxPrimitive.Root
14
  ref={ref}
15
  className={cn(
16
- "peer h-4 w-4 shrink-0 rounded-sm border border-zinc-200 border-zinc-900 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-zinc-900 data-[state=checked]:text-zinc-50 dark:border-zinc-800 dark:border-zinc-50 dark:ring-offset-zinc-950 dark:focus-visible:ring-zinc-800 dark:data-[state=checked]:bg-zinc-50 dark:data-[state=checked]:text-zinc-900",
17
  className
18
  )}
19
  {...props}
 
13
  <CheckboxPrimitive.Root
14
  ref={ref}
15
  className={cn(
16
+ "peer h-4 w-4 shrink-0 rounded-sm border border-stone-200 border-stone-900 ring-offset-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-stone-900 data-[state=checked]:text-stone-50 dark:border-stone-800 dark:border-stone-50 dark:ring-offset-stone-950 dark:focus-visible:ring-stone-800 dark:data-[state=checked]:bg-stone-50 dark:data-[state=checked]:text-stone-900",
17
  className
18
  )}
19
  {...props}
src/components/ui/command.tsx CHANGED
@@ -15,7 +15,7 @@ const Command = React.forwardRef<
15
  <CommandPrimitive
16
  ref={ref}
17
  className={cn(
18
- "flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-zinc-950 dark:bg-zinc-950 dark:text-zinc-50",
19
  className
20
  )}
21
  {...props}
@@ -29,7 +29,7 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
29
  return (
30
  <Dialog {...props}>
31
  <DialogContent className="overflow-hidden p-0 shadow-lg">
32
- <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-zinc-500 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5 dark:[&_[cmdk-group-heading]]:text-zinc-400">
33
  {children}
34
  </Command>
35
  </DialogContent>
@@ -46,7 +46,7 @@ const CommandInput = React.forwardRef<
46
  <CommandPrimitive.Input
47
  ref={ref}
48
  className={cn(
49
- "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-zinc-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-zinc-400",
50
  className
51
  )}
52
  {...props}
@@ -89,7 +89,7 @@ const CommandGroup = React.forwardRef<
89
  <CommandPrimitive.Group
90
  ref={ref}
91
  className={cn(
92
- "overflow-hidden p-1 text-zinc-950 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-zinc-500 dark:text-zinc-50 dark:[&_[cmdk-group-heading]]:text-zinc-400",
93
  className
94
  )}
95
  {...props}
@@ -104,7 +104,7 @@ const CommandSeparator = React.forwardRef<
104
  >(({ className, ...props }, ref) => (
105
  <CommandPrimitive.Separator
106
  ref={ref}
107
- className={cn("-mx-1 h-px bg-zinc-200 dark:bg-zinc-800", className)}
108
  {...props}
109
  />
110
  ))
@@ -117,7 +117,7 @@ const CommandItem = React.forwardRef<
117
  <CommandPrimitive.Item
118
  ref={ref}
119
  className={cn(
120
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-zinc-100 aria-selected:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:aria-selected:bg-zinc-800 dark:aria-selected:text-zinc-50",
121
  className
122
  )}
123
  {...props}
@@ -133,7 +133,7 @@ const CommandShortcut = ({
133
  return (
134
  <span
135
  className={cn(
136
- "ml-auto text-xs tracking-widest text-zinc-500 dark:text-zinc-400",
137
  className
138
  )}
139
  {...props}
 
15
  <CommandPrimitive
16
  ref={ref}
17
  className={cn(
18
+ "flex h-full w-full flex-col overflow-hidden rounded-md bg-white text-stone-950 dark:bg-stone-950 dark:text-stone-50",
19
  className
20
  )}
21
  {...props}
 
29
  return (
30
  <Dialog {...props}>
31
  <DialogContent className="overflow-hidden p-0 shadow-lg">
32
+ <Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5 dark:[&_[cmdk-group-heading]]:text-stone-400">
33
  {children}
34
  </Command>
35
  </DialogContent>
 
46
  <CommandPrimitive.Input
47
  ref={ref}
48
  className={cn(
49
+ "flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-stone-500 disabled:cursor-not-allowed disabled:opacity-50 dark:placeholder:text-stone-400",
50
  className
51
  )}
52
  {...props}
 
89
  <CommandPrimitive.Group
90
  ref={ref}
91
  className={cn(
92
+ "overflow-hidden p-1 text-stone-950 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-stone-500 dark:text-stone-50 dark:[&_[cmdk-group-heading]]:text-stone-400",
93
  className
94
  )}
95
  {...props}
 
104
  >(({ className, ...props }, ref) => (
105
  <CommandPrimitive.Separator
106
  ref={ref}
107
+ className={cn("-mx-1 h-px bg-stone-200 dark:bg-stone-800", className)}
108
  {...props}
109
  />
110
  ))
 
117
  <CommandPrimitive.Item
118
  ref={ref}
119
  className={cn(
120
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-stone-100 aria-selected:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:aria-selected:bg-stone-800 dark:aria-selected:text-stone-50",
121
  className
122
  )}
123
  {...props}
 
133
  return (
134
  <span
135
  className={cn(
136
+ "ml-auto text-xs tracking-widest text-stone-500 dark:text-stone-400",
137
  className
138
  )}
139
  {...props}
src/components/ui/dialog.tsx CHANGED
@@ -25,7 +25,7 @@ const DialogOverlay = React.forwardRef<
25
  <DialogPrimitive.Overlay
26
  ref={ref}
27
  className={cn(
28
- "fixed inset-0 z-50 bg-white/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-zinc-950/80",
29
  className
30
  )}
31
  {...props}
@@ -42,13 +42,13 @@ const DialogContent = React.forwardRef<
42
  <DialogPrimitive.Content
43
  ref={ref}
44
  className={cn(
45
- "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-zinc-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full dark:border-zinc-800 dark:bg-zinc-950",
46
  className
47
  )}
48
  {...props}
49
  >
50
  {children}
51
- <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-500 dark:ring-offset-zinc-950 dark:focus:ring-zinc-800 dark:data-[state=open]:bg-zinc-800 dark:data-[state=open]:text-zinc-400">
52
  <X className="h-4 w-4" />
53
  <span className="sr-only">Close</span>
54
  </DialogPrimitive.Close>
@@ -106,7 +106,7 @@ const DialogDescription = React.forwardRef<
106
  >(({ className, ...props }, ref) => (
107
  <DialogPrimitive.Description
108
  ref={ref}
109
- className={cn("text-sm text-zinc-500 dark:text-zinc-400", className)}
110
  {...props}
111
  />
112
  ))
 
25
  <DialogPrimitive.Overlay
26
  ref={ref}
27
  className={cn(
28
+ "fixed inset-0 z-50 bg-white/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 dark:bg-stone-950/80",
29
  className
30
  )}
31
  {...props}
 
42
  <DialogPrimitive.Content
43
  ref={ref}
44
  className={cn(
45
+ "fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-stone-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full dark:border-stone-800 dark:bg-stone-950",
46
  className
47
  )}
48
  {...props}
49
  >
50
  {children}
51
+ <DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-stone-100 data-[state=open]:text-stone-500 dark:ring-offset-stone-950 dark:focus:ring-stone-800 dark:data-[state=open]:bg-stone-800 dark:data-[state=open]:text-stone-400">
52
  <X className="h-4 w-4" />
53
  <span className="sr-only">Close</span>
54
  </DialogPrimitive.Close>
 
106
  >(({ className, ...props }, ref) => (
107
  <DialogPrimitive.Description
108
  ref={ref}
109
+ className={cn("text-sm text-stone-500 dark:text-stone-400", className)}
110
  {...props}
111
  />
112
  ))
src/components/ui/dropdown-menu.tsx CHANGED
@@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
27
  <DropdownMenuPrimitive.SubTrigger
28
  ref={ref}
29
  className={cn(
30
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-zinc-100 data-[state=open]:bg-zinc-100 dark:focus:bg-zinc-800 dark:data-[state=open]:bg-zinc-800",
31
  inset && "pl-8",
32
  className
33
  )}
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
47
  <DropdownMenuPrimitive.SubContent
48
  ref={ref}
49
  className={cn(
50
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
51
  className
52
  )}
53
  {...props}
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
65
  ref={ref}
66
  sideOffset={sideOffset}
67
  className={cn(
68
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
69
  className
70
  )}
71
  {...props}
@@ -83,7 +83,7 @@ const DropdownMenuItem = React.forwardRef<
83
  <DropdownMenuPrimitive.Item
84
  ref={ref}
85
  className={cn(
86
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
87
  inset && "pl-8",
88
  className
89
  )}
@@ -99,7 +99,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
99
  <DropdownMenuPrimitive.CheckboxItem
100
  ref={ref}
101
  className={cn(
102
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
103
  className
104
  )}
105
  checked={checked}
@@ -123,7 +123,7 @@ const DropdownMenuRadioItem = React.forwardRef<
123
  <DropdownMenuPrimitive.RadioItem
124
  ref={ref}
125
  className={cn(
126
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
127
  className
128
  )}
129
  {...props}
@@ -162,7 +162,7 @@ const DropdownMenuSeparator = React.forwardRef<
162
  >(({ className, ...props }, ref) => (
163
  <DropdownMenuPrimitive.Separator
164
  ref={ref}
165
- className={cn("-mx-1 my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
166
  {...props}
167
  />
168
  ))
 
27
  <DropdownMenuPrimitive.SubTrigger
28
  ref={ref}
29
  className={cn(
30
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-stone-100 data-[state=open]:bg-stone-100 dark:focus:bg-stone-800 dark:data-[state=open]:bg-stone-800",
31
  inset && "pl-8",
32
  className
33
  )}
 
47
  <DropdownMenuPrimitive.SubContent
48
  ref={ref}
49
  className={cn(
50
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
51
  className
52
  )}
53
  {...props}
 
65
  ref={ref}
66
  sideOffset={sideOffset}
67
  className={cn(
68
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
69
  className
70
  )}
71
  {...props}
 
83
  <DropdownMenuPrimitive.Item
84
  ref={ref}
85
  className={cn(
86
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
87
  inset && "pl-8",
88
  className
89
  )}
 
99
  <DropdownMenuPrimitive.CheckboxItem
100
  ref={ref}
101
  className={cn(
102
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
103
  className
104
  )}
105
  checked={checked}
 
123
  <DropdownMenuPrimitive.RadioItem
124
  ref={ref}
125
  className={cn(
126
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
127
  className
128
  )}
129
  {...props}
 
162
  >(({ className, ...props }, ref) => (
163
  <DropdownMenuPrimitive.Separator
164
  ref={ref}
165
+ className={cn("-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-800", className)}
166
  {...props}
167
  />
168
  ))
src/components/ui/input.tsx CHANGED
@@ -11,7 +11,7 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
11
  <input
12
  type={type}
13
  className={cn(
14
- "flex h-10 w-full rounded-md border border-zinc-200 border-zinc-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-zinc-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-zinc-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:border-zinc-800 dark:bg-zinc-950 dark:ring-offset-zinc-950 dark:placeholder:text-zinc-400 dark:focus-visible:ring-zinc-800",
15
  className
16
  )}
17
  ref={ref}
 
11
  <input
12
  type={type}
13
  className={cn(
14
+ "flex h-10 w-full rounded-md border border-stone-200 bg-white px-3 py-2 text-sm ring-offset-white file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-stone-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-800 dark:bg-stone-950 dark:ring-offset-stone-950 dark:placeholder:text-stone-400 dark:focus-visible:ring-stone-800",
15
  className
16
  )}
17
  ref={ref}
src/components/ui/menubar.tsx CHANGED
@@ -23,7 +23,7 @@ const Menubar = React.forwardRef<
23
  <MenubarPrimitive.Root
24
  ref={ref}
25
  className={cn(
26
- "flex h-10 items-center space-x-1 rounded-md border border-zinc-200 bg-white p-1 dark:border-zinc-800 dark:bg-zinc-950",
27
  className
28
  )}
29
  {...props}
@@ -38,7 +38,7 @@ const MenubarTrigger = React.forwardRef<
38
  <MenubarPrimitive.Trigger
39
  ref={ref}
40
  className={cn(
41
- "flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-900 dark:focus:bg-zinc-800 dark:focus:text-zinc-50 dark:data-[state=open]:bg-zinc-800 dark:data-[state=open]:text-zinc-50",
42
  className
43
  )}
44
  {...props}
@@ -55,7 +55,7 @@ const MenubarSubTrigger = React.forwardRef<
55
  <MenubarPrimitive.SubTrigger
56
  ref={ref}
57
  className={cn(
58
- "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[state=open]:bg-zinc-100 data-[state=open]:text-zinc-900 dark:focus:bg-zinc-800 dark:focus:text-zinc-50 dark:data-[state=open]:bg-zinc-800 dark:data-[state=open]:text-zinc-50",
59
  inset && "pl-8",
60
  className
61
  )}
@@ -74,7 +74,7 @@ const MenubarSubContent = React.forwardRef<
74
  <MenubarPrimitive.SubContent
75
  ref={ref}
76
  className={cn(
77
- "z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
78
  className
79
  )}
80
  {...props}
@@ -97,7 +97,7 @@ const MenubarContent = React.forwardRef<
97
  alignOffset={alignOffset}
98
  sideOffset={sideOffset}
99
  className={cn(
100
- "z-50 min-w-[12rem] overflow-hidden rounded-md border border-zinc-200 bg-white p-1 text-zinc-950 shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
101
  className
102
  )}
103
  {...props}
@@ -116,7 +116,7 @@ const MenubarItem = React.forwardRef<
116
  <MenubarPrimitive.Item
117
  ref={ref}
118
  className={cn(
119
- "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
120
  inset && "pl-8",
121
  className
122
  )}
@@ -132,7 +132,7 @@ const MenubarCheckboxItem = React.forwardRef<
132
  <MenubarPrimitive.CheckboxItem
133
  ref={ref}
134
  className={cn(
135
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
136
  className
137
  )}
138
  checked={checked}
@@ -155,7 +155,7 @@ const MenubarRadioItem = React.forwardRef<
155
  <MenubarPrimitive.RadioItem
156
  ref={ref}
157
  className={cn(
158
- "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
159
  className
160
  )}
161
  {...props}
@@ -194,7 +194,7 @@ const MenubarSeparator = React.forwardRef<
194
  >(({ className, ...props }, ref) => (
195
  <MenubarPrimitive.Separator
196
  ref={ref}
197
- className={cn("-mx-1 my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
198
  {...props}
199
  />
200
  ))
@@ -207,7 +207,7 @@ const MenubarShortcut = ({
207
  return (
208
  <span
209
  className={cn(
210
- "ml-auto text-xs tracking-widest text-zinc-500 dark:text-zinc-400",
211
  className
212
  )}
213
  {...props}
 
23
  <MenubarPrimitive.Root
24
  ref={ref}
25
  className={cn(
26
+ "flex h-10 items-center space-x-1 rounded-md border border-stone-200 bg-white p-1 dark:border-stone-800 dark:bg-stone-950",
27
  className
28
  )}
29
  {...props}
 
38
  <MenubarPrimitive.Trigger
39
  ref={ref}
40
  className={cn(
41
+ "flex cursor-default select-none items-center rounded-sm px-3 py-1.5 text-sm font-medium outline-none focus:bg-stone-100 focus:text-stone-900 data-[state=open]:bg-stone-100 data-[state=open]:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50 dark:data-[state=open]:bg-stone-800 dark:data-[state=open]:text-stone-50",
42
  className
43
  )}
44
  {...props}
 
55
  <MenubarPrimitive.SubTrigger
56
  ref={ref}
57
  className={cn(
58
+ "flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-stone-100 focus:text-stone-900 data-[state=open]:bg-stone-100 data-[state=open]:text-stone-900 dark:focus:bg-stone-800 dark:focus:text-stone-50 dark:data-[state=open]:bg-stone-800 dark:data-[state=open]:text-stone-50",
59
  inset && "pl-8",
60
  className
61
  )}
 
74
  <MenubarPrimitive.SubContent
75
  ref={ref}
76
  className={cn(
77
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
78
  className
79
  )}
80
  {...props}
 
97
  alignOffset={alignOffset}
98
  sideOffset={sideOffset}
99
  className={cn(
100
+ "z-50 min-w-[12rem] overflow-hidden rounded-md border border-stone-200 bg-white p-1 text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
101
  className
102
  )}
103
  {...props}
 
116
  <MenubarPrimitive.Item
117
  ref={ref}
118
  className={cn(
119
+ "relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
120
  inset && "pl-8",
121
  className
122
  )}
 
132
  <MenubarPrimitive.CheckboxItem
133
  ref={ref}
134
  className={cn(
135
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
136
  className
137
  )}
138
  checked={checked}
 
155
  <MenubarPrimitive.RadioItem
156
  ref={ref}
157
  className={cn(
158
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
159
  className
160
  )}
161
  {...props}
 
194
  >(({ className, ...props }, ref) => (
195
  <MenubarPrimitive.Separator
196
  ref={ref}
197
+ className={cn("-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-800", className)}
198
  {...props}
199
  />
200
  ))
 
207
  return (
208
  <span
209
  className={cn(
210
+ "ml-auto text-xs tracking-widest text-stone-500 dark:text-stone-400",
211
  className
212
  )}
213
  {...props}
src/components/ui/popover.tsx CHANGED
@@ -19,7 +19,7 @@ const PopoverContent = React.forwardRef<
19
  align={align}
20
  sideOffset={sideOffset}
21
  className={cn(
22
- "z-50 w-72 rounded-md border border-zinc-200 bg-white p-4 text-zinc-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
23
  className
24
  )}
25
  {...props}
 
19
  align={align}
20
  sideOffset={sideOffset}
21
  className={cn(
22
+ "z-50 w-72 rounded-md border border-stone-200 bg-white p-4 text-stone-950 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
23
  className
24
  )}
25
  {...props}
src/components/ui/select.tsx CHANGED
@@ -19,7 +19,7 @@ const SelectTrigger = React.forwardRef<
19
  <SelectPrimitive.Trigger
20
  ref={ref}
21
  className={cn(
22
- "flex h-10 w-full items-center justify-between rounded-md border border-zinc-200 border-zinc-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-zinc-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-800 dark:border-zinc-800 dark:ring-offset-zinc-950 dark:placeholder:text-zinc-400 dark:focus:ring-zinc-800",
23
  className
24
  )}
25
  {...props}
@@ -40,7 +40,7 @@ const SelectContent = React.forwardRef<
40
  <SelectPrimitive.Content
41
  ref={ref}
42
  className={cn(
43
- "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-zinc-200 bg-white text-zinc-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-50",
44
  position === "popper" &&
45
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
46
  className
@@ -81,7 +81,7 @@ const SelectItem = React.forwardRef<
81
  <SelectPrimitive.Item
82
  ref={ref}
83
  className={cn(
84
- "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-zinc-100 focus:text-zinc-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-zinc-800 dark:focus:text-zinc-50",
85
  className
86
  )}
87
  {...props}
@@ -103,7 +103,7 @@ const SelectSeparator = React.forwardRef<
103
  >(({ className, ...props }, ref) => (
104
  <SelectPrimitive.Separator
105
  ref={ref}
106
- className={cn("-mx-1 my-1 h-px bg-zinc-100 dark:bg-zinc-800", className)}
107
  {...props}
108
  />
109
  ))
 
19
  <SelectPrimitive.Trigger
20
  ref={ref}
21
  className={cn(
22
+ "flex h-10 w-full items-center justify-between rounded-md border border-stone-200 border-stone-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-stone-500 focus:outline-none focus:ring-2 focus:ring-stone-400 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-800 dark:border-stone-800 dark:ring-offset-stone-950 dark:placeholder:text-stone-400 dark:focus:ring-stone-800",
23
  className
24
  )}
25
  {...props}
 
40
  <SelectPrimitive.Content
41
  ref={ref}
42
  className={cn(
43
+ "relative z-50 min-w-[8rem] overflow-hidden rounded-md border border-stone-200 bg-white text-stone-950 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 dark:border-stone-800 dark:bg-stone-950 dark:text-stone-50",
44
  position === "popper" &&
45
  "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
46
  className
 
81
  <SelectPrimitive.Item
82
  ref={ref}
83
  className={cn(
84
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-stone-100 focus:text-stone-900 data-[disabled]:pointer-events-none data-[disabled]:opacity-50 dark:focus:bg-stone-800 dark:focus:text-stone-50",
85
  className
86
  )}
87
  {...props}
 
103
  >(({ className, ...props }, ref) => (
104
  <SelectPrimitive.Separator
105
  ref={ref}
106
+ className={cn("-mx-1 my-1 h-px bg-stone-100 dark:bg-stone-800", className)}
107
  {...props}
108
  />
109
  ))
src/components/ui/separator.tsx CHANGED
@@ -18,7 +18,7 @@ const Separator = React.forwardRef<
18
  decorative={decorative}
19
  orientation={orientation}
20
  className={cn(
21
- "shrink-0 bg-zinc-200 dark:bg-zinc-800",
22
  orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
23
  className
24
  )}
 
18
  decorative={decorative}
19
  orientation={orientation}
20
  className={cn(
21
+ "shrink-0 bg-stone-200 dark:bg-stone-800",
22
  orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]",
23
  className
24
  )}
src/components/ui/table.tsx CHANGED
@@ -42,7 +42,7 @@ const TableFooter = React.forwardRef<
42
  >(({ className, ...props }, ref) => (
43
  <tfoot
44
  ref={ref}
45
- className={cn("bg-zinc-900 font-medium text-zinc-50 dark:bg-zinc-50 dark:text-zinc-900", className)}
46
  {...props}
47
  />
48
  ))
@@ -55,7 +55,7 @@ const TableRow = React.forwardRef<
55
  <tr
56
  ref={ref}
57
  className={cn(
58
- "border-b transition-colors hover:bg-zinc-100/50 data-[state=selected]:bg-zinc-100 dark:hover:bg-zinc-800/50 dark:data-[state=selected]:bg-zinc-800",
59
  className
60
  )}
61
  {...props}
@@ -70,7 +70,7 @@ const TableHead = React.forwardRef<
70
  <th
71
  ref={ref}
72
  className={cn(
73
- "h-12 px-4 text-left align-middle font-medium text-zinc-500 [&:has([role=checkbox])]:pr-0 dark:text-zinc-400",
74
  className
75
  )}
76
  {...props}
@@ -96,7 +96,7 @@ const TableCaption = React.forwardRef<
96
  >(({ className, ...props }, ref) => (
97
  <caption
98
  ref={ref}
99
- className={cn("mt-4 text-sm text-zinc-500 dark:text-zinc-400", className)}
100
  {...props}
101
  />
102
  ))
 
42
  >(({ className, ...props }, ref) => (
43
  <tfoot
44
  ref={ref}
45
+ className={cn("bg-stone-900 font-medium text-stone-50 dark:bg-stone-50 dark:text-stone-900", className)}
46
  {...props}
47
  />
48
  ))
 
55
  <tr
56
  ref={ref}
57
  className={cn(
58
+ "border-b transition-colors hover:bg-stone-100/50 data-[state=selected]:bg-stone-100 dark:hover:bg-stone-800/50 dark:data-[state=selected]:bg-stone-800",
59
  className
60
  )}
61
  {...props}
 
70
  <th
71
  ref={ref}
72
  className={cn(
73
+ "h-12 px-4 text-left align-middle font-medium text-stone-500 [&:has([role=checkbox])]:pr-0 dark:text-stone-400",
74
  className
75
  )}
76
  {...props}
 
96
  >(({ className, ...props }, ref) => (
97
  <caption
98
  ref={ref}
99
+ className={cn("mt-4 text-sm text-stone-500 dark:text-stone-400", className)}
100
  {...props}
101
  />
102
  ))
src/components/ui/textarea.tsx ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as React from "react"
2
+
3
+ import { cn } from "@/lib/utils"
4
+
5
+ export interface TextareaProps
6
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
7
+
8
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
9
+ ({ className, ...props }, ref) => {
10
+ return (
11
+ <textarea
12
+ className={cn(
13
+ "flex min-h-[80px] w-full rounded-md border border-stone-200 border-stone-200 bg-transparent px-3 py-2 text-sm ring-offset-white placeholder:text-stone-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-stone-400 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:border-stone-800 dark:border-stone-800 dark:ring-offset-stone-950 dark:placeholder:text-stone-400 dark:focus-visible:ring-stone-800",
14
+ className
15
+ )}
16
+ ref={ref}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+ )
22
+ Textarea.displayName = "Textarea"
23
+
24
+ export { Textarea }
src/server/README.md ADDED
@@ -0,0 +1 @@
 
 
1
+ All those files and functions are server for server-side use only.
src/server/actions.ts ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { revalidatePath } from "next/cache"
4
+ import { submitNewTask } from "."
5
+
6
+ export async function formSubmit(formData: FormData) {
7
+ await submitNewTask({
8
+ prompt: `${formData.get("prompt") || ""}`,
9
+ ownerId: `${formData.get("ownerId") || ""}`,
10
+ })
11
+
12
+ // for doc see https://nextjs.org/docs/app/building-your-application/data-fetching/server-actions
13
+ revalidatePath('/')
14
+ }
src/{api β†’ server}/base.ts RENAMED
@@ -5,13 +5,18 @@ const apiUrl = process.env.VC_VIDEOCHAIN_API_URL
5
 
6
  export const get = async <T>(path: string = '', defaultValue: T): Promise<T> => {
7
  try {
 
8
  const res = await fetch(`${apiUrl}/${path}`, {
 
9
  headers: {
10
- method: "GET",
11
  Accept: "application/json",
12
  Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
13
- }
 
 
 
14
  })
 
15
  // The return value is *not* serialized
16
  // You can return Date, Map, Set, etc.
17
 
@@ -23,6 +28,40 @@ export const get = async <T>(path: string = '', defaultValue: T): Promise<T> =>
23
 
24
  const data = await res.json()
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  return ((data as T) || defaultValue)
27
  } catch (err) {
28
  console.error(err)
 
5
 
6
  export const get = async <T>(path: string = '', defaultValue: T): Promise<T> => {
7
  try {
8
+ console.log("fetching:", `${apiUrl}/${path} with Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`)
9
  const res = await fetch(`${apiUrl}/${path}`, {
10
+ method: "GET",
11
  headers: {
 
12
  Accept: "application/json",
13
  Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
14
+ },
15
+ cache: 'no-store',
16
+ // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
17
+ // next: { revalidate: 10 }
18
  })
19
+ console.log("res:", res)
20
  // The return value is *not* serialized
21
  // You can return Date, Map, Set, etc.
22
 
 
28
 
29
  const data = await res.json()
30
 
31
+ console.log("data:", data)
32
+ return ((data as T) || defaultValue)
33
+ } catch (err) {
34
+ console.error(err)
35
+ return defaultValue
36
+ }
37
+ }
38
+
39
+
40
+ export const post = async <S, T>(path: string = '', payload: S, defaultValue: T): Promise<T> => {
41
+ try {
42
+ const res = await fetch(`${apiUrl}/${path}`, {
43
+ method: "POST",
44
+ headers: {
45
+ Accept: "application/json",
46
+ "Content-Type": "application/json",
47
+ Authorization: `Bearer ${process.env.VC_SECRET_ACCESS_TOKEN}`,
48
+ },
49
+ body: JSON.stringify(payload),
50
+ cache: 'no-store',
51
+ // we can also use this (see https://vercel.com/blog/vercel-cache-api-nextjs-cache)
52
+ // next: { revalidate: 10 }
53
+ })
54
+ // The return value is *not* serialized
55
+ // You can return Date, Map, Set, etc.
56
+
57
+ // Recommendation: handle errors
58
+ if (res.status !== 200) {
59
+ // This will activate the closest `error.js` Error Boundary
60
+ throw new Error('Failed to post data')
61
+ }
62
+
63
+ const data = await res.json()
64
+
65
  return ((data as T) || defaultValue)
66
  } catch (err) {
67
  console.error(err)
src/server/index.ts ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use server"
2
+
3
+ import { VideoTask, VideoTaskRequest } from "@/app/types"
4
+
5
+ import { get, post } from "./base"
6
+
7
+ // note: for security purposes we do not directly expose the VideoChain API:
8
+ // all calls are protected with a token, that way it the VideooChain API can stay
9
+ // lightweight, security and quotas are handled outside
10
+
11
+ // attention: this return *ALL* pending tasks, including those of other users
12
+ export const getPendingTasks = async () => {
13
+ const tasks = await get<VideoTask[]>("", [])
14
+
15
+ return tasks
16
+ }
17
+
18
+ // return all tasks of a owner
19
+ export const getTasks = async (ownerId: string) => {
20
+ const tasks = await get<VideoTask[]>(`owner/${ownerId}`, [])
21
+
22
+ return tasks
23
+ }
24
+
25
+ export const getTask = async (ownerAndVideoId: string) => {
26
+ const task = await get<VideoTask>(ownerAndVideoId, null as unknown as VideoTask)
27
+
28
+ return task
29
+ }
30
+
31
+ export const submitNewTask = async (taskRequest: VideoTaskRequest) => {
32
+ const task = await post<VideoTaskRequest, VideoTask>(
33
+ "",
34
+ taskRequest,
35
+ null as unknown as VideoTask
36
+ )
37
+
38
+ return task
39
+ }
tsconfig.json CHANGED
@@ -1,6 +1,6 @@
1
  {
2
  "compilerOptions": {
3
- "target": "es5",
4
  "lib": ["dom", "dom.iterable", "esnext"],
5
  "allowJs": true,
6
  "skipLibCheck": true,
 
1
  {
2
  "compilerOptions": {
3
+ "target": "ES2022",
4
  "lib": ["dom", "dom.iterable", "esnext"],
5
  "allowJs": true,
6
  "skipLibCheck": true,