From 7ff2be41e7c23490b50e92f17ef69892de70845b Mon Sep 17 00:00:00 2001 From: Nathan Windisch Date: Sun, 9 Mar 2025 05:07:45 +0000 Subject: [PATCH] initial commit. --- .gitignore | 24 +++ README.md | 54 ++++++ bun.lockb | Bin 0 -> 122122 bytes components.json | 21 +++ eslint.config.js | 28 +++ index.html | 13 ++ package.json | 41 +++++ src/App.css | 42 +++++ src/App.tsx | 118 +++++++++++++ src/components/theme-provider.tsx | 75 ++++++++ src/components/theme-toggle.tsx | 37 ++++ src/components/ui/alert.tsx | 66 +++++++ src/components/ui/button.tsx | 58 +++++++ src/components/ui/card.tsx | 68 ++++++++ src/components/ui/dropdown-menu.tsx | 255 ++++++++++++++++++++++++++++ src/components/ui/input.tsx | 21 +++ src/index.css | 124 ++++++++++++++ src/lib/utils.ts | 6 + src/main.tsx | 13 ++ src/vite-env.d.ts | 1 + tsconfig.app.json | 34 ++++ tsconfig.json | 13 ++ tsconfig.node.json | 24 +++ vite.config.ts | 14 ++ 24 files changed, 1150 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 bun.lockb create mode 100644 components.json create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package.json create mode 100644 src/App.css create mode 100644 src/App.tsx create mode 100644 src/components/theme-provider.tsx create mode 100644 src/components/theme-toggle.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/dropdown-menu.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/index.css create mode 100644 src/lib/utils.ts create mode 100644 src/main.tsx create mode 100644 src/vite-env.d.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/README.md b/README.md new file mode 100644 index 0000000..40ede56 --- /dev/null +++ b/README.md @@ -0,0 +1,54 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@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 +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..53f238d260fc9350555546b449d8a20756dceb26 GIT binary patch literal 122122 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 literal 0 HcmV?d00001 diff --git a/components.json b/components.json new file mode 100644 index 0000000..73afbdb --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..092408a --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/index.html b/index.html new file mode 100644 index 0000000..e4b78ea --- /dev/null +++ b/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..1a42702 --- /dev/null +++ b/package.json @@ -0,0 +1,41 @@ +{ + "name": "tenant-id", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-slot": "^1.1.2", + "@tailwindcss/vite": "^4.0.12", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.479.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.0.12", + "tailwindcss-animate": "^1.0.7", + "use-debounce": "^10.0.4", + "zod": "^3.24.2" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/node": "^22.13.10", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react": "^4.3.4", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git a/src/App.css b/src/App.css new file mode 100644 index 0000000..b9d355d --- /dev/null +++ b/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..a55ba2c --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,118 @@ +import { useEffect, useState } from "react"; +import { useDebounce } from "use-debounce"; +import { Button } from "@/components/ui/button"; +import { ThemeToggle } from "@/components/theme-toggle"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import "@/App.css"; +const uri = + "https://login.microsoft.com/{tenant}/.well-known/openid-configuration"; + +function App() { + const [domain, setDomain] = useState(location.hash.replace("#", "")); + const [tenantId, setTenantId] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + // const location = useLocation(); + const [debouncedDomain, setDebouncedDomain] = useDebounce( + domain, + 500 + ); + const onChange = async (event: React.ChangeEvent) => + setDomain(event.target.value); + + useEffect(() => { + location.hash = debouncedDomain; + if ( + (!debouncedDomain.includes("@") && !debouncedDomain.includes(".")) || + debouncedDomain.includes(" ") || + debouncedDomain.endsWith(".") || + debouncedDomain.length < 3 || + debouncedDomain.length > 254 + ) { + setTenantId(null); + setIsLoading(false); + setError("Please enter a valid email address or domain."); + return; + } + if (debouncedDomain.includes("@")) { + const parts = debouncedDomain.split("@"); + setIsLoading(false); + setDebouncedDomain(parts[parts.length - 1]); + return; + } + setIsLoading(true); + setError(null); + (async function () { + const response = await fetch(uri.replace("{tenant}", debouncedDomain)); + const body = await response.json(); + if (!response.ok) { + setTenantId(null); + setIsLoading(false); + setError(body.error_description.split(". ")[0]); + return; + } + const myTenantId = body.authorization_endpoint.split("/")[3]; + setTenantId(myTenantId); + setIsLoading(false); + })(); + }, [debouncedDomain, setDebouncedDomain]); + + return ( + <> +
+ +
+
+ + + What's my Tenant Id? + + Enter any email address or domain that your organization uses in + Microsoft365/Azure. + + + + + + + {!isLoading && error && ( + + Error! + {error} + + )} + {isLoading && ( + + Loading... + Querying Microsoft land... + + )} + {!isLoading && tenantId && ( + + Tenant Found + + Your Tenant Id is {tenantId} + + + )} + + +
+ + ); +} + +export default App; diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx new file mode 100644 index 0000000..541b2d5 --- /dev/null +++ b/src/components/theme-provider.tsx @@ -0,0 +1,75 @@ +"use client"; + +import { createContext, useContext, useEffect, useState } from "react"; + +type Theme = "dark" | "light" | "system"; + +type ThemeProviderProps = { + children: React.ReactNode; + defaultTheme?: Theme; + storageKey?: string; +}; + +type ThemeProviderState = { + theme: Theme; + setTheme: (theme: Theme) => void; +}; + +const initialState: ThemeProviderState = { + theme: "system", + setTheme: () => null, +}; + +const ThemeProviderContext = createContext(initialState); + +export function ThemeProvider({ + children, + defaultTheme = "system", + storageKey = "vite-ui-theme", + ...props +}: ThemeProviderProps) { + const [theme, setTheme] = useState( + () => (localStorage.getItem(storageKey) as Theme) || defaultTheme, + ); + + useEffect(() => { + const root = window.document.documentElement; + + root.classList.remove("light", "dark"); + + if (theme === "system") { + const systemTheme = window.matchMedia("(prefers-color-scheme: dark)") + .matches + ? "dark" + : "light"; + + root.classList.add(systemTheme); + return; + } + + root.classList.add(theme); + }, [theme]); + + const value = { + theme, + setTheme: (theme: Theme) => { + localStorage.setItem(storageKey, theme); + setTheme(theme); + }, + }; + + return ( + + {children} + + ); +} + +export const useTheme = () => { + const context = useContext(ThemeProviderContext); + + if (context === undefined) + throw new Error("useTheme must be used within a ThemeProvider"); + + return context; +}; diff --git a/src/components/theme-toggle.tsx b/src/components/theme-toggle.tsx new file mode 100644 index 0000000..4a38b23 --- /dev/null +++ b/src/components/theme-toggle.tsx @@ -0,0 +1,37 @@ +import { Moon, Sun } from "lucide-react"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useTheme } from "@/components/theme-provider"; + +export function ThemeToggle() { + const { setTheme } = useTheme(); + + return ( + + + + + + setTheme("light")}> + Light + + setTheme("dark")}> + Dark + + setTheme("system")}> + System + + + + ); +} diff --git a/src/components/ui/alert.tsx b/src/components/ui/alert.tsx new file mode 100644 index 0000000..3b8ee79 --- /dev/null +++ b/src/components/ui/alert.tsx @@ -0,0 +1,66 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "text-destructive-foreground [&>svg]:text-current *:data-[slot=alert-description]:text-destructive-foreground/80", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Alert({ + className, + variant, + ...props +}: React.ComponentProps<"div"> & VariantProps) { + return ( +
+ ) +} + +function AlertTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AlertDescription({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Alert, AlertTitle, AlertDescription } diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..cd0857a --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,58 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,box-shadow] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", + destructive: + "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40", + outline: + "border border-input bg-background shadow-xs hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..5e960a6 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,68 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/src/components/ui/dropdown-menu.tsx b/src/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..12d7c45 --- /dev/null +++ b/src/components/ui/dropdown-menu.tsx @@ -0,0 +1,255 @@ +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..4bfe9cc --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..407571c --- /dev/null +++ b/src/index.css @@ -0,0 +1,124 @@ +@import "tailwindcss"; + +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *)); + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..c205728 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,13 @@ +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "@/index.css"; +import App from "@/App.tsx"; +import { ThemeProvider } from "@/components/theme-provider"; + +createRoot(document.getElementById("root")!).render( + + + + + , +); diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts new file mode 100644 index 0000000..11f02fe --- /dev/null +++ b/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..0634029 --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true, + + /* ShadCN */ + "baseUrl": ".", + "paths": { + "@/*": [ + "./src/*" + ] + } + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..fec8c8e --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,13 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..db0becc --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..8963950 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,14 @@ +import path from "path"; +import tailwindcss from "@tailwindcss/vite"; +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + resolve: { + alias: { + "@": path.resolve(__dirname, "./src"), + }, + }, +});