├── doc ├── static │ ├── .nojekyll │ └── img │ │ ├── favicon.ico │ │ ├── docusaurus.png │ │ ├── docusaurus-social-card.jpg │ │ ├── logo.svg │ │ ├── undraw_docusaurus_tree.svg │ │ ├── undraw_docusaurus_mountain.svg │ │ └── undraw_docusaurus_react.svg ├── babel.config.js ├── docs │ ├── tutorial-extras │ │ ├── img │ │ │ ├── localeDropdown.png │ │ │ └── docsVersionDropdown.png │ │ ├── _category_.json │ │ ├── manage-docs-versions.md │ │ └── translate-your-site.md │ ├── tutorial-basics │ │ ├── _category_.json │ │ ├── deploy-your-site.md │ │ ├── create-a-blog-post.md │ │ ├── congratulations.md │ │ ├── create-a-page.md │ │ ├── create-a-document.md │ │ └── markdown-features.mdx │ └── intro.md ├── blog │ ├── 2021-08-26-welcome │ │ ├── docusaurus-plushie-banner.jpeg │ │ └── index.md │ ├── 2019-05-28-first-blog-post.md │ ├── tags.yml │ ├── 2021-08-01-mdx-blog-post.mdx │ ├── authors.yml │ └── 2019-05-29-long-blog-post.md ├── src │ ├── pages │ │ ├── markdown-page.md │ │ ├── index.module.css │ │ └── index.tsx │ ├── components │ │ └── HomepageFeatures │ │ │ ├── styles.module.css │ │ │ └── index.tsx │ └── css │ │ └── custom.css ├── tsconfig.json ├── .gitignore ├── sidebars.ts ├── README.md ├── package.json └── docusaurus.config.ts ├── api ├── .gitignore ├── prisma │ ├── dev.db │ ├── migrations │ │ ├── migration_lock.toml │ │ └── 20241008131008_init │ │ │ └── migration.sql │ ├── schema.prisma │ └── seed │ │ └── main.ts ├── jest.config.js ├── eslint.config.mjs ├── prisma.env ├── src │ ├── index.ts │ └── routes │ │ └── user.ts ├── tsconfig.json ├── __tests__ │ └── userRouter.test.ts └── package.json ├── app ├── src │ ├── vite-env.d.ts │ ├── lib │ │ └── utils.ts │ ├── pages │ │ ├── home.tsx │ │ └── auth.tsx │ ├── app.tsx │ ├── main.tsx │ ├── components │ │ ├── ui │ │ │ ├── label.tsx │ │ │ ├── input.tsx │ │ │ ├── button.tsx │ │ │ └── dropdown-menu.tsx │ │ ├── mode-toggle.tsx │ │ ├── theme-provider.tsx │ │ ├── user-auth-form.tsx │ │ └── icons.tsx │ ├── layout.tsx │ ├── index.css │ └── assets │ │ └── react.svg ├── postcss.config.js ├── tsconfig.node.json ├── .gitignore ├── vite.config.ts ├── index.html ├── components.json ├── .eslintrc.cjs ├── tsconfig.json ├── tests │ └── main.test.jsx ├── README.md ├── public │ └── vite.svg ├── package.json └── tailwind.config.js ├── mobile ├── assets │ ├── images │ │ ├── icon.png │ │ ├── splash.png │ │ ├── favicon.png │ │ ├── react-logo.png │ │ ├── adaptive-icon.png │ │ ├── react-logo@2x.png │ │ ├── react-logo@3x.png │ │ └── partial-react-logo.png │ └── fonts │ │ └── SpaceMono-Regular.ttf ├── babel.config.js ├── .eslintrc.js ├── tsconfig.json ├── app │ ├── (home) │ │ ├── index.tsx │ │ ├── search.tsx │ │ ├── settings.tsx │ │ ├── profile.tsx │ │ └── _layout.tsx │ ├── _layout.tsx │ └── auth.tsx ├── .gitignore ├── __tests__ │ └── main-test.tsx ├── components │ ├── text.tsx │ ├── theme-switch-button.tsx │ ├── theme-provider.tsx │ └── user-auth-form.jsx ├── constants │ └── themes.tsx ├── app.json ├── package.json └── README.md ├── .prettierrc ├── .gitignore ├── LICENSE.md └── README.md /doc/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | # .env -------------------------------------------------------------------------------- /app/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /api/prisma/dev.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/api/prisma/dev.db -------------------------------------------------------------------------------- /doc/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/static/img/favicon.ico -------------------------------------------------------------------------------- /doc/static/img/docusaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/static/img/docusaurus.png -------------------------------------------------------------------------------- /mobile/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/icon.png -------------------------------------------------------------------------------- /mobile/assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/splash.png -------------------------------------------------------------------------------- /mobile/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/favicon.png -------------------------------------------------------------------------------- /mobile/assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/react-logo.png -------------------------------------------------------------------------------- /app/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /doc/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [require.resolve("@docusaurus/core/lib/babel/preset")], 3 | }; 4 | -------------------------------------------------------------------------------- /mobile/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /mobile/assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /mobile/assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /doc/static/img/docusaurus-social-card.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/static/img/docusaurus-social-card.jpg -------------------------------------------------------------------------------- /mobile/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /mobile/assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/mobile/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /doc/docs/tutorial-extras/img/localeDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/docs/tutorial-extras/img/localeDropdown.png -------------------------------------------------------------------------------- /doc/docs/tutorial-extras/img/docsVersionDropdown.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/docs/tutorial-extras/img/docsVersionDropdown.png -------------------------------------------------------------------------------- /mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = function (api) { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"], 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "bracketSameLine": true, 4 | "singleAttributePerLine": true, 5 | "tabWidth": 4, 6 | "useTabs": true 7 | } 8 | -------------------------------------------------------------------------------- /doc/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eimg/spata/HEAD/doc/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg -------------------------------------------------------------------------------- /doc/docs/tutorial-extras/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Extras", 3 | "position": 3, 4 | "link": { 5 | "type": "generated-index" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /api/prisma/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "sqlite" -------------------------------------------------------------------------------- /doc/src/pages/markdown-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Markdown page example 3 | --- 4 | 5 | # Markdown page example 6 | 7 | You don't need React to write simple standalone pages. 8 | -------------------------------------------------------------------------------- /mobile/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // https://docs.expo.dev/guides/using-eslint/ 2 | module.exports = { 3 | env: { node: true, browser: true }, 4 | extends: ["expo", "plugin:@tanstack/query/recommended"], 5 | }; 6 | -------------------------------------------------------------------------------- /app/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)); 6 | } 7 | -------------------------------------------------------------------------------- /doc/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": "." 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /doc/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "label": "Tutorial - Basics", 3 | "position": 2, 4 | "link": { 5 | "type": "generated-index", 6 | "description": "5 minutes to learn the most important Docusaurus concepts." 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /mobile/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"] 7 | } 8 | }, 9 | "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /api/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "ts-jest", 3 | testEnvironment: "node", 4 | verbose: true, 5 | moduleFileExtensions: ["ts", "js"], 6 | transform: { 7 | "^.+\\.(ts|tsx)$": "ts-jest", 8 | }, 9 | testMatch: ["**/__tests__/**/*.test.ts"], 10 | }; 11 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /mobile/app/(home)/index.tsx: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | import Text from "@/components/text"; 3 | 4 | export default function Home() { 5 | return ( 6 | 8 | Home 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /app/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | export default function Home() { 2 | return ( 3 |
4 |

7 | Home 8 |

9 |

pages/Home.tsx

10 |
11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /app/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true, 8 | "baseUrl": ".", 9 | "paths": { 10 | "@/*": ["./src/*"] 11 | } 12 | }, 13 | "include": ["vite.config.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /mobile/app/(home)/search.tsx: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | import Text from "@/components/text"; 3 | 4 | export default function Search() { 5 | return ( 6 | 8 | Search 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /api/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import globals from "globals"; 2 | import pluginJs from "@eslint/js"; 3 | import tseslint from "typescript-eslint"; 4 | 5 | export default [ 6 | { files: ["**/*.{js,mjs,cjs,ts}"] }, 7 | { languageOptions: { globals: globals.browser } }, 8 | pluginJs.configs.recommended, 9 | ...tseslint.configs.recommended, 10 | ]; 11 | -------------------------------------------------------------------------------- /mobile/app/(home)/settings.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeSwitchButton } from "@/components/theme-switch-button"; 2 | import { View } from "react-native"; 3 | 4 | export default function Settings() { 5 | return ( 6 | 8 | 9 | 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /mobile/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | dist/ 4 | npm-debug.* 5 | *.jks 6 | *.p8 7 | *.p12 8 | *.key 9 | *.mobileprovision 10 | *.orig.* 11 | web-build/ 12 | 13 | # macOS 14 | .DS_Store 15 | 16 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 17 | # The following patterns were generated by expo-cli 18 | 19 | expo-env.d.ts 20 | # @end expo-cli -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /doc/blog/2019-05-28-first-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: first-blog-post 3 | title: First Blog Post 4 | authors: [slorber, yangshun] 5 | tags: [hola, docusaurus] 6 | --- 7 | 8 | Lorem ipsum dolor sit amet... 9 | 10 | 11 | 12 | ...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 13 | -------------------------------------------------------------------------------- /app/vite.config.ts: -------------------------------------------------------------------------------- 1 | import path from "path"; 2 | import { defineConfig } from "vitest/config"; 3 | import react from "@vitejs/plugin-react"; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [react()], 8 | resolve: { 9 | alias: { 10 | "@": path.resolve(__dirname, "./src"), 11 | }, 12 | }, 13 | server: { 14 | port: 4000, 15 | open: true, 16 | }, 17 | test: { 18 | environment: "jsdom", 19 | }, 20 | }); 21 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 12 | Vite + React + TS 13 | 14 | 15 |
16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /doc/blog/tags.yml: -------------------------------------------------------------------------------- 1 | facebook: 2 | label: Facebook 3 | permalink: /facebook 4 | description: Facebook tag description 5 | 6 | hello: 7 | label: Hello 8 | permalink: /hello 9 | description: Hello tag description 10 | 11 | docusaurus: 12 | label: Docusaurus 13 | permalink: /docusaurus 14 | description: Docusaurus tag description 15 | 16 | hola: 17 | label: Hola 18 | permalink: /hola 19 | description: Hola tag description 20 | -------------------------------------------------------------------------------- /api/prisma.env: -------------------------------------------------------------------------------- 1 | # Environment variables declared in this file are automatically made available to Prisma. 2 | # See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema 3 | 4 | # Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB. 5 | # See the documentation for all the connection string options: https://pris.ly/d/connection-strings 6 | 7 | DATABASE_URL="file:./dev.db" -------------------------------------------------------------------------------- /doc/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /app/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": false, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.js", 8 | "css": "src/index.css", 9 | "baseColor": "neutral", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /api/src/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Request, Response } from "express"; 2 | const app = express(); 3 | 4 | import cors from "cors"; 5 | app.use(cors()); 6 | 7 | app.use(express.json()); 8 | app.use(express.urlencoded({ extended: true })); 9 | 10 | import { router as userRouter } from "./routes/user"; 11 | app.use(userRouter); 12 | 13 | app.get("/", (req: Request, res: Response) => { 14 | res.send("Express API Index"); 15 | }); 16 | 17 | app.listen(8000, () => { 18 | console.log("API server running at port 8000"); 19 | }); 20 | -------------------------------------------------------------------------------- /mobile/__tests__/main-test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from "@jest/globals"; 2 | import { render } from "@testing-library/react-native"; 3 | 4 | import Text from "@/components/text"; 5 | 6 | describe("sum module", () => { 7 | it("should add 1 + 2 to equal 3", () => { 8 | expect(1 + 2).toBe(3); 9 | }); 10 | }); 11 | 12 | describe("Custom Text component", () => { 13 | it("should render Text correctly", () => { 14 | const { getByText } = render(Home); 15 | 16 | getByText("Home"); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "esnext", 5 | "types": ["node"], 6 | "esModuleInterop": true, 7 | "moduleResolution": "node", 8 | "sourceMap": true, 9 | "outDir": "dist", 10 | "allowJs": true, 11 | "lib": ["dom", "es6"], 12 | "baseUrl": "./src", 13 | "paths": { 14 | "@/*": ["./*"] 15 | } 16 | }, 17 | "exclude": ["node_modules", "dist"], 18 | "include": ["src/**/*.ts", "prisma/**/*.ts", "__tests__/**/*.test.ts"], 19 | "files": ["src/index.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /app/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:@typescript-eslint/recommended", 7 | "plugin:react-hooks/recommended", 8 | "plugin:@tanstack/query/recommended", 9 | ], 10 | ignorePatterns: ["dist", ".eslintrc.cjs"], 11 | parser: "@typescript-eslint/parser", 12 | plugins: ["react-refresh"], 13 | rules: { 14 | "react-refresh/only-export-components": [ 15 | "warn", 16 | { allowConstantExport: true }, 17 | ], 18 | }, 19 | }; 20 | -------------------------------------------------------------------------------- /mobile/components/text.tsx: -------------------------------------------------------------------------------- 1 | import { Text as DefaultText, StyleProp, TextStyle } from "react-native"; 2 | import { useTheme } from "@react-navigation/native"; 3 | 4 | type TextProps = { 5 | style?: StyleProp; 6 | children: string | React.ReactNode; 7 | }; 8 | 9 | // Text component that support theme color 10 | export default function Text({ style, children }: TextProps) { 11 | const { colors } = useTheme(); 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { RouterProvider, createBrowserRouter } from "react-router"; 2 | 3 | import Layout from "./layout"; 4 | import Home from "./pages/home"; 5 | import Auth from "./pages/auth"; 6 | 7 | const router = createBrowserRouter([ 8 | { 9 | path: "/", 10 | element: , 11 | children: [ 12 | { 13 | path: "/", 14 | element: , 15 | }, 16 | { 17 | path: "/login", 18 | element: , 19 | }, 20 | ], 21 | }, 22 | ]); 23 | 24 | export default function App() { 25 | return ; 26 | } 27 | -------------------------------------------------------------------------------- /api/__tests__/userRouter.test.ts: -------------------------------------------------------------------------------- 1 | import { it, expect, describe } from "@jest/globals"; 2 | import request from "supertest"; 3 | 4 | import express from "express"; 5 | const app = express(); 6 | 7 | import { router as userRouter } from "../src/routes/user"; 8 | app.use(userRouter); 9 | 10 | describe("GET /users", () => { 11 | it("should respond 200 and json", async () => { 12 | return request(app) 13 | .get("/users") 14 | .expect("Content-Type", /json/) 15 | .expect(200) 16 | .then(res => { 17 | expect(res.statusCode).toBe(200); 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /doc/blog/2021-08-01-mdx-blog-post.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | slug: mdx-blog-post 3 | title: MDX Blog Post 4 | authors: [slorber] 5 | tags: [docusaurus] 6 | --- 7 | 8 | Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). 9 | 10 | :::tip 11 | 12 | Use the power of React to create interactive blog posts. 13 | 14 | ::: 15 | 16 | {/* truncate */} 17 | 18 | For example, use JSX to create an interactive button: 19 | 20 | ```js 21 | 22 | ``` 23 | 24 | 25 | -------------------------------------------------------------------------------- /mobile/components/theme-switch-button.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { TouchableOpacity } from "react-native"; 3 | 4 | import Ionicons from "@expo/vector-icons/Ionicons"; 5 | 6 | import { useTheme } from "./theme-provider"; 7 | 8 | export const ThemeSwitchButton = () => { 9 | const { isDark, toggleTheme } = useTheme(); 10 | 11 | return ( 12 | 13 | {isDark ? ( 14 | 19 | ) : ( 20 | 24 | )} 25 | 26 | ); 27 | }; 28 | -------------------------------------------------------------------------------- /api/prisma/migrations/20241008131008_init/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "User" ( 3 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 4 | "email" TEXT NOT NULL, 5 | "name" TEXT 6 | ); 7 | 8 | -- CreateTable 9 | CREATE TABLE "Post" ( 10 | "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, 11 | "title" TEXT NOT NULL, 12 | "content" TEXT, 13 | "published" BOOLEAN NOT NULL DEFAULT false, 14 | "authorId" INTEGER NOT NULL, 15 | CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE 16 | ); 17 | 18 | -- CreateIndex 19 | CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); 20 | -------------------------------------------------------------------------------- /mobile/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import { Stack } from "expo-router"; 2 | import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; 3 | import { ThemeProvider } from "@/components/theme-provider"; 4 | 5 | const queryClient = new QueryClient(); 6 | 7 | export default function RootLayout() { 8 | return ( 9 | 10 | 11 | 12 | 16 | 20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /mobile/constants/themes.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DefaultTheme as NavigationDefaultTheme, 3 | DarkTheme as NavigationDarkTheme, 4 | } from "@react-navigation/native"; 5 | 6 | export const LightTheme = { 7 | ...NavigationDefaultTheme, 8 | colors: { 9 | ...NavigationDefaultTheme.colors, 10 | // primary: "#6200ee", 11 | // background: "#ffffff", 12 | // text: "#000000", 13 | // card: "#f5f5f5", 14 | // border: "#cccccc", 15 | }, 16 | }; 17 | 18 | export const DarkTheme = { 19 | ...NavigationDarkTheme, 20 | colors: { 21 | ...NavigationDarkTheme.colors, 22 | // primary: "#bb86fc", 23 | // background: "#121212", 24 | // text: "#ffffff", 25 | // card: "#1f1f1f", 26 | // border: "#272727", 27 | }, 28 | }; 29 | -------------------------------------------------------------------------------- /api/prisma/schema.prisma: -------------------------------------------------------------------------------- 1 | // This is your Prisma schema file, 2 | // learn more about it in the docs: https://pris.ly/d/prisma-schema 3 | 4 | generator client { 5 | provider = "prisma-client-js" 6 | } 7 | 8 | datasource db { 9 | provider = "sqlite" 10 | url = env("DATABASE_URL") 11 | } 12 | 13 | model User { 14 | id Int @id @default(autoincrement()) 15 | email String @unique 16 | name String? 17 | posts Post[] 18 | } 19 | 20 | model Post { 21 | id Int @id @default(autoincrement()) 22 | title String 23 | content String? 24 | published Boolean @default(false) 25 | author User @relation(fields: [authorId], references: [id]) 26 | authorId Int 27 | } 28 | -------------------------------------------------------------------------------- /doc/blog/authors.yml: -------------------------------------------------------------------------------- 1 | yangshun: 2 | name: Yangshun Tay 3 | title: Front End Engineer @ Facebook 4 | url: https://github.com/yangshun 5 | image_url: https://github.com/yangshun.png 6 | page: true 7 | socials: 8 | x: yangshunz 9 | github: yangshun 10 | 11 | slorber: 12 | name: Sébastien Lorber 13 | title: Docusaurus maintainer 14 | url: https://sebastienlorber.com 15 | image_url: https://github.com/slorber.png 16 | page: 17 | # customize the url of the author page at /blog/authors/ 18 | permalink: "/all-sebastien-lorber-articles" 19 | socials: 20 | x: sebastienlorber 21 | linkedin: sebastienlorber 22 | github: slorber 23 | newsletter: https://thisweekinreact.com 24 | -------------------------------------------------------------------------------- /mobile/app/(home)/profile.tsx: -------------------------------------------------------------------------------- 1 | import { View } from "react-native"; 2 | import Text from "@/components/text"; 3 | import { Link } from "expo-router"; 4 | 5 | import { useTheme } from "@react-navigation/native"; 6 | 7 | export default function Profile() { 8 | const { colors } = useTheme(); 9 | 10 | return ( 11 | 13 | 23 | 24 | Login 25 | 26 | 27 | 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | /* Tailwind shadcn/ui */ 24 | "baseUrl": ".", 25 | "paths": { 26 | "@/*": ["./src/*"] 27 | } 28 | }, 29 | "include": ["src"], 30 | "references": [{ "path": "./tsconfig.node.json" }] 31 | } 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Essentials 2 | .env 3 | node_modules/ 4 | 5 | # OS junks 6 | .fuse_hidden* 7 | .directory 8 | .Trash-* 9 | .nfs* 10 | .DocumentRevisions-V100 11 | .fseventsd 12 | .Spotlight-V100 13 | .TemporaryItems 14 | .Trashes 15 | .VolumeIcon.icns 16 | .com.apple.timemachine.donotpresent 17 | .AppleDB 18 | .AppleDesktop 19 | Network Trash Folder 20 | Temporary Items 21 | .apdisk 22 | $RECYCLE.BIN/ 23 | 24 | # macOS 25 | *.DS_Store 26 | .AppleDouble 27 | .LSOverride 28 | 29 | # Thumbnails 30 | ._* 31 | 32 | # Windows 33 | Thumbs.db 34 | ehthumbs.db 35 | ehthumbs_vista.db 36 | Desktop.ini 37 | *.cab 38 | *.msi 39 | *.msm 40 | *.msp 41 | *.lnk 42 | 43 | # Others 44 | *.swp 45 | .idea 46 | cobertura.xml 47 | Config/secrets 48 | Packages 49 | .build 50 | xcuserdata 51 | *.xcodeproj 52 | .git/credentials 53 | -------------------------------------------------------------------------------- /app/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./app.tsx"; 4 | import "./index.css"; 5 | 6 | import { ThemeProvider } from "@/components/theme-provider"; 7 | 8 | import { QueryClientProvider, QueryClient } from "@tanstack/react-query"; 9 | import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; 10 | 11 | const queryClient = new QueryClient(); 12 | 13 | ReactDOM.createRoot(document.getElementById("root")!).render( 14 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | , 25 | ); 26 | -------------------------------------------------------------------------------- /app/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as LabelPrimitive from "@radix-ui/react-label"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const labelVariants = cva( 8 | "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", 9 | ); 10 | 11 | const Label = React.forwardRef< 12 | React.ElementRef, 13 | React.ComponentPropsWithoutRef & 14 | VariantProps 15 | >(({ className, ...props }, ref) => ( 16 | 21 | )); 22 | Label.displayName = LabelPrimitive.Root.displayName; 23 | 24 | export { Label }; 25 | -------------------------------------------------------------------------------- /app/tests/main.test.jsx: -------------------------------------------------------------------------------- 1 | import { it, expect, describe } from "vitest"; 2 | import { screen, render, fireEvent } from "@testing-library/react"; 3 | import "@testing-library/jest-dom/vitest"; 4 | 5 | import App from "@/App"; 6 | 7 | describe("math test", () => { 8 | it("should be 3", () => { 9 | expect(1 + 2).toBe(3); 10 | }); 11 | }); 12 | 13 | describe("UI test", () => { 14 | render(); 15 | 16 | it("should render correctly", () => { 17 | expect(screen.getByRole("title")).toBeInTheDocument(); 18 | }); 19 | 20 | // it("input and event", async () => { 21 | // await fireEvent.change(screen.getByRole("input"), { 22 | // target: { value: "some value" }, 23 | // }); 24 | // await fireEvent.click(screen.getByRole("button")); 25 | // expect(screen.getByRole("result").textContent).toBe("something"); 26 | // }); 27 | }); 28 | -------------------------------------------------------------------------------- /mobile/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "mobile", 4 | "slug": "mobile", 5 | "version": "1.0.0", 6 | "orientation": "portrait", 7 | "icon": "./assets/images/icon.png", 8 | "scheme": "myapp", 9 | "userInterfaceStyle": "automatic", 10 | "splash": { 11 | "image": "./assets/images/splash.png", 12 | "resizeMode": "contain", 13 | "backgroundColor": "#ffffff" 14 | }, 15 | "ios": { 16 | "supportsTablet": true 17 | }, 18 | "android": { 19 | "adaptiveIcon": { 20 | "foregroundImage": "./assets/images/adaptive-icon.png", 21 | "backgroundColor": "#ffffff" 22 | } 23 | }, 24 | "web": { 25 | "bundler": "metro", 26 | "output": "static", 27 | "favicon": "./assets/images/favicon.png" 28 | }, 29 | "plugins": ["expo-router"], 30 | "experiments": { 31 | "typedRoutes": true 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/deploy-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 5 3 | --- 4 | 5 | # Deploy your site 6 | 7 | Docusaurus is a **static-site-generator** (also called **[Jamstack](https://jamstack.org/)**). 8 | 9 | It builds your site as simple **static HTML, JavaScript and CSS files**. 10 | 11 | ## Build your site 12 | 13 | Build your site **for production**: 14 | 15 | ```bash 16 | npm run build 17 | ``` 18 | 19 | The static files are generated in the `build` folder. 20 | 21 | ## Deploy your site 22 | 23 | Test your production build locally: 24 | 25 | ```bash 26 | npm run serve 27 | ``` 28 | 29 | The `build` folder is now served at [http://localhost:3000/](http://localhost:3000/). 30 | 31 | You can now deploy the `build` folder **almost anywhere** easily, **for free** or very small cost (read the **[Deployment Guide](https://docusaurus.io/docs/deployment)**). 32 | -------------------------------------------------------------------------------- /app/src/layout.tsx: -------------------------------------------------------------------------------- 1 | import { ModeToggle } from "@/components/mode-toggle"; 2 | import { Button } from "@/components/ui/button"; 3 | import { Home, ArrowLeft } from "lucide-react"; 4 | import { Outlet, Link, useLocation } from "react-router"; 5 | 6 | export default function Layout() { 7 | const { pathname } = useLocation(); 8 | 9 | return ( 10 |
11 | 28 |
29 | 30 |
31 |
32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | export interface InputProps 6 | extends React.InputHTMLAttributes {} 7 | 8 | const Input = React.forwardRef( 9 | ({ className, type, ...props }, ref) => { 10 | return ( 11 | 20 | ); 21 | }, 22 | ); 23 | Input.displayName = "Input"; 24 | 25 | export { Input }; 26 | -------------------------------------------------------------------------------- /doc/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type { SidebarsConfig } from "@docusaurus/plugin-content-docs"; 2 | 3 | /** 4 | * Creating a sidebar enables you to: 5 | - create an ordered group of docs 6 | - render a sidebar for each doc of that group 7 | - provide next/previous navigation 8 | 9 | The sidebars can be generated from the filesystem, or explicitly defined here. 10 | 11 | Create as many sidebars as you want. 12 | */ 13 | const sidebars: SidebarsConfig = { 14 | // By default, Docusaurus generates a sidebar from the docs folder structure 15 | tutorialSidebar: [{ type: "autogenerated", dirName: "." }], 16 | 17 | // But you can create a sidebar manually 18 | /* 19 | tutorialSidebar: [ 20 | 'intro', 21 | 'hello', 22 | { 23 | type: 'category', 24 | label: 'Tutorial', 25 | items: ['tutorial-basics/create-a-document'], 26 | }, 27 | ], 28 | */ 29 | }; 30 | 31 | export default sidebars; 32 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | Using SSH: 30 | 31 | ``` 32 | $ USE_SSH=true yarn deploy 33 | ``` 34 | 35 | Not using SSH: 36 | 37 | ``` 38 | $ GIT_USER= yarn deploy 39 | ``` 40 | 41 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 42 | -------------------------------------------------------------------------------- /doc/blog/2021-08-26-welcome/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: welcome 3 | title: Welcome 4 | authors: [slorber, yangshun] 5 | tags: [facebook, hello, docusaurus] 6 | --- 7 | 8 | [Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). 9 | 10 | Here are a few tips you might find useful. 11 | 12 | 13 | 14 | Simply add Markdown files (or folders) to the `blog` directory. 15 | 16 | Regular blog authors can be added to `authors.yml`. 17 | 18 | The blog post date can be extracted from filenames, such as: 19 | 20 | - `2019-05-30-welcome.md` 21 | - `2019-05-30-welcome/index.md` 22 | 23 | A blog post folder can be convenient to co-locate blog post images: 24 | 25 | ![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) 26 | 27 | The blog supports tags as well! 28 | 29 | **And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. 30 | -------------------------------------------------------------------------------- /api/src/routes/user.ts: -------------------------------------------------------------------------------- 1 | import express, { Request, Response } from "express"; 2 | export const router = express.Router(); 3 | 4 | import { query, validationResult } from "express-validator"; 5 | 6 | import { PrismaClient } from "@prisma/client"; 7 | const prisma = new PrismaClient(); 8 | 9 | router.get("/users", async (req: Request, res: Response) => { 10 | const users = await prisma.user.findMany({ 11 | include: { 12 | posts: true, 13 | }, 14 | }); 15 | 16 | res.send(users); 17 | }); 18 | 19 | router.get( 20 | "/users/search", 21 | query("q").notEmpty(), 22 | async (req: Request, res: Response) => { 23 | const result = validationResult(req); 24 | 25 | if (result.isEmpty()) { 26 | const { q } = req.query; 27 | 28 | const data = await prisma.user.findMany({ 29 | where: { 30 | name: { 31 | contains: String(q), 32 | }, 33 | }, 34 | }); 35 | 36 | res.json(data); 37 | } else { 38 | res.status(400).json({ errors: result.array() }); 39 | } 40 | }, 41 | ); 42 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2024 Fairway Technology 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/create-a-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 3 3 | --- 4 | 5 | # Create a Blog Post 6 | 7 | Docusaurus creates a **page for each blog post**, but also a **blog index page**, a **tag system**, an **RSS** feed... 8 | 9 | ## Create your first Post 10 | 11 | Create a file at `blog/2021-02-28-greetings.md`: 12 | 13 | ```md title="blog/2021-02-28-greetings.md" 14 | --- 15 | slug: greetings 16 | title: Greetings! 17 | authors: 18 | - name: Joel Marcey 19 | title: Co-creator of Docusaurus 1 20 | url: https://github.com/JoelMarcey 21 | image_url: https://github.com/JoelMarcey.png 22 | - name: Sébastien Lorber 23 | title: Docusaurus maintainer 24 | url: https://sebastienlorber.com 25 | image_url: https://github.com/slorber.png 26 | tags: [greetings] 27 | --- 28 | 29 | Congratulations, you have made your first post! 30 | 31 | Feel free to play around and edit this post as much as you like. 32 | ``` 33 | 34 | A new blog post is now available at [http://localhost:3000/blog/greetings](http://localhost:3000/blog/greetings). 35 | -------------------------------------------------------------------------------- /app/src/pages/auth.tsx: -------------------------------------------------------------------------------- 1 | import { UserAuthForm } from "@/components/user-auth-form"; 2 | 3 | export default function Auth() { 4 | return ( 5 |
6 |
7 |
8 |

9 | Spata Login 10 |

11 |

12 | Enter your email below to sign in or create a new 13 | account 14 |

15 |
16 | 17 |

18 | By clicking continue, you agree to our{" "} 19 | 22 | Terms of Service 23 | {" "} 24 | and{" "} 25 | 28 | Privacy Policy 29 | 30 | . 31 |

32 |
33 |
34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /doc/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme="dark"] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/congratulations.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 6 3 | --- 4 | 5 | # Congratulations! 6 | 7 | You have just learned the **basics of Docusaurus** and made some changes to the **initial template**. 8 | 9 | Docusaurus has **much more to offer**! 10 | 11 | Have **5 more minutes**? Take a look at **[versioning](../tutorial-extras/manage-docs-versions.md)** and **[i18n](../tutorial-extras/translate-your-site.md)**. 12 | 13 | Anything **unclear** or **buggy** in this tutorial? [Please report it!](https://github.com/facebook/docusaurus/discussions/4610) 14 | 15 | ## What's next? 16 | 17 | - Read the [official documentation](https://docusaurus.io/) 18 | - Modify your site configuration with [`docusaurus.config.js`](https://docusaurus.io/docs/api/docusaurus-config) 19 | - Add navbar and footer items with [`themeConfig`](https://docusaurus.io/docs/api/themes/configuration) 20 | - Add a custom [Design and Layout](https://docusaurus.io/docs/styling-layout) 21 | - Add a [search bar](https://docusaurus.io/docs/search) 22 | - Find inspirations in the [Docusaurus showcase](https://docusaurus.io/showcase) 23 | - Get involved in the [Docusaurus Community](https://docusaurus.io/community/support) 24 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/create-a-page.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Create a Page 6 | 7 | Add **Markdown or React** files to `src/pages` to create a **standalone page**: 8 | 9 | - `src/pages/index.js` → `localhost:3000/` 10 | - `src/pages/foo.md` → `localhost:3000/foo` 11 | - `src/pages/foo/bar.js` → `localhost:3000/foo/bar` 12 | 13 | ## Create your first React Page 14 | 15 | Create a file at `src/pages/my-react-page.js`: 16 | 17 | ```jsx title="src/pages/my-react-page.js" 18 | import React from "react"; 19 | import Layout from "@theme/Layout"; 20 | 21 | export default function MyReactPage() { 22 | return ( 23 | 24 |

My React page

25 |

This is a React page

26 |
27 | ); 28 | } 29 | ``` 30 | 31 | A new page is now available at [http://localhost:3000/my-react-page](http://localhost:3000/my-react-page). 32 | 33 | ## Create your first Markdown Page 34 | 35 | Create a file at `src/pages/my-markdown-page.md`: 36 | 37 | ```mdx title="src/pages/my-markdown-page.md" 38 | # My Markdown page 39 | 40 | This is a Markdown page 41 | ``` 42 | 43 | A new page is now available at [http://localhost:3000/my-markdown-page](http://localhost:3000/my-markdown-page). 44 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "jest", 7 | "build": "npx tsc", 8 | "start": "node dist/index.js", 9 | "dev": "nodemon src/index.ts", 10 | "seed": "prisma db seed", 11 | "migrate": "prisma migrate dev", 12 | "migrate:reset": "prisma migrate reset", 13 | "lint": "eslint src" 14 | }, 15 | "keywords": [], 16 | "author": "", 17 | "license": "ISC", 18 | "description": "", 19 | "devDependencies": { 20 | "@eslint/js": "^9.12.0", 21 | "@snaplet/copycat": "^5.0.0", 22 | "@types/express": "^5.0.0", 23 | "@types/jest": "^29.5.13", 24 | "@types/node": "^22.7.5", 25 | "@types/supertest": "^6.0.2", 26 | "eslint": "^9.12.0", 27 | "express": "^4.21.0", 28 | "globals": "^15.10.0", 29 | "jest": "^29.7.0", 30 | "prisma": "^6.1.0", 31 | "supertest": "^7.0.0", 32 | "ts-jest": "^29.2.5", 33 | "ts-node": "^10.9.2", 34 | "typescript": "^5.6.2", 35 | "typescript-eslint": "^8.8.1" 36 | }, 37 | "dependencies": { 38 | "@prisma/client": "^6.1.0", 39 | "cors": "^2.8.5", 40 | "express-validator": "^7.2.0", 41 | "nodemon": "^3.1.7" 42 | }, 43 | "prisma": { 44 | "seed": "ts-node prisma/seed/main.ts" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /api/prisma/seed/main.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | const prisma = new PrismaClient(); 3 | 4 | async function main() { 5 | const alice = await prisma.user.upsert({ 6 | where: { email: "alice@example.com" }, 7 | update: {}, 8 | create: { 9 | email: "alice@example.com", 10 | name: "Alice", 11 | posts: { 12 | create: { 13 | title: "Setting up express", 14 | content: "We are going to use TypeScript", 15 | published: true, 16 | }, 17 | }, 18 | }, 19 | }); 20 | const bob = await prisma.user.upsert({ 21 | where: { email: "bob@example.com" }, 22 | update: {}, 23 | create: { 24 | email: "bob@example.com", 25 | name: "Bob", 26 | posts: { 27 | create: [ 28 | { 29 | title: "Prisma ORM", 30 | content: "With SQLite, win!", 31 | published: true, 32 | }, 33 | { 34 | title: "Migration and Seeding", 35 | content: "Let's get started", 36 | published: true, 37 | }, 38 | ], 39 | }, 40 | }, 41 | }); 42 | console.log({ alice, bob }); 43 | } 44 | main() 45 | .then(async () => { 46 | await prisma.$disconnect(); 47 | }) 48 | .catch(async e => { 49 | console.error(e); 50 | await prisma.$disconnect(); 51 | process.exit(1); 52 | }); 53 | -------------------------------------------------------------------------------- /doc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "doc", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "3.5.2", 19 | "@docusaurus/preset-classic": "3.5.2", 20 | "@mdx-js/react": "^3.0.0", 21 | "clsx": "^2.0.0", 22 | "prism-react-renderer": "^2.3.0", 23 | "react": "^18.0.0", 24 | "react-dom": "^18.0.0" 25 | }, 26 | "devDependencies": { 27 | "@docusaurus/module-type-aliases": "3.5.2", 28 | "@docusaurus/tsconfig": "3.5.2", 29 | "@docusaurus/types": "3.5.2", 30 | "typescript": "~5.5.2" 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.5%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 3 chrome version", 40 | "last 3 firefox version", 41 | "last 5 safari version" 42 | ] 43 | }, 44 | "engines": { 45 | "node": ">=18.0" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/create-a-document.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Create a Document 6 | 7 | Documents are **groups of pages** connected through: 8 | 9 | - a **sidebar** 10 | - **previous/next navigation** 11 | - **versioning** 12 | 13 | ## Create your first Doc 14 | 15 | Create a Markdown file at `docs/hello.md`: 16 | 17 | ```md title="docs/hello.md" 18 | # Hello 19 | 20 | This is my **first Docusaurus document**! 21 | ``` 22 | 23 | A new document is now available at [http://localhost:3000/docs/hello](http://localhost:3000/docs/hello). 24 | 25 | ## Configure the Sidebar 26 | 27 | Docusaurus automatically **creates a sidebar** from the `docs` folder. 28 | 29 | Add metadata to customize the sidebar label and position: 30 | 31 | ```md title="docs/hello.md" {1-4} 32 | --- 33 | sidebar_label: "Hi!" 34 | sidebar_position: 3 35 | --- 36 | 37 | # Hello 38 | 39 | This is my **first Docusaurus document**! 40 | ``` 41 | 42 | It is also possible to create your sidebar explicitly in `sidebars.js`: 43 | 44 | ```js title="sidebars.js" 45 | export default { 46 | tutorialSidebar: [ 47 | "intro", 48 | // highlight-next-line 49 | "hello", 50 | { 51 | type: "category", 52 | label: "Tutorial", 53 | items: ["tutorial-basics/create-a-document"], 54 | }, 55 | ], 56 | }; 57 | ``` 58 | -------------------------------------------------------------------------------- /app/src/components/mode-toggle.tsx: -------------------------------------------------------------------------------- 1 | import { Moon, Sun } from "lucide-react"; 2 | 3 | import { Button } from "@/components/ui/button"; 4 | 5 | import { 6 | DropdownMenu, 7 | DropdownMenuContent, 8 | DropdownMenuItem, 9 | DropdownMenuTrigger, 10 | } from "@/components/ui/dropdown-menu"; 11 | 12 | import { useTheme } from "@/components/theme-provider"; 13 | 14 | export function ModeToggle() { 15 | const { setTheme } = useTheme(); 16 | 17 | return ( 18 | 19 | 20 | 27 | 28 | 29 | setTheme("light")}> 30 | Light 31 | 32 | setTheme("dark")}> 33 | Dark 34 | 35 | setTheme("system")}> 36 | System 37 | 38 | 39 | 40 | ); 41 | } 42 | -------------------------------------------------------------------------------- /doc/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | import Link from "@docusaurus/Link"; 3 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 4 | import Layout from "@theme/Layout"; 5 | import HomepageFeatures from "@site/src/components/HomepageFeatures"; 6 | import Heading from "@theme/Heading"; 7 | 8 | import styles from "./index.module.css"; 9 | 10 | function HomepageHeader() { 11 | const { siteConfig } = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 | 18 | {siteConfig.title} 19 | 20 |

{siteConfig.tagline}

21 |
22 | 25 | Tutorial - 5min ⏱️ 26 | 27 |
28 |
29 |
30 | ); 31 | } 32 | 33 | export default function Home(): JSX.Element { 34 | const { siteConfig } = useDocusaurusContext(); 35 | return ( 36 | 39 | 40 |
41 | 42 |
43 |
44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: "latest", 21 | sourceType: "module", 22 | project: ["./tsconfig.json", "./tsconfig.node.json"], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | }; 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /app/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/docs/tutorial-extras/manage-docs-versions.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Manage Docs Versions 6 | 7 | Docusaurus can manage multiple versions of your docs. 8 | 9 | ## Create a docs version 10 | 11 | Release a version 1.0 of your project: 12 | 13 | ```bash 14 | npm run docusaurus docs:version 1.0 15 | ``` 16 | 17 | The `docs` folder is copied into `versioned_docs/version-1.0` and `versions.json` is created. 18 | 19 | Your docs now have 2 versions: 20 | 21 | - `1.0` at `http://localhost:3000/docs/` for the version 1.0 docs 22 | - `current` at `http://localhost:3000/docs/next/` for the **upcoming, unreleased docs** 23 | 24 | ## Add a Version Dropdown 25 | 26 | To navigate seamlessly across versions, add a version dropdown. 27 | 28 | Modify the `docusaurus.config.js` file: 29 | 30 | ```js title="docusaurus.config.js" 31 | export default { 32 | themeConfig: { 33 | navbar: { 34 | items: [ 35 | // highlight-start 36 | { 37 | type: "docsVersionDropdown", 38 | }, 39 | // highlight-end 40 | ], 41 | }, 42 | }, 43 | }; 44 | ``` 45 | 46 | The docs version dropdown appears in your navbar: 47 | 48 | ![Docs Version Dropdown](./img/docsVersionDropdown.png) 49 | 50 | ## Update an existing version 51 | 52 | It is possible to edit versioned docs in their respective folder: 53 | 54 | - `versioned_docs/version-1.0/hello.md` updates `http://localhost:3000/docs/hello` 55 | - `docs/hello.md` updates `http://localhost:3000/docs/next/hello` 56 | -------------------------------------------------------------------------------- /mobile/app/(home)/_layout.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Tabs } from "expo-router"; 3 | import Ionicons from "@expo/vector-icons/Ionicons"; 4 | 5 | export default function TabLayout() { 6 | return ( 7 | <> 8 | 13 | ( 18 | 23 | ), 24 | }} 25 | /> 26 | ( 31 | 36 | ), 37 | }} 38 | /> 39 | ( 44 | 49 | ), 50 | }} 51 | /> 52 | ( 57 | 62 | ), 63 | }} 64 | /> 65 | 66 | 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /doc/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | --- 4 | 5 | # Tutorial Intro 6 | 7 | Let's discover **Docusaurus in less than 5 minutes**. 8 | 9 | ## Getting Started 10 | 11 | Get started by **creating a new site**. 12 | 13 | Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. 14 | 15 | ### What you'll need 16 | 17 | - [Node.js](https://nodejs.org/en/download/) version 18.0 or above: 18 | - When installing Node.js, you are recommended to check all checkboxes related to dependencies. 19 | 20 | ## Generate a new site 21 | 22 | Generate a new Docusaurus site using the **classic template**. 23 | 24 | The classic template will automatically be added to your project after you run the command: 25 | 26 | ```bash 27 | npm init docusaurus@latest my-website classic 28 | ``` 29 | 30 | You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor. 31 | 32 | The command also installs all necessary dependencies you need to run Docusaurus. 33 | 34 | ## Start your site 35 | 36 | Run the development server: 37 | 38 | ```bash 39 | cd my-website 40 | npm run start 41 | ``` 42 | 43 | The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there. 44 | 45 | The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/. 46 | 47 | Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes. 48 | -------------------------------------------------------------------------------- /mobile/app/auth.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { View, StyleSheet } from "react-native"; 3 | import { UserAuthForm } from "@/components/user-auth-form"; 4 | import Text from "@/components/text"; 5 | 6 | export default function Auth() { 7 | return ( 8 | 9 | 10 | 11 | Spata 12 | 13 | Enter your email below to sign in 14 | 15 | 16 | 17 | 18 | 19 | 20 | By clicking continue, you agree to our{" "} 21 | 22 | 23 | Terms of Service and{" "} 24 | Privacy Policy . 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | const styles = StyleSheet.create({ 32 | container: { 33 | paddingTop: 80, 34 | flex: 1, 35 | alignItems: "center", 36 | }, 37 | innerContainer: { 38 | justifyContent: "center", 39 | alignItems: "center", 40 | }, 41 | header: { 42 | flexDirection: "column", 43 | alignItems: "center", 44 | marginBottom: 16, 45 | }, 46 | title: { 47 | fontSize: 24, 48 | fontWeight: "600", 49 | textAlign: "center", 50 | }, 51 | subtitle: { 52 | fontSize: 14, 53 | color: "#6b7280", 54 | textAlign: "center", 55 | }, 56 | termsText: { 57 | paddingHorizontal: 32, 58 | paddingVertical: 2, 59 | fontSize: 14, 60 | color: "#6b7280", 61 | textAlign: "center", 62 | }, 63 | link: { 64 | textDecorationLine: "underline", 65 | color: "#3b82f6", 66 | }, 67 | }); 68 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "test": "vitest", 8 | "dev": "vite", 9 | "build": "tsc && vite build", 10 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 11 | "preview": "vite preview" 12 | }, 13 | "dependencies": { 14 | "@radix-ui/react-dropdown-menu": "^2.1.2", 15 | "@radix-ui/react-icons": "^1.3.0", 16 | "@radix-ui/react-label": "^2.1.0", 17 | "@radix-ui/react-slot": "^1.1.0", 18 | "@tanstack/react-query": "^5.59.0", 19 | "@tanstack/react-query-devtools": "^5.59.0", 20 | "class-variance-authority": "^0.7.0", 21 | "clsx": "^2.1.1", 22 | "lucide-react": "^0.447.0", 23 | "react": "^18.2.0", 24 | "react-dom": "^18.2.0", 25 | "react-hook-form": "^7.53.0", 26 | "react-router": "^7.1.1", 27 | "tailwind-merge": "^2.5.3", 28 | "tailwindcss-animate": "^1.0.7" 29 | }, 30 | "devDependencies": { 31 | "@tanstack/eslint-plugin-query": "^5.59.1", 32 | "@testing-library/dom": "^10.4.0", 33 | "@testing-library/jest-dom": "^6.5.0", 34 | "@testing-library/react": "^16.0.1", 35 | "@types/node": "^22.7.4", 36 | "@types/react": "^18.2.43", 37 | "@types/react-dom": "^18.2.17", 38 | "@typescript-eslint/eslint-plugin": "^6.14.0", 39 | "@typescript-eslint/parser": "^6.14.0", 40 | "@vitejs/plugin-react": "^4.2.1", 41 | "autoprefixer": "^10.4.20", 42 | "eslint": "^8.55.0", 43 | "eslint-plugin-react-hooks": "^4.6.0", 44 | "eslint-plugin-react-refresh": "^0.4.5", 45 | "jsdom": "^25.0.1", 46 | "postcss": "^8.4.47", 47 | "tailwindcss": "^3.4.13", 48 | "typescript": "^5.2.2", 49 | "vite": "^5.0.8", 50 | "vitest": "^2.1.2" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | darkMode: ["class"], 4 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 5 | theme: { 6 | extend: { 7 | borderRadius: { 8 | lg: "var(--radius)", 9 | md: "calc(var(--radius) - 2px)", 10 | sm: "calc(var(--radius) - 4px)", 11 | }, 12 | colors: { 13 | background: "hsl(var(--background))", 14 | foreground: "hsl(var(--foreground))", 15 | card: { 16 | DEFAULT: "hsl(var(--card))", 17 | foreground: "hsl(var(--card-foreground))", 18 | }, 19 | popover: { 20 | DEFAULT: "hsl(var(--popover))", 21 | foreground: "hsl(var(--popover-foreground))", 22 | }, 23 | primary: { 24 | DEFAULT: "hsl(var(--primary))", 25 | foreground: "hsl(var(--primary-foreground))", 26 | }, 27 | secondary: { 28 | DEFAULT: "hsl(var(--secondary))", 29 | foreground: "hsl(var(--secondary-foreground))", 30 | }, 31 | muted: { 32 | DEFAULT: "hsl(var(--muted))", 33 | foreground: "hsl(var(--muted-foreground))", 34 | }, 35 | accent: { 36 | DEFAULT: "hsl(var(--accent))", 37 | foreground: "hsl(var(--accent-foreground))", 38 | }, 39 | destructive: { 40 | DEFAULT: "hsl(var(--destructive))", 41 | foreground: "hsl(var(--destructive-foreground))", 42 | }, 43 | border: "hsl(var(--border))", 44 | input: "hsl(var(--input))", 45 | ring: "hsl(var(--ring))", 46 | chart: { 47 | 1: "hsl(var(--chart-1))", 48 | 2: "hsl(var(--chart-2))", 49 | 3: "hsl(var(--chart-3))", 50 | 4: "hsl(var(--chart-4))", 51 | 5: "hsl(var(--chart-5))", 52 | }, 53 | }, 54 | }, 55 | }, 56 | plugins: [require("tailwindcss-animate")], 57 | }; 58 | -------------------------------------------------------------------------------- /app/src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | @layer base { 5 | :root { 6 | --background: 0 0% 100%; 7 | --foreground: 0 0% 3.9%; 8 | --card: 0 0% 100%; 9 | --card-foreground: 0 0% 3.9%; 10 | --popover: 0 0% 100%; 11 | --popover-foreground: 0 0% 3.9%; 12 | --primary: 0 0% 9%; 13 | --primary-foreground: 0 0% 98%; 14 | --secondary: 0 0% 96.1%; 15 | --secondary-foreground: 0 0% 9%; 16 | --muted: 0 0% 96.1%; 17 | --muted-foreground: 0 0% 45.1%; 18 | --accent: 0 0% 96.1%; 19 | --accent-foreground: 0 0% 9%; 20 | --destructive: 0 84.2% 60.2%; 21 | --destructive-foreground: 0 0% 98%; 22 | --border: 0 0% 89.8%; 23 | --input: 0 0% 89.8%; 24 | --ring: 0 0% 3.9%; 25 | --chart-1: 12 76% 61%; 26 | --chart-2: 173 58% 39%; 27 | --chart-3: 197 37% 24%; 28 | --chart-4: 43 74% 66%; 29 | --chart-5: 27 87% 67%; 30 | --radius: 0.5rem; 31 | } 32 | .dark { 33 | --background: 0 0% 3.9%; 34 | --foreground: 0 0% 98%; 35 | --card: 0 0% 3.9%; 36 | --card-foreground: 0 0% 98%; 37 | --popover: 0 0% 3.9%; 38 | --popover-foreground: 0 0% 98%; 39 | --primary: 0 0% 98%; 40 | --primary-foreground: 0 0% 9%; 41 | --secondary: 0 0% 14.9%; 42 | --secondary-foreground: 0 0% 98%; 43 | --muted: 0 0% 14.9%; 44 | --muted-foreground: 0 0% 63.9%; 45 | --accent: 0 0% 14.9%; 46 | --accent-foreground: 0 0% 98%; 47 | --destructive: 0 62.8% 30.6%; 48 | --destructive-foreground: 0 0% 98%; 49 | --border: 0 0% 14.9%; 50 | --input: 0 0% 14.9%; 51 | --ring: 0 0% 83.1%; 52 | --chart-1: 220 70% 50%; 53 | --chart-2: 160 60% 45%; 54 | --chart-3: 30 80% 55%; 55 | --chart-4: 280 65% 60%; 56 | --chart-5: 340 75% 55%; 57 | } 58 | } 59 | @layer base { 60 | * { 61 | @apply border-border; 62 | } 63 | body { 64 | @apply bg-background text-foreground; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | import { createContext, useContext, useEffect, useState } from "react"; 2 | 3 | type Theme = "dark" | "light" | "system"; 4 | 5 | type ThemeProviderProps = { 6 | children: React.ReactNode; 7 | defaultTheme?: Theme; 8 | storageKey?: string; 9 | }; 10 | 11 | type ThemeProviderState = { 12 | theme: Theme; 13 | setTheme: (theme: Theme) => void; 14 | }; 15 | 16 | const initialState: ThemeProviderState = { 17 | theme: "system", 18 | setTheme: () => null, 19 | }; 20 | 21 | const ThemeProviderContext = createContext(initialState); 22 | 23 | export function ThemeProvider({ 24 | children, 25 | defaultTheme = "system", 26 | storageKey = "app-ui-theme", 27 | ...props 28 | }: ThemeProviderProps) { 29 | const [theme, setTheme] = useState( 30 | () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, 31 | ); 32 | 33 | useEffect(() => { 34 | const root = window.document.documentElement; 35 | 36 | root.classList.remove("light", "dark"); 37 | 38 | if (theme === "system") { 39 | const systemTheme = window.matchMedia( 40 | "(prefers-color-scheme: dark)", 41 | ).matches 42 | ? "dark" 43 | : "light"; 44 | 45 | root.classList.add(systemTheme); 46 | return; 47 | } 48 | 49 | root.classList.add(theme); 50 | }, [theme]); 51 | 52 | const value = { 53 | theme, 54 | setTheme: (theme: Theme) => { 55 | localStorage.setItem(storageKey, theme); 56 | setTheme(theme); 57 | }, 58 | }; 59 | 60 | return ( 61 | 64 | {children} 65 | 66 | ); 67 | } 68 | 69 | export const useTheme = () => { 70 | const context = useContext(ThemeProviderContext); 71 | 72 | if (context === undefined) 73 | throw new Error("useTheme must be used within a ThemeProvider"); 74 | 75 | return context; 76 | }; 77 | -------------------------------------------------------------------------------- /mobile/components/theme-provider.tsx: -------------------------------------------------------------------------------- 1 | import React, { createContext, useContext, useEffect, useState } from "react"; 2 | 3 | import { ThemeProvider as NavigationThemeProvider } from "@react-navigation/native"; 4 | 5 | import AsyncStorage from "@react-native-async-storage/async-storage"; 6 | import { Appearance } from "react-native"; 7 | import { StatusBar } from "expo-status-bar"; 8 | 9 | import { LightTheme, DarkTheme } from "@/constants/themes"; 10 | 11 | type ThemeContextType = { 12 | isDark: boolean; 13 | toggleTheme: () => void; 14 | }; 15 | 16 | const ThemeContext = createContext(null); 17 | 18 | const THEME_STORAGE_KEY = "app-ui-theme"; 19 | 20 | export const ThemeProvider = ({ children }: { children: React.ReactNode }) => { 21 | const systemColorScheme = Appearance.getColorScheme(); 22 | const [isDark, setisDark] = useState(systemColorScheme === "dark"); 23 | 24 | useEffect(() => { 25 | const loadTheme = async () => { 26 | const savedTheme = await AsyncStorage.getItem(THEME_STORAGE_KEY); 27 | if (savedTheme !== null) { 28 | setisDark(savedTheme === "dark"); 29 | } 30 | }; 31 | 32 | loadTheme(); 33 | }, []); 34 | 35 | const toggleTheme = async () => { 36 | const newTheme = isDark ? "light" : "dark"; 37 | setisDark(!isDark); 38 | 39 | await AsyncStorage.setItem(THEME_STORAGE_KEY, newTheme); 40 | }; 41 | 42 | return ( 43 | 44 | 45 | {children} 46 | 47 | 48 | 49 | ); 50 | }; 51 | 52 | export const useTheme = () => { 53 | const context = useContext(ThemeContext); 54 | if (!context) { 55 | throw new Error("useTheme must be used within an ThemeProvider"); 56 | } 57 | 58 | return context; 59 | }; 60 | -------------------------------------------------------------------------------- /mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "reset-project": "node ./scripts/reset-project.js", 8 | "android": "expo start --android", 9 | "ios": "expo start --ios", 10 | "web": "expo start --web", 11 | "test": "jest --watchAll", 12 | "lint": "expo lint" 13 | }, 14 | "jest": { 15 | "preset": "jest-expo" 16 | }, 17 | "dependencies": { 18 | "@expo/vector-icons": "^14.0.2", 19 | "@react-native-async-storage/async-storage": "1.23.1", 20 | "@react-navigation/native": "^6.0.2", 21 | "@tanstack/react-query": "^5.59.0", 22 | "expo": "~51.0.28", 23 | "expo-constants": "~16.0.2", 24 | "expo-font": "~12.0.9", 25 | "expo-linking": "~6.3.1", 26 | "expo-router": "~3.5.23", 27 | "expo-splash-screen": "~0.27.5", 28 | "expo-status-bar": "~1.12.1", 29 | "expo-system-ui": "~3.0.7", 30 | "expo-web-browser": "~13.0.3", 31 | "react": "18.2.0", 32 | "react-dom": "18.2.0", 33 | "react-hook-form": "^7.53.0", 34 | "react-native": "0.74.5", 35 | "react-native-gesture-handler": "~2.16.1", 36 | "react-native-reanimated": "~3.10.1", 37 | "react-native-safe-area-context": "4.10.5", 38 | "react-native-screens": "3.31.1", 39 | "react-native-web": "~0.19.10", 40 | "react-native-svg": "15.2.0" 41 | }, 42 | "devDependencies": { 43 | "@babel/core": "^7.20.0", 44 | "@tanstack/eslint-plugin-query": "^5.59.1", 45 | "@testing-library/react-native": "^12.7.2", 46 | "@types/jest": "^29.5.12", 47 | "@types/react": "~18.2.45", 48 | "@types/react-test-renderer": "^18.0.7", 49 | "eslint": "^8.57.0", 50 | "eslint-config-expo": "^7.1.2", 51 | "jest": "^29.2.1", 52 | "jest-expo": "~51.0.3", 53 | "react-test-renderer": "18.2.0", 54 | "typescript": "~5.3.3" 55 | }, 56 | "private": true, 57 | "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72" 58 | } 59 | -------------------------------------------------------------------------------- /mobile/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your Expo app 👋 2 | 3 | This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). 4 | 5 | ## Get started 6 | 7 | 1. Install dependencies 8 | 9 | ```bash 10 | npm install 11 | ``` 12 | 13 | 2. Start the app 14 | 15 | ```bash 16 | npx expo start 17 | ``` 18 | 19 | In the output, you'll find options to open the app in a 20 | 21 | - [development build](https://docs.expo.dev/develop/development-builds/introduction/) 22 | - [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) 23 | - [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) 24 | - [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo 25 | 26 | You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). 27 | 28 | ## Get a fresh project 29 | 30 | When you're ready, run: 31 | 32 | ```bash 33 | npm run reset-project 34 | ``` 35 | 36 | This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. 37 | 38 | ## Learn more 39 | 40 | To learn more about developing your project with Expo, look at the following resources: 41 | 42 | - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). 43 | - [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. 44 | 45 | ## Join the community 46 | 47 | Join our community of developers creating universal apps. 48 | 49 | - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. 50 | - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. 51 | -------------------------------------------------------------------------------- /doc/docs/tutorial-extras/translate-your-site.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 2 3 | --- 4 | 5 | # Translate your site 6 | 7 | Let's translate `docs/intro.md` to French. 8 | 9 | ## Configure i18n 10 | 11 | Modify `docusaurus.config.js` to add support for the `fr` locale: 12 | 13 | ```js title="docusaurus.config.js" 14 | export default { 15 | i18n: { 16 | defaultLocale: "en", 17 | locales: ["en", "fr"], 18 | }, 19 | }; 20 | ``` 21 | 22 | ## Translate a doc 23 | 24 | Copy the `docs/intro.md` file to the `i18n/fr` folder: 25 | 26 | ```bash 27 | mkdir -p i18n/fr/docusaurus-plugin-content-docs/current/ 28 | 29 | cp docs/intro.md i18n/fr/docusaurus-plugin-content-docs/current/intro.md 30 | ``` 31 | 32 | Translate `i18n/fr/docusaurus-plugin-content-docs/current/intro.md` in French. 33 | 34 | ## Start your localized site 35 | 36 | Start your site on the French locale: 37 | 38 | ```bash 39 | npm run start -- --locale fr 40 | ``` 41 | 42 | Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated. 43 | 44 | :::caution 45 | 46 | In development, you can only use one locale at a time. 47 | 48 | ::: 49 | 50 | ## Add a Locale Dropdown 51 | 52 | To navigate seamlessly across languages, add a locale dropdown. 53 | 54 | Modify the `docusaurus.config.js` file: 55 | 56 | ```js title="docusaurus.config.js" 57 | export default { 58 | themeConfig: { 59 | navbar: { 60 | items: [ 61 | // highlight-start 62 | { 63 | type: "localeDropdown", 64 | }, 65 | // highlight-end 66 | ], 67 | }, 68 | }, 69 | }; 70 | ``` 71 | 72 | The locale dropdown now appears in your navbar: 73 | 74 | ![Locale Dropdown](./img/localeDropdown.png) 75 | 76 | ## Build your localized site 77 | 78 | Build your site for a specific locale: 79 | 80 | ```bash 81 | npm run build -- --locale fr 82 | ``` 83 | 84 | Or build your site to include all the locales at once: 85 | 86 | ```bash 87 | npm run build 88 | ``` 89 | -------------------------------------------------------------------------------- /app/src/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | }, 35 | ); 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean; 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button"; 46 | return ( 47 | 52 | ); 53 | }, 54 | ); 55 | Button.displayName = "Button"; 56 | 57 | export { Button, buttonVariants }; 58 | -------------------------------------------------------------------------------- /doc/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import clsx from "clsx"; 2 | import Heading from "@theme/Heading"; 3 | import styles from "./styles.module.css"; 4 | 5 | type FeatureItem = { 6 | title: string; 7 | Svg: React.ComponentType>; 8 | description: JSX.Element; 9 | }; 10 | 11 | const FeatureList: FeatureItem[] = [ 12 | { 13 | title: "Easy to Use", 14 | Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, 15 | description: ( 16 | <> 17 | Docusaurus was designed from the ground up to be easily 18 | installed and used to get your website up and running quickly. 19 | 20 | ), 21 | }, 22 | { 23 | title: "Focus on What Matters", 24 | Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, 25 | description: ( 26 | <> 27 | Docusaurus lets you focus on your docs, and we'll do the 28 | chores. Go ahead and move your docs into the docs{" "} 29 | directory. 30 | 31 | ), 32 | }, 33 | { 34 | title: "Powered by React", 35 | Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, 36 | description: ( 37 | <> 38 | Extend or customize your website layout by reusing React. 39 | Docusaurus can be extended while reusing the same header and 40 | footer. 41 | 42 | ), 43 | }, 44 | ]; 45 | 46 | function Feature({ title, Svg, description }: FeatureItem) { 47 | return ( 48 |
49 |
50 | 54 |
55 |
56 | {title} 57 |

{description}

58 |
59 |
60 | ); 61 | } 62 | 63 | export default function HomepageFeatures(): JSX.Element { 64 | return ( 65 |
66 |
67 |
68 | {FeatureList.map((props, idx) => ( 69 | 73 | ))} 74 |
75 |
76 |
77 | ); 78 | } 79 | -------------------------------------------------------------------------------- /app/src/components/user-auth-form.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { Icons } from "@/components/icons"; 4 | import { Button } from "@/components/ui/button"; 5 | import { Input } from "@/components/ui/input"; 6 | import { Label } from "@/components/ui/label"; 7 | 8 | import { useForm, SubmitHandler } from "react-hook-form"; 9 | 10 | interface UserAuthFormProps extends React.HTMLAttributes {} 11 | type Inputs = { email: string }; 12 | 13 | export function UserAuthForm({ ...props }: UserAuthFormProps) { 14 | const [isLoading, setIsLoading] = React.useState(false); 15 | 16 | const { 17 | register, 18 | handleSubmit, 19 | formState: { errors }, 20 | } = useForm(); 21 | 22 | const onSubmit: SubmitHandler = data => { 23 | setIsLoading(true); 24 | 25 | setTimeout(() => { 26 | console.log(data); 27 | setIsLoading(false); 28 | }, 3000); 29 | }; 30 | 31 | return ( 32 |
35 |
36 |
37 |
38 | 43 | 53 | {errors.email && ( 54 | 55 | Email is required 56 | 57 | )} 58 |
59 | 65 |
66 |
67 |
68 |
69 | 70 |
71 |
72 | 73 | Or continue with 74 | 75 |
76 |
77 |
78 | 89 | 100 |
101 |
102 | ); 103 | } 104 | -------------------------------------------------------------------------------- /doc/blog/2019-05-29-long-blog-post.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: long-blog-post 3 | title: Long Blog Post 4 | authors: yangshun 5 | tags: [hello, docusaurus] 6 | --- 7 | 8 | This is the summary of a very long blog post, 9 | 10 | Use a `` comment to limit blog post size in the list view. 11 | 12 | 13 | 14 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 15 | 16 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 17 | 18 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 19 | 20 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 21 | 22 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 23 | 24 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 25 | 26 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 27 | 28 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 29 | 30 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 31 | 32 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 33 | 34 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 35 | 36 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 37 | 38 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 39 | 40 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 41 | 42 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 43 | 44 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet 45 | -------------------------------------------------------------------------------- /doc/docs/tutorial-basics/markdown-features.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 4 3 | --- 4 | 5 | # Markdown Features 6 | 7 | Docusaurus supports **[Markdown](https://daringfireball.net/projects/markdown/syntax)** and a few **additional features**. 8 | 9 | ## Front Matter 10 | 11 | Markdown documents have metadata at the top called [Front Matter](https://jekyllrb.com/docs/front-matter/): 12 | 13 | ```text title="my-doc.md" 14 | // highlight-start 15 | --- 16 | id: my-doc-id 17 | title: My document title 18 | description: My document description 19 | slug: /my-custom-url 20 | --- 21 | // highlight-end 22 | 23 | ## Markdown heading 24 | 25 | Markdown text with [links](./hello.md) 26 | ``` 27 | 28 | ## Links 29 | 30 | Regular Markdown links are supported, using url paths or relative file paths. 31 | 32 | ```md 33 | Let's see how to [Create a page](/create-a-page). 34 | ``` 35 | 36 | ```md 37 | Let's see how to [Create a page](./create-a-page.md). 38 | ``` 39 | 40 | **Result:** Let's see how to [Create a page](./create-a-page.md). 41 | 42 | ## Images 43 | 44 | Regular Markdown images are supported. 45 | 46 | You can use absolute paths to reference images in the static directory (`static/img/docusaurus.png`): 47 | 48 | ```md 49 | ![Docusaurus logo](/img/docusaurus.png) 50 | ``` 51 | 52 | ![Docusaurus logo](/img/docusaurus.png) 53 | 54 | You can reference images relative to the current file as well. This is particularly useful to colocate images close to the Markdown files using them: 55 | 56 | ```md 57 | ![Docusaurus logo](./img/docusaurus.png) 58 | ``` 59 | 60 | ## Code Blocks 61 | 62 | Markdown code blocks are supported with Syntax highlighting. 63 | 64 | ````md 65 | ```jsx title="src/components/HelloDocusaurus.js" 66 | function HelloDocusaurus() { 67 | return

Hello, Docusaurus!

; 68 | } 69 | ``` 70 | ```` 71 | 72 | ```jsx title="src/components/HelloDocusaurus.js" 73 | function HelloDocusaurus() { 74 | return

Hello, Docusaurus!

; 75 | } 76 | ``` 77 | 78 | ## Admonitions 79 | 80 | Docusaurus has a special syntax to create admonitions and callouts: 81 | 82 | ```md 83 | :::tip My tip 84 | 85 | Use this awesome feature option 86 | 87 | ::: 88 | 89 | :::danger Take care 90 | 91 | This action is dangerous 92 | 93 | ::: 94 | ``` 95 | 96 | :::tip My tip 97 | 98 | Use this awesome feature option 99 | 100 | ::: 101 | 102 | :::danger Take care 103 | 104 | This action is dangerous 105 | 106 | ::: 107 | 108 | ## MDX and React Components 109 | 110 | [MDX](https://mdxjs.com/) can make your documentation more **interactive** and allows using any **React components inside Markdown**: 111 | 112 | ```jsx 113 | export const Highlight = ({children, color}) => ( 114 | { 123 | alert(`You clicked the color ${color} with label ${children}`) 124 | }}> 125 | {children} 126 | 127 | ); 128 | 129 | This is Docusaurus green ! 130 | 131 | This is Facebook blue ! 132 | ``` 133 | 134 | export const Highlight = ({ children, color }) => ( 135 | { 144 | alert(`You clicked the color ${color} with label ${children}`); 145 | }}> 146 | {children} 147 | 148 | ); 149 | 150 | This is Docusaurus green ! 151 | 152 | This is Facebook blue ! 153 | -------------------------------------------------------------------------------- /app/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/docusaurus.config.ts: -------------------------------------------------------------------------------- 1 | import { themes as prismThemes } from "prism-react-renderer"; 2 | import type { Config } from "@docusaurus/types"; 3 | import type * as Preset from "@docusaurus/preset-classic"; 4 | 5 | const config: Config = { 6 | title: "Spata", 7 | tagline: "SPA Starter", 8 | favicon: "img/favicon.ico", 9 | 10 | // Set the production url of your site here 11 | url: "https://your-docusaurus-site.example.com", 12 | // Set the // pathname under which your site is served 13 | // For GitHub pages deployment, it is often '//' 14 | baseUrl: "/", 15 | 16 | // GitHub pages deployment config. 17 | // If you aren't using GitHub pages, you don't need these. 18 | organizationName: "eimg", // Usually your GitHub org/user name. 19 | projectName: "spata", // Usually your repo name. 20 | 21 | onBrokenLinks: "throw", 22 | onBrokenMarkdownLinks: "warn", 23 | 24 | // Even if you don't use internationalization, you can use this field to set 25 | // useful metadata like html lang. For example, if your site is Chinese, you 26 | // may want to replace "en" with "zh-Hans". 27 | i18n: { 28 | defaultLocale: "en", 29 | locales: ["en"], 30 | }, 31 | 32 | presets: [ 33 | [ 34 | "classic", 35 | { 36 | docs: { 37 | sidebarPath: "./sidebars.ts", 38 | // Please change this to your repo. 39 | // Remove this to remove the "edit this page" links. 40 | editUrl: 41 | "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", 42 | }, 43 | blog: { 44 | showReadingTime: true, 45 | feedOptions: { 46 | type: ["rss", "atom"], 47 | xslt: true, 48 | }, 49 | // Please change this to your repo. 50 | // Remove this to remove the "edit this page" links. 51 | editUrl: 52 | "https://github.com/facebook/docusaurus/tree/main/packages/create-docusaurus/templates/shared/", 53 | // Useful options to enforce blogging best practices 54 | onInlineTags: "warn", 55 | onInlineAuthors: "warn", 56 | onUntruncatedBlogPosts: "warn", 57 | }, 58 | theme: { 59 | customCss: "./src/css/custom.css", 60 | }, 61 | } satisfies Preset.Options, 62 | ], 63 | ], 64 | 65 | themeConfig: { 66 | // Replace with your project's social card 67 | image: "img/docusaurus-social-card.jpg", 68 | navbar: { 69 | title: "Spata", 70 | logo: { 71 | alt: "Spata Logo", 72 | src: "img/logo.svg", 73 | }, 74 | items: [ 75 | { 76 | type: "docSidebar", 77 | sidebarId: "tutorialSidebar", 78 | position: "left", 79 | label: "Tutorial", 80 | }, 81 | { to: "/blog", label: "Blog", position: "left" }, 82 | { 83 | href: "https://github.com/facebook/docusaurus", 84 | label: "GitHub", 85 | position: "right", 86 | }, 87 | ], 88 | }, 89 | footer: { 90 | style: "light", 91 | links: [ 92 | { 93 | title: "Docs", 94 | items: [ 95 | { 96 | label: "Tutorial", 97 | to: "/docs/intro", 98 | }, 99 | ], 100 | }, 101 | { 102 | title: "Community", 103 | items: [ 104 | { 105 | label: "Stack Overflow", 106 | href: "https://stackoverflow.com/questions/tagged/docusaurus", 107 | }, 108 | { 109 | label: "Discord", 110 | href: "https://discordapp.com/invite/docusaurus", 111 | }, 112 | { 113 | label: "Twitter", 114 | href: "https://twitter.com/docusaurus", 115 | }, 116 | ], 117 | }, 118 | { 119 | title: "More", 120 | items: [ 121 | { 122 | label: "Blog", 123 | to: "/blog", 124 | }, 125 | { 126 | label: "GitHub", 127 | href: "https://github.com/facebook/docusaurus", 128 | }, 129 | ], 130 | }, 131 | ], 132 | copyright: `Copyright © ${new Date().getFullYear()} Spata. Built with Docusaurus.`, 133 | }, 134 | prism: { 135 | theme: prismThemes.github, 136 | darkTheme: prismThemes.dracula, 137 | }, 138 | } satisfies Preset.ThemeConfig, 139 | }; 140 | 141 | export default config; 142 | -------------------------------------------------------------------------------- /mobile/components/user-auth-form.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { 3 | View, 4 | TextInput, 5 | StyleSheet, 6 | ActivityIndicator, 7 | TouchableOpacity, 8 | } from "react-native"; 9 | 10 | import Text from "./text"; 11 | import { useTheme } from "@react-navigation/native"; 12 | import Ionicons from "@expo/vector-icons/Ionicons"; 13 | import { useForm, Controller } from "react-hook-form"; 14 | 15 | export function UserAuthForm() { 16 | const [isLoading, setIsLoading] = useState(false); 17 | 18 | const { 19 | control, 20 | handleSubmit, 21 | formState: { errors }, 22 | } = useForm(); 23 | 24 | const onSubmit = data => { 25 | setIsLoading(true); 26 | 27 | setTimeout(() => { 28 | console.log(data); 29 | setIsLoading(false); 30 | }, 3000); 31 | }; 32 | 33 | const { colors } = useTheme(); 34 | 35 | return ( 36 | 37 | 38 | ( 44 | 55 | )} 56 | name="email" 57 | /> 58 | {errors.email && ( 59 | 60 | Email is required. 61 | 62 | )} 63 | 64 | 65 | 69 | Sign in with email 70 | 71 | {isLoading && } 72 | 73 | 74 | 75 | 80 | Or continue with 81 | 82 | 83 | 84 | 85 | {}} 88 | style={[styles.socialButton, { borderColor: colors.text }]}> 89 | 94 | Github 95 | 96 | {}} 99 | style={[styles.socialButton, { borderColor: colors.text }]}> 100 | 105 | Google 106 | 107 | 108 | 109 | ); 110 | } 111 | 112 | const styles = StyleSheet.create({ 113 | container: { 114 | marginVertical: 10, 115 | }, 116 | inputContainer: { 117 | width: 300, 118 | marginBottom: 12, 119 | }, 120 | input: { 121 | borderWidth: 1, 122 | padding: 8, 123 | borderRadius: 4, 124 | borderColor: "#ccc", 125 | }, 126 | spinner: { 127 | marginTop: 8, 128 | }, 129 | button: { 130 | paddingHorizontal: 20, 131 | paddingVertical: 12, 132 | borderRadius: 4, 133 | alignItems: "center", 134 | }, 135 | separator: { 136 | marginVertical: 16, 137 | alignItems: "center", 138 | justifyContent: "center", 139 | }, 140 | line: { 141 | position: "absolute", 142 | borderBottomWidth: 1, 143 | borderColor: "#6b7280", 144 | width: "100%", 145 | }, 146 | orText: { 147 | marginVertical: 8, 148 | fontSize: 12, 149 | textAlign: "center", 150 | paddingHorizontal: 10, 151 | color: "#6b7280", 152 | }, 153 | socialButtons: { 154 | gap: 10, 155 | }, 156 | socialButton: { 157 | flexDirection: "row", 158 | paddingHorizontal: 20, 159 | paddingVertical: 12, 160 | borderRadius: 4, 161 | justifyContent: "center", 162 | alignItems: "center", 163 | gap: 5, 164 | borderWidth: 1, 165 | marginBottom: 10, 166 | }, 167 | }); 168 | -------------------------------------------------------------------------------- /doc/static/img/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; 3 | import { 4 | CheckIcon, 5 | ChevronRightIcon, 6 | DotFilledIcon, 7 | } from "@radix-ui/react-icons"; 8 | 9 | import { cn } from "@/lib/utils"; 10 | 11 | const DropdownMenu = DropdownMenuPrimitive.Root; 12 | 13 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; 14 | 15 | const DropdownMenuGroup = DropdownMenuPrimitive.Group; 16 | 17 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal; 18 | 19 | const DropdownMenuSub = DropdownMenuPrimitive.Sub; 20 | 21 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; 22 | 23 | const DropdownMenuSubTrigger = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef & { 26 | inset?: boolean; 27 | } 28 | >(({ className, inset, children, ...props }, ref) => ( 29 | 37 | {children} 38 | 39 | 40 | )); 41 | DropdownMenuSubTrigger.displayName = 42 | DropdownMenuPrimitive.SubTrigger.displayName; 43 | 44 | const DropdownMenuSubContent = React.forwardRef< 45 | React.ElementRef, 46 | React.ComponentPropsWithoutRef 47 | >(({ className, ...props }, ref) => ( 48 | 56 | )); 57 | DropdownMenuSubContent.displayName = 58 | DropdownMenuPrimitive.SubContent.displayName; 59 | 60 | const DropdownMenuContent = React.forwardRef< 61 | React.ElementRef, 62 | React.ComponentPropsWithoutRef 63 | >(({ className, sideOffset = 4, ...props }, ref) => ( 64 | 65 | 75 | 76 | )); 77 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; 78 | 79 | const DropdownMenuItem = React.forwardRef< 80 | React.ElementRef, 81 | React.ComponentPropsWithoutRef & { 82 | inset?: boolean; 83 | } 84 | >(({ className, inset, ...props }, ref) => ( 85 | 94 | )); 95 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; 96 | 97 | const DropdownMenuCheckboxItem = React.forwardRef< 98 | React.ElementRef, 99 | React.ComponentPropsWithoutRef 100 | >(({ className, children, checked, ...props }, ref) => ( 101 | 109 | 110 | 111 | 112 | 113 | 114 | {children} 115 | 116 | )); 117 | DropdownMenuCheckboxItem.displayName = 118 | DropdownMenuPrimitive.CheckboxItem.displayName; 119 | 120 | const DropdownMenuRadioItem = React.forwardRef< 121 | React.ElementRef, 122 | React.ComponentPropsWithoutRef 123 | >(({ className, children, ...props }, ref) => ( 124 | 131 | 132 | 133 | 134 | 135 | 136 | {children} 137 | 138 | )); 139 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; 140 | 141 | const DropdownMenuLabel = React.forwardRef< 142 | React.ElementRef, 143 | React.ComponentPropsWithoutRef & { 144 | inset?: boolean; 145 | } 146 | >(({ className, inset, ...props }, ref) => ( 147 | 156 | )); 157 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; 158 | 159 | const DropdownMenuSeparator = React.forwardRef< 160 | React.ElementRef, 161 | React.ComponentPropsWithoutRef 162 | >(({ className, ...props }, ref) => ( 163 | 168 | )); 169 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; 170 | 171 | const DropdownMenuShortcut = ({ 172 | className, 173 | ...props 174 | }: React.HTMLAttributes) => { 175 | return ( 176 | 183 | ); 184 | }; 185 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; 186 | 187 | export { 188 | DropdownMenu, 189 | DropdownMenuTrigger, 190 | DropdownMenuContent, 191 | DropdownMenuItem, 192 | DropdownMenuCheckboxItem, 193 | DropdownMenuRadioItem, 194 | DropdownMenuLabel, 195 | DropdownMenuSeparator, 196 | DropdownMenuShortcut, 197 | DropdownMenuGroup, 198 | DropdownMenuPortal, 199 | DropdownMenuSub, 200 | DropdownMenuSubContent, 201 | DropdownMenuSubTrigger, 202 | DropdownMenuRadioGroup, 203 | }; 204 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SPATA - Full-Stack Starter Template 2 | 3 | [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/) 4 | [![Node.js](https://img.shields.io/badge/Node.js-18%2B-brightgreen.svg)](https://nodejs.org/) 5 | [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) 6 | 7 | > **SPATA** (SPA Starter) - A comprehensive, production-ready full-stack starter template that gets you from zero to deployment in minutes, not hours. 8 | 9 | ## 🚀 What is SPATA? 10 | 11 | SPATA eliminates the tedious initial setup tasks that every modern web project requires. Instead of spending days configuring linters, tests, routing, state management, and essential packages, you can focus on building your actual product from day one. 12 | 13 | ### ✨ Perfect for: 14 | - 🏢 **Startups** needing to move fast with proven architecture 15 | - 👨‍💻 **Developers** who want to skip the boilerplate setup 16 | - 🎯 **Product teams** focusing on features, not configuration 17 | - 📚 **Learning** modern full-stack development patterns 18 | 19 | ## 🏗️ Architecture Overview 20 | 21 | SPATA provides a complete ecosystem with four integrated components: 22 | 23 | ``` 24 | 📦 SPATA 25 | ├── 🌐 Web App (React SPA) 26 | ├── 📱 Mobile App (React Native + Expo) 27 | ├── ⚡ API Backend (Express + TypeScript) 28 | └── 📖 Documentation (Docusaurus) 29 | ``` 30 | 31 | ## 🛠️ Tech Stack 32 | 33 | ### Web App (`/app`) 34 | - **Framework**: React 18 + Vite (lightning-fast development) 35 | - **Language**: TypeScript (type safety) 36 | - **Styling**: Tailwind CSS + shadcn/ui (beautiful, accessible components) 37 | - **Routing**: React Router v7 (client-side routing) 38 | - **State Management**: React Query (server state) + React Hook Form (form state) 39 | - **Testing**: Vitest + React Testing Library + jsdom 40 | - **Theme**: Built-in light/dark mode switcher 41 | - **Linting**: ESLint with React Query rules 42 | 43 | ### Mobile App (`/mobile`) 44 | - **Framework**: React Native + Expo (cross-platform mobile) 45 | - **Language**: TypeScript 46 | - **Navigation**: Expo Router (file-based routing) 47 | - **State Management**: React Query + React Hook Form 48 | - **Testing**: Jest + React Native Testing Library 49 | - **Theme**: Custom theme system with light/dark mode 50 | - **Linting**: ESLint with React Query rules 51 | 52 | ### API Backend (`/api`) 53 | - **Runtime**: Node.js + Express 54 | - **Language**: TypeScript 55 | - **Database**: Prisma ORM + SQLite (easily swappable) 56 | - **Validation**: Express Validator 57 | - **Testing**: Jest + supertest 58 | - **Development**: Hot reload with nodemon 59 | - **Database Tools**: Migrations, seeding, and reset capabilities 60 | 61 | ### Documentation (`/doc`) 62 | - **Framework**: Docusaurus v3 63 | - **Language**: TypeScript + MDX 64 | - **Features**: Blog, versioning, search, and internationalization ready 65 | 66 | ## 🚀 Quick Start 67 | 68 | ### Prerequisites 69 | - **Node.js** 18+ ([Download](https://nodejs.org/)) 70 | - **npm** 9+ (comes with Node.js) 71 | - **Git** ([Download](https://git-scm.com/)) 72 | 73 | ### 1️⃣ Clone and Setup 74 | ```bash 75 | # Clone the repository 76 | git clone 77 | cd spata 78 | 79 | # Choose your component and get started! 80 | ``` 81 | 82 | ### 2️⃣ Start Development 83 | 84 | #### 🌐 Web App 85 | ```bash 86 | cd app 87 | npm install 88 | npm run dev 89 | ``` 90 | - Opens at `http://localhost:5173` 91 | - Hot reload enabled 92 | - React DevTools & React Query DevTools included 93 | 94 | #### 📱 Mobile App 95 | ```bash 96 | cd mobile 97 | npm install 98 | npm run start 99 | ``` 100 | - Scan QR code with Expo Go app 101 | - Works on iOS, Android, and web 102 | - Hot reload across all platforms 103 | 104 | #### ⚡ API Backend 105 | ```bash 106 | cd api 107 | # Setup environment 108 | cp prisma.env .env 109 | 110 | npm install 111 | npm run migrate # Setup database 112 | npm run seed # Add sample data 113 | npm run dev # Start development server 114 | ``` 115 | - Runs at `http://localhost:3000` 116 | - Database UI at `npx prisma studio` 117 | - API automatically restarts on changes 118 | 119 | #### 📖 Documentation 120 | ```bash 121 | cd doc 122 | npm install 123 | npm run start 124 | ``` 125 | - Opens at `http://localhost:3000` 126 | - Live editing with hot reload 127 | - Production build with `npm run build` 128 | 129 | ## 📁 Project Structure 130 | 131 | ``` 132 | spata/ 133 | ├── 📱 app/ # React Web Application 134 | │ ├── src/ 135 | │ │ ├── components/ # Reusable UI components 136 | │ │ ├── pages/ # Route components 137 | │ │ ├── lib/ # Utilities and configuration 138 | │ │ └── assets/ # Static assets 139 | │ ├── tests/ # Component and integration tests 140 | │ └── public/ # Public assets 141 | │ 142 | ├── 📲 mobile/ # React Native Mobile App 143 | │ ├── app/ # Expo Router pages 144 | │ ├── components/ # Reusable mobile components 145 | │ ├── constants/ # App constants and themes 146 | │ └── __tests__/ # Mobile app tests 147 | │ 148 | ├── 🔧 api/ # Express API Backend 149 | │ ├── src/ 150 | │ │ ├── routes/ # API route handlers 151 | │ │ └── index.ts # Express server setup 152 | │ ├── prisma/ # Database schema and migrations 153 | │ └── __tests__/ # API endpoint tests 154 | │ 155 | └── 📚 doc/ # Docusaurus Documentation 156 | ├── docs/ # Documentation pages 157 | ├── blog/ # Blog posts 158 | └── src/ # Custom components 159 | ``` 160 | 161 | ## 🧪 Testing 162 | 163 | Each component includes comprehensive testing setup: 164 | 165 | ```bash 166 | # Web App Testing 167 | cd app && npm test 168 | 169 | # Mobile App Testing 170 | cd mobile && npm test 171 | 172 | # API Testing 173 | cd api && npm test 174 | ``` 175 | 176 | ## 🔧 Available Scripts 177 | 178 | ### Web App (`/app`) 179 | - `npm run dev` - Start development server 180 | - `npm run build` - Production build 181 | - `npm run preview` - Preview production build 182 | - `npm test` - Run tests with Vitest 183 | - `npm run lint` - Run ESLint 184 | 185 | ### Mobile App (`/mobile`) 186 | - `npm run start` - Start Expo development server 187 | - `npm run android` - Run on Android device/emulator 188 | - `npm run ios` - Run on iOS device/simulator 189 | - `npm run web` - Run as web app 190 | - `npm test` - Run tests with Jest 191 | 192 | ### API (`/api`) 193 | - `npm run dev` - Start development server with hot reload 194 | - `npm run build` - Compile TypeScript 195 | - `npm run start` - Start production server 196 | - `npm run migrate` - Run database migrations 197 | - `npm run seed` - Seed database with sample data 198 | - `npm run migrate:reset` - Reset database 199 | - `npm test` - Run API tests 200 | 201 | ### Documentation (`/doc`) 202 | - `npm run start` - Start development server 203 | - `npm run build` - Build for production 204 | - `npm run serve` - Serve production build locally 205 | 206 | ## 🎨 Customization 207 | 208 | ### Themes & Styling 209 | - **Web**: Modify `tailwind.config.js` and shadcn/ui components 210 | - **Mobile**: Update theme constants in `constants/Colors.ts` 211 | - Both apps include light/dark mode switching out of the box 212 | 213 | ### Database Schema 214 | ```bash 215 | cd api 216 | # Edit prisma/schema.prisma 217 | npm run migrate 218 | ``` 219 | 220 | ### Environment Variables 221 | - **API**: Copy `prisma.env` to `.env` and customize 222 | - **Web/Mobile**: Add environment variables as needed 223 | 224 | ## 🚀 Deployment 225 | 226 | ### Web App 227 | - **Vercel/Netlify**: Connect your Git repository 228 | - **Static Hosting**: Run `npm run build` and deploy `dist/` folder 229 | 230 | ### Mobile App 231 | - **Expo**: Run `expo build` for app store deployment 232 | - **Development Build**: Use EAS Build for custom native code 233 | 234 | ### API 235 | - **Railway/Render**: Connect your Git repository 236 | - **VPS**: Use PM2 with `npm run build && npm run start` 237 | 238 | ### Documentation 239 | - **GitHub Pages**: Use `npm run deploy` 240 | - **Static Hosting**: Deploy `build/` folder after `npm run build` 241 | 242 | ## 🤝 Contributing 243 | 244 | We welcome contributions! Here's how to get started: 245 | 246 | 1. **Fork** the repository 247 | 2. **Create** a feature branch: `git checkout -b feature/amazing-feature` 248 | 3. **Commit** your changes: `git commit -m 'Add amazing feature'` 249 | 4. **Push** to the branch: `git push origin feature/amazing-feature` 250 | 5. **Open** a Pull Request 251 | 252 | ### Development Guidelines 253 | - Follow the existing code style 254 | - Add tests for new features 255 | - Update documentation as needed 256 | - Ensure all tests pass before submitting 257 | 258 | ## 🗺️ Roadmap 259 | 260 | - [ ] **Authentication System** - JWT-based auth with user management 261 | - [ ] **Database Options** - PostgreSQL, MySQL configuration guides 262 | - [ ] **Deployment Scripts** - One-command deployment to popular platforms 263 | - [ ] **Component Library** - Extended shadcn/ui component collection 264 | - [ ] **Testing Coverage** - E2E testing with Playwright 265 | - [ ] **Monitoring** - Built-in error tracking and analytics 266 | - [ ] **i18n Support** - Internationalization for global apps 267 | 268 | ## 📄 License 269 | 270 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. 271 | 272 | ## 💬 Support 273 | 274 | - 🐛 **Issues**: [GitHub Issues](https://github.com/your-org/spata/issues) 275 | - 💡 **Feature Requests**: [GitHub Discussions](https://github.com/your-org/spata/discussions) 276 | - 📖 **Documentation**: Visit `/doc` after running the docs server 277 | 278 | --- 279 | 280 |
281 | 282 | **Built with ❤️ by [Fairway Technology](https://fairway.technology)** 283 | 284 | ⭐ **Star this repo if SPATA helps you build faster!** ⭐ 285 | 286 |
287 | -------------------------------------------------------------------------------- /doc/static/img/undraw_docusaurus_tree.svg: -------------------------------------------------------------------------------- 1 | 2 | Focus on What Matters 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/components/icons.tsx: -------------------------------------------------------------------------------- 1 | type IconProps = React.HTMLAttributes; 2 | 3 | export const Icons = { 4 | logo: (props: IconProps) => ( 5 | 9 | 14 | 25 | 36 | 37 | ), 38 | twitter: (props: IconProps) => ( 39 | 45 | 46 | 47 | ), 48 | gitHub: (props: IconProps) => ( 49 | 52 | 55 | 56 | ), 57 | radix: (props: IconProps) => ( 58 | 62 | 65 | 68 | 71 | 72 | ), 73 | aria: (props: IconProps) => ( 74 | 79 | 80 | 81 | ), 82 | npm: (props: IconProps) => ( 83 | 86 | 90 | 91 | ), 92 | yarn: (props: IconProps) => ( 93 | 96 | 100 | 101 | ), 102 | pnpm: (props: IconProps) => ( 103 | 106 | 110 | 111 | ), 112 | react: (props: IconProps) => ( 113 | 116 | 120 | 121 | ), 122 | tailwind: (props: IconProps) => ( 123 | 126 | 130 | 131 | ), 132 | google: (props: IconProps) => ( 133 | 137 | 141 | 142 | ), 143 | apple: (props: IconProps) => ( 144 | 148 | 152 | 153 | ), 154 | paypal: (props: IconProps) => ( 155 | 159 | 163 | 164 | ), 165 | spinner: (props: IconProps) => ( 166 | 177 | 178 | 179 | ), 180 | }; 181 | -------------------------------------------------------------------------------- /doc/static/img/undraw_docusaurus_mountain.svg: -------------------------------------------------------------------------------- 1 | 2 | Easy to Use 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /doc/static/img/undraw_docusaurus_react.svg: -------------------------------------------------------------------------------- 1 | 2 | Powered by React 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | --------------------------------------------------------------------------------