tanstack_start_ts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.wrangler/deploy/config.json +1 -0
- package/bunfig.toml +6 -0
- package/components.json +22 -0
- package/config.json +0 -0
- package/dist/client/.assetsignore +2 -0
- package/dist/client/assets/ProductCard-DbIkJAE-.js +1 -0
- package/dist/client/assets/about-AskxOruL.js +1 -0
- package/dist/client/assets/admin-BZVcAQM3.js +1 -0
- package/dist/client/assets/admin.functions--RdVcuBx.js +1 -0
- package/dist/client/assets/admin.login-QgrF_9Fp.js +1 -0
- package/dist/client/assets/affiliate-disclosure-BIAsA-HO.js +1 -0
- package/dist/client/assets/categories-D0N418mK.js +1 -0
- package/dist/client/assets/category._slug-aCaQm14E.js +1 -0
- package/dist/client/assets/contact-PhvO-V15.js +1 -0
- package/dist/client/assets/faq-BsiHWPM8.js +1 -0
- package/dist/client/assets/hero-bg-BP2eVUIX.jpg +0 -0
- package/dist/client/assets/index-BU9rnkF3.js +1 -0
- package/dist/client/assets/index-BpJWZkva.js +1 -0
- package/dist/client/assets/index-vRX-zAyq.js +1 -0
- package/dist/client/assets/login-DteE0ZGp.js +1 -0
- package/dist/client/assets/logo-pSNfLJQk.png +0 -0
- package/dist/client/assets/privacy-B_Pu7040.js +1 -0
- package/dist/client/assets/product-links-BkZ41Gv3.js +1 -0
- package/dist/client/assets/product._id-BVUysCW-.js +1 -0
- package/dist/client/assets/products.functions-cGzRziKO.js +1 -0
- package/dist/client/assets/profile-CveRcKq2.js +1 -0
- package/dist/client/assets/reset-password-ySEjItX_.js +1 -0
- package/dist/client/assets/saved-CHtdQDJF.js +1 -0
- package/dist/client/assets/search-CXWfET1y.js +1 -0
- package/dist/client/assets/signup-CEx90iuV.js +1 -0
- package/dist/client/assets/styles-DrNJG0BO.css +1 -0
- package/dist/client/assets/terms-VqJ9kX9b.js +1 -0
- package/dist/client/assets/update-password-C-d0ix5e.js +1 -0
- package/dist/client/assets/vendor-aria-hidden-DvXkyWUv.js +1 -0
- package/dist/client/assets/vendor-class-variance-authority-5VPnzWs2.js +1 -0
- package/dist/client/assets/vendor-clsx-B-dksMZM.js +1 -0
- package/dist/client/assets/vendor-cookie-es-CS0aJGDi.js +1 -0
- package/dist/client/assets/vendor-detect-node-es-l0sNRNKZ.js +1 -0
- package/dist/client/assets/vendor-floating-ui-core-BlUy28sp.js +1 -0
- package/dist/client/assets/vendor-floating-ui-dom-BxK0hn2R.js +1 -0
- package/dist/client/assets/vendor-floating-ui-react-dom-Bas3975S.js +1 -0
- package/dist/client/assets/vendor-floating-ui-utils-BfYUAVcw.js +1 -0
- package/dist/client/assets/vendor-framer-motion-BMdL-cuX.js +9 -0
- package/dist/client/assets/vendor-get-nonce-C-Z93AgS.js +1 -0
- package/dist/client/assets/vendor-iceberg-js-tWD4K6Lg.js +1 -0
- package/dist/client/assets/vendor-lovable.dev-cloud-auth-js-VuzqtJVg.js +1 -0
- package/dist/client/assets/vendor-lucide-react-b5K2fehp.js +1 -0
- package/dist/client/assets/vendor-motion-dom-BETJamZt.js +1 -0
- package/dist/client/assets/vendor-motion-utils-BuWewJbj.js +1 -0
- package/dist/client/assets/vendor-radix-ui-primitive-Dc_FVRD7.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-accordion-C22Rgxe9.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-arrow-DMHj2mKI.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-avatar-CVPBkFXg.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-collapsible-BvM-4sKX.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-collection-D9KtqmHm.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-compose-refs-Cvq0AS8Z.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-context-CAqqn5Nx.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-dialog-DZ01vOLq.js +5 -0
- package/dist/client/assets/vendor-radix-ui-react-direction-DxZwNuei.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-dismissable-layer-Dqgrs55Y.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-dropdown-menu-0uzvrqkn.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-focus-guards-DgWoZ-fP.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-focus-scope-BLIu5QaL.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-id-bpga_rLa.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-menu-D0qf2r6_.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-popper-BafIylxU.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-portal-BnAsfNCS.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-presence-C-f3UKQ2.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-primitive-zTHwXNoz.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-roving-focus-jyJB8K2E.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-slot-6LXHJrHl.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-callback-ref-E91aPc6s.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-controllable-state-Ca3eMtxa.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-effect-event-CPeX4A3c.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-escape-keydown-7n3YsXFo.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-is-hydrated-C1PY1qNv.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-layout-effect-B3AcGWPy.js +1 -0
- package/dist/client/assets/vendor-radix-ui-react-use-size-CXS04sct.js +1 -0
- package/dist/client/assets/vendor-react-dom-BnNs-kzm.js +9 -0
- package/dist/client/assets/vendor-react-gJPiVnX5.js +1 -0
- package/dist/client/assets/vendor-react-remove-scroll-DHKl-IMP.js +4 -0
- package/dist/client/assets/vendor-react-remove-scroll-bar-CSjdInc2.js +38 -0
- package/dist/client/assets/vendor-react-style-singleton-BqHpkgXn.js +1 -0
- package/dist/client/assets/vendor-scheduler-7OC5HNn7.js +1 -0
- package/dist/client/assets/vendor-seroval-B_Fur-nl.js +3 -0
- package/dist/client/assets/vendor-seroval-plugins-CBHnPkZJ.js +1 -0
- package/dist/client/assets/vendor-sonner-71-LdGG1.js +1 -0
- package/dist/client/assets/vendor-supabase-auth-js-DWrN-bIx.js +18 -0
- package/dist/client/assets/vendor-supabase-functions-js-uY_V-TxC.js +1 -0
- package/dist/client/assets/vendor-supabase-phoenix-BzEf37Ve.js +2 -0
- package/dist/client/assets/vendor-supabase-postgrest-js-C4rBWbCx.js +4 -0
- package/dist/client/assets/vendor-supabase-realtime-js-D6BlOYKE.js +23 -0
- package/dist/client/assets/vendor-supabase-storage-js-BG98L3Zz.js +1 -0
- package/dist/client/assets/vendor-supabase-supabase-js-DCCzdwBJ.js +1 -0
- package/dist/client/assets/vendor-tailwind-merge-Ct12j0u0.js +1 -0
- package/dist/client/assets/vendor-tanstack-history-C617CaxG.js +1 -0
- package/dist/client/assets/vendor-tanstack-query-core-7wuJJ5ZL.js +1 -0
- package/dist/client/assets/vendor-tanstack-react-query-HImzo8sX.js +1 -0
- package/dist/client/assets/vendor-tanstack-react-router-sIZLK-LU.js +1 -0
- package/dist/client/assets/vendor-tanstack-react-start-client-GiYCfWmf.js +1 -0
- package/dist/client/assets/vendor-tanstack-react-store-EvTi3ahh.js +1 -0
- package/dist/client/assets/vendor-tanstack-router-core-Cr7bYUZv.js +1 -0
- package/dist/client/assets/vendor-tanstack-start-client-core-C-00BBOu.js +2 -0
- package/dist/client/assets/vendor-tanstack-start-fn-stubs-l0sNRNKZ.js +1 -0
- package/dist/client/assets/vendor-tanstack-store-BC7mA7pq.js +1 -0
- package/dist/client/assets/vendor-tslib-Du-meQkk.js +1 -0
- package/dist/client/assets/vendor-use-callback-ref-C_fIAtot.js +1 -0
- package/dist/client/assets/vendor-use-sidecar-Bh0DDN6h.js +1 -0
- package/dist/client/assets/vendor-use-sync-external-store-ZvKHXaIn.js +1 -0
- package/dist/client/assets/vendor-vercel-analytics-DwPM5BWs.js +1 -0
- package/dist/client/assets/vendor-zod-By9teAtI.js +1 -0
- package/dist/client/robots.txt +2 -0
- package/dist/server/.dev.vars +5 -0
- package/dist/server/.vite/manifest.json +2528 -0
- package/dist/server/assets/ProductCard-CUPXy5Eo.js +149 -0
- package/dist/server/assets/_tanstack-start-manifest_v-do7vTWFD.js +4 -0
- package/dist/server/assets/about-TfKQw0Ga.js +28 -0
- package/dist/server/assets/admin-DspfJOJk.js +578 -0
- package/dist/server/assets/admin.functions-B78ppWLR.js +645 -0
- package/dist/server/assets/admin.functions-BWlKBoTL.js +93 -0
- package/dist/server/assets/admin.login-CV7QfeA6.js +139 -0
- package/dist/server/assets/affiliate-disclosure-B1wI1cDb.js +86 -0
- package/dist/server/assets/auth-middleware-Cn49MidW.js +62 -0
- package/dist/server/assets/categories-Z7jnAYZP.js +108 -0
- package/dist/server/assets/category._slug-D0XY3FGK.js +112 -0
- package/dist/server/assets/contact-IzyONsXs.js +104 -0
- package/dist/server/assets/faq-aRhB_CR3.js +133 -0
- package/dist/server/assets/hero-bg-BP2eVUIX.jpg +0 -0
- package/dist/server/assets/index-BTPHbXw9.js +221 -0
- package/dist/server/assets/index-ByJkHkrU.js +30 -0
- package/dist/server/assets/login-Dvy5Dm0f.js +175 -0
- package/dist/server/assets/logo-pSNfLJQk.png +0 -0
- package/dist/server/assets/privacy-B6Wiez1P.js +93 -0
- package/dist/server/assets/product-links-CGYEPP56.js +16 -0
- package/dist/server/assets/product._id-BpRa-1z0.js +231 -0
- package/dist/server/assets/products.functions-DSlmibYN.js +209 -0
- package/dist/server/assets/products.functions-DlHkRiqi.js +24 -0
- package/dist/server/assets/profile-B0NWzVAZ.js +314 -0
- package/dist/server/assets/reset-password-CY-rmqMr.js +115 -0
- package/dist/server/assets/saved-7FA6Dbom.js +126 -0
- package/dist/server/assets/search-Yw5c_fZa.js +329 -0
- package/dist/server/assets/signup-UPzgZo4i.js +143 -0
- package/dist/server/assets/styles-DrNJG0BO.css +1 -0
- package/dist/server/assets/terms-CMnX95bP.js +89 -0
- package/dist/server/assets/update-password-Cr94ea8n.js +131 -0
- package/dist/server/assets/vendor-aria-hidden-DPa16MWu.js +122 -0
- package/dist/server/assets/vendor-class-variance-authority-0YxJPB9Y.js +44 -0
- package/dist/server/assets/vendor-cloudflare-unenv-preset-ya0VEFBz.js +250 -0
- package/dist/server/assets/vendor-clsx-DgYk2OaC.js +16 -0
- package/dist/server/assets/vendor-cookie-es-DAoofYiI.js +44 -0
- package/dist/server/assets/vendor-detect-node-es-l0sNRNKZ.js +1 -0
- package/dist/server/assets/vendor-floating-ui-core-3tkK0THV.js +726 -0
- package/dist/server/assets/vendor-floating-ui-dom-C-cPtgJv.js +626 -0
- package/dist/server/assets/vendor-floating-ui-react-dom-CRG6gBpH.js +319 -0
- package/dist/server/assets/vendor-floating-ui-utils-DmXANH-E.js +320 -0
- package/dist/server/assets/vendor-framer-motion-X4zAkX3J.js +1979 -0
- package/dist/server/assets/vendor-get-nonce-DiSj3EHl.js +9 -0
- package/dist/server/assets/vendor-h3-v2-CCobnLY5.js +287 -0
- package/dist/server/assets/vendor-iceberg-js-bHCkXyJn.js +534 -0
- package/dist/server/assets/vendor-isbot-CZ7WjwVs.js +21 -0
- package/dist/server/assets/vendor-lovable.dev-cloud-auth-js-BE03njZw.js +180 -0
- package/dist/server/assets/vendor-lucide-react-Ddew6HYb.js +458 -0
- package/dist/server/assets/vendor-motion-dom-D2MTwGIG.js +5983 -0
- package/dist/server/assets/vendor-motion-utils-LJlIFN6m.js +161 -0
- package/dist/server/assets/vendor-radix-ui-primitive-B-mNdDrH.js +11 -0
- package/dist/server/assets/vendor-radix-ui-react-accordion-1Izf6x00.js +308 -0
- package/dist/server/assets/vendor-radix-ui-react-arrow-B882lnFK.js +23 -0
- package/dist/server/assets/vendor-radix-ui-react-avatar-BVgZt2Ab.js +209 -0
- package/dist/server/assets/vendor-radix-ui-react-collapsible-DCBbMZiS.js +147 -0
- package/dist/server/assets/vendor-radix-ui-react-collection-BZ2srfgU.js +150 -0
- package/dist/server/assets/vendor-radix-ui-react-compose-refs-D3qsKVk1.js +39 -0
- package/dist/server/assets/vendor-radix-ui-react-context-BVoNDLue.js +78 -0
- package/dist/server/assets/vendor-radix-ui-react-dialog-DlxMaNYK.js +406 -0
- package/dist/server/assets/vendor-radix-ui-react-direction-Dt_WDL1t.js +9 -0
- package/dist/server/assets/vendor-radix-ui-react-dismissable-layer-CjsuPohV.js +210 -0
- package/dist/server/assets/vendor-radix-ui-react-dropdown-menu-DVxKumY8.js +263 -0
- package/dist/server/assets/vendor-radix-ui-react-focus-guards-D_6NoePE.js +29 -0
- package/dist/server/assets/vendor-radix-ui-react-focus-scope-DEIhTJJH.js +206 -0
- package/dist/server/assets/vendor-radix-ui-react-id-DFFpgh6m.js +14 -0
- package/dist/server/assets/vendor-radix-ui-react-menu-CiTMLwjT.js +893 -0
- package/dist/server/assets/vendor-radix-ui-react-popper-23Ye2Vyc.js +286 -0
- package/dist/server/assets/vendor-radix-ui-react-portal-CZCH5uPk.js +16 -0
- package/dist/server/assets/vendor-radix-ui-react-presence-CaAULlDU.js +128 -0
- package/dist/server/assets/vendor-radix-ui-react-primitive-BeOk3UYa.js +124 -0
- package/dist/server/assets/vendor-radix-ui-react-roving-focus-DES9GR8l.js +224 -0
- package/dist/server/assets/vendor-radix-ui-react-slot-DUhZbzoH.js +103 -0
- package/dist/server/assets/vendor-radix-ui-react-use-callback-ref-BynBgohw.js +11 -0
- package/dist/server/assets/vendor-radix-ui-react-use-controllable-state-C9KpT6DG.js +69 -0
- package/dist/server/assets/vendor-radix-ui-react-use-effect-event-gpNY2xjS.js +1 -0
- package/dist/server/assets/vendor-radix-ui-react-use-escape-keydown-CcYRQ2pp.js +17 -0
- package/dist/server/assets/vendor-radix-ui-react-use-is-hydrated-D_LcBPXY.js +15 -0
- package/dist/server/assets/vendor-radix-ui-react-use-layout-effect-1LNLXAjr.js +6 -0
- package/dist/server/assets/vendor-radix-ui-react-use-size-D6fiKJQo.js +39 -0
- package/dist/server/assets/vendor-react-DvBrY0qp.js +511 -0
- package/dist/server/assets/vendor-react-dom-yvMLPM0j.js +10484 -0
- package/dist/server/assets/vendor-react-remove-scroll-BNtiEvVN.js +328 -0
- package/dist/server/assets/vendor-react-remove-scroll-bar-hLqRASRk.js +82 -0
- package/dist/server/assets/vendor-react-style-singleton-BXjcXskB.js +69 -0
- package/dist/server/assets/vendor-rou3-3NaGPdI8.js +8 -0
- package/dist/server/assets/vendor-seroval-dJyC-Zhz.js +1775 -0
- package/dist/server/assets/vendor-seroval-plugins-Pq_U2meB.js +58 -0
- package/dist/server/assets/vendor-sonner-CqbjhsRh.js +1086 -0
- package/dist/server/assets/vendor-srvx-BA-baEX9.js +6 -0
- package/dist/server/assets/vendor-supabase-auth-js-D4xjVprw.js +7602 -0
- package/dist/server/assets/vendor-supabase-functions-js-sWy4UYn1.js +322 -0
- package/dist/server/assets/vendor-supabase-phoenix-Bw3Uh2Nn.js +1777 -0
- package/dist/server/assets/vendor-supabase-postgrest-js-AO-BXa7I.js +4938 -0
- package/dist/server/assets/vendor-supabase-realtime-js-BtdNgJbm.js +2111 -0
- package/dist/server/assets/vendor-supabase-storage-js-Dk_MrPYO.js +2679 -0
- package/dist/server/assets/vendor-supabase-supabase-js-D1EEtG3j.js +697 -0
- package/dist/server/assets/vendor-tailwind-merge-BHb_obmC.js +3255 -0
- package/dist/server/assets/vendor-tanstack-history-C4pKJmkt.js +204 -0
- package/dist/server/assets/vendor-tanstack-query-core-PwwTR5ld.js +2552 -0
- package/dist/server/assets/vendor-tanstack-react-query-hhHzXAK1.js +190 -0
- package/dist/server/assets/vendor-tanstack-react-router-XzqpA65A.js +1120 -0
- package/dist/server/assets/vendor-tanstack-react-start-RvWUpvat.js +37 -0
- package/dist/server/assets/vendor-tanstack-react-start-client-gpNY2xjS.js +1 -0
- package/dist/server/assets/vendor-tanstack-react-start-server-uj_Y9pEN.js +15 -0
- package/dist/server/assets/vendor-tanstack-react-store-gpNY2xjS.js +1 -0
- package/dist/server/assets/vendor-tanstack-router-core-6wywV3KN.js +4252 -0
- package/dist/server/assets/vendor-tanstack-start-client-core-DoOKV2pA.js +1741 -0
- package/dist/server/assets/vendor-tanstack-start-fn-stubs-l0sNRNKZ.js +1 -0
- package/dist/server/assets/vendor-tanstack-start-server-core-CsAstXv7.js +1421 -0
- package/dist/server/assets/vendor-tanstack-start-storage-context-DgH9hIJT.js +17 -0
- package/dist/server/assets/vendor-tanstack-store-l0sNRNKZ.js +1 -0
- package/dist/server/assets/vendor-tslib-_8ICaZ64.js +67 -0
- package/dist/server/assets/vendor-unenv-DUvF4YIF.js +544 -0
- package/dist/server/assets/vendor-use-callback-ref-DMFDRvmi.js +66 -0
- package/dist/server/assets/vendor-use-sidecar-DG1tHua4.js +106 -0
- package/dist/server/assets/vendor-use-sync-external-store-rZ8vi0It.js +64 -0
- package/dist/server/assets/vendor-vercel-analytics-oP8BDp0L.js +168 -0
- package/dist/server/assets/vendor-zod-BRyQdbC-.js +3580 -0
- package/dist/server/index.js +158 -0
- package/dist/server/wrangler.json +1 -0
- package/enable-powershell.ps1 +7 -0
- package/eslint.config.js +41 -0
- package/lint.bat +4 -0
- package/package.json +95 -0
- package/public/robots.txt +2 -0
- package/run-npm-build.cjs +20 -0
- package/run-npm-build.js +20 -0
- package/src/assets/hero-bg.jpg +0 -0
- package/src/assets/logo.png +0 -0
- package/src/components/scrollsy/Footer.tsx +68 -0
- package/src/components/scrollsy/LiveTicker.tsx +31 -0
- package/src/components/scrollsy/Logo.tsx +28 -0
- package/src/components/scrollsy/Nav.tsx +255 -0
- package/src/components/scrollsy/ProductCard.tsx +190 -0
- package/src/components/scrollsy/ProductFilters.tsx +226 -0
- package/src/components/scrollsy/SupportWidget.tsx +197 -0
- package/src/components/ui/accordion.tsx +51 -0
- package/src/components/ui/alert-dialog.tsx +115 -0
- package/src/components/ui/alert.tsx +49 -0
- package/src/components/ui/aspect-ratio.tsx +5 -0
- package/src/components/ui/avatar.tsx +47 -0
- package/src/components/ui/badge.tsx +32 -0
- package/src/components/ui/breadcrumb.tsx +101 -0
- package/src/components/ui/button.tsx +49 -0
- package/src/components/ui/calendar.tsx +177 -0
- package/src/components/ui/card.tsx +55 -0
- package/src/components/ui/carousel.tsx +240 -0
- package/src/components/ui/chart.tsx +331 -0
- package/src/components/ui/checkbox.tsx +26 -0
- package/src/components/ui/collapsible.tsx +11 -0
- package/src/components/ui/command.tsx +143 -0
- package/src/components/ui/context-menu.tsx +187 -0
- package/src/components/ui/dialog.tsx +104 -0
- package/src/components/ui/drawer.tsx +98 -0
- package/src/components/ui/dropdown-menu.tsx +188 -0
- package/src/components/ui/form.tsx +171 -0
- package/src/components/ui/hover-card.tsx +27 -0
- package/src/components/ui/input-otp.tsx +69 -0
- package/src/components/ui/input.tsx +22 -0
- package/src/components/ui/label.tsx +21 -0
- package/src/components/ui/menubar.tsx +229 -0
- package/src/components/ui/navigation-menu.tsx +120 -0
- package/src/components/ui/pagination.tsx +98 -0
- package/src/components/ui/popover.tsx +31 -0
- package/src/components/ui/progress.tsx +25 -0
- package/src/components/ui/radio-group.tsx +36 -0
- package/src/components/ui/resizable.tsx +37 -0
- package/src/components/ui/scroll-area.tsx +44 -0
- package/src/components/ui/select.tsx +152 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/sheet.tsx +122 -0
- package/src/components/ui/sidebar.tsx +744 -0
- package/src/components/ui/skeleton.tsx +7 -0
- package/src/components/ui/slider.tsx +23 -0
- package/src/components/ui/sonner.tsx +23 -0
- package/src/components/ui/switch.tsx +27 -0
- package/src/components/ui/table.tsx +94 -0
- package/src/components/ui/tabs.tsx +53 -0
- package/src/components/ui/textarea.tsx +21 -0
- package/src/components/ui/toggle-group.tsx +57 -0
- package/src/components/ui/toggle.tsx +42 -0
- package/src/components/ui/tooltip.tsx +32 -0
- package/src/hooks/use-auth.ts +26 -0
- package/src/hooks/use-mobile.tsx +19 -0
- package/src/integrations/lovable/index.ts +41 -0
- package/src/lib/admin.functions.ts +564 -0
- package/src/lib/error-capture.ts +27 -0
- package/src/lib/error-page.ts +30 -0
- package/src/lib/product-links.ts +39 -0
- package/src/lib/products.functions.ts +101 -0
- package/src/lib/utils.ts +6 -0
- package/src/routeTree.gen.ts +480 -0
- package/src/router.tsx +16 -0
- package/src/routes/__root.tsx +177 -0
- package/src/routes/about.tsx +66 -0
- package/src/routes/admin.login.tsx +95 -0
- package/src/routes/admin.tsx +811 -0
- package/src/routes/affiliate-disclosure.tsx +35 -0
- package/src/routes/categories.tsx +57 -0
- package/src/routes/category.$slug.tsx +51 -0
- package/src/routes/contact.tsx +69 -0
- package/src/routes/faq.tsx +63 -0
- package/src/routes/index.tsx +269 -0
- package/src/routes/login.tsx +160 -0
- package/src/routes/privacy.tsx +39 -0
- package/src/routes/product.$id.tsx +212 -0
- package/src/routes/profile.tsx +393 -0
- package/src/routes/reset-password.tsx +71 -0
- package/src/routes/saved.tsx +83 -0
- package/src/routes/search.tsx +136 -0
- package/src/routes/signup.tsx +108 -0
- package/src/routes/sitemap[.]xml.ts +34 -0
- package/src/routes/terms.tsx +40 -0
- package/src/routes/update-password.tsx +91 -0
- package/src/server.ts +80 -0
- package/src/start.ts +24 -0
- package/src/styles.css +333 -0
- package/terminal-test-output.txt +1 -0
- package/tsconfig.json +27 -0
- package/vercel.json +26 -0
- package/vite.config.ts +38 -0
- package/wrangler.jsonc +7 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { Prose } from "./about";
|
|
3
|
+
|
|
4
|
+
export const Route = createFileRoute("/affiliate-disclosure")({
|
|
5
|
+
head: () => ({
|
|
6
|
+
meta: [
|
|
7
|
+
{ title: "Affiliate Disclosure · Scrollsy" },
|
|
8
|
+
{ name: "description", content: "How Scrollsy makes money — full affiliate disclosure." },
|
|
9
|
+
],
|
|
10
|
+
}),
|
|
11
|
+
component: () => (
|
|
12
|
+
<Prose title="Affiliate Disclosure" subtitle="Full transparency, always.">
|
|
13
|
+
<p>
|
|
14
|
+
<strong>Scrollsy may earn commissions from purchases made through affiliate links.</strong>
|
|
15
|
+
</p>
|
|
16
|
+
<p>
|
|
17
|
+
When you click "View Deal" on a product, you'll be sent to a partner retailer — for example
|
|
18
|
+
AliExpress, Amazon, Temu or Kilimall. If you buy something during that visit, Scrollsy may
|
|
19
|
+
receive a small commission from the retailer.
|
|
20
|
+
</p>
|
|
21
|
+
<h2>Does this cost you anything?</h2>
|
|
22
|
+
<p>
|
|
23
|
+
Nope. Affiliate commissions are paid by the retailer out of their margin. You pay the same
|
|
24
|
+
price you would have paid going to the retailer directly.
|
|
25
|
+
</p>
|
|
26
|
+
<h2>Does it influence what we feature?</h2>
|
|
27
|
+
<p>
|
|
28
|
+
We feature products based on virality, quality and price — not commission rate. Our
|
|
29
|
+
editorial picks stay independent of any merchant relationship.
|
|
30
|
+
</p>
|
|
31
|
+
<h2>Want to learn more?</h2>
|
|
32
|
+
<p>Email us at affiliates@scrollsy.app.</p>
|
|
33
|
+
</Prose>
|
|
34
|
+
),
|
|
35
|
+
});
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { getCategories } from "@/lib/products.functions";
|
|
4
|
+
|
|
5
|
+
export const Route = createFileRoute("/categories")({
|
|
6
|
+
head: () => ({
|
|
7
|
+
meta: [
|
|
8
|
+
{ title: "Categories · Scrollsy" },
|
|
9
|
+
{
|
|
10
|
+
name: "description",
|
|
11
|
+
content:
|
|
12
|
+
"Browse Scrollsy product categories — tech, fashion, smart home, gaming, viral finds and more.",
|
|
13
|
+
},
|
|
14
|
+
],
|
|
15
|
+
links: [{ rel: "canonical", href: "/categories" }],
|
|
16
|
+
}),
|
|
17
|
+
component: Categories,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function Categories() {
|
|
21
|
+
const { data } = useQuery({ queryKey: ["cats"], queryFn: () => getCategories() });
|
|
22
|
+
return (
|
|
23
|
+
<div className="px-4 max-w-7xl mx-auto pt-10">
|
|
24
|
+
<h1 className="font-display text-4xl md:text-5xl font-bold tracking-tight">
|
|
25
|
+
Explore <span className="text-gradient">categories</span>
|
|
26
|
+
</h1>
|
|
27
|
+
<p className="text-muted-foreground mt-2">
|
|
28
|
+
Every aesthetic. Every obsession. One scroll away.
|
|
29
|
+
</p>
|
|
30
|
+
|
|
31
|
+
<div className="mt-10 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
32
|
+
{(data ?? []).map((c, i) => (
|
|
33
|
+
<Link
|
|
34
|
+
key={c.id}
|
|
35
|
+
to="/category/$slug"
|
|
36
|
+
params={{ slug: c.slug }}
|
|
37
|
+
className="group glass rounded-3xl p-6 aspect-square flex flex-col justify-between hover:scale-[1.03] transition-transform relative overflow-hidden"
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
className="absolute -top-10 -right-10 w-40 h-40 rounded-full opacity-40 group-hover:opacity-70 transition-opacity blur-2xl"
|
|
41
|
+
style={{
|
|
42
|
+
background: `conic-gradient(from ${i * 40}deg, #ff6b35, #e84393, #6c5ce7, #ff6b35)`,
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
<div className="relative">
|
|
46
|
+
<div className="text-xs text-muted-foreground uppercase tracking-wider">Category</div>
|
|
47
|
+
<div className="font-display text-2xl font-bold mt-2">{c.name}</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div className="relative text-xs text-muted-foreground group-hover:text-foreground transition-colors">
|
|
50
|
+
Explore →
|
|
51
|
+
</div>
|
|
52
|
+
</Link>
|
|
53
|
+
))}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { createFileRoute, Link, notFound } from "@tanstack/react-router";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { getProductsByCategory } from "@/lib/products.functions";
|
|
4
|
+
import { ProductCard } from "@/components/scrollsy/ProductCard";
|
|
5
|
+
import { ChevronLeft } from "lucide-react";
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute("/category/$slug")({
|
|
8
|
+
head: ({ params }) => ({
|
|
9
|
+
meta: [
|
|
10
|
+
{ title: `${params.slug.replace(/-/g, " ")} · Scrollsy` },
|
|
11
|
+
{
|
|
12
|
+
name: "description",
|
|
13
|
+
content: `Browse trending ${params.slug.replace(/-/g, " ")} finds on Scrollsy.`,
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
}),
|
|
17
|
+
component: CategoryPage,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function CategoryPage() {
|
|
21
|
+
const { slug } = Route.useParams();
|
|
22
|
+
const { data, isLoading } = useQuery({
|
|
23
|
+
queryKey: ["cat", slug],
|
|
24
|
+
queryFn: () => getProductsByCategory({ data: { slug } }),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (!isLoading && !data?.category) throw notFound();
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className="px-4 max-w-7xl mx-auto pt-10">
|
|
31
|
+
<Link
|
|
32
|
+
to="/categories"
|
|
33
|
+
className="inline-flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
|
|
34
|
+
>
|
|
35
|
+
<ChevronLeft className="w-4 h-4" /> All categories
|
|
36
|
+
</Link>
|
|
37
|
+
<h1 className="mt-3 font-display text-4xl md:text-5xl font-bold tracking-tight">
|
|
38
|
+
{data?.category?.name ?? "…"}
|
|
39
|
+
</h1>
|
|
40
|
+
<p className="text-muted-foreground mt-1">
|
|
41
|
+
{data?.products.length ?? 0} trending finds in this drop
|
|
42
|
+
</p>
|
|
43
|
+
|
|
44
|
+
<div className="mt-8 grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
45
|
+
{(data?.products ?? []).map((p) => (
|
|
46
|
+
<ProductCard key={p.id} product={p} aspect="tall" />
|
|
47
|
+
))}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { Prose } from "./about";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { Mail, Send } from "lucide-react";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
export const Route = createFileRoute("/contact")({
|
|
8
|
+
head: () => ({
|
|
9
|
+
meta: [
|
|
10
|
+
{ title: "Contact · Scrollsy" },
|
|
11
|
+
{
|
|
12
|
+
name: "description",
|
|
13
|
+
content:
|
|
14
|
+
"Get in touch with the Scrollsy team — partnerships, press, support and creator collabs.",
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
}),
|
|
18
|
+
component: Contact,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function Contact() {
|
|
22
|
+
const [email, setEmail] = useState("");
|
|
23
|
+
const [message, setMessage] = useState("");
|
|
24
|
+
|
|
25
|
+
const send = (e: React.FormEvent) => {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
toast.success("Thanks! We'll be in touch soon.");
|
|
28
|
+
setEmail("");
|
|
29
|
+
setMessage("");
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<Prose
|
|
34
|
+
title="Get in touch"
|
|
35
|
+
subtitle="Partnerships, press, support or creator collabs — we'd love to hear from you."
|
|
36
|
+
>
|
|
37
|
+
<form onSubmit={send} className="not-prose mt-8 space-y-3">
|
|
38
|
+
<label className="flex items-center gap-2 glass rounded-xl px-3 py-2.5">
|
|
39
|
+
<Mail className="w-4 h-4 text-muted-foreground" />
|
|
40
|
+
<input
|
|
41
|
+
required
|
|
42
|
+
type="email"
|
|
43
|
+
value={email}
|
|
44
|
+
onChange={(e) => setEmail(e.target.value)}
|
|
45
|
+
placeholder="Your email"
|
|
46
|
+
className="flex-1 bg-transparent outline-none text-sm"
|
|
47
|
+
/>
|
|
48
|
+
</label>
|
|
49
|
+
<textarea
|
|
50
|
+
required
|
|
51
|
+
value={message}
|
|
52
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
53
|
+
placeholder="How can we help?"
|
|
54
|
+
rows={5}
|
|
55
|
+
className="w-full glass rounded-xl px-3 py-2.5 text-sm bg-transparent outline-none resize-none"
|
|
56
|
+
/>
|
|
57
|
+
<button className="btn-glow rounded-full px-6 py-3 font-semibold text-sm inline-flex items-center gap-2">
|
|
58
|
+
<Send className="w-4 h-4" /> Send message
|
|
59
|
+
</button>
|
|
60
|
+
</form>
|
|
61
|
+
<p className="text-sm mt-6">
|
|
62
|
+
Or email us directly at{" "}
|
|
63
|
+
<a href="mailto:hello@scrollsy.app" className="text-gradient font-semibold">
|
|
64
|
+
hello@scrollsy.app
|
|
65
|
+
</a>
|
|
66
|
+
</p>
|
|
67
|
+
</Prose>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { createFileRoute } from "@tanstack/react-router";
|
|
2
|
+
import { Prose } from "./about";
|
|
3
|
+
import {
|
|
4
|
+
Accordion,
|
|
5
|
+
AccordionContent,
|
|
6
|
+
AccordionItem,
|
|
7
|
+
AccordionTrigger,
|
|
8
|
+
} from "@/components/ui/accordion";
|
|
9
|
+
|
|
10
|
+
const faqs = [
|
|
11
|
+
{
|
|
12
|
+
q: "What is Scrollsy?",
|
|
13
|
+
a: "Scrollsy is a visual product discovery platform that surfaces trending gadgets, viral finds and the best deals from across the web.",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
q: "Does Scrollsy sell products directly?",
|
|
17
|
+
a: "No — when you tap 'View Deal' we send you to the retailer (AliExpress, Amazon, Temu, Kilimall, etc.) where you complete your purchase as normal.",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
q: "How do you earn money?",
|
|
21
|
+
a: "Scrollsy may earn a small commission from purchases made through our affiliate links. It costs you nothing extra and keeps Scrollsy free to use.",
|
|
22
|
+
},
|
|
23
|
+
{ q: "Is Scrollsy free?", a: "Yes — totally free for shoppers. Always." },
|
|
24
|
+
{
|
|
25
|
+
q: "How do I save products?",
|
|
26
|
+
a: "Tap the heart on any product card. Saved items live in your Saved page (you'll need a free account).",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
q: "How are trending products selected?",
|
|
30
|
+
a: "Our team curates each drop based on virality signals (TikTok, Pinterest, social shares) plus real shopper engagement on Scrollsy.",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
q: "Are the deals real?",
|
|
34
|
+
a: "Yes. We verify discount pricing at the time of curation, but stock and pricing on third-party retailers can change without notice.",
|
|
35
|
+
},
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
export const Route = createFileRoute("/faq")({
|
|
39
|
+
head: () => ({
|
|
40
|
+
meta: [
|
|
41
|
+
{ title: "FAQ · Scrollsy" },
|
|
42
|
+
{
|
|
43
|
+
name: "description",
|
|
44
|
+
content:
|
|
45
|
+
"Frequently asked questions about Scrollsy — affiliate links, deals, accounts and more.",
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
}),
|
|
49
|
+
component: () => (
|
|
50
|
+
<Prose title="Questions, answered" subtitle="Everything you might want to know about Scrollsy.">
|
|
51
|
+
<Accordion type="single" collapsible className="not-prose mt-6 space-y-2">
|
|
52
|
+
{faqs.map((f, i) => (
|
|
53
|
+
<AccordionItem key={i} value={`f${i}`} className="glass rounded-2xl px-4 border-none">
|
|
54
|
+
<AccordionTrigger className="text-left font-display font-semibold hover:no-underline">
|
|
55
|
+
{f.q}
|
|
56
|
+
</AccordionTrigger>
|
|
57
|
+
<AccordionContent className="text-muted-foreground">{f.a}</AccordionContent>
|
|
58
|
+
</AccordionItem>
|
|
59
|
+
))}
|
|
60
|
+
</Accordion>
|
|
61
|
+
</Prose>
|
|
62
|
+
),
|
|
63
|
+
});
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { createFileRoute, Link } from "@tanstack/react-router";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { Search, Sparkles, TrendingUp, Flame, Zap, ArrowRight } from "lucide-react";
|
|
4
|
+
import { getHomeData } from "@/lib/products.functions";
|
|
5
|
+
import { ProductCard } from "@/components/scrollsy/ProductCard";
|
|
6
|
+
import { LiveTicker } from "@/components/scrollsy/LiveTicker";
|
|
7
|
+
import heroBg from "@/assets/hero-bg.jpg";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { useNavigate } from "@tanstack/react-router";
|
|
10
|
+
|
|
11
|
+
export const Route = createFileRoute("/")({
|
|
12
|
+
head: () => ({
|
|
13
|
+
meta: [
|
|
14
|
+
{ title: "Scrollsy — Scroll. Discover. Save." },
|
|
15
|
+
{
|
|
16
|
+
name: "description",
|
|
17
|
+
content:
|
|
18
|
+
"Discover viral gadgets, trending finds and hidden deals on Scrollsy — the visual marketplace built for endless scrolling.",
|
|
19
|
+
},
|
|
20
|
+
{ property: "og:title", content: "Scrollsy — Scroll. Discover. Save." },
|
|
21
|
+
{ property: "og:image", content: heroBg },
|
|
22
|
+
],
|
|
23
|
+
links: [{ rel: "canonical", href: "/" }],
|
|
24
|
+
}),
|
|
25
|
+
component: Home,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
function Home() {
|
|
29
|
+
const { data } = useQuery({ queryKey: ["home"], queryFn: () => getHomeData() });
|
|
30
|
+
const [q, setQ] = useState("");
|
|
31
|
+
const nav = useNavigate();
|
|
32
|
+
|
|
33
|
+
const featured = data?.featured ?? [];
|
|
34
|
+
const trending = data?.trending ?? [];
|
|
35
|
+
const recent = data?.recent ?? [];
|
|
36
|
+
const categories = data?.categories ?? [];
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="px-4">
|
|
40
|
+
{/* HERO */}
|
|
41
|
+
<section className="relative max-w-7xl mx-auto rounded-[2rem] overflow-hidden mt-4 border border-white/5">
|
|
42
|
+
<img
|
|
43
|
+
src={heroBg}
|
|
44
|
+
alt=""
|
|
45
|
+
className="absolute inset-0 w-full h-full object-cover opacity-60"
|
|
46
|
+
width={1920}
|
|
47
|
+
height={1080}
|
|
48
|
+
/>
|
|
49
|
+
<div className="absolute inset-0 bg-gradient-to-b from-background/30 via-background/40 to-background" />
|
|
50
|
+
<div className="absolute inset-0 bg-gradient-radial" />
|
|
51
|
+
|
|
52
|
+
<div className="relative z-10 px-6 md:px-14 py-20 md:py-28 max-w-4xl">
|
|
53
|
+
<div className="mb-6">
|
|
54
|
+
<LiveTicker />
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<h1 className="font-display text-5xl md:text-7xl font-bold leading-[1.02] tracking-tight">
|
|
58
|
+
Scroll. Discover. <span className="text-gradient animate-gradient">Save.</span>
|
|
59
|
+
</h1>
|
|
60
|
+
<p className="mt-5 text-lg md:text-xl text-muted-foreground max-w-xl">
|
|
61
|
+
The visual marketplace of the internet's hidden gems. Viral gadgets, smart finds and
|
|
62
|
+
trending deals — curated for endless scrolling.
|
|
63
|
+
</p>
|
|
64
|
+
|
|
65
|
+
<form
|
|
66
|
+
onSubmit={(e) => {
|
|
67
|
+
e.preventDefault();
|
|
68
|
+
nav({ to: "/search", search: { q } });
|
|
69
|
+
}}
|
|
70
|
+
className="mt-8 max-w-xl flex glass-strong rounded-2xl p-1.5 shadow-elevated"
|
|
71
|
+
>
|
|
72
|
+
<div className="flex items-center gap-2 px-3 text-muted-foreground">
|
|
73
|
+
<Search className="w-5 h-5" />
|
|
74
|
+
</div>
|
|
75
|
+
<input
|
|
76
|
+
value={q}
|
|
77
|
+
onChange={(e) => setQ(e.target.value)}
|
|
78
|
+
placeholder="Search products, gadgets, viral finds…"
|
|
79
|
+
className="flex-1 bg-transparent outline-none text-sm placeholder:text-muted-foreground py-3"
|
|
80
|
+
/>
|
|
81
|
+
<button
|
|
82
|
+
type="submit"
|
|
83
|
+
className="btn-glow rounded-xl px-5 py-2.5 text-sm font-semibold inline-flex items-center gap-1.5"
|
|
84
|
+
>
|
|
85
|
+
<Sparkles className="w-4 h-4" /> Discover
|
|
86
|
+
</button>
|
|
87
|
+
</form>
|
|
88
|
+
|
|
89
|
+
<div className="mt-8 flex flex-wrap gap-2">
|
|
90
|
+
{categories.slice(0, 8).map((c) => (
|
|
91
|
+
<Link
|
|
92
|
+
key={c.id}
|
|
93
|
+
to="/category/$slug"
|
|
94
|
+
params={{ slug: c.slug }}
|
|
95
|
+
className="glass rounded-full px-3.5 py-1.5 text-xs font-medium hover:bg-white/10 transition"
|
|
96
|
+
>
|
|
97
|
+
{c.name}
|
|
98
|
+
</Link>
|
|
99
|
+
))}
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</section>
|
|
103
|
+
|
|
104
|
+
{/* FEATURED BENTO */}
|
|
105
|
+
{featured.length > 0 && (
|
|
106
|
+
<Section
|
|
107
|
+
eyebrow={
|
|
108
|
+
<>
|
|
109
|
+
<Zap className="w-3.5 h-3.5" /> Featured today
|
|
110
|
+
</>
|
|
111
|
+
}
|
|
112
|
+
title="Most popular right now"
|
|
113
|
+
subtitle="Hand-picked finds the community is loving"
|
|
114
|
+
href="/search"
|
|
115
|
+
>
|
|
116
|
+
<BentoGrid products={featured} />
|
|
117
|
+
</Section>
|
|
118
|
+
)}
|
|
119
|
+
|
|
120
|
+
{/* CATEGORY SLIDER */}
|
|
121
|
+
<Section title="Shop by mood" eyebrow={<>Categories</>}>
|
|
122
|
+
<div className="flex gap-3 overflow-x-auto pb-3 -mx-4 px-4 snap-x">
|
|
123
|
+
{categories.map((c, i) => (
|
|
124
|
+
<Link
|
|
125
|
+
key={c.id}
|
|
126
|
+
to="/category/$slug"
|
|
127
|
+
params={{ slug: c.slug }}
|
|
128
|
+
className="snap-start shrink-0 glass rounded-2xl px-5 py-4 hover:scale-105 transition-transform min-w-[140px]"
|
|
129
|
+
style={{
|
|
130
|
+
background: `linear-gradient(135deg, hsl(${(i * 27) % 360} 80% 55% / 0.18), transparent)`,
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
<div className="text-xs text-muted-foreground">Category</div>
|
|
134
|
+
<div className="text-base font-display font-semibold mt-1">{c.name}</div>
|
|
135
|
+
</Link>
|
|
136
|
+
))}
|
|
137
|
+
</div>
|
|
138
|
+
</Section>
|
|
139
|
+
|
|
140
|
+
{/* TRENDING */}
|
|
141
|
+
{trending.length > 0 && (
|
|
142
|
+
<Section
|
|
143
|
+
eyebrow={
|
|
144
|
+
<>
|
|
145
|
+
<TrendingUp className="w-3.5 h-3.5" /> Trending
|
|
146
|
+
</>
|
|
147
|
+
}
|
|
148
|
+
title="Trending on TikTok"
|
|
149
|
+
subtitle="Going viral right now"
|
|
150
|
+
href="/search"
|
|
151
|
+
>
|
|
152
|
+
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
153
|
+
{trending.slice(0, 8).map((p) => (
|
|
154
|
+
<ProductCard key={p.id} product={p} aspect="tall" />
|
|
155
|
+
))}
|
|
156
|
+
</div>
|
|
157
|
+
</Section>
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
{/* BEST DEALS BANNER */}
|
|
161
|
+
<section className="max-w-7xl mx-auto mt-16 rounded-3xl overflow-hidden relative border border-white/5">
|
|
162
|
+
<div className="bg-gradient-brand animate-gradient p-8 md:p-12">
|
|
163
|
+
<div className="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
|
|
164
|
+
<div>
|
|
165
|
+
<span className="inline-flex items-center gap-1.5 text-xs font-semibold bg-black/30 rounded-full px-3 py-1">
|
|
166
|
+
<Flame className="w-3.5 h-3.5" /> Hot deals
|
|
167
|
+
</span>
|
|
168
|
+
<h2 className="font-display text-3xl md:text-4xl font-bold mt-3 text-white">
|
|
169
|
+
Up to 70% off · today only
|
|
170
|
+
</h2>
|
|
171
|
+
<p className="text-white/90 mt-2 max-w-md">
|
|
172
|
+
Limited drops curated from across the web. When they're gone, they're gone.
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
<Link
|
|
176
|
+
to="/search"
|
|
177
|
+
search={{ q: "" }}
|
|
178
|
+
className="bg-black text-white rounded-full px-5 py-3 font-semibold text-sm inline-flex items-center gap-2 hover:bg-black/80 transition"
|
|
179
|
+
>
|
|
180
|
+
Shop deals <ArrowRight className="w-4 h-4" />
|
|
181
|
+
</Link>
|
|
182
|
+
</div>
|
|
183
|
+
</div>
|
|
184
|
+
</section>
|
|
185
|
+
|
|
186
|
+
{/* MASONRY FEED */}
|
|
187
|
+
{recent.length > 0 && (
|
|
188
|
+
<Section
|
|
189
|
+
eyebrow={<>Fresh drops</>}
|
|
190
|
+
title="Recently added"
|
|
191
|
+
subtitle="The newest finds, just in"
|
|
192
|
+
>
|
|
193
|
+
<div className="columns-2 md:columns-3 lg:columns-4 gap-4 space-y-4 [&>*]:break-inside-avoid">
|
|
194
|
+
{recent.map((p, i) => (
|
|
195
|
+
<ProductCard
|
|
196
|
+
key={p.id}
|
|
197
|
+
product={p}
|
|
198
|
+
aspect={(["tall", "square", "wide", "tall"] as const)[i % 4]}
|
|
199
|
+
/>
|
|
200
|
+
))}
|
|
201
|
+
</div>
|
|
202
|
+
</Section>
|
|
203
|
+
)}
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function Section({
|
|
209
|
+
title,
|
|
210
|
+
subtitle,
|
|
211
|
+
eyebrow,
|
|
212
|
+
href,
|
|
213
|
+
children,
|
|
214
|
+
}: {
|
|
215
|
+
title: string;
|
|
216
|
+
subtitle?: string;
|
|
217
|
+
eyebrow?: React.ReactNode;
|
|
218
|
+
href?: string;
|
|
219
|
+
children: React.ReactNode;
|
|
220
|
+
}) {
|
|
221
|
+
return (
|
|
222
|
+
<section className="max-w-7xl mx-auto mt-16 animate-fade-up">
|
|
223
|
+
<div className="flex items-end justify-between mb-6">
|
|
224
|
+
<div>
|
|
225
|
+
{eyebrow && (
|
|
226
|
+
<div className="inline-flex items-center gap-1.5 text-xs font-semibold text-brand-magenta bg-brand-magenta/10 rounded-full px-3 py-1 mb-2">
|
|
227
|
+
{eyebrow}
|
|
228
|
+
</div>
|
|
229
|
+
)}
|
|
230
|
+
<h2 className="font-display text-2xl md:text-3xl font-bold tracking-tight">{title}</h2>
|
|
231
|
+
{subtitle && <p className="text-sm text-muted-foreground mt-1">{subtitle}</p>}
|
|
232
|
+
</div>
|
|
233
|
+
{href && (
|
|
234
|
+
<Link
|
|
235
|
+
to={href}
|
|
236
|
+
className="text-xs text-muted-foreground hover:text-foreground inline-flex items-center gap-1"
|
|
237
|
+
>
|
|
238
|
+
View all <ArrowRight className="w-3.5 h-3.5" />
|
|
239
|
+
</Link>
|
|
240
|
+
)}
|
|
241
|
+
</div>
|
|
242
|
+
{children}
|
|
243
|
+
</section>
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function BentoGrid({ products }: { products: any[] }) {
|
|
248
|
+
// 8 products into bento layout
|
|
249
|
+
if (products.length < 4) {
|
|
250
|
+
return (
|
|
251
|
+
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
|
|
252
|
+
{products.map((p) => (
|
|
253
|
+
<ProductCard key={p.id} product={p} aspect="square" />
|
|
254
|
+
))}
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
const [big, ...rest] = products;
|
|
259
|
+
return (
|
|
260
|
+
<div className="grid grid-cols-2 md:grid-cols-4 grid-rows-2 gap-4 md:h-[640px]">
|
|
261
|
+
<div className="col-span-2 row-span-2 md:row-span-2">
|
|
262
|
+
<ProductCard product={big} aspect="auto" className="h-full" priority />
|
|
263
|
+
</div>
|
|
264
|
+
{rest.slice(0, 4).map((p) => (
|
|
265
|
+
<ProductCard key={p.id} product={p} aspect="auto" className="h-full" />
|
|
266
|
+
))}
|
|
267
|
+
</div>
|
|
268
|
+
);
|
|
269
|
+
}
|