From 1a6fddb8bdf7ba6be83db79087ed1cae09f6c7b2 Mon Sep 17 00:00:00 2001 From: adrians5j Date: Fri, 19 Jul 2024 06:26:08 +0200 Subject: [PATCH 01/34] wip: new admin ui --- apps/admin/package.json | 15 +- apps/admin/src/App.scss | 102 ++++++- apps/admin/src/App.tsx | 11 +- apps/admin/tailwind.config.js | 84 ++++++ apps/admin/webiny.config.ts | 29 +- packages/admin/.babelrc.js | 1 + packages/admin/LICENSE | 21 ++ packages/admin/README.md | 19 ++ packages/admin/package.json | 48 +++ packages/admin/src/assets/logo-small.svg | 3 + packages/admin/src/assets/logo.svg | 5 + packages/admin/src/index.tsx | 10 + packages/admin/src/modules/Dashboard.tsx | 274 ++++++++++++++++++ .../src/modules/Dashboard/cms-sketch.svg | 24 ++ .../admin/src/modules/Dashboard/fb-sshot.png | Bin 0 -> 13099 bytes .../src/modules/Dashboard/forms-sketch.svg | 24 ++ packages/admin/src/ui/Avatar.tsx | 50 ++++ packages/admin/src/ui/Badge.tsx | 36 +++ packages/admin/src/ui/Button.tsx | 57 ++++ packages/admin/src/ui/Card.tsx | 79 +++++ packages/admin/src/ui/Drawer.tsx | 100 +++++++ packages/admin/src/ui/DropdownMenu.tsx | 200 +++++++++++++ packages/admin/src/ui/Input.tsx | 25 ++ packages/admin/src/ui/Label.tsx | 26 ++ packages/admin/src/ui/Select.tsx | 160 ++++++++++ packages/admin/src/ui/Separator.tsx | 31 ++ packages/admin/src/ui/Sheet.tsx | 139 +++++++++ packages/admin/src/ui/Table.tsx | 117 ++++++++ packages/admin/src/ui/Textarea.tsx | 24 ++ packages/admin/src/ui/Tooltip.tsx | 30 ++ packages/admin/src/ui/Typography.tsx | 65 +++++ packages/admin/src/ui/utils.ts | 6 + packages/admin/tsconfig.build.json | 19 ++ packages/admin/tsconfig.json | 34 +++ packages/admin/webiny.config.js | 8 + 35 files changed, 1851 insertions(+), 25 deletions(-) create mode 100644 apps/admin/tailwind.config.js create mode 100644 packages/admin/.babelrc.js create mode 100644 packages/admin/LICENSE create mode 100644 packages/admin/README.md create mode 100644 packages/admin/package.json create mode 100644 packages/admin/src/assets/logo-small.svg create mode 100644 packages/admin/src/assets/logo.svg create mode 100644 packages/admin/src/index.tsx create mode 100644 packages/admin/src/modules/Dashboard.tsx create mode 100644 packages/admin/src/modules/Dashboard/cms-sketch.svg create mode 100644 packages/admin/src/modules/Dashboard/fb-sshot.png create mode 100644 packages/admin/src/modules/Dashboard/forms-sketch.svg create mode 100644 packages/admin/src/ui/Avatar.tsx create mode 100644 packages/admin/src/ui/Badge.tsx create mode 100644 packages/admin/src/ui/Button.tsx create mode 100644 packages/admin/src/ui/Card.tsx create mode 100644 packages/admin/src/ui/Drawer.tsx create mode 100644 packages/admin/src/ui/DropdownMenu.tsx create mode 100644 packages/admin/src/ui/Input.tsx create mode 100644 packages/admin/src/ui/Label.tsx create mode 100644 packages/admin/src/ui/Select.tsx create mode 100644 packages/admin/src/ui/Separator.tsx create mode 100644 packages/admin/src/ui/Sheet.tsx create mode 100644 packages/admin/src/ui/Table.tsx create mode 100644 packages/admin/src/ui/Textarea.tsx create mode 100644 packages/admin/src/ui/Tooltip.tsx create mode 100644 packages/admin/src/ui/Typography.tsx create mode 100644 packages/admin/src/ui/utils.ts create mode 100644 packages/admin/tsconfig.build.json create mode 100644 packages/admin/tsconfig.json create mode 100644 packages/admin/webiny.config.js diff --git a/apps/admin/package.json b/apps/admin/package.json index dda29f3968a..31ef5394f4d 100644 --- a/apps/admin/package.json +++ b/apps/admin/package.json @@ -7,7 +7,15 @@ "@editorjs/list": "^1.6.0", "@editorjs/quote": "^2.4.0", "@editorjs/underline": "^1.0.0", + "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-dialog": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-tooltip": "^1.1.2", "@types/react": "18.2.79", + "@webiny/admin": "0.0.0", "@webiny/app-admin": "0.0.0", "@webiny/app-admin-users-cognito": "0.0.0", "@webiny/app-form-builder": "0.0.0", @@ -19,14 +27,19 @@ "@webiny/plugins": "0.0.0", "@webiny/react-properties": "0.0.0", "@webiny/serverless-cms-aws": "0.0.0", + "autoprefixer": "^10.4.19", "core-js": "^3.0.1", "cross-fetch": "^3.0.4", + "lucide-react": "^0.408.0", + "postcss": "^8.4.39", "prop-types": "^15.7.2", "react": "18.2.0", "react-dom": "18.2.0", "regenerator-runtime": "^0.13.5", + "tailwindcss": "^3.4.6", "theme": "^1.0.0", - "tslib": "^2.4.0" + "tslib": "^2.4.0", + "vaul": "^0.9.1" }, "devDependencies": { "cross-env": "^5.0.2" diff --git a/apps/admin/src/App.scss b/apps/admin/src/App.scss index 54439b54975..8512f21432d 100644 --- a/apps/admin/src/App.scss +++ b/apps/admin/src/App.scss @@ -1,16 +1,86 @@ -// Webiny theme variables -// $webiny-theme-light-primary: #fa5723; -// $webiny-theme-light-secondary: #00ccb0; -// $webiny-theme-light-background: #f2f2f2; -// $webiny-theme-light-surface: #fff; -// $webiny-theme-light-on-primary: #ffffff; -// $webiny-theme-light-on-secondary: #ffffff; -// $webiny-theme-light-on-surface: #000000; -// $webiny-theme-light-on-background: rgba(212, 212, 212, 0.5); -// $webiny-theme-light-text-primary-on-background: rgba(0, 0, 0, 0.87); -// $webiny-theme-light-text-secondary-on-background: rgba(0, 0, 0, 0.54); -// $webiny-theme-light-text-hint-on-dark: rgba(255, 255, 255, 0.5); -// $webiny-theme-typography-font-family: "Source Sans Pro"; - -// Import main styles -@import "~@webiny/app-serverless-cms/styles.scss"; +@import url("https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,600,700|Open+Sans:300,400,600,700|Source+Code+Pro:400,700|Material+Icons"); + +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 47.4% 11.2%; + + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + + --popover: 0 0% 100%; + --popover-foreground: 222.2 47.4% 11.2%; + + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + + --card: 0 0% 100%; + --card-foreground: 222.2 47.4% 11.2%; + + --primary: 14.5 95.6% 55.9%; + --primary-foreground: 210 40% 98%; + + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + + --destructive: 0 100% 50%; + --destructive-foreground: 210 40% 98%; + + --ring: 215 20.2% 65.1%; + + --radius: 8px; + + --font-sans: "Inter", "Source Sans Pro", "Open Sans", sans-serif; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 8px; + } +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; + } +} diff --git a/apps/admin/src/App.tsx b/apps/admin/src/App.tsx index ac6cee40cd1..a8e413e7dbd 100644 --- a/apps/admin/src/App.tsx +++ b/apps/admin/src/App.tsx @@ -1,14 +1,11 @@ import React from "react"; -import { Admin } from "@webiny/app-serverless-cms"; -import { Cognito } from "@webiny/app-admin-users-cognito"; -import { Extensions } from "./Extensions"; +import { Admin } from "@webiny/admin"; import "./App.scss"; export const App = () => { return ( - - - - + <> + + ); }; diff --git a/apps/admin/tailwind.config.js b/apps/admin/tailwind.config.js new file mode 100644 index 00000000000..1dd3ea31040 --- /dev/null +++ b/apps/admin/tailwind.config.js @@ -0,0 +1,84 @@ +const { fontFamily } = require("tailwindcss/defaultTheme"); + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: ["node_modules/@webiny/admin/**/*.js", __dirname + "/src/**/*.tsx"], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px" + } + }, + fontSize: { + sm: '0.75rem', // 12px + md: "0.875rem", // 14px + base: "1rem", // 16px + lg: "1rem", // 16px + xl: "1.25rem", // 20px + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))" + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))" + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))" + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))" + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))" + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))" + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))" + } + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: "calc(var(--radius) - 4px)" + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans] + }, + + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" } + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" } + } + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out" + } + } + }, + plugins: [require("tailwindcss-animate")] +}; diff --git a/apps/admin/webiny.config.ts b/apps/admin/webiny.config.ts index 165b4d4937a..1c69b5f0637 100644 --- a/apps/admin/webiny.config.ts +++ b/apps/admin/webiny.config.ts @@ -1,3 +1,30 @@ import { createAdminAppConfig } from "@webiny/serverless-cms-aws"; +// @ts-ignore `traverseLoaders` has no type declarations. +import { traverseLoaders } from "@webiny/project-utils/traverseLoaders"; +import tailwindcss from "tailwindcss"; -export default createAdminAppConfig(); +export default createAdminAppConfig(({ config }) => { + /** + * Add a webpack config modifier. + */ + config.webpack(config => { + /** + * Traverse all loaders, find `postcss-loader`, and overwrite plugins. + */ + traverseLoaders(config.module?.rules, (loader: any) => { + /** + * `loader` can also be a string, so check for `.loader` property. + */ + if (loader.loader && loader.loader.includes("postcss-loader")) { + loader.options.postcssOptions.plugins = [ + ...loader.options.postcssOptions.plugins(), + tailwindcss({ + config: __dirname + "/tailwind.config.js" + }) + ]; + } + }); + + return config; + }); +}); \ No newline at end of file diff --git a/packages/admin/.babelrc.js b/packages/admin/.babelrc.js new file mode 100644 index 00000000000..bec58b263bd --- /dev/null +++ b/packages/admin/.babelrc.js @@ -0,0 +1 @@ +module.exports = require("@webiny/project-utils").createBabelConfigForReact({ path: __dirname }); diff --git a/packages/admin/LICENSE b/packages/admin/LICENSE new file mode 100644 index 00000000000..f772d04d4db --- /dev/null +++ b/packages/admin/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) Webiny + +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: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +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. diff --git a/packages/admin/README.md b/packages/admin/README.md new file mode 100644 index 00000000000..cc871c32648 --- /dev/null +++ b/packages/admin/README.md @@ -0,0 +1,19 @@ +# @webiny/admin +[![](https://img.shields.io/npm/dw/@webiny/admin.svg)](https://www.npmjs.com/package/@webiny/admin) +[![](https://img.shields.io/npm/v/@webiny/admin.svg)](https://www.npmjs.com/package/@webiny/admin) +[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) + +The base package for building Webiny and React powered web apps. + +For more information, please visit [the official docs](https://docs.webiny.com/docs/webiny/introduction). + +## Install +``` +npm install --save @webiny/admin +``` + +Or if you prefer yarn: +``` +yarn add @webiny/admin +``` diff --git a/packages/admin/package.json b/packages/admin/package.json new file mode 100644 index 00000000000..d5824b3d1e0 --- /dev/null +++ b/packages/admin/package.json @@ -0,0 +1,48 @@ +{ + "name": "@webiny/admin", + "version": "0.0.0", + "main": "index.js", + "repository": { + "type": "git", + "url": "https://github.com/webiny/webiny-js.git" + }, + "description": "The Webiny Admin app.", + "license": "MIT", + "devDependencies": { + "@babel/cli": "^7.23.9", + "@babel/core": "^7.24.0", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/preset-env": "^7.24.0", + "@babel/preset-react": "^7.23.3", + "@babel/preset-typescript": "^7.23.3", + "@types/lodash": "^4.14.191", + "@types/warning": "^3.0.0", + "@webiny/cli": "0.0.0", + "@webiny/project-utils": "0.0.0", + "rimraf": "^5.0.5", + "typescript": "4.7.4" + }, + "publishConfig": { + "access": "public", + "directory": "dist" + }, + "scripts": { + "build": "yarn webiny run build", + "watch": "yarn webiny run watch" + }, + "adio": { + "ignore": { + "dependencies": [ + "react-dom" + ] + } + }, + "gitHead": "8476da73b653c89cc1474d968baf55c1b0ae0e5f", + "dependencies": { + "@radix-ui/react-slot": "^1.1.0", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "tailwind-merge": "^2.4.0", + "tailwindcss-animate": "^1.0.7" + } +} diff --git a/packages/admin/src/assets/logo-small.svg b/packages/admin/src/assets/logo-small.svg new file mode 100644 index 00000000000..18b48760bd4 --- /dev/null +++ b/packages/admin/src/assets/logo-small.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/admin/src/assets/logo.svg b/packages/admin/src/assets/logo.svg new file mode 100644 index 00000000000..a45397cfd07 --- /dev/null +++ b/packages/admin/src/assets/logo.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/admin/src/index.tsx b/packages/admin/src/index.tsx new file mode 100644 index 00000000000..2c8fb388a5a --- /dev/null +++ b/packages/admin/src/index.tsx @@ -0,0 +1,10 @@ +import React from "react"; +import { Dashboard } from "./modules/Dashboard"; + +export const Admin = () => { + return ( +
+ +
+ ); +}; diff --git a/packages/admin/src/modules/Dashboard.tsx b/packages/admin/src/modules/Dashboard.tsx new file mode 100644 index 00000000000..444badfa6a7 --- /dev/null +++ b/packages/admin/src/modules/Dashboard.tsx @@ -0,0 +1,274 @@ +import React from "react"; +import { + CircleUser, + Home, + LineChart, + Menu, + Package, + Package2, + ShoppingCart, + Users +} from "lucide-react"; + +import { Badge } from "~/ui/Badge"; +import { Button } from "~/ui/Button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "~/ui/Card"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger +} from "~/ui/DropdownMenu"; +import { Sheet, SheetContent, SheetTrigger } from "~/ui/Sheet"; +import { ReactComponent as WebinyLogo } from "~/assets/logo.svg"; +import { ReactComponent as CmsSketch } from "./Dashboard/cms-sketch.svg"; +import { ReactComponent as FormsSketch } from "./Dashboard/forms-sketch.svg"; +import fbSshot from "./Dashboard/fb-sshot.png"; +import { TypographyH4, TypographyH5, TypographyH6 } from "~/ui/Typography"; + +export function Dashboard() { + return ( +
+
+
+
+ +
+ +
+
+
+
+ + + + + + + + +
+ + + + + + My Account + + Settings + Support + + Logout + + +
+
+
+
+ +

+ To get started - pick one of the actions below: +

+
+ +
+ + + + + + +

+ Build stunning landing pages with an easy to use drag and + drop editor. +

+
+ + + +
+ + + + + + + +

+ Create forms using a drag and drop interface and track + conversions. +

+
+ + + +
+ + + + + + +

+ GraphQL based headless CMS with powerful content modeling + features. +

+
+ + + +
+
+
+ + + + +

+ Explore the Webiny documentation, learn about the + architecture and check out code examples and guides: +

+ + +
+
+ + + +

+ Explore the Webiny documentation, learn about the + architecture and check out code examples and guides: +

+ +
+
+
+
+
+
+
+ ); +} diff --git a/packages/admin/src/modules/Dashboard/cms-sketch.svg b/packages/admin/src/modules/Dashboard/cms-sketch.svg new file mode 100644 index 00000000000..a1c19de86a7 --- /dev/null +++ b/packages/admin/src/modules/Dashboard/cms-sketch.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/admin/src/modules/Dashboard/fb-sshot.png b/packages/admin/src/modules/Dashboard/fb-sshot.png new file mode 100644 index 0000000000000000000000000000000000000000..c408eaec8806ac309aeb892cb9a6245a8a2323db GIT binary patch literal 13099 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 literal 0 HcmV?d00001 diff --git a/packages/admin/src/modules/Dashboard/forms-sketch.svg b/packages/admin/src/modules/Dashboard/forms-sketch.svg new file mode 100644 index 00000000000..ebfceba605a --- /dev/null +++ b/packages/admin/src/modules/Dashboard/forms-sketch.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/admin/src/ui/Avatar.tsx b/packages/admin/src/ui/Avatar.tsx new file mode 100644 index 00000000000..70f1e5bf134 --- /dev/null +++ b/packages/admin/src/ui/Avatar.tsx @@ -0,0 +1,50 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "~/ui/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/packages/admin/src/ui/Badge.tsx b/packages/admin/src/ui/Badge.tsx new file mode 100644 index 00000000000..53f476aeb1a --- /dev/null +++ b/packages/admin/src/ui/Badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/ui/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/packages/admin/src/ui/Button.tsx b/packages/admin/src/ui/Button.tsx new file mode 100644 index 00000000000..f9391009e0c --- /dev/null +++ b/packages/admin/src/ui/Button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/ui/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2 text-md", + medium: "h-10 px-4 py-2 text-md", + sm: "h-9 rounded-md px-3 text-sm", + lg: "h-9 rounded-md py-2.5 px-4 text-md leading-5", + icon: "h-10 w-10", + }, + }, + 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/packages/admin/src/ui/Card.tsx b/packages/admin/src/ui/Card.tsx new file mode 100644 index 00000000000..f3010117632 --- /dev/null +++ b/packages/admin/src/ui/Card.tsx @@ -0,0 +1,79 @@ +import * as React from "react" + +import { cn } from "~/ui/utils" + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +Card.displayName = "Card" + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardHeader.displayName = "CardHeader" + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardTitle.displayName = "CardTitle" + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardDescription.displayName = "CardDescription" + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)) +CardContent.displayName = "CardContent" + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +CardFooter.displayName = "CardFooter" + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent } diff --git a/packages/admin/src/ui/Drawer.tsx b/packages/admin/src/ui/Drawer.tsx new file mode 100644 index 00000000000..a0a6ad4ab42 --- /dev/null +++ b/packages/admin/src/ui/Drawer.tsx @@ -0,0 +1,100 @@ +"use client"; + +import * as React from "react"; +import { Drawer as DrawerPrimitive } from "vaul"; + +import { cn } from "~/ui/utils"; + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +); +Drawer.displayName = "Drawer"; + +const DrawerTrigger = DrawerPrimitive.Trigger; + +const DrawerPortal = DrawerPrimitive.Portal; + +const DrawerClose = DrawerPrimitive.Close; + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName; + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
+ {children} + + +)); +DrawerContent.displayName = "DrawerContent"; + +const DrawerHeader = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DrawerHeader.displayName = "DrawerHeader"; + +const DrawerFooter = ({ className, ...props }: React.HTMLAttributes) => ( +
+); +DrawerFooter.displayName = "DrawerFooter"; + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerTitle.displayName = DrawerPrimitive.Title.displayName; + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DrawerDescription.displayName = DrawerPrimitive.Description.displayName; + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription +}; diff --git a/packages/admin/src/ui/DropdownMenu.tsx b/packages/admin/src/ui/DropdownMenu.tsx new file mode 100644 index 00000000000..b108b09a067 --- /dev/null +++ b/packages/admin/src/ui/DropdownMenu.tsx @@ -0,0 +1,200 @@ +"use client" + +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { Check, ChevronRight, Circle } from "lucide-react" + +import { cn } from "~/ui/utils" + +const DropdownMenu = DropdownMenuPrimitive.Root + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger + +const DropdownMenuGroup = DropdownMenuPrimitive.Group + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal + +const DropdownMenuSub = DropdownMenuPrimitive.Sub + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)) +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)) +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)) +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean +} +>(({ className, inset, ...props }, ref) => ( + +)) +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName + +const DropdownMenuShortcut = ({ + className, + ...props + }: React.HTMLAttributes) => { + return ( + + ) +} +DropdownMenuShortcut.displayName = "DropdownMenuShortcut" + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +} diff --git a/packages/admin/src/ui/Input.tsx b/packages/admin/src/ui/Input.tsx new file mode 100644 index 00000000000..fad02910fd7 --- /dev/null +++ b/packages/admin/src/ui/Input.tsx @@ -0,0 +1,25 @@ +import * as React from "react" + +import { cn } from "~/ui/utils" + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" + +export { Input } diff --git a/packages/admin/src/ui/Label.tsx b/packages/admin/src/ui/Label.tsx new file mode 100644 index 00000000000..c48ba5517e5 --- /dev/null +++ b/packages/admin/src/ui/Label.tsx @@ -0,0 +1,26 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/ui/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/packages/admin/src/ui/Select.tsx b/packages/admin/src/ui/Select.tsx new file mode 100644 index 00000000000..500fb5d9bb0 --- /dev/null +++ b/packages/admin/src/ui/Select.tsx @@ -0,0 +1,160 @@ +"use client" + +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { Check, ChevronDown, ChevronUp } from "lucide-react" + +import { cn } from "~/ui/utils" + +const Select = SelectPrimitive.Root + +const SelectGroup = SelectPrimitive.Group + +const SelectValue = SelectPrimitive.Value + +const SelectTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + span]:line-clamp-1", + className + )} + {...props} + > + {children} + + + + +)) +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)) +SelectContent.displayName = SelectPrimitive.Content.displayName + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectLabel.displayName = SelectPrimitive.Label.displayName + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + +)) +SelectItem.displayName = SelectPrimitive.Item.displayName + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SelectSeparator.displayName = SelectPrimitive.Separator.displayName + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +} diff --git a/packages/admin/src/ui/Separator.tsx b/packages/admin/src/ui/Separator.tsx new file mode 100644 index 00000000000..fcdab05c695 --- /dev/null +++ b/packages/admin/src/ui/Separator.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "~/ui/utils" + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/packages/admin/src/ui/Sheet.tsx b/packages/admin/src/ui/Sheet.tsx new file mode 100644 index 00000000000..19c0a980a7d --- /dev/null +++ b/packages/admin/src/ui/Sheet.tsx @@ -0,0 +1,139 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" +import { cn } from "~/ui/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props + }: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props + }: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/packages/admin/src/ui/Table.tsx b/packages/admin/src/ui/Table.tsx new file mode 100644 index 00000000000..9792b049e91 --- /dev/null +++ b/packages/admin/src/ui/Table.tsx @@ -0,0 +1,117 @@ +import * as React from "react" + +import { cn } from "~/ui/utils" + +const Table = React.forwardRef< + HTMLTableElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+ + +)) +Table.displayName = "Table" + +const TableHeader = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableHeader.displayName = "TableHeader" + +const TableBody = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableBody.displayName = "TableBody" + +const TableFooter = React.forwardRef< + HTMLTableSectionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + tr]:last:border-b-0", + className + )} + {...props} + /> +)) +TableFooter.displayName = "TableFooter" + +const TableRow = React.forwardRef< + HTMLTableRowElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableRow.displayName = "TableRow" + +const TableHead = React.forwardRef< + HTMLTableCellElement, + React.ThHTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableHead.displayName = "TableHead" + +const TableCell = React.forwardRef< + HTMLTableCellElement, + React.TdHTMLAttributes +>(({ className, ...props }, ref) => ( + +)) +TableCell.displayName = "TableCell" + +const TableCaption = React.forwardRef< + HTMLTableCaptionElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +TableCaption.displayName = "TableCaption" + +export { + Table, + TableHeader, + TableBody, + TableFooter, + TableHead, + TableRow, + TableCell, + TableCaption, +} diff --git a/packages/admin/src/ui/Textarea.tsx b/packages/admin/src/ui/Textarea.tsx new file mode 100644 index 00000000000..f99b3b99d21 --- /dev/null +++ b/packages/admin/src/ui/Textarea.tsx @@ -0,0 +1,24 @@ +import * as React from "react" + +import { cn } from "~/ui/utils" + +export interface TextareaProps + extends React.TextareaHTMLAttributes {} + +const Textarea = React.forwardRef( + ({ className, ...props }, ref) => { + return ( +