From 764e8fcc0282748ce3841b6bfbbf26ba1cf12bb4 Mon Sep 17 00:00:00 2001 From: George Desipris <73396808+desiprisg@users.noreply.github.com> Date: Wed, 25 Sep 2024 10:56:30 +0300 Subject: [PATCH] feat(dashboard): Add tailwind, shadcn-ui config and react router (#6559) --- apps/dashboard/.prettierrc | 3 + apps/dashboard/components.json | 20 + apps/dashboard/package.json | 20 +- apps/dashboard/postcss.config.js | 6 + apps/dashboard/src/App.css | 42 - apps/dashboard/src/App.tsx | 31 - apps/dashboard/src/components/error-page.tsx | 16 + .../src/components/primitives/button.tsx | 56 + .../src/components/primitives/form.tsx | 177 ++ .../src/components/primitives/label.tsx | 24 + apps/dashboard/src/index.css | 124 +- apps/dashboard/src/main.tsx | 31 +- apps/dashboard/src/routes/root.tsx | 17 + apps/dashboard/src/routes/workflows.tsx | 3 + apps/dashboard/src/utils/ui.ts | 6 + apps/dashboard/tailwind.config.js | 57 + apps/dashboard/tsconfig.app.json | 5 + apps/dashboard/tsconfig.json | 8 +- apps/dashboard/vite.config.ts | 9 +- pnpm-lock.yaml | 1785 ++++++++--------- 20 files changed, 1320 insertions(+), 1120 deletions(-) create mode 100644 apps/dashboard/.prettierrc create mode 100644 apps/dashboard/components.json create mode 100644 apps/dashboard/postcss.config.js delete mode 100644 apps/dashboard/src/App.css delete mode 100644 apps/dashboard/src/App.tsx create mode 100644 apps/dashboard/src/components/error-page.tsx create mode 100644 apps/dashboard/src/components/primitives/button.tsx create mode 100644 apps/dashboard/src/components/primitives/form.tsx create mode 100644 apps/dashboard/src/components/primitives/label.tsx create mode 100644 apps/dashboard/src/routes/root.tsx create mode 100644 apps/dashboard/src/routes/workflows.tsx create mode 100644 apps/dashboard/src/utils/ui.ts create mode 100644 apps/dashboard/tailwind.config.js diff --git a/apps/dashboard/.prettierrc b/apps/dashboard/.prettierrc new file mode 100644 index 00000000000..b4bfed3579c --- /dev/null +++ b/apps/dashboard/.prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/apps/dashboard/components.json b/apps/dashboard/components.json new file mode 100644 index 00000000000..7799082c097 --- /dev/null +++ b/apps/dashboard/components.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/index.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/utils/ui", + "ui": "@/components/primitives", + "lib": "@/utils", + "hooks": "@/hooks" + } +} diff --git a/apps/dashboard/package.json b/apps/dashboard/package.json index 5fae14daf64..0c58f7babbb 100644 --- a/apps/dashboard/package.json +++ b/apps/dashboard/package.json @@ -11,18 +11,36 @@ "preview": "vite preview" }, "dependencies": { + "@hookform/resolvers": "^2.9.1", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@tanstack/react-query": "^4.20.4", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.439.0", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-hook-form": "7.43.9", + "react-router-dom": "6.26.2", + "tailwind-merge": "^2.4.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^3.23.8" }, "devDependencies": { "@eslint/js": "^9.9.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react": "^4.3.1", + "autoprefixer": "^10.4.20", "eslint": "^9.9.0", "eslint-plugin-react-hooks": "^5.1.0-rc.0", "eslint-plugin-react-refresh": "^0.4.9", "globals": "^15.9.0", + "postcss": "^8.4.47", + "prettier": "^3.3.3", + "prettier-plugin-tailwindcss": "^0.6.5", + "tailwindcss": "^3.4.13", "typescript": "5.6.2", "typescript-eslint": "^8.0.1", "vite": "^5.4.1" diff --git a/apps/dashboard/postcss.config.js b/apps/dashboard/postcss.config.js new file mode 100644 index 00000000000..2aa7205d4b4 --- /dev/null +++ b/apps/dashboard/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/apps/dashboard/src/App.css b/apps/dashboard/src/App.css deleted file mode 100644 index b9d355df2a5..00000000000 --- a/apps/dashboard/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#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/apps/dashboard/src/App.tsx b/apps/dashboard/src/App.tsx deleted file mode 100644 index 0e61754e5f0..00000000000 --- a/apps/dashboard/src/App.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import { useState } from 'react'; -import reactLogo from './assets/react.svg'; -import viteLogo from '/vite.svg'; -import './App.css'; - -function App() { - const [count, setCount] = useState(0); - - return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.tsx and save to test HMR -

-
-

Click on the Vite and React logos to learn more

- - ); -} - -export default App; diff --git a/apps/dashboard/src/components/error-page.tsx b/apps/dashboard/src/components/error-page.tsx new file mode 100644 index 00000000000..db03127e947 --- /dev/null +++ b/apps/dashboard/src/components/error-page.tsx @@ -0,0 +1,16 @@ +import { useRouteError } from "react-router-dom"; + +export default function ErrorPage() { + const error = useRouteError() as any; + console.error(error); + + return ( +
+

Oops!

+

Sorry, an unexpected error has occurred.

+

+ {error.statusText || error.message} +

+
+ ); +} diff --git a/apps/dashboard/src/components/primitives/button.tsx b/apps/dashboard/src/components/primitives/button.tsx new file mode 100644 index 00000000000..43a95d4e712 --- /dev/null +++ b/apps/dashboard/src/components/primitives/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/utils/ui"; + +const buttonVariants = cva( + "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", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm 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", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/apps/dashboard/src/components/primitives/form.tsx b/apps/dashboard/src/components/primitives/form.tsx new file mode 100644 index 00000000000..7f5770da9c3 --- /dev/null +++ b/apps/dashboard/src/components/primitives/form.tsx @@ -0,0 +1,177 @@ +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { Slot } from "@radix-ui/react-slot"; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from "react-hook-form"; + +import { cn } from "@/utils/ui"; +import { Label } from "@/components/primitives/label"; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error("useFormField should be used within "); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
+ + ); +}); +FormItem.displayName = "FormItem"; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +