From 0fe672efa5b0367762353ace4024ea30d245ebc1 Mon Sep 17 00:00:00 2001 From: Brooooooklyn Date: Tue, 18 Jun 2024 06:01:13 +0000 Subject: [PATCH] feat(admin): init project (#7197) --- .eslintrc.js | 5 - .github/deployment/front/Dockerfile | 1 + .github/deployment/front/affine.nginx.conf | 22 +- .github/deployment/node/Dockerfile | 1 + .github/workflows/build-server-image.yml | 38 + .github/workflows/deploy.yml | 40 + packages/backend/server/package.json | 3 +- packages/backend/server/src/app.module.ts | 8 + packages/backend/server/src/index.ts | 2 +- packages/frontend/admin/components.json | 17 + packages/frontend/admin/package.json | 70 + packages/frontend/admin/src/app.tsx | 72 + .../admin/src/components/ui/accordion.tsx | 55 + .../admin/src/components/ui/alert-dialog.tsx | 138 ++ .../admin/src/components/ui/alert.tsx | 58 + .../admin/src/components/ui/aspect-ratio.tsx | 5 + .../admin/src/components/ui/avatar.tsx | 47 + .../admin/src/components/ui/badge.tsx | 35 + .../admin/src/components/ui/breadcrumb.tsx | 114 ++ .../admin/src/components/ui/button.tsx | 55 + .../admin/src/components/ui/calendar.tsx | 63 + .../frontend/admin/src/components/ui/card.tsx | 85 ++ .../admin/src/components/ui/carousel.tsx | 259 ++++ .../admin/src/components/ui/checkbox.tsx | 27 + .../admin/src/components/ui/collapsible.tsx | 9 + .../admin/src/components/ui/command.tsx | 153 ++ .../admin/src/components/ui/context-menu.tsx | 197 +++ .../admin/src/components/ui/dialog.tsx | 119 ++ .../admin/src/components/ui/drawer.tsx | 115 ++ .../admin/src/components/ui/dropdown-menu.tsx | 197 +++ .../frontend/admin/src/components/ui/form.tsx | 170 +++ .../admin/src/components/ui/hover-card.tsx | 26 + .../admin/src/components/ui/input-otp.tsx | 68 + .../admin/src/components/ui/input.tsx | 24 + .../admin/src/components/ui/label.tsx | 23 + .../admin/src/components/ui/menubar.tsx | 233 ++++ .../src/components/ui/navigation-menu.tsx | 127 ++ .../admin/src/components/ui/pagination.tsx | 117 ++ .../admin/src/components/ui/popover.tsx | 28 + .../admin/src/components/ui/progress.tsx | 25 + .../admin/src/components/ui/radio-group.tsx | 41 + .../admin/src/components/ui/resizable.tsx | 42 + .../admin/src/components/ui/scroll-area.tsx | 45 + .../admin/src/components/ui/select.tsx | 157 +++ .../admin/src/components/ui/separator.tsx | 28 + .../admin/src/components/ui/sheet.tsx | 137 ++ .../admin/src/components/ui/skeleton.tsx | 15 + .../admin/src/components/ui/slider.tsx | 25 + .../admin/src/components/ui/sonner.tsx | 29 + .../admin/src/components/ui/switch.tsx | 26 + .../admin/src/components/ui/table.tsx | 116 ++ .../frontend/admin/src/components/ui/tabs.tsx | 52 + .../admin/src/components/ui/textarea.tsx | 23 + .../admin/src/components/ui/toast.tsx | 126 ++ .../admin/src/components/ui/toaster.tsx | 33 + .../admin/src/components/ui/toggle-group.tsx | 58 + .../admin/src/components/ui/toggle.tsx | 42 + .../admin/src/components/ui/tooltip.tsx | 27 + .../admin/src/components/ui/use-toast.ts | 191 +++ packages/frontend/admin/src/global.css | 76 + packages/frontend/admin/src/global.d.ts | 4 + packages/frontend/admin/src/index.tsx | 8 + .../frontend/admin/src/modules/auth/index.tsx | 102 ++ .../frontend/admin/src/modules/auth/logo.svg | 18 + .../frontend/admin/src/modules/home/index.tsx | 304 ++++ .../frontend/admin/src/modules/nav/index.tsx | 113 ++ .../admin/src/modules/nav/user-dropdown.tsx | 59 + .../admin/src/modules/users/index.tsx | 194 +++ packages/frontend/admin/src/utils.ts | 6 + packages/frontend/admin/tailwind.config.js | 72 + packages/frontend/admin/tsconfig.json | 13 + packages/frontend/core/package.json | 4 + packages/frontend/core/src/telemetry.tsx | 2 +- packages/frontend/graphql/package.json | 1 + .../src/graphql/get-current-user-features.gql | 11 + .../frontend/graphql/src/graphql/index.ts | 46 + .../graphql/src/graphql/list-users.gql | 20 + packages/frontend/graphql/src/schema.ts | 56 + tools/cli/package.json | 3 + tools/cli/src/bin/build.ts | 28 +- tools/cli/src/bin/dev.ts | 14 +- tools/cli/src/config/cwd.cjs | 30 + tools/cli/src/config/index.ts | 8 +- tools/cli/src/util/i18n.ts | 2 +- tools/cli/src/util/infra.ts | 2 +- tools/cli/src/webpack/config.ts | 36 +- tools/cli/src/webpack/postcss.config.cjs | 27 + tools/cli/src/webpack/webpack.config.ts | 4 +- tools/cli/tsconfig.json | 1 + tools/commitlint/.commitlintrc.json | 1 + tsconfig.json | 7 + yarn.lock | 1231 +++++++++++++++-- 92 files changed, 6392 insertions(+), 175 deletions(-) create mode 100644 packages/frontend/admin/components.json create mode 100644 packages/frontend/admin/package.json create mode 100644 packages/frontend/admin/src/app.tsx create mode 100644 packages/frontend/admin/src/components/ui/accordion.tsx create mode 100644 packages/frontend/admin/src/components/ui/alert-dialog.tsx create mode 100644 packages/frontend/admin/src/components/ui/alert.tsx create mode 100644 packages/frontend/admin/src/components/ui/aspect-ratio.tsx create mode 100644 packages/frontend/admin/src/components/ui/avatar.tsx create mode 100644 packages/frontend/admin/src/components/ui/badge.tsx create mode 100644 packages/frontend/admin/src/components/ui/breadcrumb.tsx create mode 100644 packages/frontend/admin/src/components/ui/button.tsx create mode 100644 packages/frontend/admin/src/components/ui/calendar.tsx create mode 100644 packages/frontend/admin/src/components/ui/card.tsx create mode 100644 packages/frontend/admin/src/components/ui/carousel.tsx create mode 100644 packages/frontend/admin/src/components/ui/checkbox.tsx create mode 100644 packages/frontend/admin/src/components/ui/collapsible.tsx create mode 100644 packages/frontend/admin/src/components/ui/command.tsx create mode 100644 packages/frontend/admin/src/components/ui/context-menu.tsx create mode 100644 packages/frontend/admin/src/components/ui/dialog.tsx create mode 100644 packages/frontend/admin/src/components/ui/drawer.tsx create mode 100644 packages/frontend/admin/src/components/ui/dropdown-menu.tsx create mode 100644 packages/frontend/admin/src/components/ui/form.tsx create mode 100644 packages/frontend/admin/src/components/ui/hover-card.tsx create mode 100644 packages/frontend/admin/src/components/ui/input-otp.tsx create mode 100644 packages/frontend/admin/src/components/ui/input.tsx create mode 100644 packages/frontend/admin/src/components/ui/label.tsx create mode 100644 packages/frontend/admin/src/components/ui/menubar.tsx create mode 100644 packages/frontend/admin/src/components/ui/navigation-menu.tsx create mode 100644 packages/frontend/admin/src/components/ui/pagination.tsx create mode 100644 packages/frontend/admin/src/components/ui/popover.tsx create mode 100644 packages/frontend/admin/src/components/ui/progress.tsx create mode 100644 packages/frontend/admin/src/components/ui/radio-group.tsx create mode 100644 packages/frontend/admin/src/components/ui/resizable.tsx create mode 100644 packages/frontend/admin/src/components/ui/scroll-area.tsx create mode 100644 packages/frontend/admin/src/components/ui/select.tsx create mode 100644 packages/frontend/admin/src/components/ui/separator.tsx create mode 100644 packages/frontend/admin/src/components/ui/sheet.tsx create mode 100644 packages/frontend/admin/src/components/ui/skeleton.tsx create mode 100644 packages/frontend/admin/src/components/ui/slider.tsx create mode 100644 packages/frontend/admin/src/components/ui/sonner.tsx create mode 100644 packages/frontend/admin/src/components/ui/switch.tsx create mode 100644 packages/frontend/admin/src/components/ui/table.tsx create mode 100644 packages/frontend/admin/src/components/ui/tabs.tsx create mode 100644 packages/frontend/admin/src/components/ui/textarea.tsx create mode 100644 packages/frontend/admin/src/components/ui/toast.tsx create mode 100644 packages/frontend/admin/src/components/ui/toaster.tsx create mode 100644 packages/frontend/admin/src/components/ui/toggle-group.tsx create mode 100644 packages/frontend/admin/src/components/ui/toggle.tsx create mode 100644 packages/frontend/admin/src/components/ui/tooltip.tsx create mode 100644 packages/frontend/admin/src/components/ui/use-toast.ts create mode 100644 packages/frontend/admin/src/global.css create mode 100644 packages/frontend/admin/src/global.d.ts create mode 100644 packages/frontend/admin/src/index.tsx create mode 100644 packages/frontend/admin/src/modules/auth/index.tsx create mode 100644 packages/frontend/admin/src/modules/auth/logo.svg create mode 100644 packages/frontend/admin/src/modules/home/index.tsx create mode 100644 packages/frontend/admin/src/modules/nav/index.tsx create mode 100644 packages/frontend/admin/src/modules/nav/user-dropdown.tsx create mode 100644 packages/frontend/admin/src/modules/users/index.tsx create mode 100644 packages/frontend/admin/src/utils.ts create mode 100644 packages/frontend/admin/tailwind.config.js create mode 100644 packages/frontend/admin/tsconfig.json create mode 100644 packages/frontend/graphql/src/graphql/get-current-user-features.gql create mode 100644 packages/frontend/graphql/src/graphql/list-users.gql create mode 100644 tools/cli/src/config/cwd.cjs diff --git a/.eslintrc.js b/.eslintrc.js index fb0b5d235b..e1eb1430d0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -157,11 +157,6 @@ const config = { message: "Import from '@blocksuite/global/utils'", importNames: ['assertExists', 'assertEquals'], }, - { - group: ['react-router-dom'], - message: 'Use `useNavigateHelper` instead', - importNames: ['useNavigate'], - }, ], }, ], diff --git a/.github/deployment/front/Dockerfile b/.github/deployment/front/Dockerfile index 81030c7aa1..0e2f90f1e7 100644 --- a/.github/deployment/front/Dockerfile +++ b/.github/deployment/front/Dockerfile @@ -1,6 +1,7 @@ FROM openresty/openresty:1.25.3.1-0-buster WORKDIR /app COPY ./packages/frontend/web/dist ./dist +COPY ./packages/frontend/admin/dist ./admin COPY ./.github/deployment/front/nginx.conf /usr/local/openresty/nginx/conf/nginx.conf COPY ./.github/deployment/front/affine.nginx.conf /etc/nginx/conf.d/affine.nginx.conf diff --git a/.github/deployment/front/affine.nginx.conf b/.github/deployment/front/affine.nginx.conf index c424ee3655..67e2caaf53 100644 --- a/.github/deployment/front/affine.nginx.conf +++ b/.github/deployment/front/affine.nginx.conf @@ -1,13 +1,17 @@ server { - listen 8080; - root /app/dist; + listen 8080; + root /app; - location / { - try_files $uri $uri/ /index.html; - } + location /admin { + try_files $uri $uri/ /admin/index.html; + } - error_page 404 /404.html; - location = /404.html { - internal; - } + location / { + try_files $uri $uri/ /dist/index.html; + } + + error_page 404 /404.html; + location = /404.html { + internal; + } } diff --git a/.github/deployment/node/Dockerfile b/.github/deployment/node/Dockerfile index 716431210f..6190734232 100644 --- a/.github/deployment/node/Dockerfile +++ b/.github/deployment/node/Dockerfile @@ -2,6 +2,7 @@ FROM node:20-bookworm-slim COPY ./packages/backend/server /app COPY ./packages/frontend/web/dist /app/static +COPY ./packages/frontend/admin/dist /app/static/admin WORKDIR /app RUN apt-get update && \ diff --git a/.github/workflows/build-server-image.yml b/.github/workflows/build-server-image.yml index 98fb207188..d6e82a0591 100644 --- a/.github/workflows/build-server-image.yml +++ b/.github/workflows/build-server-image.yml @@ -6,6 +6,11 @@ on: flavor: type: string required: true + workflow_dispatch: + inputs: + flavor: + type: string + required: false env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} @@ -66,6 +71,32 @@ jobs: path: ./packages/frontend/web/dist if-no-files-found: error + build-admin-selfhost: + name: Build @affine/admin selfhost + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.flavor }} + steps: + - uses: actions/checkout@v4 + - name: Setup Version + id: version + uses: ./.github/actions/setup-version + - name: Setup Node.js + uses: ./.github/actions/setup-node + - name: Build Core + run: yarn nx build @affine/admin --skip-nx-cache + env: + BUILD_TYPE: ${{ github.event.inputs.flavor }} + SHOULD_REPORT_TRACE: false + PUBLIC_PATH: '/admin/' + SELF_HOSTED: true + MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }} + - name: Upload admin artifact + uses: actions/upload-artifact@v4 + with: + name: selfhost-admin + path: ./packages/frontend/admin/dist + if-no-files-found: error + build-server-native: name: Build Server native - ${{ matrix.targets.name }} runs-on: ubuntu-latest @@ -108,6 +139,7 @@ jobs: needs: - build-server - build-web-selfhost + - build-admin-selfhost - build-server-native steps: - uses: actions/checkout@v4 @@ -171,6 +203,12 @@ jobs: name: selfhost-web path: ./packages/frontend/web/dist + - name: Download selfhost admin artifact + uses: actions/download-artifact@v4 + with: + name: selfhost-admin + path: ./packages/frontend/admin/dist + - name: Install Node.js dependencies run: | yarn config set --json supportedArchitectures.cpu '["x64", "arm64", "arm"]' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 32f1ca77c8..0d713347a1 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -61,11 +61,46 @@ jobs: path: ./packages/frontend/web/dist if-no-files-found: error + build-admin: + name: Build @affine/admin + runs-on: ubuntu-latest + environment: ${{ github.event.inputs.flavor }} + steps: + - uses: actions/checkout@v4 + - name: Setup Version + id: version + uses: ./.github/actions/setup-version + - name: Setup Node.js + uses: ./.github/actions/setup-node + - name: Build Core + run: yarn nx build @affine/admin --skip-nx-cache + env: + R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} + R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} + R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} + BUILD_TYPE: ${{ github.event.inputs.flavor }} + SHOULD_REPORT_TRACE: true + TRACE_REPORT_ENDPOINT: ${{ secrets.TRACE_REPORT_ENDPOINT }} + CAPTCHA_SITE_KEY: ${{ secrets.CAPTCHA_SITE_KEY }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: 'affine-admin' + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_DSN: ${{ secrets.SENTRY_DSN }} + PERFSEE_TOKEN: ${{ secrets.PERFSEE_TOKEN }} + MIXPANEL_TOKEN: ${{ secrets.MIXPANEL_TOKEN }} + - name: Upload admin artifact + uses: actions/upload-artifact@v4 + with: + name: admin + path: ./packages/frontend/admin/dist + if-no-files-found: error + build-frontend-image: name: Build Frontend Image runs-on: ubuntu-latest needs: - build-web + - build-admin steps: - uses: actions/checkout@v4 - name: Download web artifact @@ -73,6 +108,11 @@ jobs: with: name: web path: ./packages/frontend/web/dist + - name: Download admin artifact + uses: actions/download-artifact@v4 + with: + name: admin + path: ./packages/frontend/admin/dist - name: Setup env run: | echo "GIT_SHORT_HASH=$(git rev-parse --short HEAD)" >> "$GITHUB_ENV" diff --git a/packages/backend/server/package.json b/packages/backend/server/package.json index e94e4bb16c..5880a2ae99 100644 --- a/packages/backend/server/package.json +++ b/packages/backend/server/package.json @@ -15,7 +15,8 @@ "test:coverage": "c8 ava --concurrency 1 --serial", "postinstall": "prisma generate", "data-migration": "node --loader ts-node/esm/transpile-only.mjs ./src/data/index.ts", - "predeploy": "yarn prisma migrate deploy && node --import ./scripts/register.js ./dist/data/index.js run" + "predeploy": "yarn prisma migrate deploy && node --import ./scripts/register.js ./dist/data/index.js run", + "predeploy:ts": "yarn prisma migrate deploy && node --loader ts-node/esm/transpile-only.mjs ./src/data/index.ts run" }, "dependencies": { "@apollo/server": "^4.10.2", diff --git a/packages/backend/server/src/app.module.ts b/packages/backend/server/src/app.module.ts index f0ff5b68c2..4a3915e985 100644 --- a/packages/backend/server/src/app.module.ts +++ b/packages/backend/server/src/app.module.ts @@ -177,6 +177,14 @@ function buildAppModule() { config => config.isSelfhosted, ServeStaticModule.forRoot({ rootPath: join('/app', 'static'), + exclude: ['/admin*'], + }) + ) + .useIf( + config => config.isSelfhosted, + ServeStaticModule.forRoot({ + rootPath: join('/app', 'static', 'admin'), + serveRoot: '/admin', }) ); diff --git a/packages/backend/server/src/index.ts b/packages/backend/server/src/index.ts index d0d65a7db2..02e7d87d91 100644 --- a/packages/backend/server/src/index.ts +++ b/packages/backend/server/src/index.ts @@ -16,7 +16,7 @@ const logger = new Logger('App'); logger.log(`AFFiNE Server is running in [${AFFiNE.type}] mode`); if (AFFiNE.node.dev) { - logger.log('Startup Configration:'); + logger.log('Startup Configuration:'); logger.log(omit(globalThis.AFFiNE, 'ENV_MAP')); } logger.log(`Listening on http://${listeningHost}:${AFFiNE.server.port}`); diff --git a/packages/frontend/admin/components.json b/packages/frontend/admin/components.json new file mode 100644 index 0000000000..b6b9529a7e --- /dev/null +++ b/packages/frontend/admin/components.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "default", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "tailwind.config.js", + "css": "src/global.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@affine/admin/components", + "utils": "@affine/admin/utils" + } +} diff --git a/packages/frontend/admin/package.json b/packages/frontend/admin/package.json new file mode 100644 index 0000000000..54e243882a --- /dev/null +++ b/packages/frontend/admin/package.json @@ -0,0 +1,70 @@ +{ + "name": "@affine/admin", + "version": "0.14.0", + "private": true, + "dependencies": { + "@affine/core": "workspace:*", + "@affine/graphql": "workspace:*", + "@hookform/resolvers": "^3.6.0", + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-aspect-ratio": "^1.0.3", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-context-menu": "^2.1.5", + "@radix-ui/react-dialog": "^1.0.5", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-hover-card": "^1.0.7", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-menubar": "^1.0.4", + "@radix-ui/react-navigation-menu": "^1.1.4", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.0.3", + "@radix-ui/react-radio-group": "^1.1.3", + "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.0.3", + "@radix-ui/react-slider": "^1.1.2", + "@radix-ui/react-slot": "^1.0.2", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-toggle": "^1.0.3", + "@radix-ui/react-toggle-group": "^1.0.4", + "@radix-ui/react-tooltip": "^1.0.7", + "@sentry/react": "^8.9.0", + "cmdk": "^1.0.0", + "date-fns": "^3.6.0", + "embla-carousel-react": "^8.1.4", + "input-otp": "^1.2.4", + "lucide-react": "^0.394.0", + "next-themes": "^0.3.0", + "react": "^18.3.1", + "react-day-picker": "^8.10.1", + "react-dom": "^18.3.1", + "react-hook-form": "^7.51.5", + "react-resizable-panels": "^2.0.19", + "react-router-dom": "^6.23.1", + "sonner": "^1.5.0", + "vaul": "^0.9.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cross-env": "^7.0.3", + "shadcn-ui": "^0.8.0", + "tailwind-merge": "^2.3.0", + "tailwindcss": "^3.4.4", + "tailwindcss-animate": "^1.0.7" + }, + "scripts": { + "build": "cross-env DISTRIBUTION=admin yarn workspace @affine/cli build", + "update-shadcn": "shadcn-ui add -p src/components/ui" + }, + "exports": { + "./utils": "./src/utils.ts", + "./components/ui/*": "./src/components/ui/*.tsx" + } +} diff --git a/packages/frontend/admin/src/app.tsx b/packages/frontend/admin/src/app.tsx new file mode 100644 index 0000000000..75c34c19ab --- /dev/null +++ b/packages/frontend/admin/src/app.tsx @@ -0,0 +1,72 @@ +import { Toaster } from '@affine/admin/components/ui/sonner'; +import { Telemetry } from '@affine/core/telemetry'; +import { wrapCreateBrowserRouter } from '@sentry/react'; +import { useEffect } from 'react'; +import { + createBrowserRouter as reactRouterCreateBrowserRouter, + RouterProvider, + useLocation, + useNavigate, +} from 'react-router-dom'; + +import { TooltipProvider } from './components/ui/tooltip'; + +const createBrowserRouter = wrapCreateBrowserRouter( + reactRouterCreateBrowserRouter +); + +const _createBrowserRouter = window.SENTRY_RELEASE + ? createBrowserRouter + : reactRouterCreateBrowserRouter; + +const Redirect = function Redirect() { + const location = useLocation(); + const navigate = useNavigate(); + useEffect(() => { + if (!location.pathname.startsWith('/admin')) { + navigate('/admin', { replace: true }); + } + }, [location, navigate]); + return null; +}; + +export const router = _createBrowserRouter( + [ + { + path: '/', + element: , + }, + { + path: '/admin', + children: [ + { + path: '', + lazy: () => import('./modules/home'), + }, + { + path: '/admin/auth', + lazy: () => import('./modules/auth'), + }, + { + path: '/admin/users', + lazy: () => import('./modules/users'), + }, + ], + }, + ], + { + future: { + v7_normalizeFormMethod: true, + }, + } +); + +export const App = () => { + return ( + + + + + + ); +}; diff --git a/packages/frontend/admin/src/components/ui/accordion.tsx b/packages/frontend/admin/src/components/ui/accordion.tsx new file mode 100644 index 0000000000..c2b0b9e5eb --- /dev/null +++ b/packages/frontend/admin/src/components/ui/accordion.tsx @@ -0,0 +1,55 @@ +import { cn } from '@affine/admin/utils'; +import * as AccordionPrimitive from '@radix-ui/react-accordion'; +import { ChevronDown } from 'lucide-react'; +import * as React from 'react'; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = 'AccordionItem'; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180', + className + )} + {...props} + > + {children} + + + +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); + +AccordionContent.displayName = AccordionPrimitive.Content.displayName; + +export { Accordion, AccordionContent, AccordionItem, AccordionTrigger }; diff --git a/packages/frontend/admin/src/components/ui/alert-dialog.tsx b/packages/frontend/admin/src/components/ui/alert-dialog.tsx new file mode 100644 index 0000000000..3fd0b60dc8 --- /dev/null +++ b/packages/frontend/admin/src/components/ui/alert-dialog.tsx @@ -0,0 +1,138 @@ +import { buttonVariants } from '@affine/admin/components/ui/button'; +import { cn } from '@affine/admin/utils'; +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import * as React from 'react'; + +const AlertDialog = AlertDialogPrimitive.Root; + +const AlertDialogTrigger = AlertDialogPrimitive.Trigger; + +const AlertDialogPortal = AlertDialogPrimitive.Portal; + +const AlertDialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName; + +const AlertDialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + +)); +AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName; + +const AlertDialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogHeader.displayName = 'AlertDialogHeader'; + +const AlertDialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +AlertDialogFooter.displayName = 'AlertDialogFooter'; + +const AlertDialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName; + +const AlertDialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogDescription.displayName = + AlertDialogPrimitive.Description.displayName; + +const AlertDialogAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName; + +const AlertDialogCancel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName; + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/packages/frontend/admin/src/components/ui/alert.tsx b/packages/frontend/admin/src/components/ui/alert.tsx new file mode 100644 index 0000000000..97caed1cc6 --- /dev/null +++ b/packages/frontend/admin/src/components/ui/alert.tsx @@ -0,0 +1,58 @@ +import { cn } from '@affine/admin/utils'; +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; + +const alertVariants = cva( + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground', + { + variants: { + variant: { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + }, + }, + defaultVariants: { + variant: 'default', + }, + } +); + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)); +Alert.displayName = 'Alert'; + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertTitle.displayName = 'AlertTitle'; + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +AlertDescription.displayName = 'AlertDescription'; + +export { Alert, AlertDescription, AlertTitle }; diff --git a/packages/frontend/admin/src/components/ui/aspect-ratio.tsx b/packages/frontend/admin/src/components/ui/aspect-ratio.tsx new file mode 100644 index 0000000000..5dfdf1e67a --- /dev/null +++ b/packages/frontend/admin/src/components/ui/aspect-ratio.tsx @@ -0,0 +1,5 @@ +import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio'; + +const AspectRatio = AspectRatioPrimitive.Root; + +export { AspectRatio }; diff --git a/packages/frontend/admin/src/components/ui/avatar.tsx b/packages/frontend/admin/src/components/ui/avatar.tsx new file mode 100644 index 0000000000..a0f5256511 --- /dev/null +++ b/packages/frontend/admin/src/components/ui/avatar.tsx @@ -0,0 +1,47 @@ +import { cn } from '@affine/admin/utils'; +import * as AvatarPrimitive from '@radix-ui/react-avatar'; +import * as React from 'react'; + +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, AvatarFallback, AvatarImage }; diff --git a/packages/frontend/admin/src/components/ui/badge.tsx b/packages/frontend/admin/src/components/ui/badge.tsx new file mode 100644 index 0000000000..9e8cf4011e --- /dev/null +++ b/packages/frontend/admin/src/components/ui/badge.tsx @@ -0,0 +1,35 @@ +import { cn } from '@affine/admin/utils'; +import { cva, type VariantProps } from 'class-variance-authority'; +import type * as React from 'react'; + +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/frontend/admin/src/components/ui/breadcrumb.tsx b/packages/frontend/admin/src/components/ui/breadcrumb.tsx new file mode 100644 index 0000000000..0d973e60a4 --- /dev/null +++ b/packages/frontend/admin/src/components/ui/breadcrumb.tsx @@ -0,0 +1,114 @@ +import { cn } from '@affine/admin/utils'; +import { Slot } from '@radix-ui/react-slot'; +import { ChevronRight, MoreHorizontal } from 'lucide-react'; +import * as React from 'react'; + +const Breadcrumb = React.forwardRef< + HTMLElement, + React.ComponentPropsWithoutRef<'nav'> & { + separator?: React.ReactNode; + } +>(({ ...props }, ref) =>