@tscircuit/fake-snippets 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (396) hide show
  1. package/.github/CODEOWNERS +1 -0
  2. package/.github/workflows/bun-formatcheck.yml +26 -0
  3. package/.github/workflows/bun-test.yml +28 -0
  4. package/.github/workflows/bun-typecheck.yml +26 -0
  5. package/.github/workflows/bundle-size-analysis.yml +79 -0
  6. package/.github/workflows/playwright-test.yml +37 -0
  7. package/.github/workflows/stale.yml +40 -0
  8. package/.github/workflows/update-snapshots.yml +49 -0
  9. package/CONTRIBUTING.md +59 -0
  10. package/LICENSE +21 -0
  11. package/README.md +113 -0
  12. package/biome.json +60 -0
  13. package/bun-tests/fake-snippets-api/fixtures/get-circuit-json.ts +148 -0
  14. package/bun-tests/fake-snippets-api/fixtures/get-test-server.ts +96 -0
  15. package/bun-tests/fake-snippets-api/fixtures/start-server.ts +44 -0
  16. package/bun-tests/fake-snippets-api/routes/accounts/get_account_balance.test.ts +18 -0
  17. package/bun-tests/fake-snippets-api/routes/health.test.ts +9 -0
  18. package/bun-tests/fake-snippets-api/routes/order_files/get.test.ts +48 -0
  19. package/bun-tests/fake-snippets-api/routes/order_files/upload.test.ts +77 -0
  20. package/bun-tests/fake-snippets-api/routes/orders/create.test.ts +19 -0
  21. package/bun-tests/fake-snippets-api/routes/orders/get.test.ts +38 -0
  22. package/bun-tests/fake-snippets-api/routes/orders/list.test.ts +30 -0
  23. package/bun-tests/fake-snippets-api/routes/orders/update.test.ts +46 -0
  24. package/bun-tests/fake-snippets-api/routes/snippets/add_star.test.ts +114 -0
  25. package/bun-tests/fake-snippets-api/routes/snippets/create.test.ts +28 -0
  26. package/bun-tests/fake-snippets-api/routes/snippets/delete.test.ts +106 -0
  27. package/bun-tests/fake-snippets-api/routes/snippets/download.test.ts +90 -0
  28. package/bun-tests/fake-snippets-api/routes/snippets/generate_from_jlcpcb.test.ts +25 -0
  29. package/bun-tests/fake-snippets-api/routes/snippets/get_image.test.ts +113 -0
  30. package/bun-tests/fake-snippets-api/routes/snippets/images.test.ts +112 -0
  31. package/bun-tests/fake-snippets-api/routes/snippets/list.test.ts +62 -0
  32. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +48 -0
  33. package/bun-tests/fake-snippets-api/routes/snippets/list_trending.test.ts +69 -0
  34. package/bun-tests/fake-snippets-api/routes/snippets/remove_star.test.ts +110 -0
  35. package/bun-tests/fake-snippets-api/routes/snippets/search.test.ts +75 -0
  36. package/bun-tests/fake-snippets-api/routes/snippets/star-count.test.ts +44 -0
  37. package/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +116 -0
  38. package/bun-tests/parts-engine.test.ts +18 -0
  39. package/bun.lockb +0 -0
  40. package/bunfig.toml +2 -0
  41. package/components.json +20 -0
  42. package/dist/assets/editor_example_1-1000w.webp +0 -0
  43. package/dist/assets/editor_example_1-1200w.webp +0 -0
  44. package/dist/assets/editor_example_1-1600w.webp +0 -0
  45. package/dist/assets/editor_example_1-2000w.webp +0 -0
  46. package/dist/assets/editor_example_1-400w.webp +0 -0
  47. package/dist/assets/editor_example_1-600w.webp +0 -0
  48. package/dist/assets/editor_example_1-800w.webp +0 -0
  49. package/dist/assets/editor_example_1_more_square-1000w.webp +0 -0
  50. package/dist/assets/editor_example_1_more_square-1200w.webp +0 -0
  51. package/dist/assets/editor_example_1_more_square-1600w.webp +0 -0
  52. package/dist/assets/editor_example_1_more_square-2000w.webp +0 -0
  53. package/dist/assets/editor_example_1_more_square-400w.webp +0 -0
  54. package/dist/assets/editor_example_1_more_square-600w.webp +0 -0
  55. package/dist/assets/editor_example_1_more_square-800w.webp +0 -0
  56. package/dist/assets/editor_example_2-1000w.webp +0 -0
  57. package/dist/assets/editor_example_2-1200w.webp +0 -0
  58. package/dist/assets/editor_example_2-1600w.webp +0 -0
  59. package/dist/assets/editor_example_2-2000w.webp +0 -0
  60. package/dist/assets/editor_example_2-400w.webp +0 -0
  61. package/dist/assets/editor_example_2-600w.webp +0 -0
  62. package/dist/assets/editor_example_2-800w.webp +0 -0
  63. package/dist/assets/example_schematic-1000w.webp +0 -0
  64. package/dist/assets/example_schematic-1200w.webp +0 -0
  65. package/dist/assets/example_schematic-1600w.webp +0 -0
  66. package/dist/assets/example_schematic-2000w.webp +0 -0
  67. package/dist/assets/example_schematic-400w.webp +0 -0
  68. package/dist/assets/example_schematic-600w.webp +0 -0
  69. package/dist/assets/example_schematic-800w.webp +0 -0
  70. package/dist/bundle.js +3270 -0
  71. package/dist/robots.txt +9 -0
  72. package/dist/sitemap.xml +118 -0
  73. package/docs/CIRCUIT_JSON_SOURCE_COMPONENT_OVERVIEW.md +151 -0
  74. package/fake-snippets-api/README.md +6 -0
  75. package/fake-snippets-api/biome.json +47 -0
  76. package/fake-snippets-api/bun.lockb +0 -0
  77. package/fake-snippets-api/lib/db/autoload-dev-snippets.ts +84 -0
  78. package/fake-snippets-api/lib/db/autoload-snippets.json +24 -0
  79. package/fake-snippets-api/lib/db/db-client.ts +343 -0
  80. package/fake-snippets-api/lib/db/schema.ts +112 -0
  81. package/fake-snippets-api/lib/db/seed.ts +1608 -0
  82. package/fake-snippets-api/lib/middleware/with-ctx-error.ts +26 -0
  83. package/fake-snippets-api/lib/middleware/with-db.ts +15 -0
  84. package/fake-snippets-api/lib/middleware/with-error-handling.ts +24 -0
  85. package/fake-snippets-api/lib/middleware/with-optional-session-auth.ts +34 -0
  86. package/fake-snippets-api/lib/middleware/with-request-logging.ts +54 -0
  87. package/fake-snippets-api/lib/middleware/with-session-auth.ts +39 -0
  88. package/fake-snippets-api/lib/middleware/with-winter-spec.ts +24 -0
  89. package/fake-snippets-api/next-env.d.ts +5 -0
  90. package/fake-snippets-api/routes/api/accounts/get.ts +21 -0
  91. package/fake-snippets-api/routes/api/accounts/get_account_balance.ts +22 -0
  92. package/fake-snippets-api/routes/api/accounts/update.ts +32 -0
  93. package/fake-snippets-api/routes/api/ai/[...anyroute].ts +31 -0
  94. package/fake-snippets-api/routes/api/ai.ts +2 -0
  95. package/fake-snippets-api/routes/api/aistream/[...anyroute].ts +65 -0
  96. package/fake-snippets-api/routes/api/health.ts +9 -0
  97. package/fake-snippets-api/routes/api/internal/sessions/create_without_auth.ts +63 -0
  98. package/fake-snippets-api/routes/api/order_files/get.ts +28 -0
  99. package/fake-snippets-api/routes/api/order_files/upload.ts +43 -0
  100. package/fake-snippets-api/routes/api/orders/create.ts +41 -0
  101. package/fake-snippets-api/routes/api/orders/get.ts +28 -0
  102. package/fake-snippets-api/routes/api/orders/list.ts +15 -0
  103. package/fake-snippets-api/routes/api/orders/update.ts +50 -0
  104. package/fake-snippets-api/routes/api/snippets/add_star.ts +42 -0
  105. package/fake-snippets-api/routes/api/snippets/create.ts +55 -0
  106. package/fake-snippets-api/routes/api/snippets/delete.ts +41 -0
  107. package/fake-snippets-api/routes/api/snippets/download.ts +148 -0
  108. package/fake-snippets-api/routes/api/snippets/generate_from_jlcpcb.ts +55 -0
  109. package/fake-snippets-api/routes/api/snippets/get.ts +50 -0
  110. package/fake-snippets-api/routes/api/snippets/get_image.ts +65 -0
  111. package/fake-snippets-api/routes/api/snippets/images/[author]/[snippet_name]/[typeFormat].ts +74 -0
  112. package/fake-snippets-api/routes/api/snippets/list.ts +28 -0
  113. package/fake-snippets-api/routes/api/snippets/list_newest.ts +13 -0
  114. package/fake-snippets-api/routes/api/snippets/list_trending.ts +21 -0
  115. package/fake-snippets-api/routes/api/snippets/remove_star.ts +42 -0
  116. package/fake-snippets-api/routes/api/snippets/search.ts +18 -0
  117. package/fake-snippets-api/routes/api/snippets/update.ts +86 -0
  118. package/favicon.ico +0 -0
  119. package/index.html +23 -0
  120. package/landing.html +23 -0
  121. package/package.json +164 -0
  122. package/playwright-tests/ai-page.spec.ts +19 -0
  123. package/playwright-tests/cmd-click.spec.ts +43 -0
  124. package/playwright-tests/dashboard-page.spec.ts +10 -0
  125. package/playwright-tests/editor-page.spec.ts +15 -0
  126. package/playwright-tests/files-dialog.spec.ts +19 -0
  127. package/playwright-tests/footprint-dialog/footprint-dialog.spec.ts +27 -0
  128. package/playwright-tests/footprint-dialog/footprint-insertion.spec.ts +38 -0
  129. package/playwright-tests/footprint-dialog/footprint-preview.spec.ts +34 -0
  130. package/playwright-tests/footprint-dialog/footprint-selection.spec.ts +29 -0
  131. package/playwright-tests/handle-manual-edits.spec.ts +55 -0
  132. package/playwright-tests/home-page.spec.ts +10 -0
  133. package/playwright-tests/images.spec.ts +17 -0
  134. package/playwright-tests/manual-edits.spec.ts +89 -0
  135. package/playwright-tests/preview-page.spec.ts +14 -0
  136. package/playwright-tests/quickstart-page.spec.ts +10 -0
  137. package/playwright-tests/search-links.spec.ts +21 -0
  138. package/playwright-tests/search.spec.ts +27 -0
  139. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-lg.png +0 -0
  140. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-md.png +0 -0
  141. package/playwright-tests/snapshots/ai-page.spec.ts-AI-Page-xs.png +0 -0
  142. package/playwright-tests/snapshots/cmd-click.spec.ts-underlined-imports.png +0 -0
  143. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-lg.png +0 -0
  144. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-md.png +0 -0
  145. package/playwright-tests/snapshots/dashboard-page.spec.ts-Dashboard-page-xs.png +0 -0
  146. package/playwright-tests/snapshots/editor-page.spec.ts-editor-with-snippet.png +0 -0
  147. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-lg.png +0 -0
  148. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-md.png +0 -0
  149. package/playwright-tests/snapshots/error-fallback.spec.ts-error-fallback-xs.png +0 -0
  150. package/playwright-tests/snapshots/files-dialog.spec.ts-view-snippet-files.png +0 -0
  151. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-lg.png +0 -0
  152. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-md.png +0 -0
  153. package/playwright-tests/snapshots/footprint-dialog/footprint-dialog.spec.ts-footprint-preview-xs.png +0 -0
  154. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-lg.png +0 -0
  155. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-md.png +0 -0
  156. package/playwright-tests/snapshots/footprint-dialog/footprint-insertion.spec.ts-footprint-insertion-xs.png +0 -0
  157. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-lg.png +0 -0
  158. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-md.png +0 -0
  159. package/playwright-tests/snapshots/footprint-dialog/footprint-preview.spec.ts-footprint-preview-xs.png +0 -0
  160. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-lg.png +0 -0
  161. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-md.png +0 -0
  162. package/playwright-tests/snapshots/footprint-dialog/footprint-selection.spec.ts-footprint-preview-xs.png +0 -0
  163. package/playwright-tests/snapshots/handle-manual-edits.spec.ts-handle-manual-edits.png +0 -0
  164. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-lg.png +0 -0
  165. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-md.png +0 -0
  166. package/playwright-tests/snapshots/home-page.spec.ts-Home-page-xs.png +0 -0
  167. package/playwright-tests/snapshots/images.spec.ts-pcb-image.png +0 -0
  168. package/playwright-tests/snapshots/images.spec.ts-schematic-image.png +0 -0
  169. package/playwright-tests/snapshots/manual-edits.spec.ts-editor-manual-edits.png +0 -0
  170. package/playwright-tests/snapshots/manual-edits.spec.ts-manual-edits-view.png +0 -0
  171. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-pcb.png +0 -0
  172. package/playwright-tests/snapshots/preview-page.spec.ts-preview-snippet-schematic.png +0 -0
  173. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagelg.png +0 -0
  174. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagemd.png +0 -0
  175. package/playwright-tests/snapshots/quickstart-page.spec.ts-Quickstart-Pagexs.png +0 -0
  176. package/playwright-tests/snapshots/search-links.spec.ts-search-links.png +0 -0
  177. package/playwright-tests/snapshots/search.spec.ts-search-lg.png +0 -0
  178. package/playwright-tests/snapshots/search.spec.ts-search-md.png +0 -0
  179. package/playwright-tests/snapshots/search.spec.ts-search-xs.png +0 -0
  180. package/playwright-tests/snapshots/star.spec.ts-remove-star-button.png +0 -0
  181. package/playwright-tests/snapshots/star.spec.ts-star-button.png +0 -0
  182. package/playwright-tests/snapshots/update-description.spec.ts-update-description.png +0 -0
  183. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-lg.png +0 -0
  184. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-md.png +0 -0
  185. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-after-xs.png +0 -0
  186. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-lg.png +0 -0
  187. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-md.png +0 -0
  188. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-before-xs.png +0 -0
  189. package/playwright-tests/snapshots/view-snippet.spec.ts-view-snippet-files.png +0 -0
  190. package/playwright-tests/star.spec.ts +40 -0
  191. package/playwright-tests/update-description.spec.ts +18 -0
  192. package/playwright-tests/view-snippet.spec.ts +35 -0
  193. package/playwright-tests/viewports.ts +5 -0
  194. package/playwright.config.ts +27 -0
  195. package/postcss.config.js +6 -0
  196. package/public/robots.txt +9 -0
  197. package/renovate.json +24 -0
  198. package/scripts/generate-image-sizes.ts +58 -0
  199. package/scripts/generate-sitemap.ts +103 -0
  200. package/scripts/generate_bundle_stats.js +192 -0
  201. package/scripts/snapshot.ts +35 -0
  202. package/src/App.tsx +113 -0
  203. package/src/ContextProviders.tsx +9 -0
  204. package/src/assets/originals/editor_example_1.webp +0 -0
  205. package/src/assets/originals/editor_example_1_more_square.webp +0 -0
  206. package/src/assets/originals/editor_example_2.webp +0 -0
  207. package/src/assets/originals/example_schematic.webp +0 -0
  208. package/src/components/AiChatInterface.tsx +221 -0
  209. package/src/components/AiChatMessage.tsx +86 -0
  210. package/src/components/Analytics.tsx +30 -0
  211. package/src/components/BomTable.tsx +69 -0
  212. package/src/components/ChatInput.tsx +53 -0
  213. package/src/components/CircuitToSvgWithMouseControl.tsx +78 -0
  214. package/src/components/CmdKMenu.tsx +301 -0
  215. package/src/components/CodeAndPreview.tsx +258 -0
  216. package/src/components/CodeEditor.tsx +460 -0
  217. package/src/components/CodeEditorHeader.tsx +160 -0
  218. package/src/components/CreateNewSnippetWithAiHero.tsx +77 -0
  219. package/src/components/DownloadButtonAndMenu.tsx +212 -0
  220. package/src/components/EditorNav.tsx +428 -0
  221. package/src/components/ErrorFallback.tsx +25 -0
  222. package/src/components/ErrorTabContent.tsx +122 -0
  223. package/src/components/FAQ.tsx +113 -0
  224. package/src/components/Footer.tsx +134 -0
  225. package/src/components/Footer2.tsx +100 -0
  226. package/src/components/FootprintDialog.tsx +342 -0
  227. package/src/components/Header.tsx +187 -0
  228. package/src/components/Header2.tsx +135 -0
  229. package/src/components/HeaderDropdown.tsx +83 -0
  230. package/src/components/HeaderLogin.tsx +94 -0
  231. package/src/components/JLCPCBImportDialog.tsx +155 -0
  232. package/src/components/LandingHero.tsx +175 -0
  233. package/src/components/LatestSnippets.tsx +39 -0
  234. package/src/components/OptimizedImage.tsx +96 -0
  235. package/src/components/OrderPreviewContent.tsx +61 -0
  236. package/src/components/ParametersEditor.tsx +140 -0
  237. package/src/components/PcbViewerWithContainerHeight.tsx +47 -0
  238. package/src/components/PrefetchPageLink.tsx +45 -0
  239. package/src/components/PreviewContent.tsx +375 -0
  240. package/src/components/PreviewEmptyState.tsx +16 -0
  241. package/src/components/RunButton.tsx +27 -0
  242. package/src/components/SearchComponent.tsx +163 -0
  243. package/src/components/ShippingInformationForm.tsx +423 -0
  244. package/src/components/SnippetLink.tsx +37 -0
  245. package/src/components/StaticPreviewContent.tsx +89 -0
  246. package/src/components/StaticViewSnippetHeader.tsx +70 -0
  247. package/src/components/StaticViewSnippetSidebar.tsx +100 -0
  248. package/src/components/TableViewer/CircuitJsonTableViewer.tsx +316 -0
  249. package/src/components/TableViewer/ClickableText.tsx +21 -0
  250. package/src/components/TableViewer/HeaderCell.tsx +27 -0
  251. package/src/components/TableViewer/Modal.tsx +39 -0
  252. package/src/components/TrendingSnippetCarousel.tsx +77 -0
  253. package/src/components/TypeBadge.tsx +31 -0
  254. package/src/components/ViewSnippetHeader.tsx +144 -0
  255. package/src/components/ViewSnippetSidebar.tsx +162 -0
  256. package/src/components/dialogs/confirm-delete-snippet-dialog.tsx +80 -0
  257. package/src/components/dialogs/create-order-dialog.tsx +146 -0
  258. package/src/components/dialogs/create-use-dialog.tsx +34 -0
  259. package/src/components/dialogs/edit-description-dialog.tsx +96 -0
  260. package/src/components/dialogs/files-dialog.tsx +70 -0
  261. package/src/components/dialogs/import-snippet-dialog.tsx +84 -0
  262. package/src/components/dialogs/rename-snippet-dialog.tsx +81 -0
  263. package/src/components/dialogs/view-ts-files-dialog.tsx +89 -0
  264. package/src/components/ui/accordion.tsx +55 -0
  265. package/src/components/ui/alert-dialog.tsx +139 -0
  266. package/src/components/ui/alert.tsx +59 -0
  267. package/src/components/ui/aspect-ratio.tsx +5 -0
  268. package/src/components/ui/avatar.tsx +48 -0
  269. package/src/components/ui/badge.tsx +36 -0
  270. package/src/components/ui/breadcrumb.tsx +118 -0
  271. package/src/components/ui/button.tsx +58 -0
  272. package/src/components/ui/calendar.tsx +73 -0
  273. package/src/components/ui/card.tsx +76 -0
  274. package/src/components/ui/carousel.tsx +260 -0
  275. package/src/components/ui/chart.tsx +363 -0
  276. package/src/components/ui/checkbox.tsx +28 -0
  277. package/src/components/ui/collapsible.tsx +9 -0
  278. package/src/components/ui/combobox.tsx +178 -0
  279. package/src/components/ui/command.tsx +151 -0
  280. package/src/components/ui/context-menu.tsx +202 -0
  281. package/src/components/ui/dialog.tsx +120 -0
  282. package/src/components/ui/drawer.tsx +116 -0
  283. package/src/components/ui/dropdown-menu.tsx +203 -0
  284. package/src/components/ui/form.tsx +182 -0
  285. package/src/components/ui/hover-card.tsx +27 -0
  286. package/src/components/ui/input-otp.tsx +69 -0
  287. package/src/components/ui/input.tsx +25 -0
  288. package/src/components/ui/label.tsx +24 -0
  289. package/src/components/ui/menubar.tsx +238 -0
  290. package/src/components/ui/navigation-menu.tsx +129 -0
  291. package/src/components/ui/pagination.tsx +121 -0
  292. package/src/components/ui/popover.tsx +31 -0
  293. package/src/components/ui/progress.tsx +26 -0
  294. package/src/components/ui/radio-group.tsx +42 -0
  295. package/src/components/ui/resizable.tsx +43 -0
  296. package/src/components/ui/scroll-area.tsx +46 -0
  297. package/src/components/ui/searchable-select.tsx +94 -0
  298. package/src/components/ui/select.tsx +162 -0
  299. package/src/components/ui/separator.tsx +29 -0
  300. package/src/components/ui/sheet.tsx +141 -0
  301. package/src/components/ui/skeleton.tsx +18 -0
  302. package/src/components/ui/slider.tsx +26 -0
  303. package/src/components/ui/sonner.tsx +30 -0
  304. package/src/components/ui/switch.tsx +27 -0
  305. package/src/components/ui/table.tsx +120 -0
  306. package/src/components/ui/tabs.tsx +53 -0
  307. package/src/components/ui/textarea.tsx +24 -0
  308. package/src/components/ui/toast.tsx +128 -0
  309. package/src/components/ui/toaster.tsx +33 -0
  310. package/src/components/ui/toggle-group.tsx +59 -0
  311. package/src/components/ui/toggle.tsx +43 -0
  312. package/src/components/ui/tooltip.tsx +28 -0
  313. package/src/entry-server.tsx +12 -0
  314. package/src/hooks/use-account-balance.ts +24 -0
  315. package/src/hooks/use-ai-api.ts +35 -0
  316. package/src/hooks/use-axios.ts +20 -0
  317. package/src/hooks/use-code-completion-ai-api.ts +11 -0
  318. package/src/hooks/use-compiled-tsx.ts +37 -0
  319. package/src/hooks/use-copy-to-clipboard.ts +26 -0
  320. package/src/hooks/use-create-snippet-mutation.ts +62 -0
  321. package/src/hooks/use-current-snippet-id.ts +75 -0
  322. package/src/hooks/use-current-snippet.ts +24 -0
  323. package/src/hooks/use-global-store.ts +33 -0
  324. package/src/hooks/use-is-using-fake-api.ts +3 -0
  325. package/src/hooks/use-run-tsx/construct-circuit.tsx +64 -0
  326. package/src/hooks/use-run-tsx/eval-compiled-js.ts +9 -0
  327. package/src/hooks/use-run-tsx/index.tsx +251 -0
  328. package/src/hooks/use-save-snippet.ts +66 -0
  329. package/src/hooks/use-sign-in.ts +22 -0
  330. package/src/hooks/use-snippet-by-name.ts +25 -0
  331. package/src/hooks/use-snippet.ts +23 -0
  332. package/src/hooks/use-snippets-base-api-url.ts +3 -0
  333. package/src/hooks/use-toast.tsx +208 -0
  334. package/src/hooks/use-typecheck.ts +54 -0
  335. package/src/hooks/use-url-params.ts +31 -0
  336. package/src/hooks/use-warn-user-on-page-change.ts +23 -0
  337. package/src/hooks/useForkSnippetMutation.ts +52 -0
  338. package/src/index.css +21 -0
  339. package/src/lib/__tests__/constants.test.ts +66 -0
  340. package/src/lib/base64ToBytes.ts +5 -0
  341. package/src/lib/bytesToBase64.ts +4 -0
  342. package/src/lib/codemirror/basic-setup.ts +51 -0
  343. package/src/lib/constants.ts +12 -0
  344. package/src/lib/decodeUrlHashToText.ts +15 -0
  345. package/src/lib/defaultCodeForBlankCode.tsx +7 -0
  346. package/src/lib/download-fns/createBlobURL.ts +4 -0
  347. package/src/lib/download-fns/download-assembly-svg.ts +12 -0
  348. package/src/lib/download-fns/download-circuit-json-fn.ts +12 -0
  349. package/src/lib/download-fns/download-dsn-file-fn.ts +12 -0
  350. package/src/lib/download-fns/download-fabrication-files.ts +233 -0
  351. package/src/lib/download-fns/download-gltf.ts +49 -0
  352. package/src/lib/download-fns/download-kicad-files.ts +27 -0
  353. package/src/lib/download-fns/download-readable-netlist.ts +12 -0
  354. package/src/lib/download-fns/download-schematic-svg.ts +12 -0
  355. package/src/lib/download-fns/download-simple-route-json.ts +17 -0
  356. package/src/lib/encodeTextToUrlHash.ts +17 -0
  357. package/src/lib/get-snippet-template.ts +26 -0
  358. package/src/lib/handleManualEditsImport.tsx +65 -0
  359. package/src/lib/jlc-parts-engine.ts +69 -0
  360. package/src/lib/templates/blank-3d-model-template.ts +12 -0
  361. package/src/lib/templates/blank-circuit-board-template.ts +24 -0
  362. package/src/lib/templates/blank-footprint-template.ts +29 -0
  363. package/src/lib/templates/blank-package-template.ts +22 -0
  364. package/src/lib/templates/blinking-led-board-template.ts +63 -0
  365. package/src/lib/templates/manual-edits-template.ts +5 -0
  366. package/src/lib/templates/usb-c-led-flashlight-template.ts +22 -0
  367. package/src/lib/utils/checkIfManualEditsImported.ts +6 -0
  368. package/src/lib/utils/getSyntaxError.ts +13 -0
  369. package/src/lib/utils/index.ts +6 -0
  370. package/src/lib/utils/load-prettier.ts +18 -0
  371. package/src/lib/utils/parseFootprintParams.ts +52 -0
  372. package/src/lib/utils/parseJsonOrNull.ts +8 -0
  373. package/src/lib/utils/pcbManualEditEventHandler.ts +156 -0
  374. package/src/main.tsx +10 -0
  375. package/src/pages/ai.tsx +92 -0
  376. package/src/pages/authorize.tsx +54 -0
  377. package/src/pages/dashboard.tsx +165 -0
  378. package/src/pages/dev-login.tsx +68 -0
  379. package/src/pages/editor.tsx +28 -0
  380. package/src/pages/landing.tsx +236 -0
  381. package/src/pages/my-orders.tsx +54 -0
  382. package/src/pages/newest.tsx +16 -0
  383. package/src/pages/preview.tsx +70 -0
  384. package/src/pages/quickstart.tsx +198 -0
  385. package/src/pages/search.tsx +26 -0
  386. package/src/pages/settings.tsx +25 -0
  387. package/src/pages/user-profile.tsx +97 -0
  388. package/src/pages/view-order.tsx +123 -0
  389. package/src/pages/view-snippet.tsx +149 -0
  390. package/src/prettier.ts +9 -0
  391. package/src/vite-env.d.ts +1 -0
  392. package/tailwind.config.js +47 -0
  393. package/tsconfig.json +30 -0
  394. package/vercel.json +50 -0
  395. package/vite.config.ts +146 -0
  396. package/winterspec.config.ts +6 -0
@@ -0,0 +1,77 @@
1
+ import { useQuery } from "react-query"
2
+ import { useAxios } from "@/hooks/use-axios"
3
+ import { StarFilledIcon } from "@radix-ui/react-icons"
4
+ import { Link } from "wouter"
5
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
6
+ import { useEffect, useRef, useState } from "react"
7
+ import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
8
+ import { OptimizedImage } from "./OptimizedImage"
9
+
10
+ export const TrendingSnippetCarousel = () => {
11
+ const axios = useAxios()
12
+ const scrollRef = useRef<HTMLDivElement>(null)
13
+ const [isHovered, setIsHovered] = useState(false)
14
+ const apiBaseUrl = useSnippetsBaseApiUrl()
15
+
16
+ const { data: trendingSnippets } = useQuery<Snippet[]>(
17
+ "trendingSnippets",
18
+ async () => {
19
+ const response = await axios.get("/snippets/list_trending")
20
+ return response.data.snippets
21
+ },
22
+ {
23
+ refetchOnWindowFocus: false,
24
+ refetchOnMount: false,
25
+ refetchOnReconnect: false,
26
+ staleTime: 1000 * 60 * 60 * 24, // 24 hours
27
+ },
28
+ )
29
+
30
+ return (
31
+ <div className="w-full bg-gray-50 py-8 min-h-[280px]">
32
+ {trendingSnippets?.length ? (
33
+ <>
34
+ <div className="container mx-auto px-4">
35
+ <h2 className="text-2xl font-semibold mb-6">Trending Snippets</h2>
36
+ </div>
37
+ <div
38
+ className="flex gap-6 overflow-x-hidden relative"
39
+ onMouseEnter={() => setIsHovered(true)}
40
+ onMouseLeave={() => setIsHovered(false)}
41
+ >
42
+ <div
43
+ ref={scrollRef}
44
+ className="flex gap-6 transition-transform duration-1000 animate-carousel-left"
45
+ >
46
+ {[...(trendingSnippets ?? []), ...(trendingSnippets ?? [])].map(
47
+ (snippet, i) => (
48
+ <Link
49
+ key={`${snippet.snippet_id}-${i}`}
50
+ href={`/${snippet.owner_name}/${snippet.unscoped_name}`}
51
+ >
52
+ <div className="flex-shrink-0 w-[200px] bg-white p-3 py-2 rounded-lg shadow-sm border border-gray-200 hover:border-gray-300 transition-colors">
53
+ <div className="font-medium text-blue-600 mb-1 truncate text-sm">
54
+ {snippet.owner_name}/{snippet.unscoped_name}
55
+ </div>
56
+ <div className="mb-2 h-24 w-full bg-black rounded overflow-hidden">
57
+ <OptimizedImage
58
+ src={`${apiBaseUrl}/snippets/images/${snippet.owner_name}/${snippet.unscoped_name}/pcb.svg`}
59
+ alt="PCB preview"
60
+ className="w-full h-full object-contain p-2 scale-[3] rotate-45 hover:scale-[3.5] transition-transform"
61
+ />
62
+ </div>
63
+ <div className="flex items-center text-xs text-gray-500">
64
+ <StarFilledIcon className="w-3 h-3 mr-1" />
65
+ {snippet.star_count || 0} stars
66
+ </div>
67
+ </div>
68
+ </Link>
69
+ ),
70
+ )}
71
+ </div>
72
+ </div>
73
+ </>
74
+ ) : null}
75
+ </div>
76
+ )
77
+ }
@@ -0,0 +1,31 @@
1
+ import React from "react"
2
+ import { cn } from "@/lib/utils"
3
+
4
+ type BadgeType = "board" | "package" | "footprint" | "model"
5
+
6
+ interface TypeBadgeProps {
7
+ type: string | BadgeType
8
+ className?: string
9
+ }
10
+
11
+ const typeColors: Record<BadgeType, string> = {
12
+ board: "bg-blue-500",
13
+ package: "bg-green-500",
14
+ footprint: "bg-purple-500",
15
+ model: "bg-indigo-500",
16
+ }
17
+
18
+ export const TypeBadge: React.FC<TypeBadgeProps> = ({ type, className }) => {
19
+ if (!type) return null
20
+ return (
21
+ <span
22
+ className={cn(
23
+ "text-white px-2 py-1 rounded text-xs font-semibold",
24
+ typeColors[type as BadgeType],
25
+ className,
26
+ )}
27
+ >
28
+ {type.toUpperCase()}
29
+ </span>
30
+ )
31
+ }
@@ -0,0 +1,144 @@
1
+ import { TypeBadge } from "@/components/TypeBadge"
2
+ import { Button } from "@/components/ui/button"
3
+ import { useAxios } from "@/hooks/use-axios"
4
+ import { useCurrentSnippet } from "@/hooks/use-current-snippet"
5
+ import { useGlobalStore } from "@/hooks/use-global-store"
6
+ import { toast, useToast } from "@/hooks/use-toast"
7
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
8
+ import { ChevronLeft, Eye, GitFork, Star } from "lucide-react"
9
+ import { useEffect, useState } from "react"
10
+ import { useMutation, useQueryClient } from "react-query"
11
+ import { Link } from "wouter"
12
+ import { navigate } from "wouter/use-browser-location"
13
+
14
+ export default function ViewSnippetHeader() {
15
+ const { snippet } = useCurrentSnippet()
16
+ const axios = useAxios()
17
+ const qc = useQueryClient()
18
+ const session = useGlobalStore((s) => s.session)
19
+ const [isStarred, setIsStarred] = useState(snippet?.is_starred || false)
20
+
21
+ const useForkSnippetMutation = ({
22
+ snippet,
23
+ onSuccess,
24
+ }: {
25
+ snippet: Snippet
26
+ onSuccess?: (forkedSnippet: Snippet) => void
27
+ }) => {
28
+ const axios = useAxios()
29
+ const session = useGlobalStore((s) => s.session)
30
+
31
+ return useMutation(
32
+ ["createForkSnippet"],
33
+ async () => {
34
+ if (!session) throw new Error("No session")
35
+ if (!snippet) throw new Error("No snippet to fork")
36
+
37
+ const { data } = await axios.post("/snippets/create", {
38
+ unscoped_name: snippet.unscoped_name,
39
+ snippet_type: snippet.snippet_type,
40
+ owner_name: session.github_username,
41
+ code: snippet.code,
42
+ })
43
+ return data.snippet
44
+ },
45
+ {
46
+ onSuccess: (forkedSnippet: Snippet) => {
47
+ toast({
48
+ title: `Forked snippet`,
49
+ description: `You have successfully forked the snippet. Redirecting...`,
50
+ })
51
+ onSuccess?.(forkedSnippet)
52
+ },
53
+ onError: (error: any) => {
54
+ console.error("Error forking snippet:", error)
55
+ },
56
+ },
57
+ )
58
+ }
59
+
60
+ const { mutate: forkSnippet, isLoading: isForking } = useForkSnippetMutation({
61
+ snippet: snippet!,
62
+ onSuccess: (forkedSnippet) => {
63
+ navigate("/editor?snippet_id=" + forkedSnippet.snippet_id)
64
+ },
65
+ })
66
+ const handleStarClick = async () => {
67
+ try {
68
+ if (isStarred) {
69
+ await axios.post("/snippets/remove_star", {
70
+ snippet_id: snippet!.snippet_id,
71
+ })
72
+ setIsStarred(false)
73
+ toast({
74
+ title: "Unstarred!",
75
+ description: "You've unstarred this snippet",
76
+ })
77
+ } else {
78
+ await axios.post("/snippets/add_star", {
79
+ snippet_id: snippet!.snippet_id,
80
+ })
81
+ setIsStarred(true)
82
+ toast({
83
+ title: "Starred!",
84
+ description: "You've starred this snippet",
85
+ })
86
+ }
87
+ qc.invalidateQueries(["snippets", snippet!.snippet_id])
88
+ } catch (error: any) {
89
+ toast({
90
+ title: "Error",
91
+ description: `Failed to ${isStarred ? "unstar" : "star"} snippet`,
92
+ variant: "destructive",
93
+ })
94
+ }
95
+ }
96
+
97
+ return (
98
+ <header className="bg-white border-b border-gray-200 py-4 px-6">
99
+ <div className="flex items-center justify-between">
100
+ <div className="flex items-center">
101
+ <h1 className="text-xl font-bold mr-2">
102
+ <Link href={`/${snippet?.owner_name}`} className="text-blue-600">
103
+ {snippet?.owner_name}
104
+ </Link>
105
+ <span className="px-1 text-gray-500">/</span>
106
+ <Link
107
+ className="text-blue-600"
108
+ href={`/${snippet?.owner_name}/${snippet?.unscoped_name}`}
109
+ >
110
+ {snippet?.unscoped_name}
111
+ </Link>
112
+ </h1>
113
+ {snippet?.snippet_type && <TypeBadge type={snippet.snippet_type} />}
114
+ </div>
115
+ <div className="flex items-center space-x-2">
116
+ <Button variant="outline" size="sm" onClick={handleStarClick}>
117
+ <Star
118
+ className={`w-4 h-4 mr-2 ${isStarred ? "fill-yellow-500 text-yellow-500" : ""}`}
119
+ />
120
+ {isStarred ? "Starred" : "Star"}
121
+ {snippet!.star_count > 0 && (
122
+ <span className="ml-1.5 bg-gray-100 text-gray-700 rounded-full px-1.5 py-0.5 text-xs font-medium">
123
+ {snippet!.star_count}
124
+ </span>
125
+ )}
126
+ </Button>
127
+ {/* <Button variant="outline" size="sm">
128
+ <Eye className="w-4 h-4 mr-2" />
129
+ Watch
130
+ </Button> */}
131
+
132
+ <Button variant="outline" size="sm" onClick={() => forkSnippet()}>
133
+ <GitFork className="w-4 h-4 mr-2" />
134
+ {snippet?.owner_name === session?.github_username ? "Save" : "Fork"}
135
+ </Button>
136
+ </div>
137
+ </div>
138
+ {/* <div className="mt-4 flex justify-end items-center text-sm text-gray-500">
139
+ <span className="mr-4">Last updated: 2 days ago</span>
140
+ <span>Version: 1.0.0</span>
141
+ </div> */}
142
+ </header>
143
+ )
144
+ }
@@ -0,0 +1,162 @@
1
+ import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard"
2
+ import { useCurrentSnippet } from "@/hooks/use-current-snippet"
3
+ import { useToast } from "@/hooks/use-toast"
4
+ import { cn } from "@/lib/utils"
5
+ import { AtSign, Bot, Clock, Code, File, GitFork, Package } from "lucide-react"
6
+ import { useFilesDialog } from "./dialogs/files-dialog"
7
+ import { PrefetchPageLink } from "./PrefetchPageLink"
8
+
9
+ export default function ViewSnippetSidebar({
10
+ className,
11
+ }: {
12
+ className?: string
13
+ }) {
14
+ const { snippet } = useCurrentSnippet()
15
+ const { toast } = useToast()
16
+ const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog()
17
+ const { copyToClipboard } = useCopyToClipboard()
18
+
19
+ return (
20
+ <div
21
+ className={cn(
22
+ "w-64 h-full bg-gray-100 text-gray-700 flex flex-col flex-shrink-0",
23
+ "hidden sm:block h-screen sticky top-0",
24
+ className,
25
+ )}
26
+ >
27
+ <nav className="flex-grow overflow-y-auto">
28
+ <ul className="p-2 space-y-2">
29
+ {[
30
+ {
31
+ icon: <Code className="w-5 h-5" />,
32
+ label: "Edit Code",
33
+ href: `/editor?snippet_id=${snippet?.snippet_id}`,
34
+ },
35
+ {
36
+ icon: <Bot className="w-5 h-5" />,
37
+ label: "Edit with AI",
38
+ badge: "AI",
39
+ href: `/ai?snippet_id=${snippet?.snippet_id}`,
40
+ },
41
+ // {
42
+ // icon: <GitHubLogoIcon className="w-5 h-5" />,
43
+ // label: "Github",
44
+ // },
45
+ {
46
+ icon: <GitFork className="w-5 h-5" />,
47
+ label: "Forks",
48
+ notImplemented: true,
49
+ },
50
+ {
51
+ icon: <AtSign className="w-5 h-5" />,
52
+ label: "References",
53
+ notImplemented: true,
54
+ },
55
+ {
56
+ icon: <Package className="w-5 h-5" />,
57
+ label: "Dependencies",
58
+ notImplemented: true,
59
+ },
60
+ {
61
+ icon: <Clock className="w-5 h-5" />,
62
+ label: "Versions",
63
+ notImplemented: true,
64
+ },
65
+ {
66
+ icon: <File className="w-5 h-5" />,
67
+ label: "Files",
68
+ onClick: () => {
69
+ if (snippet) {
70
+ openFilesDialog()
71
+ }
72
+ },
73
+ },
74
+ // { icon: <Settings className="w-5 h-5" />, label: "Settings" },
75
+ ].map((item, index) => (
76
+ <li key={index}>
77
+ <PrefetchPageLink
78
+ href={item.href ?? "#"}
79
+ onClick={
80
+ item.notImplemented
81
+ ? () => {
82
+ toast({
83
+ title: "Not Implemented!",
84
+ description: (
85
+ <div>
86
+ The {item.label} selection is not implemented yet.
87
+ Help us out!{" "}
88
+ <a
89
+ className="text-blue-500 hover:underline font-semibold"
90
+ href="https://github.com/tscircuit/snippets"
91
+ >
92
+ Check out our Github
93
+ </a>
94
+ </div>
95
+ ),
96
+ })
97
+ }
98
+ : item.onClick
99
+ }
100
+ className="flex items-center gap-3 px-2 py-1.5 hover:bg-gray-200 rounded-md"
101
+ >
102
+ {item.icon}
103
+ <span className="text-sm">{item.label}</span>
104
+ {item.badge && (
105
+ <span className="ml-auto bg-blue-500 text-white text-xs px-1.5 py-0.5 rounded">
106
+ {item.badge}
107
+ </span>
108
+ )}
109
+ </PrefetchPageLink>
110
+ </li>
111
+ ))}
112
+ </ul>
113
+ </nav>
114
+ <div className="p-4 border-t border-gray-200 space-y-4">
115
+ <div className="space-y-1">
116
+ <div className="text-xs font-medium">Copy embed code</div>
117
+ <div
118
+ className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
119
+ onClick={() => {
120
+ const embedCode = `<iframe src="${window.location.origin}/preview?snippet_id=${snippet?.snippet_id}" width="100%" height="500" frameborder="0"></iframe>`
121
+ navigator.clipboard.writeText(embedCode)
122
+ toast({
123
+ title: "Copied!",
124
+ description: "Embed code copied to clipboard",
125
+ })
126
+ }}
127
+ >
128
+ {`<iframe src="${window.location.origin}/preview?snippet_id=${snippet?.snippet_id}" width="100%" height="500" frameborder="0"></iframe>`}
129
+ </div>
130
+ </div>
131
+ <div className="space-y-1">
132
+ <div className="text-xs font-medium">Copy import code</div>
133
+ <div
134
+ className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
135
+ onClick={() =>
136
+ copyToClipboard(
137
+ `import CircuitModule from "@tsci/${snippet?.owner_name}.${snippet?.unscoped_name}"`,
138
+ )
139
+ }
140
+ >
141
+ import CircuitModule from "@tsci/{snippet?.owner_name}.
142
+ {snippet?.unscoped_name}"
143
+ </div>
144
+ </div>
145
+ <div className="space-y-1">
146
+ <div className="text-xs font-medium">Copy install command</div>
147
+ <div
148
+ className="text-[0.5em] p-2 rounded-sm bg-blue-50 border border-blue-200 cursor-pointer font-mono whitespace-nowrap overflow-hidden text-ellipsis"
149
+ onClick={() =>
150
+ copyToClipboard(
151
+ `tsci add @tsci/${snippet?.owner_name}.${snippet?.unscoped_name}`,
152
+ )
153
+ }
154
+ >
155
+ tsci add @tsci/{snippet?.owner_name}.{snippet?.unscoped_name}
156
+ </div>
157
+ </div>
158
+ </div>
159
+ {snippet && <FilesDialog snippetId={snippet.snippet_id} />}
160
+ </div>
161
+ )
162
+ }
@@ -0,0 +1,80 @@
1
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
2
+ import { Button } from "../ui/button"
3
+ import { useState } from "react"
4
+ import { createUseDialog } from "./create-use-dialog"
5
+ import { useAxios } from "@/hooks/use-axios"
6
+ import { useToast } from "@/hooks/use-toast"
7
+ import { useQueryClient } from "react-query"
8
+ import { useLocation } from "wouter"
9
+
10
+ export const ConfirmDeleteSnippetDialog = ({
11
+ open,
12
+ onOpenChange,
13
+ snippetId,
14
+ snippetName,
15
+ }: {
16
+ open: boolean
17
+ onOpenChange: (open: boolean) => void
18
+ snippetId: string
19
+ snippetName: string
20
+ }) => {
21
+ const axios = useAxios()
22
+ const { toast } = useToast()
23
+ const qc = useQueryClient()
24
+ const [pending, setPending] = useState(false)
25
+ const [, navigate] = useLocation()
26
+
27
+ const handleDelete = async () => {
28
+ try {
29
+ setPending(true)
30
+ await axios.post("/snippets/delete", {
31
+ snippet_id: snippetId,
32
+ })
33
+ onOpenChange(false)
34
+ setPending(false)
35
+ toast({
36
+ title: "Snippet deleted",
37
+ description: `Successfully deleted "${snippetName}"`,
38
+ })
39
+ qc.invalidateQueries({ queryKey: ["snippets"] })
40
+ navigate("/dashboard")
41
+ } catch (error) {
42
+ console.error("Error deleting snippet:", error)
43
+ toast({
44
+ title: "Error",
45
+ description: "Failed to delete the snippet. Please try again.",
46
+ variant: "destructive",
47
+ })
48
+ } finally {
49
+ setPending(false)
50
+ }
51
+ }
52
+
53
+ return (
54
+ <Dialog open={open} onOpenChange={onOpenChange}>
55
+ <DialogContent>
56
+ <DialogHeader>
57
+ <DialogTitle>Confirm Delete Snippet</DialogTitle>
58
+ </DialogHeader>
59
+ <p>Are you sure you want to delete the snippet "{snippetName}"?</p>
60
+ <p>This action cannot be undone.</p>
61
+ <div className="flex justify-end space-x-2 mt-4">
62
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
63
+ Cancel
64
+ </Button>
65
+ <Button
66
+ variant="destructive"
67
+ onClick={handleDelete}
68
+ disabled={pending}
69
+ >
70
+ {pending ? "Deleting..." : "Delete"}
71
+ </Button>
72
+ </div>
73
+ </DialogContent>
74
+ </Dialog>
75
+ )
76
+ }
77
+
78
+ export const useConfirmDeleteSnippetDialog = createUseDialog(
79
+ ConfirmDeleteSnippetDialog,
80
+ )
@@ -0,0 +1,146 @@
1
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
2
+ import { Button } from "../ui/button"
3
+ import { Checkbox } from "../ui/checkbox"
4
+ import { useState, useEffect } from "react"
5
+ import { createUseDialog } from "./create-use-dialog"
6
+ import { useAxios } from "@/hooks/use-axios"
7
+ import { useToast } from "@/hooks/use-toast"
8
+ import { useQueryClient } from "react-query"
9
+
10
+ export const CreateOrderDialog = ({
11
+ open,
12
+ onOpenChange,
13
+ }: {
14
+ open: boolean
15
+ onOpenChange: (open: boolean) => void
16
+ }) => {
17
+ const axios = useAxios()
18
+ const { toast } = useToast()
19
+ const qc = useQueryClient()
20
+ const [pending, setPending] = useState(false)
21
+ const [checkpoints, setCheckpoints] = useState({
22
+ shipping: false,
23
+ errors: false,
24
+ parts: false,
25
+ })
26
+
27
+ useEffect(() => {
28
+ if (open) {
29
+ validateCheckpoints()
30
+ }
31
+ }, [open])
32
+
33
+ const validateCheckpoints = async () => {
34
+ try {
35
+ // Placeholder: Check if shipping information is in profile
36
+ const hasShippingInfo = await checkShippingInfo()
37
+
38
+ // Placeholder: Check if PCB has no errors
39
+ const hasNoErrors = await checkPCBErrors()
40
+
41
+ // Placeholder: Check if all parts are available at PCB fab
42
+ const allPartsAvailable = await checkPartsAvailability()
43
+
44
+ setCheckpoints({
45
+ shipping: hasShippingInfo,
46
+ errors: hasNoErrors,
47
+ parts: allPartsAvailable,
48
+ })
49
+ } catch (error) {
50
+ console.error("Error validating checkpoints:", error)
51
+ }
52
+ }
53
+
54
+ const checkShippingInfo = async () => {
55
+ // Placeholder: Implement actual check for shipping info
56
+ return true
57
+ }
58
+
59
+ const checkPCBErrors = async () => {
60
+ // Placeholder: Implement actual check for PCB errors
61
+ return true
62
+ }
63
+
64
+ const checkPartsAvailability = async () => {
65
+ // Placeholder: Implement actual check for parts availability
66
+ return true
67
+ }
68
+
69
+ const handleSubmit = async () => {
70
+ try {
71
+ setPending(true)
72
+ // TODO: Implement order submission logic
73
+ onOpenChange(false)
74
+ setPending(false)
75
+ toast({
76
+ title: "Order submitted",
77
+ description: "Your order has been successfully submitted.",
78
+ })
79
+ qc.invalidateQueries({ queryKey: ["orders"] })
80
+ } catch (error) {
81
+ console.error("Error submitting order:", error)
82
+ toast({
83
+ title: "Error",
84
+ description: "Failed to submit the order. Please try again.",
85
+ variant: "destructive",
86
+ })
87
+ } finally {
88
+ setPending(false)
89
+ }
90
+ }
91
+
92
+ return (
93
+ <Dialog open={open} onOpenChange={onOpenChange}>
94
+ <DialogContent>
95
+ <DialogHeader>
96
+ <DialogTitle>Create Order</DialogTitle>
97
+ </DialogHeader>
98
+ <p className="text-sm text-gray-500 mb-4">
99
+ Order the circuit board fully assembled from a PCB fabricator
100
+ </p>
101
+ <div className="space-y-4">
102
+ <div className="flex items-center space-x-2">
103
+ <Checkbox id="shipping" checked={checkpoints.shipping} />
104
+ <label
105
+ htmlFor="shipping"
106
+ className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
107
+ >
108
+ Shipping Information in Profile
109
+ </label>
110
+ </div>
111
+ <div className="flex items-center space-x-2">
112
+ <Checkbox id="errors" checked={checkpoints.errors} />
113
+ <label
114
+ htmlFor="errors"
115
+ className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
116
+ >
117
+ PCB Has No Errors
118
+ </label>
119
+ </div>
120
+ <div className="flex items-center space-x-2">
121
+ <Checkbox id="parts" checked={checkpoints.parts} />
122
+ <label
123
+ htmlFor="parts"
124
+ className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
125
+ >
126
+ All parts available at PCB fabricator
127
+ </label>
128
+ </div>
129
+ </div>
130
+ <div className="flex justify-end space-x-2 mt-4">
131
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
132
+ Cancel
133
+ </Button>
134
+ <Button
135
+ onClick={handleSubmit}
136
+ disabled={pending || !Object.values(checkpoints).every(Boolean)}
137
+ >
138
+ {pending ? "Submitting..." : "Submit Order"}
139
+ </Button>
140
+ </div>
141
+ </DialogContent>
142
+ </Dialog>
143
+ )
144
+ }
145
+
146
+ export const useCreateOrderDialog = createUseDialog(CreateOrderDialog)
@@ -0,0 +1,34 @@
1
+ import { FunctionComponent, useMemo, useState } from "react"
2
+
3
+ export const createUseDialog = <DialogType extends React.ComponentType<any>>(
4
+ DialogComponent: DialogType,
5
+ ) => {
6
+ return () => {
7
+ const [open, setOpen] = useState(false)
8
+
9
+ return useMemo(
10
+ () => ({
11
+ openDialog: () => {
12
+ setOpen(true)
13
+ },
14
+ closeDialog: () => {
15
+ setOpen(false)
16
+ },
17
+ Dialog: (
18
+ props: Omit<
19
+ React.ComponentProps<DialogType>,
20
+ "open" | "onOpenChange"
21
+ >,
22
+ ) => (
23
+ <DialogComponent
24
+ {...(props as any)}
25
+ open={open}
26
+ onOpenChange={setOpen}
27
+ />
28
+ ),
29
+ open,
30
+ }),
31
+ [open],
32
+ )
33
+ }
34
+ }