virastack 0.0.1 → 1.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.
- package/LICENSE +1 -1
- package/README.md +94 -18
- package/bin/virastack.js +7 -0
- package/package.json +43 -9
- package/src/commands/add.js +64 -0
- package/src/commands/init.js +286 -0
- package/src/i18n.js +231 -0
- package/src/index.js +64 -0
- package/src/utils/args.js +60 -0
- package/src/utils/copy-template.js +81 -0
- package/src/utils/exec.js +29 -0
- package/src/utils/git.js +38 -0
- package/src/utils/is-online.js +14 -0
- package/src/utils/package-info.js +12 -0
- package/src/utils/package-manager.js +33 -0
- package/src/utils/setup-env.js +25 -0
- package/src/utils/telemetry.js +86 -0
- package/src/utils/tools.js +48 -0
- package/src/utils/update-package-json.js +44 -0
- package/src/utils/update-readme.js +25 -0
- package/src/utils/validate-project-name.js +26 -0
- package/templates/nextjs/.changeset/README.md +8 -0
- package/templates/nextjs/.changeset/config.json +11 -0
- package/templates/nextjs/.env.example +6 -0
- package/templates/nextjs/.env.local +6 -0
- package/templates/nextjs/.eslintcache +1 -0
- package/templates/nextjs/.github/workflows/ci.yml +41 -0
- package/templates/nextjs/.husky/commit-msg +1 -0
- package/templates/nextjs/.husky/pre-commit +1 -0
- package/templates/nextjs/.husky/pre-push +1 -0
- package/templates/nextjs/.prettierignore +6 -0
- package/templates/nextjs/.prettierrc +42 -0
- package/templates/nextjs/.vscode/extensions.json +7 -0
- package/templates/nextjs/.vscode/settings.json +20 -0
- package/templates/nextjs/LICENSE +21 -0
- package/templates/nextjs/README.md +202 -0
- package/templates/nextjs/_gitignore +45 -0
- package/templates/nextjs/commitlint.config.js +6 -0
- package/templates/nextjs/eslint.config.mjs +27 -0
- package/templates/nextjs/knip.config.js +25 -0
- package/templates/nextjs/next-env.d.ts +6 -0
- package/templates/nextjs/next.config.ts +39 -0
- package/templates/nextjs/package.json +115 -0
- package/templates/nextjs/pnpm-lock.yaml +6467 -0
- package/templates/nextjs/pnpm-workspace.yaml +3 -0
- package/templates/nextjs/postcss.config.mjs +7 -0
- package/templates/nextjs/public/favicon.ico +0 -0
- package/templates/nextjs/public/llms.txt +96 -0
- package/templates/nextjs/public/logo.png +0 -0
- package/templates/nextjs/public/logo.webp +0 -0
- package/templates/nextjs/public/og.png +0 -0
- package/templates/nextjs/src/app/error.tsx +34 -0
- package/templates/nextjs/src/app/favicon.ico +0 -0
- package/templates/nextjs/src/app/global-error.tsx +32 -0
- package/templates/nextjs/src/app/layout.tsx +37 -0
- package/templates/nextjs/src/app/loading.tsx +12 -0
- package/templates/nextjs/src/app/manifest.ts +22 -0
- package/templates/nextjs/src/app/not-found.tsx +18 -0
- package/templates/nextjs/src/app/page.tsx +5 -0
- package/templates/nextjs/src/app/robots.ts +13 -0
- package/templates/nextjs/src/app/sitemap.ts +14 -0
- package/templates/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates/nextjs/src/components/ui/button.tsx +46 -0
- package/templates/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates/nextjs/src/components/ui/field.tsx +24 -0
- package/templates/nextjs/src/components/ui/index.ts +27 -0
- package/templates/nextjs/src/components/ui/input.tsx +23 -0
- package/templates/nextjs/src/components/ui/label.tsx +17 -0
- package/templates/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates/nextjs/src/components/ui/table.tsx +94 -0
- package/templates/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates/nextjs/src/config/seo.config.ts +37 -0
- package/templates/nextjs/src/config/site.config.ts +15 -0
- package/templates/nextjs/src/constants/index.ts +5 -0
- package/templates/nextjs/src/env.ts +41 -0
- package/templates/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates/nextjs/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/Features.tsx +39 -0
- package/templates/nextjs/src/features/landing/components/Hero.tsx +65 -0
- package/templates/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/nextjs/src/features/landing/components/ProjectFormDemo.tsx +62 -0
- package/templates/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/nextjs/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/nextjs/src/features/landing/components/UsersDemo.tsx +188 -0
- package/templates/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/nextjs/src/features/landing/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates/nextjs/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates/nextjs/src/helpers/format-date.ts +13 -0
- package/templates/nextjs/src/helpers/index.ts +1 -0
- package/templates/nextjs/src/hooks/index.ts +5 -0
- package/templates/nextjs/src/lib/api.ts +97 -0
- package/templates/nextjs/src/lib/query-client.ts +18 -0
- package/templates/nextjs/src/lib/utils.ts +12 -0
- package/templates/nextjs/src/providers/Providers.tsx +15 -0
- package/templates/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates/nextjs/src/providers/index.ts +1 -0
- package/templates/nextjs/src/proxy.ts +6 -0
- package/templates/nextjs/src/schemas/index.ts +5 -0
- package/templates/nextjs/src/stores/index.ts +5 -0
- package/templates/nextjs/src/styles/tailwind.css +122 -0
- package/templates/nextjs/src/types/site-config.types.ts +10 -0
- package/templates/nextjs/tsconfig.json +45 -0
- package/templates/nextjs/tsconfig.tsbuildinfo +1 -0
- package/templates/tanstack/.changeset/README.md +8 -0
- package/templates/tanstack/.changeset/config.json +11 -0
- package/templates/tanstack/.env.example +6 -0
- package/templates/tanstack/.env.local +6 -0
- package/templates/tanstack/.eslintcache +1 -0
- package/templates/tanstack/.github/workflows/ci.yml +41 -0
- package/templates/tanstack/.husky/commit-msg +1 -0
- package/templates/tanstack/.husky/pre-commit +1 -0
- package/templates/tanstack/.husky/pre-push +1 -0
- package/templates/tanstack/.prettierignore +7 -0
- package/templates/tanstack/.prettierrc +41 -0
- package/templates/tanstack/.vscode/extensions.json +7 -0
- package/templates/tanstack/.vscode/settings.json +22 -0
- package/templates/tanstack/LICENSE +21 -0
- package/templates/tanstack/README.md +204 -0
- package/templates/tanstack/_gitignore +52 -0
- package/templates/tanstack/commitlint.config.js +6 -0
- package/templates/tanstack/eslint.config.js +28 -0
- package/templates/tanstack/knip.config.js +22 -0
- package/templates/tanstack/package.json +130 -0
- package/templates/tanstack/pnpm-lock.yaml +5835 -0
- package/templates/tanstack/pnpm-workspace.yaml +3 -0
- package/templates/tanstack/public/favicon.ico +0 -0
- package/templates/tanstack/public/llms.txt +86 -0
- package/templates/tanstack/public/logo.png +0 -0
- package/templates/tanstack/public/logo.webp +0 -0
- package/templates/tanstack/public/og.png +0 -0
- package/templates/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates/tanstack/src/components/ui/button.tsx +47 -0
- package/templates/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates/tanstack/src/components/ui/field.tsx +24 -0
- package/templates/tanstack/src/components/ui/index.ts +27 -0
- package/templates/tanstack/src/components/ui/input.tsx +23 -0
- package/templates/tanstack/src/components/ui/label.tsx +17 -0
- package/templates/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates/tanstack/src/components/ui/table.tsx +94 -0
- package/templates/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates/tanstack/src/config/seo.config.ts +52 -0
- package/templates/tanstack/src/config/site.config.ts +15 -0
- package/templates/tanstack/src/constants/index.ts +5 -0
- package/templates/tanstack/src/env.ts +47 -0
- package/templates/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates/tanstack/src/features/landing/components/CartDemo.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/Features.tsx +39 -0
- package/templates/tanstack/src/features/landing/components/Hero.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates/tanstack/src/features/landing/components/ProjectFormDemo.tsx +63 -0
- package/templates/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates/tanstack/src/features/landing/components/Showcase.tsx +66 -0
- package/templates/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates/tanstack/src/features/landing/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates/tanstack/src/features/landing/schemas/project.schema.ts +7 -0
- package/templates/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates/tanstack/src/helpers/format-date.ts +13 -0
- package/templates/tanstack/src/helpers/index.ts +1 -0
- package/templates/tanstack/src/hooks/index.ts +5 -0
- package/templates/tanstack/src/lib/api.ts +97 -0
- package/templates/tanstack/src/lib/query-client.ts +18 -0
- package/templates/tanstack/src/lib/utils.ts +13 -0
- package/templates/tanstack/src/providers/Providers.tsx +12 -0
- package/templates/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates/tanstack/src/providers/devtools.tsx +6 -0
- package/templates/tanstack/src/providers/index.ts +1 -0
- package/templates/tanstack/src/providers/root-provider.ts +9 -0
- package/templates/tanstack/src/routeTree.gen.ts +123 -0
- package/templates/tanstack/src/router.tsx +27 -0
- package/templates/tanstack/src/routes/__root.tsx +78 -0
- package/templates/tanstack/src/routes/index.tsx +13 -0
- package/templates/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates/tanstack/src/schemas/index.ts +5 -0
- package/templates/tanstack/src/start.ts +20 -0
- package/templates/tanstack/src/stores/index.ts +5 -0
- package/templates/tanstack/src/styles/tailwind.css +123 -0
- package/templates/tanstack/src/types/site-config.types.ts +10 -0
- package/templates/tanstack/tsconfig.json +25 -0
- package/templates/tanstack/tsr.config.json +3 -0
- package/templates/tanstack/vite.config.ts +12 -0
- package/templates-i18n/nextjs/.changeset/README.md +8 -0
- package/templates-i18n/nextjs/.changeset/config.json +11 -0
- package/templates-i18n/nextjs/.env.example +6 -0
- package/templates-i18n/nextjs/.eslintcache +1 -0
- package/templates-i18n/nextjs/.github/workflows/ci.yml +41 -0
- package/templates-i18n/nextjs/.husky/commit-msg +1 -0
- package/templates-i18n/nextjs/.husky/pre-commit +1 -0
- package/templates-i18n/nextjs/.husky/pre-push +1 -0
- package/templates-i18n/nextjs/.prettierignore +6 -0
- package/templates-i18n/nextjs/.prettierrc +42 -0
- package/templates-i18n/nextjs/.vscode/extensions.json +7 -0
- package/templates-i18n/nextjs/.vscode/settings.json +20 -0
- package/templates-i18n/nextjs/LICENSE +21 -0
- package/templates-i18n/nextjs/README.md +202 -0
- package/templates-i18n/nextjs/_gitignore +45 -0
- package/templates-i18n/nextjs/commitlint.config.js +6 -0
- package/templates-i18n/nextjs/eslint.config.mjs +27 -0
- package/templates-i18n/nextjs/knip.config.js +29 -0
- package/templates-i18n/nextjs/next-env.d.ts +6 -0
- package/templates-i18n/nextjs/next.config.ts +42 -0
- package/templates-i18n/nextjs/package.json +117 -0
- package/templates-i18n/nextjs/pnpm-lock.yaml +6854 -0
- package/templates-i18n/nextjs/pnpm-workspace.yaml +3 -0
- package/templates-i18n/nextjs/postcss.config.mjs +7 -0
- package/templates-i18n/nextjs/public/favicon.ico +0 -0
- package/templates-i18n/nextjs/public/llms.txt +96 -0
- package/templates-i18n/nextjs/public/logo.png +0 -0
- package/templates-i18n/nextjs/public/logo.webp +0 -0
- package/templates-i18n/nextjs/public/og.png +0 -0
- package/templates-i18n/nextjs/src/app/[locale]/error.tsx +34 -0
- package/templates-i18n/nextjs/src/app/[locale]/layout.tsx +60 -0
- package/templates-i18n/nextjs/src/app/[locale]/loading.tsx +12 -0
- package/templates-i18n/nextjs/src/app/[locale]/not-found.tsx +18 -0
- package/templates-i18n/nextjs/src/app/[locale]/page.tsx +10 -0
- package/templates-i18n/nextjs/src/app/favicon.ico +0 -0
- package/templates-i18n/nextjs/src/app/global-error.tsx +32 -0
- package/templates-i18n/nextjs/src/app/manifest.ts +22 -0
- package/templates-i18n/nextjs/src/app/robots.ts +13 -0
- package/templates-i18n/nextjs/src/app/sitemap.ts +14 -0
- package/templates-i18n/nextjs/src/components/shared/LanguageSwitcher.tsx +48 -0
- package/templates-i18n/nextjs/src/components/shared/ThemeToggle.tsx +30 -0
- package/templates-i18n/nextjs/src/components/ui/avatar.tsx +54 -0
- package/templates-i18n/nextjs/src/components/ui/button.tsx +46 -0
- package/templates-i18n/nextjs/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/nextjs/src/components/ui/field.tsx +24 -0
- package/templates-i18n/nextjs/src/components/ui/index.ts +27 -0
- package/templates-i18n/nextjs/src/components/ui/input.tsx +23 -0
- package/templates-i18n/nextjs/src/components/ui/label.tsx +17 -0
- package/templates-i18n/nextjs/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/nextjs/src/components/ui/table.tsx +94 -0
- package/templates-i18n/nextjs/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/nextjs/src/config/seo.config.ts +37 -0
- package/templates-i18n/nextjs/src/config/site.config.ts +15 -0
- package/templates-i18n/nextjs/src/constants/index.ts +5 -0
- package/templates-i18n/nextjs/src/env.ts +41 -0
- package/templates-i18n/nextjs/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/nextjs/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/components/CartDemo.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/components/Features.tsx +84 -0
- package/templates-i18n/nextjs/src/features/landing/components/Hero.tsx +76 -0
- package/templates-i18n/nextjs/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/nextjs/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/nextjs/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/Showcase.tsx +83 -0
- package/templates-i18n/nextjs/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/nextjs/src/features/landing/data/features.data.tsx +47 -0
- package/templates-i18n/nextjs/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/nextjs/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/hooks/use-users.ts +16 -0
- package/templates-i18n/nextjs/src/features/landing/icons/index.ts +6 -0
- package/templates-i18n/nextjs/src/features/landing/icons/nextjs.tsx +68 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/nextjs/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/nextjs/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/nextjs/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/nextjs/src/features/landing/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/schemas/project.schema.ts +9 -0
- package/templates-i18n/nextjs/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/nextjs/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/nextjs/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/nextjs/src/helpers/format-date.ts +13 -0
- package/templates-i18n/nextjs/src/helpers/index.ts +1 -0
- package/templates-i18n/nextjs/src/hooks/index.ts +5 -0
- package/templates-i18n/nextjs/src/i18n/request.ts +16 -0
- package/templates-i18n/nextjs/src/i18n/routing.ts +11 -0
- package/templates-i18n/nextjs/src/lib/api.ts +97 -0
- package/templates-i18n/nextjs/src/lib/query-client.ts +18 -0
- package/templates-i18n/nextjs/src/lib/utils.ts +12 -0
- package/templates-i18n/nextjs/src/messages/en.json +55 -0
- package/templates-i18n/nextjs/src/messages/tr.json +55 -0
- package/templates-i18n/nextjs/src/providers/Providers.tsx +15 -0
- package/templates-i18n/nextjs/src/providers/QueryProvider.tsx +19 -0
- package/templates-i18n/nextjs/src/providers/ThemeProvider.tsx +9 -0
- package/templates-i18n/nextjs/src/providers/index.ts +1 -0
- package/templates-i18n/nextjs/src/proxy.ts +16 -0
- package/templates-i18n/nextjs/src/schemas/index.ts +5 -0
- package/templates-i18n/nextjs/src/stores/index.ts +5 -0
- package/templates-i18n/nextjs/src/styles/tailwind.css +122 -0
- package/templates-i18n/nextjs/src/types/site-config.types.ts +10 -0
- package/templates-i18n/nextjs/tsconfig.json +45 -0
- package/templates-i18n/nextjs/tsconfig.tsbuildinfo +1 -0
- package/templates-i18n/tanstack/.changeset/README.md +8 -0
- package/templates-i18n/tanstack/.changeset/config.json +11 -0
- package/templates-i18n/tanstack/.env.example +6 -0
- package/templates-i18n/tanstack/.eslintcache +1 -0
- package/templates-i18n/tanstack/.github/workflows/ci.yml +41 -0
- package/templates-i18n/tanstack/.husky/commit-msg +1 -0
- package/templates-i18n/tanstack/.husky/pre-commit +1 -0
- package/templates-i18n/tanstack/.husky/pre-push +1 -0
- package/templates-i18n/tanstack/.prettierignore +7 -0
- package/templates-i18n/tanstack/.prettierrc +41 -0
- package/templates-i18n/tanstack/.vscode/extensions.json +7 -0
- package/templates-i18n/tanstack/.vscode/settings.json +22 -0
- package/templates-i18n/tanstack/LICENSE +21 -0
- package/templates-i18n/tanstack/README.md +204 -0
- package/templates-i18n/tanstack/_gitignore +52 -0
- package/templates-i18n/tanstack/commitlint.config.js +6 -0
- package/templates-i18n/tanstack/eslint.config.js +29 -0
- package/templates-i18n/tanstack/knip.config.js +26 -0
- package/templates-i18n/tanstack/messages/en.json +65 -0
- package/templates-i18n/tanstack/messages/tr.json +65 -0
- package/templates-i18n/tanstack/package.json +132 -0
- package/templates-i18n/tanstack/pnpm-lock.yaml +5998 -0
- package/templates-i18n/tanstack/pnpm-workspace.yaml +3 -0
- package/templates-i18n/tanstack/project.inlang/README.md +157 -0
- package/templates-i18n/tanstack/project.inlang/settings.json +9 -0
- package/templates-i18n/tanstack/public/favicon.ico +0 -0
- package/templates-i18n/tanstack/public/llms.txt +86 -0
- package/templates-i18n/tanstack/public/logo.png +0 -0
- package/templates-i18n/tanstack/public/logo.webp +0 -0
- package/templates-i18n/tanstack/public/og.png +0 -0
- package/templates-i18n/tanstack/src/components/shared/DefaultCatchBoundary.tsx +36 -0
- package/templates-i18n/tanstack/src/components/shared/LanguageSwitcher.tsx +57 -0
- package/templates-i18n/tanstack/src/components/shared/NotFound.tsx +18 -0
- package/templates-i18n/tanstack/src/components/shared/Pending.tsx +13 -0
- package/templates-i18n/tanstack/src/components/shared/ThemeToggle.tsx +31 -0
- package/templates-i18n/tanstack/src/components/ui/avatar.tsx +52 -0
- package/templates-i18n/tanstack/src/components/ui/button.tsx +47 -0
- package/templates-i18n/tanstack/src/components/ui/dialog.tsx +107 -0
- package/templates-i18n/tanstack/src/components/ui/field.tsx +24 -0
- package/templates-i18n/tanstack/src/components/ui/index.ts +27 -0
- package/templates-i18n/tanstack/src/components/ui/input.tsx +23 -0
- package/templates-i18n/tanstack/src/components/ui/label.tsx +17 -0
- package/templates-i18n/tanstack/src/components/ui/skeleton.tsx +13 -0
- package/templates-i18n/tanstack/src/components/ui/table.tsx +94 -0
- package/templates-i18n/tanstack/src/components/ui/tabs.tsx +46 -0
- package/templates-i18n/tanstack/src/config/seo.config.ts +52 -0
- package/templates-i18n/tanstack/src/config/site.config.ts +15 -0
- package/templates-i18n/tanstack/src/constants/index.ts +5 -0
- package/templates-i18n/tanstack/src/env.ts +47 -0
- package/templates-i18n/tanstack/src/features/landing/api/get-users.api.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/api/index.ts +2 -0
- package/templates-i18n/tanstack/src/features/landing/api/query-keys.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/components/CartDemo.tsx +67 -0
- package/templates-i18n/tanstack/src/features/landing/components/Features.tsx +79 -0
- package/templates-i18n/tanstack/src/features/landing/components/Hero.tsx +68 -0
- package/templates-i18n/tanstack/src/features/landing/components/LandingPage.tsx +17 -0
- package/templates-i18n/tanstack/src/features/landing/components/ProjectFormDemo.tsx +64 -0
- package/templates-i18n/tanstack/src/features/landing/components/Reveal.tsx +83 -0
- package/templates-i18n/tanstack/src/features/landing/components/Showcase.tsx +81 -0
- package/templates-i18n/tanstack/src/features/landing/components/UsersDemo.tsx +191 -0
- package/templates-i18n/tanstack/src/features/landing/data/features.data.tsx +45 -0
- package/templates-i18n/tanstack/src/features/landing/data/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/get-initials.ts +15 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/index.ts +7 -0
- package/templates-i18n/tanstack/src/features/landing/helpers/motion.ts +21 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/hooks/use-users.ts +19 -0
- package/templates-i18n/tanstack/src/features/landing/icons/index.ts +5 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react-hook-form.tsx +18 -0
- package/templates-i18n/tanstack/src/features/landing/icons/react.tsx +20 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tailwind.tsx +14 -0
- package/templates-i18n/tanstack/src/features/landing/icons/tanstack.tsx +321 -0
- package/templates-i18n/tanstack/src/features/landing/icons/typescript.tsx +16 -0
- package/templates-i18n/tanstack/src/features/landing/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/schemas/project.schema.ts +10 -0
- package/templates-i18n/tanstack/src/features/landing/stores/counter.store.ts +18 -0
- package/templates-i18n/tanstack/src/features/landing/stores/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/index.ts +1 -0
- package/templates-i18n/tanstack/src/features/landing/types/user.types.ts +9 -0
- package/templates-i18n/tanstack/src/helpers/format-date.ts +13 -0
- package/templates-i18n/tanstack/src/helpers/index.ts +1 -0
- package/templates-i18n/tanstack/src/hooks/index.ts +5 -0
- package/templates-i18n/tanstack/src/lib/api.ts +97 -0
- package/templates-i18n/tanstack/src/lib/query-client.ts +18 -0
- package/templates-i18n/tanstack/src/lib/utils.ts +13 -0
- package/templates-i18n/tanstack/src/paraglide/README.md +162 -0
- package/templates-i18n/tanstack/src/providers/Providers.tsx +12 -0
- package/templates-i18n/tanstack/src/providers/ThemeProvider.tsx +98 -0
- package/templates-i18n/tanstack/src/providers/devtools.tsx +6 -0
- package/templates-i18n/tanstack/src/providers/index.ts +1 -0
- package/templates-i18n/tanstack/src/providers/root-provider.ts +9 -0
- package/templates-i18n/tanstack/src/routeTree.gen.ts +179 -0
- package/templates-i18n/tanstack/src/router.tsx +27 -0
- package/templates-i18n/tanstack/src/routes/$lang/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/$lang.tsx +22 -0
- package/templates-i18n/tanstack/src/routes/__root.tsx +80 -0
- package/templates-i18n/tanstack/src/routes/index.tsx +13 -0
- package/templates-i18n/tanstack/src/routes/robots[.]txt.ts +21 -0
- package/templates-i18n/tanstack/src/routes/site[.]webmanifest.ts +32 -0
- package/templates-i18n/tanstack/src/routes/sitemap[.]xml.ts +27 -0
- package/templates-i18n/tanstack/src/schemas/index.ts +5 -0
- package/templates-i18n/tanstack/src/server.ts +10 -0
- package/templates-i18n/tanstack/src/start.ts +20 -0
- package/templates-i18n/tanstack/src/stores/index.ts +5 -0
- package/templates-i18n/tanstack/src/styles/tailwind.css +123 -0
- package/templates-i18n/tanstack/src/types/site-config.types.ts +10 -0
- package/templates-i18n/tanstack/tsconfig.json +26 -0
- package/templates-i18n/tanstack/tsr.config.json +3 -0
- package/templates-i18n/tanstack/vite.config.ts +31 -0
- package/index.js +0 -3
- package/public/start.jpg +0 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
|
+
import { Controller, useForm } from "react-hook-form";
|
|
5
|
+
import { toast } from "sonner";
|
|
6
|
+
|
|
7
|
+
import { Button } from "@/components/ui/button";
|
|
8
|
+
import { FieldDescription, FieldError } from "@/components/ui/field";
|
|
9
|
+
import { Input } from "@/components/ui/input";
|
|
10
|
+
import { Label } from "@/components/ui/label";
|
|
11
|
+
import type { ProjectInput } from "@/features/landing/schemas/project.schema";
|
|
12
|
+
import { getProjectSchema } from "@/features/landing/schemas/project.schema";
|
|
13
|
+
import * as m from "@/paraglide/messages.js";
|
|
14
|
+
|
|
15
|
+
export function ProjectFormDemo() {
|
|
16
|
+
const {
|
|
17
|
+
control,
|
|
18
|
+
handleSubmit,
|
|
19
|
+
reset,
|
|
20
|
+
formState: { errors, isSubmitting },
|
|
21
|
+
} = useForm<ProjectInput>({
|
|
22
|
+
resolver: zodResolver(getProjectSchema()),
|
|
23
|
+
defaultValues: { name: "" },
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
async function onSubmit(values: ProjectInput) {
|
|
27
|
+
await new Promise((resolve) => setTimeout(resolve, 600));
|
|
28
|
+
toast.success(m.project_success({ name: values.name }));
|
|
29
|
+
reset();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<form onSubmit={handleSubmit(onSubmit)} noValidate className="flex flex-col gap-4">
|
|
34
|
+
<div className="flex flex-col gap-2.5 text-left">
|
|
35
|
+
<Label htmlFor="name">{m.project_label()}</Label>
|
|
36
|
+
<Controller
|
|
37
|
+
control={control}
|
|
38
|
+
name="name"
|
|
39
|
+
render={({ field }) => (
|
|
40
|
+
<Input
|
|
41
|
+
id="name"
|
|
42
|
+
type="text"
|
|
43
|
+
placeholder={m.project_placeholder()}
|
|
44
|
+
aria-invalid={!!errors.name}
|
|
45
|
+
value={field.value}
|
|
46
|
+
onChange={(event) => field.onChange(event.target.value)}
|
|
47
|
+
onBlur={field.onBlur}
|
|
48
|
+
ref={field.ref}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
/>
|
|
52
|
+
{errors.name ? (
|
|
53
|
+
<FieldError>{errors.name.message}</FieldError>
|
|
54
|
+
) : (
|
|
55
|
+
<FieldDescription>{m.project_desc()}</FieldDescription>
|
|
56
|
+
)}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
60
|
+
{isSubmitting ? m.project_submitting() : m.project_submit()}
|
|
61
|
+
</Button>
|
|
62
|
+
</form>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { motion } from "framer-motion";
|
|
6
|
+
|
|
7
|
+
import { fadeInUp, staggerContainer, viewport } from "@/features/landing/helpers";
|
|
8
|
+
|
|
9
|
+
type RevealProps = {
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
className?: string;
|
|
12
|
+
/** Mount animation delay in seconds (hero-style). Ignored when `mode="view"`. */
|
|
13
|
+
delay?: number;
|
|
14
|
+
/** `mount` = animate on load; `view` = animate when scrolled into view. */
|
|
15
|
+
mode?: "mount" | "view";
|
|
16
|
+
/** Initial Y offset for mount animations. */
|
|
17
|
+
y?: number;
|
|
18
|
+
/**
|
|
19
|
+
* When false (mount mode), only translate — keep opacity at 1 so LCP text
|
|
20
|
+
* is painted on first frame. Default true.
|
|
21
|
+
*/
|
|
22
|
+
fadeOpacity?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Client-only motion shell. Keep copy/markup in Server Components and wrap
|
|
27
|
+
* animated regions with this so the page stays SSR-first.
|
|
28
|
+
*/
|
|
29
|
+
export function Reveal({
|
|
30
|
+
children,
|
|
31
|
+
className,
|
|
32
|
+
delay = 0,
|
|
33
|
+
mode = "view",
|
|
34
|
+
y = 12,
|
|
35
|
+
fadeOpacity = true,
|
|
36
|
+
}: RevealProps) {
|
|
37
|
+
if (mode === "mount") {
|
|
38
|
+
return (
|
|
39
|
+
<motion.div
|
|
40
|
+
className={className}
|
|
41
|
+
initial={fadeOpacity ? { opacity: 0, y } : { y }}
|
|
42
|
+
animate={fadeOpacity ? { opacity: 1, y: 0 } : { y: 0 }}
|
|
43
|
+
transition={{ duration: 0.45, delay, ease: "easeOut" }}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</motion.div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<motion.div
|
|
52
|
+
className={className}
|
|
53
|
+
initial="hidden"
|
|
54
|
+
whileInView="visible"
|
|
55
|
+
viewport={viewport}
|
|
56
|
+
variants={fadeInUp}
|
|
57
|
+
>
|
|
58
|
+
{children}
|
|
59
|
+
</motion.div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function RevealGroup({ children, className }: { children: ReactNode; className?: string }) {
|
|
64
|
+
return (
|
|
65
|
+
<motion.div
|
|
66
|
+
className={className}
|
|
67
|
+
initial="hidden"
|
|
68
|
+
whileInView="visible"
|
|
69
|
+
viewport={viewport}
|
|
70
|
+
variants={staggerContainer}
|
|
71
|
+
>
|
|
72
|
+
{children}
|
|
73
|
+
</motion.div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function RevealItem({ children, className }: { children: ReactNode; className?: string }) {
|
|
78
|
+
return (
|
|
79
|
+
<motion.div className={className} variants={fadeInUp}>
|
|
80
|
+
{children}
|
|
81
|
+
</motion.div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { siteConfig } from "@/config/site.config";
|
|
2
|
+
|
|
3
|
+
import { formatDate } from "@/helpers";
|
|
4
|
+
|
|
5
|
+
import { CartDemo } from "@/features/landing/components/CartDemo";
|
|
6
|
+
import { ProjectFormDemo } from "@/features/landing/components/ProjectFormDemo";
|
|
7
|
+
import { RevealGroup, RevealItem } from "@/features/landing/components/Reveal";
|
|
8
|
+
import { UsersDemo } from "@/features/landing/components/UsersDemo";
|
|
9
|
+
import { featureCardClassName } from "@/features/landing/helpers";
|
|
10
|
+
import * as m from "@/paraglide/messages.js";
|
|
11
|
+
import { getLocale } from "@/paraglide/runtime.js";
|
|
12
|
+
|
|
13
|
+
export function Showcase() {
|
|
14
|
+
const currentLang = getLocale();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<section id="showcase" className="mx-auto max-w-5xl px-6 pb-16">
|
|
18
|
+
<RevealGroup className="mb-10 text-center">
|
|
19
|
+
<RevealItem>
|
|
20
|
+
<h2 className="text-3xl font-semibold tracking-tight text-balance">
|
|
21
|
+
{m.showcase_title()}
|
|
22
|
+
</h2>
|
|
23
|
+
</RevealItem>
|
|
24
|
+
<RevealItem>
|
|
25
|
+
<p className="mx-auto mt-3 max-w-2xl text-base text-balance text-muted-foreground">
|
|
26
|
+
{m.showcase_subtitle()}
|
|
27
|
+
</p>
|
|
28
|
+
</RevealItem>
|
|
29
|
+
</RevealGroup>
|
|
30
|
+
|
|
31
|
+
<RevealGroup className="grid grid-cols-1 gap-6 lg:grid-cols-2 lg:items-stretch">
|
|
32
|
+
<RevealItem className={`${featureCardClassName} h-full min-h-0 [&>*]:min-h-0 [&>*]:flex-1`}>
|
|
33
|
+
<UsersDemo />
|
|
34
|
+
</RevealItem>
|
|
35
|
+
|
|
36
|
+
<div className="flex flex-col gap-6">
|
|
37
|
+
<RevealItem className={featureCardClassName}>
|
|
38
|
+
<div className="mb-4">
|
|
39
|
+
<h4 className="text-base font-semibold text-foreground md:text-lg">
|
|
40
|
+
{m.showcase_project_title()}
|
|
41
|
+
</h4>
|
|
42
|
+
<p className="mt-1 text-sm text-pretty text-muted-foreground">
|
|
43
|
+
{m.showcase_project_desc_1()}
|
|
44
|
+
<span className="font-semibold text-foreground">React Hook Form</span>
|
|
45
|
+
{m.showcase_project_desc_2()}
|
|
46
|
+
<span className="font-semibold text-foreground">Zod</span>
|
|
47
|
+
{m.showcase_project_desc_3()}
|
|
48
|
+
</p>
|
|
49
|
+
</div>
|
|
50
|
+
<ProjectFormDemo />
|
|
51
|
+
</RevealItem>
|
|
52
|
+
|
|
53
|
+
<RevealItem className={`${featureCardClassName} flex flex-col`}>
|
|
54
|
+
<div>
|
|
55
|
+
<h4 className="text-base font-semibold text-foreground md:text-lg">
|
|
56
|
+
{m.showcase_state_title()}
|
|
57
|
+
</h4>
|
|
58
|
+
<p className="mt-1 text-sm text-pretty text-muted-foreground">
|
|
59
|
+
{m.showcase_state_desc_1()}
|
|
60
|
+
<span className="font-semibold text-foreground">Zustand</span>
|
|
61
|
+
{m.showcase_state_desc_2()}
|
|
62
|
+
</p>
|
|
63
|
+
</div>
|
|
64
|
+
<CartDemo />
|
|
65
|
+
</RevealItem>
|
|
66
|
+
</div>
|
|
67
|
+
</RevealGroup>
|
|
68
|
+
|
|
69
|
+
<div className="mt-20 flex flex-col items-center justify-center gap-2 text-sm text-muted-foreground">
|
|
70
|
+
<p suppressHydrationWarning>
|
|
71
|
+
{m.showcase_footer_license({ year: new Date().getFullYear(), name: siteConfig.name })}
|
|
72
|
+
</p>
|
|
73
|
+
<p className="italic" suppressHydrationWarning>
|
|
74
|
+
{m.showcase_footer_built({
|
|
75
|
+
date: formatDate(new Date(), currentLang === "tr" ? "tr-TR" : "en-US"),
|
|
76
|
+
})}
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</section>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
|
|
5
|
+
import { RefreshCwIcon, SearchIcon } from "lucide-react";
|
|
6
|
+
import { parseAsString, useQueryState } from "nuqs";
|
|
7
|
+
|
|
8
|
+
import { ApiError } from "@/lib/api";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
Avatar,
|
|
12
|
+
AvatarFallback,
|
|
13
|
+
Button,
|
|
14
|
+
Input,
|
|
15
|
+
Skeleton,
|
|
16
|
+
Table,
|
|
17
|
+
TableBody,
|
|
18
|
+
TableCell,
|
|
19
|
+
TableHead,
|
|
20
|
+
TableHeader,
|
|
21
|
+
TableRow,
|
|
22
|
+
} from "@/components/ui";
|
|
23
|
+
import { getInitials } from "@/features/landing/helpers";
|
|
24
|
+
import { useUsers } from "@/features/landing/hooks";
|
|
25
|
+
import * as m from "@/paraglide/messages.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Demonstrates server-state fetching & caching with TanStack Query.
|
|
29
|
+
*/
|
|
30
|
+
export function UsersDemo() {
|
|
31
|
+
const { data, isLoading, isError, error, refetch, isFetching } = useUsers();
|
|
32
|
+
const [search, setSearch] = useQueryState("q", parseAsString.withDefault(""));
|
|
33
|
+
|
|
34
|
+
const filteredData = data?.filter(
|
|
35
|
+
(user) =>
|
|
36
|
+
user.name.toLowerCase().includes(search.toLowerCase()) ||
|
|
37
|
+
user.email.toLowerCase().includes(search.toLowerCase()) ||
|
|
38
|
+
user.username.toLowerCase().includes(search.toLowerCase()),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const header = (
|
|
42
|
+
<div className="mb-4">
|
|
43
|
+
<h4 className="text-base font-semibold text-foreground md:text-lg">{m.users_title()}</h4>
|
|
44
|
+
<p className="mt-1 text-sm text-pretty text-muted-foreground">
|
|
45
|
+
{m.users_desc_1()}
|
|
46
|
+
<span className="font-semibold text-foreground">TanStack Query</span>
|
|
47
|
+
{m.users_desc_2()}
|
|
48
|
+
<span className="font-semibold text-foreground">nuqs</span>
|
|
49
|
+
{m.users_desc_3()}
|
|
50
|
+
</p>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const toolbar = (
|
|
55
|
+
<div className="mb-3 flex items-center justify-between gap-2">
|
|
56
|
+
<div className="relative w-full max-w-[200px] min-w-0">
|
|
57
|
+
<SearchIcon className="absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" />
|
|
58
|
+
<Input
|
|
59
|
+
type="search"
|
|
60
|
+
placeholder={m.users_search()}
|
|
61
|
+
className="h-8 w-full bg-background/50 pl-8 text-xs"
|
|
62
|
+
value={search}
|
|
63
|
+
onChange={(event) => setSearch(event.target.value || null)}
|
|
64
|
+
/>
|
|
65
|
+
</div>
|
|
66
|
+
<Button
|
|
67
|
+
variant="outline"
|
|
68
|
+
size="sm"
|
|
69
|
+
className="h-8 gap-1.5 text-xs"
|
|
70
|
+
onClick={() => refetch()}
|
|
71
|
+
disabled={isFetching || isLoading}
|
|
72
|
+
>
|
|
73
|
+
<RefreshCwIcon className={`size-3 ${isFetching ? "animate-spin" : ""}`} />
|
|
74
|
+
{isFetching ? m.users_refreshing() : m.users_refresh()}
|
|
75
|
+
</Button>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const tableShell = (children: ReactNode) => (
|
|
80
|
+
<div className="min-h-0 flex-1 overflow-hidden rounded border border-border/40 bg-background">
|
|
81
|
+
<Table>{children}</Table>
|
|
82
|
+
</div>
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
if (isLoading) {
|
|
86
|
+
return (
|
|
87
|
+
<div className="flex h-full min-h-0 flex-col">
|
|
88
|
+
{header}
|
|
89
|
+
{toolbar}
|
|
90
|
+
{tableShell(
|
|
91
|
+
<>
|
|
92
|
+
<TableHeader className="bg-muted/40">
|
|
93
|
+
<TableRow className="hover:bg-transparent">
|
|
94
|
+
<TableHead className="h-9 px-3 text-xs">{m.users_col_user()}</TableHead>
|
|
95
|
+
<TableHead className="h-9 px-3 text-right text-xs">{m.users_col_city()}</TableHead>
|
|
96
|
+
</TableRow>
|
|
97
|
+
</TableHeader>
|
|
98
|
+
<TableBody>
|
|
99
|
+
{Array.from({ length: 5 }).map((_, index) => (
|
|
100
|
+
<TableRow key={index} className="hover:bg-transparent">
|
|
101
|
+
<TableCell className="px-3 py-3">
|
|
102
|
+
<div className="flex items-center gap-2.5">
|
|
103
|
+
<Skeleton className="h-8 w-8 rounded-full" />
|
|
104
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1.5">
|
|
105
|
+
<Skeleton className="h-3.5 w-24" />
|
|
106
|
+
<Skeleton className="h-3 w-32" />
|
|
107
|
+
</div>
|
|
108
|
+
</div>
|
|
109
|
+
</TableCell>
|
|
110
|
+
<TableCell className="px-3 py-3 text-right">
|
|
111
|
+
<Skeleton className="ml-auto h-3.5 w-16" />
|
|
112
|
+
</TableCell>
|
|
113
|
+
</TableRow>
|
|
114
|
+
))}
|
|
115
|
+
</TableBody>
|
|
116
|
+
</>,
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (isError) {
|
|
123
|
+
const message =
|
|
124
|
+
error instanceof ApiError
|
|
125
|
+
? m.users_error_status({ status: error.status.toString() })
|
|
126
|
+
: m.users_error_network();
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div className="flex h-full min-h-0 flex-col">
|
|
130
|
+
{header}
|
|
131
|
+
{toolbar}
|
|
132
|
+
{tableShell(
|
|
133
|
+
<TableBody>
|
|
134
|
+
<TableRow className="hover:bg-transparent">
|
|
135
|
+
<TableCell colSpan={2} className="py-8 text-center text-sm text-destructive">
|
|
136
|
+
{message}
|
|
137
|
+
</TableCell>
|
|
138
|
+
</TableRow>
|
|
139
|
+
</TableBody>,
|
|
140
|
+
)}
|
|
141
|
+
</div>
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<div className="flex h-full min-h-0 flex-col">
|
|
147
|
+
{header}
|
|
148
|
+
{toolbar}
|
|
149
|
+
{tableShell(
|
|
150
|
+
<>
|
|
151
|
+
<TableHeader className="bg-muted/40">
|
|
152
|
+
<TableRow className="hover:bg-transparent">
|
|
153
|
+
<TableHead className="h-9 px-3 text-xs">{m.users_col_user()}</TableHead>
|
|
154
|
+
<TableHead className="h-9 px-3 text-right text-xs">{m.users_col_city()}</TableHead>
|
|
155
|
+
</TableRow>
|
|
156
|
+
</TableHeader>
|
|
157
|
+
<TableBody>
|
|
158
|
+
{filteredData?.length === 0 ? (
|
|
159
|
+
<TableRow className="hover:bg-transparent">
|
|
160
|
+
<TableCell colSpan={2} className="py-8 text-center text-sm text-muted-foreground">
|
|
161
|
+
{m.users_empty()}
|
|
162
|
+
</TableCell>
|
|
163
|
+
</TableRow>
|
|
164
|
+
) : (
|
|
165
|
+
filteredData?.map((user) => (
|
|
166
|
+
<TableRow key={user.id}>
|
|
167
|
+
<TableCell className="px-3 py-2.5">
|
|
168
|
+
<div className="flex min-w-0 items-center gap-2.5">
|
|
169
|
+
<Avatar className="h-8 w-8 shrink-0">
|
|
170
|
+
<AvatarFallback className="bg-primary/10 text-[11px] font-medium text-primary">
|
|
171
|
+
{getInitials(user.name)}
|
|
172
|
+
</AvatarFallback>
|
|
173
|
+
</Avatar>
|
|
174
|
+
<div className="min-w-0">
|
|
175
|
+
<p className="truncate text-sm font-medium text-foreground">{user.name}</p>
|
|
176
|
+
<p className="truncate text-xs text-muted-foreground">@{user.username}</p>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
</TableCell>
|
|
180
|
+
<TableCell className="px-3 py-2.5 text-right text-xs text-muted-foreground">
|
|
181
|
+
{user.address.city}
|
|
182
|
+
</TableCell>
|
|
183
|
+
</TableRow>
|
|
184
|
+
))
|
|
185
|
+
)}
|
|
186
|
+
</TableBody>
|
|
187
|
+
</>,
|
|
188
|
+
)}
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ReactHookFormIcon,
|
|
3
|
+
TailwindIcon,
|
|
4
|
+
TanstackIcon,
|
|
5
|
+
TypescriptIcon,
|
|
6
|
+
} from "@/features/landing/icons";
|
|
7
|
+
|
|
8
|
+
export const FEATURES = [
|
|
9
|
+
{
|
|
10
|
+
title: "TanStack Start",
|
|
11
|
+
description:
|
|
12
|
+
"File-based routing, type-safe loaders, and nested layouts with end-to-end SSR on TanStack Router.",
|
|
13
|
+
icon: <TanstackIcon className="size-7 text-primary" />,
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
title: "Type-Safe Everything",
|
|
17
|
+
description:
|
|
18
|
+
"End-to-end type safety from environment variables to API responses with TypeScript and Zod.",
|
|
19
|
+
icon: <TypescriptIcon className="size-7 text-primary" />,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
title: "TanStack Query",
|
|
23
|
+
description:
|
|
24
|
+
"Server-state caching and synchronization, plus URL state via nuqs - all wired and ready to use.",
|
|
25
|
+
icon: <TanstackIcon className="size-7 text-primary" />,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
title: "Tailwind & shadcn/ui",
|
|
29
|
+
description:
|
|
30
|
+
"shadcn/ui patterns on Base UI primitives - Tailwind CSS v4, accessible, and fully yours to own.",
|
|
31
|
+
icon: <TailwindIcon className="size-7 text-primary" />,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
title: "React Hook Form",
|
|
35
|
+
description:
|
|
36
|
+
"Performant, flexible, and extensible forms with easy-to-use validation integration.",
|
|
37
|
+
icon: <ReactHookFormIcon className="size-7 text-primary" />,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
title: "ViraStack AI",
|
|
41
|
+
description:
|
|
42
|
+
"Pre-configured agent context - coding rules, design skills, and AGENTS.md so any AI assistant matches this codebase.",
|
|
43
|
+
icon: <img src="/logo.webp" alt="ViraStack AI" className="size-7 object-contain" />,
|
|
44
|
+
},
|
|
45
|
+
] as const;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { FEATURES } from "@/features/landing/data/features.data";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function getInitials(name: string) {
|
|
2
|
+
const parts = name.trim().split(/\s+/).filter(Boolean);
|
|
3
|
+
const first = parts[0];
|
|
4
|
+
const last = parts[parts.length - 1];
|
|
5
|
+
|
|
6
|
+
if (!first) {
|
|
7
|
+
return "?";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!last || parts.length === 1) {
|
|
11
|
+
return first.charAt(0).toUpperCase();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return `${first.charAt(0)}${last.charAt(0)}`.toUpperCase();
|
|
15
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const fadeInUp = {
|
|
2
|
+
hidden: { opacity: 0, y: 12, filter: "blur(4px)" },
|
|
3
|
+
visible: {
|
|
4
|
+
opacity: 1,
|
|
5
|
+
y: 0,
|
|
6
|
+
filter: "blur(0px)",
|
|
7
|
+
transition: { duration: 0.4, ease: "easeOut" as const },
|
|
8
|
+
},
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
11
|
+
export const staggerContainer = {
|
|
12
|
+
hidden: {},
|
|
13
|
+
visible: {
|
|
14
|
+
transition: { staggerChildren: 0.1 },
|
|
15
|
+
},
|
|
16
|
+
} as const;
|
|
17
|
+
|
|
18
|
+
export const viewport = { once: true, margin: "-80px" as const };
|
|
19
|
+
|
|
20
|
+
export const featureCardClassName =
|
|
21
|
+
"group flex flex-col gap-3 p-4 md:p-6 transition-[border-color,box-shadow] duration-150 ease-out rounded-2xl border border-border ring-1 ring-border ring-offset-4 ring-offset-background hover:border-foreground/10 hover:ring-foreground/8 bg-card";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { useUsers } from "@/features/landing/hooks/use-users";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useQuery } from "@tanstack/react-query";
|
|
4
|
+
|
|
5
|
+
import { getUsers, userKeys } from "@/features/landing/api";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Example TanStack Query hook for the landing Showcase demo.
|
|
9
|
+
*
|
|
10
|
+
* Demo-only: client `useQuery` is intentional here (interactive table + nuqs).
|
|
11
|
+
* Production routes should prefetch in a route `loader` per `tanstack-start.mdc`.
|
|
12
|
+
* Delete `src/features/landing` when you start your real product.
|
|
13
|
+
*/
|
|
14
|
+
export function useUsers() {
|
|
15
|
+
return useQuery({
|
|
16
|
+
queryKey: userKeys.list(),
|
|
17
|
+
queryFn: ({ signal }) => getUsers(signal),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { ReactIcon } from "@/features/landing/icons/react";
|
|
2
|
+
export { TailwindIcon } from "@/features/landing/icons/tailwind";
|
|
3
|
+
export { TanstackIcon } from "@/features/landing/icons/tanstack";
|
|
4
|
+
export { TypescriptIcon } from "@/features/landing/icons/typescript";
|
|
5
|
+
export { ReactHookFormIcon } from "@/features/landing/icons/react-hook-form";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
export function ReactHookFormIcon(props: ComponentProps<"svg">) {
|
|
4
|
+
return (
|
|
5
|
+
<svg viewBox="0 0 203 202" fillRule="evenodd" aria-hidden {...props}>
|
|
6
|
+
<path
|
|
7
|
+
d="M157.995 8.5c10.08 0 19.206 4.087 25.812 10.692S194.5 34.924 194.5 45.003h0v111.994c0 10.08-4.086 19.206-10.692 25.812a36.39 36.39 0 0 1-25.813 10.691h0-112.99c-10.08 0-19.206-4.087-25.812-10.692S8.5 167.076 8.5 156.997h0V45.003c0-10.08 4.086-19.206 10.692-25.812A36.39 36.39 0 0 1 45.005 8.5h0z"
|
|
8
|
+
stroke="#fff"
|
|
9
|
+
strokeWidth="17"
|
|
10
|
+
fill="#ec5990"
|
|
11
|
+
/>
|
|
12
|
+
<path
|
|
13
|
+
d="M133.385 51.604h-20.868c-1.53-4.766-5.981-8.001-11.009-8.001s-9.479 3.235-11.009 8.001H69.616c-8.222 0-14.887 6.632-14.887 14.813v77.177c0 8.181 6.665 14.813 14.887 14.813h63.769c8.222 0 14.887-6.632 14.887-14.813V66.417c0-8.181-6.665-14.813-14.887-14.813h0zM91.54 54.297a1.35 1.35 0 0 0 1.353-1.064c.886-4.046 4.486-6.932 8.648-6.932s7.762 2.885 8.648 6.932a1.35 1.35 0 0 0 1.353 1.064h7.281v8.821c0 2.231-1.818 4.04-4.06 4.04H88.305c-2.242 0-4.06-1.809-4.06-4.04v-8.821h7.294zm54.025 89.297c0 6.694-5.453 12.12-12.18 12.12H69.616c-6.727 0-12.18-5.426-12.18-12.12V66.417c0-6.694 5.453-12.12 12.18-12.12h11.923v8.821c0 3.719 3.03 6.733 6.767 6.733h26.39c3.737 0 6.767-3.015 6.767-6.733v-8.821h11.923c6.727 0 12.18 5.426 12.18 12.12v77.177zm-14.833-47.981h-23.819a1.35 1.35 0 0 0-1.353 1.347 1.35 1.35 0 0 0 1.353 1.347h23.819a1.35 1.35 0 0 0 1.353-1.347 1.35 1.35 0 0 0-1.353-1.347h0zm-35.186 0H71.727a1.35 1.35 0 0 0-1.353 1.347 1.35 1.35 0 0 0 1.353 1.347h23.819a1.35 1.35 0 0 0 1.353-1.347 1.35 1.35 0 0 0-1.353-1.347h0zm35.254 32.589h-23.886a1.35 1.35 0 0 0-1.353 1.347 1.35 1.35 0 0 0 1.353 1.347H130.8a1.35 1.35 0 0 0 1.353-1.347 1.35 1.35 0 0 0-1.353-1.347h0zm-35.187 0H71.727a1.35 1.35 0 0 0-1.353 1.347 1.35 1.35 0 0 0 1.353 1.347h23.886a1.35 1.35 0 0 0 1.353-1.347 1.35 1.35 0 0 0-1.353-1.347h0z"
|
|
14
|
+
fill="#fff"
|
|
15
|
+
/>
|
|
16
|
+
</svg>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
export function ReactIcon(props: ComponentProps<"svg">) {
|
|
4
|
+
return (
|
|
5
|
+
<svg viewBox="0 0 569 512" xmlnsXlink="http://www.w3.org/1999/xlink" {...props}>
|
|
6
|
+
<g fill="none" fillRule="evenodd">
|
|
7
|
+
<g fill="#087EA4" fillRule="nonzero">
|
|
8
|
+
<path
|
|
9
|
+
d="M285.5,201 C255.400481,201 231,225.400481 231,255.5 C231,285.599519 255.400481,310 285.5,310 C315.599519,310 340,285.599519 340,255.5 C340,225.400481 315.599519,201 285.5,201"
|
|
10
|
+
id="react_light__Path"
|
|
11
|
+
/>
|
|
12
|
+
<path
|
|
13
|
+
d="M568.959856,255.99437 C568.959856,213.207656 529.337802,175.68144 466.251623,150.985214 C467.094645,145.423543 467.85738,139.922107 468.399323,134.521063 C474.621631,73.0415145 459.808523,28.6686204 426.709856,9.5541429 C389.677085,-11.8291748 337.36955,3.69129898 284.479928,46.0162134 C231.590306,3.69129898 179.282771,-11.8291748 142.25,9.5541429 C109.151333,28.6686204 94.3382249,73.0415145 100.560533,134.521063 C101.102476,139.922107 101.845139,145.443621 102.708233,151.02537 C97.4493791,153.033193 92.2908847,155.161486 87.3331099,157.39017 C31.0111824,182.708821 0,217.765415 0,255.99437 C0,298.781084 39.6220545,336.307301 102.708233,361.003527 C101.845139,366.565197 101.102476,372.066633 100.560533,377.467678 C94.3382249,438.947226 109.151333,483.32012 142.25,502.434597 C153.629683,508.887578 166.52439,512.186771 179.603923,511.991836 C210.956328,511.991836 247.567589,495.487529 284.479928,465.972527 C321.372196,495.487529 358.003528,511.991836 389.396077,511.991836 C402.475265,512.183856 415.36922,508.884856 426.75,502.434597 C459.848667,483.32012 474.661775,438.947226 468.439467,377.467678 C467.897524,372.066633 467.134789,366.565197 466.291767,361.003527 C529.377946,336.347457 569,298.761006 569,255.99437 M389.155214,27.1025182 C397.565154,26.899606 405.877839,28.9368502 413.241569,33.0055186 C436.223966,46.2772304 446.540955,82.2775015 441.522965,131.770345 C441.181741,135.143488 440.780302,138.556788 440.298575,141.990165 C414.066922,134.08804 387.205771,128.452154 360.010724,125.144528 C343.525021,103.224055 325.192524,82.7564475 305.214266,63.9661533 C336.586743,39.7116483 366.032313,27.1025182 389.135142,27.1025182 M378.356498,310.205598 C368.204912,327.830733 357.150626,344.919965 345.237759,361.405091 C325.045049,363.479997 304.758818,364.51205 284.459856,364.497299 C264.167589,364.51136 243.888075,363.479308 223.702025,361.405091 C211.820914,344.919381 200.80007,327.83006 190.683646,310.205598 C180.532593,292.629285 171.306974,274.534187 163.044553,255.99437 C171.306974,237.454554 180.532593,219.359455 190.683646,201.783142 C200.784121,184.229367 211.770999,167.201087 223.601665,150.764353 C243.824636,148.63809 264.145559,147.579168 284.479928,147.591877 C304.772146,147.579725 325.051559,148.611772 345.237759,150.68404 C357.109048,167.14607 368.136094,184.201112 378.27621,201.783142 C388.419418,219.363718 397.644825,237.458403 405.915303,255.99437 C397.644825,274.530337 388.419418,292.625022 378.27621,310.205598 M419.724813,290.127366 C426.09516,307.503536 431.324985,325.277083 435.380944,343.334682 C417.779633,348.823635 399.836793,353.149774 381.668372,356.285142 C388.573127,345.871232 395.263781,335.035679 401.740334,323.778483 C408.143291,312.655143 414.144807,301.431411 419.805101,290.207679 M246.363271,390.377981 C258.848032,391.140954 271.593728,391.582675 284.5,391.582675 C297.406272,391.582675 310.232256,391.140954 322.737089,390.377981 C310.880643,404.583418 298.10766,417.997563 284.5,430.534446 C270.921643,417.999548 258.18192,404.585125 246.363271,390.377981 Z M187.311556,356.244986 C169.137286,353.123646 151.187726,348.810918 133.578912,343.334682 C137.618549,325.305649 142.828222,307.559058 149.174827,290.207679 C154.754833,301.431411 160.736278,312.655143 167.239594,323.778483 C173.74291,334.901824 180.467017,345.864539 187.311556,356.285142 M149.174827,221.760984 C142.850954,204.473938 137.654787,186.794745 133.619056,168.834762 C151.18418,163.352378 169.085653,159.013101 187.211197,155.844146 C180.346585,166.224592 173.622478,176.986525 167.139234,188.210257 C160.65599,199.433989 154.734761,210.517173 149.074467,221.760984 M322.616657,121.590681 C310.131896,120.827708 297.3862,120.385987 284.379568,120.385987 C271.479987,120.385987 258.767744,120.787552 246.242839,121.590681 C258.061488,107.383537 270.801211,93.9691137 284.379568,81.4342157 C297.99241,93.9658277 310.765727,107.380324 322.616657,121.590681 Z M401.70019,188.210257 C395.196875,176.939676 388.472767,166.09743 381.527868,155.68352 C399.744224,158.819049 417.734224,163.151949 435.380944,168.654058 C431.331963,186.680673 426.122466,204.426664 419.785029,221.781062 C414.205023,210.55733 408.203506,199.333598 401.720262,188.230335 M127.517179,131.790423 C122.438973,82.3176579 132.816178,46.2973086 155.778503,33.0255968 C163.144699,28.9632474 171.455651,26.9264282 179.864858,27.1225964 C202.967687,27.1225964 232.413257,39.7317265 263.785734,63.9862316 C243.794133,82.7898734 225.448298,103.270812 208.949132,125.204763 C181.761691,128.528025 154.90355,134.14313 128.661281,141.990165 C128.199626,138.556788 127.778115,135.163566 127.456963,131.790423 M98.4529773,182.106474 C101.54406,180.767925 104.695358,179.429376 107.906872,178.090828 C114.220532,204.735668 122.781793,230.7969 133.498624,255.99437 C122.761529,281.241316 114.193296,307.357063 107.8868,334.058539 C56.7434387,313.076786 27.0971497,284.003505 27.0971497,255.99437 C27.0971497,229.450947 53.1907013,202.526037 98.4529773,182.106474 Z M155.778503,478.963143 C132.816178,465.691432 122.438973,429.671082 127.517179,380.198317 C127.838331,376.825174 128.259842,373.431953 128.721497,369.978497 C154.953686,377.878517 181.814655,383.514365 209.009348,386.824134 C225.500295,408.752719 243.832321,429.233234 263.805806,448.042665 C220.069,481.834331 180.105722,492.97775 155.838719,478.963143 M441.502893,380.198317 C446.520883,429.691161 436.203894,465.691432 413.221497,478.963143 C388.974566,493.017906 348.991216,481.834331 305.274481,448.042665 C325.241364,429.232737 343.566681,408.752215 360.050868,386.824134 C387.245915,383.516508 414.107066,377.880622 440.338719,369.978497 C440.820446,373.431953 441.221885,376.825174 441.563109,380.198317 M461.193488,334.018382 C454.869166,307.332523 446.294494,281.231049 435.561592,255.99437 C446.289797,230.744081 454.857778,204.629101 461.173416,177.930202 C512.216417,198.911955 541.942994,227.985236 541.942994,255.99437 C541.942994,284.003505 512.296705,313.076786 461.153344,334.058539"
|
|
14
|
+
id="react_light__Shape"
|
|
15
|
+
/>
|
|
16
|
+
</g>
|
|
17
|
+
</g>
|
|
18
|
+
</svg>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
|
|
3
|
+
export function TailwindIcon(props: ComponentProps<"svg">) {
|
|
4
|
+
return (
|
|
5
|
+
<svg fill="none" viewBox="0 0 54 33" aria-hidden {...props}>
|
|
6
|
+
<path
|
|
7
|
+
fill="#38bdf8"
|
|
8
|
+
fillRule="evenodd"
|
|
9
|
+
d="M27 0c-7.2 0-11.7 3.6-13.5 10.8 2.7-3.6 5.85-4.95 9.45-4.05 2.054.513 3.522 2.004 5.147 3.653C30.744 13.09 33.808 16.2 40.5 16.2c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C36.756 3.11 33.692 0 27 0zM13.5 16.2C6.3 16.2 1.8 19.8 0 27c2.7-3.6 5.85-4.95 9.45-4.05 2.054.514 3.522 2.004 5.147 3.653C17.244 29.29 20.308 32.4 27 32.4c7.2 0 11.7-3.6 13.5-10.8-2.7 3.6-5.85 4.95-9.45 4.05-2.054-.513-3.522-2.004-5.147-3.653C23.256 19.31 20.192 16.2 13.5 16.2z"
|
|
10
|
+
clipRule="evenodd"
|
|
11
|
+
/>
|
|
12
|
+
</svg>
|
|
13
|
+
);
|
|
14
|
+
}
|