From 0524b951391a0826f38745ca4514339e1fac5b3f Mon Sep 17 00:00:00 2001 From: Dominiek Ter Heide Date: Fri, 5 Jun 2026 16:02:35 -0400 Subject: [PATCH 1/3] Migrate web UI from Mantine to shadcn/ui MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace Mantine entirely with shadcn/ui (Radix + Tailwind v4) across services/web, and add a Chrome-based visual + conformance + layout-audit CI gate. ~100 screen/component files converted; Mantine, postcss-preset- mantine and the custom theme are removed and Tailwind preflight re-enabled. - Foundation: Tailwind v4 + @tailwindcss/vite, components.json, cn(), globals.css design tokens (literal shadcn-neutral default with a documented brand-primary knob), ThemeProvider (dark mode) + sonner. - ~28 shadcn/ui primitives under src/components/ui. - All chrome (AppShell -> Sidebar), auth flows, CRUD list/detail/form screens, settings, the shared Search framework, modals, form-fields, and the API Docs app migrated. - Removed @mantine/*, postcss-preset-mantine, postcss-simple-vars, theme.js/theme.less, postcss.config.js; MantineProvider/Notifications dropped from Wrapper; preflight re-enabled (single @import "tailwindcss"). - Visual gate (visual/, .github/workflows/visual.yml): Playwright + Chromium screenshots, §5 conformance checks, and a layout audit (font consistency, breadcrumb alignment, list-marker, broken-image, dark-mode text-colour). CSS bundle ~237kB -> ~49kB. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/visual.yml | 85 ++ services/web/.gitignore | 8 + services/web/components.json | 21 + services/web/jsconfig.json | 4 + services/web/package.json | 39 +- services/web/postcss.config.js | 14 - services/web/src/Wrapper.js | 16 +- .../components/Auth/Apple/DisableButton.js | 13 +- .../src/components/Auth/Apple/SignInButton.js | 16 +- services/web/src/components/Auth/Federated.js | 19 +- .../components/Auth/Google/DisableButton.js | 11 +- .../components/Auth/Google/SignInButton.js | 19 +- .../src/components/Auth/OptionalPassword.js | 2 +- .../web/src/components/Auth/PasskeyButton.js | 13 +- services/web/src/components/Authenticator.js | 90 +- services/web/src/components/BackLink.js | 12 +- services/web/src/components/Code/index.js | 5 +- services/web/src/components/ErrorBoundary.jsx | 27 +- services/web/src/components/ErrorMessage.js | 10 +- services/web/src/components/Footer.js | 61 +- .../web/src/components/Link/ExternalLink.js | 14 +- services/web/src/components/Link/JumpLink.js | 10 +- services/web/src/components/LoadButton.js | 60 +- services/web/src/components/Logo.js | 8 +- services/web/src/components/MenuItem.js | 110 +- services/web/src/components/ModalWrapper.jsx | 59 +- .../components/NewSearchDropdown/Multiple.js | 159 ++- .../components/NewSearchDropdown/Single.js | 147 ++- services/web/src/components/PageHeader.js | 88 +- services/web/src/components/PrivateImage.js | 5 +- .../web/src/components/RequestBlock/index.js | 35 +- services/web/src/components/Search/Export.js | 86 +- .../src/components/Search/Filters/Boolean.js | 47 +- .../src/components/Search/Filters/Checkbox.js | 29 +- .../components/Search/Filters/DateRange.js | 33 +- .../src/components/Search/Filters/Input.js | 51 +- .../src/components/Search/Filters/Keyword.js | 68 +- .../src/components/Search/Filters/Modal.js | 113 +- .../src/components/Search/Filters/Number.js | 35 +- .../src/components/Search/Filters/Select.js | 316 ++++- services/web/src/components/Search/Header.js | 4 +- .../web/src/components/Search/Pagination.js | 17 +- .../src/components/Search/SortableHeader.js | 47 +- services/web/src/components/Search/Status.js | 14 +- services/web/src/components/SearchDropdown.js | 180 ++- services/web/src/components/ThemeProvider.jsx | 64 ++ services/web/src/components/Thumbnail.js | 34 + services/web/src/components/UserImage.js | 32 +- .../web/src/components/form-fields/Chips.js | 30 +- .../src/components/form-fields/Countries.js | 39 +- .../web/src/components/form-fields/Date.js | 33 +- .../src/components/form-fields/DateTime.js | 115 +- .../web/src/components/form-fields/Email.js | 24 +- .../components/form-fields/NativeSelect.js | 58 +- .../web/src/components/form-fields/Number.js | 23 +- .../web/src/components/form-fields/Phone.js | 27 +- .../web/src/components/form-fields/Tags.js | 68 +- .../web/src/components/form-fields/Uploads.js | 85 +- .../src/components/form-fields/UsStates.js | 39 +- services/web/src/components/ui/alert.jsx | 64 ++ services/web/src/components/ui/avatar.jsx | 41 + services/web/src/components/ui/badge.jsx | 37 + services/web/src/components/ui/breadcrumb.jsx | 95 ++ services/web/src/components/ui/button.jsx | 61 + services/web/src/components/ui/card.jsx | 62 + services/web/src/components/ui/checkbox.jsx | 24 + services/web/src/components/ui/command.jsx | 93 ++ .../web/src/components/ui/definition-list.jsx | 29 + services/web/src/components/ui/dialog.jsx | 112 ++ .../web/src/components/ui/dropdown-menu.jsx | 82 ++ services/web/src/components/ui/form.jsx | 131 +++ services/web/src/components/ui/input-otp.jsx | 62 + services/web/src/components/ui/input.jsx | 19 + services/web/src/components/ui/label.jsx | 18 + services/web/src/components/ui/pagination.jsx | 106 ++ .../web/src/components/ui/password-input.jsx | 28 + services/web/src/components/ui/popover.jsx | 30 + services/web/src/components/ui/select.jsx | 129 +++ services/web/src/components/ui/separator.jsx | 25 + services/web/src/components/ui/sheet.jsx | 94 ++ services/web/src/components/ui/skeleton.jsx | 13 + services/web/src/components/ui/sonner.jsx | 23 + services/web/src/components/ui/spinner.jsx | 16 + services/web/src/components/ui/switch.jsx | 24 + services/web/src/components/ui/table.jsx | 106 ++ services/web/src/components/ui/tabs.jsx | 51 + services/web/src/components/ui/textarea.jsx | 16 + services/web/src/components/ui/tooltip.jsx | 45 + .../web/src/docs/components/EditButton.js | 10 +- .../web/src/docs/components/EditFieldModal.js | 90 +- .../web/src/docs/components/EditableField.js | 30 +- .../web/src/docs/components/Properties.js | 19 +- .../web/src/docs/components/RequestBuilder.js | 279 +++-- .../docs/components/RouteAuthentication.js | 13 +- .../web/src/docs/components/RouteExample.js | 35 +- .../web/src/docs/components/RouteParams.js | 9 +- services/web/src/docs/components/RoutePath.js | 7 +- .../web/src/docs/components/VisitedSchemas.js | 5 +- .../web/src/docs/screens/ApiDocs/index.js | 18 +- services/web/src/helpers/notifications.js | 9 +- services/web/src/hooks/useDisclosure.js | 15 + services/web/src/hooks/useMediaQuery.js | 24 + services/web/src/layouts/Basic.js | 24 +- services/web/src/layouts/Dashboard.js | 248 ++-- services/web/src/layouts/Portal.js | 116 +- services/web/src/lib/utils.js | 10 + services/web/src/modals/Confirm.js | 16 +- services/web/src/modals/InspectObject.js | 89 +- .../web/src/screens/Applications/Actions.js | 48 +- .../src/screens/Applications/Details/Edit.js | 11 +- services/web/src/screens/Applications/Form.js | 138 ++- services/web/src/screens/Applications/List.js | 93 +- services/web/src/screens/Applications/New.js | 11 +- .../src/screens/AuditLog/Details/Overview.js | 183 ++- services/web/src/screens/AuditLog/List.js | 231 ++-- services/web/src/screens/Auth/AcceptInvite.js | 213 ++-- .../screens/Auth/AcceptInviteAuthenticated.js | 4 +- services/web/src/screens/Auth/ConfirmCode.js | 130 +-- .../web/src/screens/Auth/ForgotPassword.js | 152 +-- services/web/src/screens/Auth/Login.js | 151 ++- .../web/src/screens/Auth/ResetPassword.js | 189 ++- services/web/src/screens/Auth/Signup.js | 251 ++-- services/web/src/screens/Error/index.js | 13 +- services/web/src/screens/Invites/Actions.js | 53 +- services/web/src/screens/Invites/Form.js | 37 +- services/web/src/screens/Invites/List.js | 194 ++-- services/web/src/screens/Loading/index.js | 9 +- services/web/src/screens/Lockout.js | 28 +- services/web/src/screens/Onboard/index.js | 132 ++- .../web/src/screens/Organizations/Actions.js | 93 +- .../src/screens/Organizations/Detail/Edit.js | 5 +- .../screens/Organizations/Detail/Overview.js | 35 +- .../web/src/screens/Organizations/Form.js | 100 +- .../web/src/screens/Organizations/List.js | 177 ++- services/web/src/screens/Organizations/New.js | 11 +- services/web/src/screens/Products/Actions.js | 93 +- .../web/src/screens/Products/Detail/Edit.js | 5 +- .../src/screens/Products/Detail/Overview.js | 66 +- services/web/src/screens/Products/Form.js | 338 ++++-- services/web/src/screens/Products/List.js | 165 ++- services/web/src/screens/Products/New.js | 11 +- services/web/src/screens/Settings/Details.js | 152 ++- .../web/src/screens/Settings/Notifications.js | 91 +- .../src/screens/Settings/Security/Sessions.js | 105 +- .../Security/TwoFactorAuthentication.js | 109 +- .../src/screens/Settings/Security/index.js | 177 ++- services/web/src/screens/Shops/Actions.js | 90 +- services/web/src/screens/Shops/Detail/Edit.js | 5 +- .../web/src/screens/Shops/Detail/Overview.js | 73 +- .../web/src/screens/Shops/Detail/Products.js | 88 +- services/web/src/screens/Shops/Form.js | 295 +++-- services/web/src/screens/Shops/List.js | 255 ++--- services/web/src/screens/Shops/New.js | 11 +- services/web/src/screens/Templates/Actions.js | 72 +- .../src/screens/Templates/Detail/Content.js | 92 +- .../src/screens/Templates/Detail/HelpModal.js | 65 +- .../web/src/screens/Templates/Detail/Menu.js | 14 +- .../src/screens/Templates/Detail/Overview.js | 54 +- .../screens/Templates/Detail/ParamsModal.js | 17 +- .../src/screens/Templates/Detail/Preview.js | 25 +- .../Templates/Detail/SendPreviewButton.js | 6 +- .../Templates/Detail/SendPreviewModal.js | 29 +- services/web/src/screens/Templates/Form.js | 207 ++-- services/web/src/screens/Templates/List.js | 214 ++-- services/web/src/screens/Templates/New.js | 3 +- services/web/src/screens/Unsubscribe.js | 25 +- services/web/src/screens/Users/Actions.js | 99 +- services/web/src/screens/Users/Detail/Edit.js | 5 +- .../web/src/screens/Users/Detail/Overview.js | 193 ++-- services/web/src/screens/Users/Form.js | 281 +++-- services/web/src/screens/Users/List.js | 254 ++-- services/web/src/screens/Users/New.js | 11 +- services/web/src/stores/page.js | 11 +- services/web/src/styles/globals.css | 172 +++ services/web/src/theme.js | 150 --- services/web/src/theme.less | 110 -- services/web/src/utils/notify.js | 37 + services/web/visual/README.md | 72 ++ services/web/visual/app.spec.js | 52 + services/web/visual/auth.spec.js | 25 + services/web/visual/global-setup.js | 62 + services/web/visual/helpers/audit.js | 104 ++ services/web/visual/helpers/capture.js | 47 + services/web/visual/helpers/conformance.js | 75 ++ services/web/visual/playwright.config.js | 68 ++ services/web/visual/routes.js | 48 + services/web/vite.config.js | 3 + services/web/yarn.lock | 1018 +++++++++++++---- 188 files changed, 9271 insertions(+), 4863 deletions(-) create mode 100644 .github/workflows/visual.yml create mode 100644 services/web/.gitignore create mode 100644 services/web/components.json delete mode 100644 services/web/postcss.config.js create mode 100644 services/web/src/components/ThemeProvider.jsx create mode 100644 services/web/src/components/Thumbnail.js create mode 100644 services/web/src/components/ui/alert.jsx create mode 100644 services/web/src/components/ui/avatar.jsx create mode 100644 services/web/src/components/ui/badge.jsx create mode 100644 services/web/src/components/ui/breadcrumb.jsx create mode 100644 services/web/src/components/ui/button.jsx create mode 100644 services/web/src/components/ui/card.jsx create mode 100644 services/web/src/components/ui/checkbox.jsx create mode 100644 services/web/src/components/ui/command.jsx create mode 100644 services/web/src/components/ui/definition-list.jsx create mode 100644 services/web/src/components/ui/dialog.jsx create mode 100644 services/web/src/components/ui/dropdown-menu.jsx create mode 100644 services/web/src/components/ui/form.jsx create mode 100644 services/web/src/components/ui/input-otp.jsx create mode 100644 services/web/src/components/ui/input.jsx create mode 100644 services/web/src/components/ui/label.jsx create mode 100644 services/web/src/components/ui/pagination.jsx create mode 100644 services/web/src/components/ui/password-input.jsx create mode 100644 services/web/src/components/ui/popover.jsx create mode 100644 services/web/src/components/ui/select.jsx create mode 100644 services/web/src/components/ui/separator.jsx create mode 100644 services/web/src/components/ui/sheet.jsx create mode 100644 services/web/src/components/ui/skeleton.jsx create mode 100644 services/web/src/components/ui/sonner.jsx create mode 100644 services/web/src/components/ui/spinner.jsx create mode 100644 services/web/src/components/ui/switch.jsx create mode 100644 services/web/src/components/ui/table.jsx create mode 100644 services/web/src/components/ui/tabs.jsx create mode 100644 services/web/src/components/ui/textarea.jsx create mode 100644 services/web/src/components/ui/tooltip.jsx create mode 100644 services/web/src/hooks/useDisclosure.js create mode 100644 services/web/src/hooks/useMediaQuery.js create mode 100644 services/web/src/lib/utils.js create mode 100644 services/web/src/styles/globals.css delete mode 100644 services/web/src/theme.js delete mode 100644 services/web/src/theme.less create mode 100644 services/web/src/utils/notify.js create mode 100644 services/web/visual/README.md create mode 100644 services/web/visual/app.spec.js create mode 100644 services/web/visual/auth.spec.js create mode 100644 services/web/visual/global-setup.js create mode 100644 services/web/visual/helpers/audit.js create mode 100644 services/web/visual/helpers/capture.js create mode 100644 services/web/visual/helpers/conformance.js create mode 100644 services/web/visual/playwright.config.js create mode 100644 services/web/visual/routes.js diff --git a/.github/workflows/visual.yml b/.github/workflows/visual.yml new file mode 100644 index 000000000..3fbdb2025 --- /dev/null +++ b/.github/workflows/visual.yml @@ -0,0 +1,85 @@ +name: visual + +# Chrome-based visual + conformance gate for the Mantine → shadcn migration. +# See change-ui-framework-to-shadcn.html §7.3. + +on: + pull_request: + paths: + - 'services/web/**' + - '.github/workflows/visual.yml' + +jobs: + screenshots: + runs-on: ubuntu-latest + timeout-minutes: 25 + + services: + mongo: + image: mongo:8.0.9 + ports: + - 27017:27017 + + env: + MONGO_URI: mongodb://localhost/bedrock_test + API_URL: http://localhost:2300 + PLAYWRIGHT_BASE_URL: http://localhost:2200 + # Seeded admin fixture used to capture the authenticated session. + E2E_EMAIL: ${{ vars.E2E_EMAIL }} + E2E_PASSWORD: ${{ secrets.E2E_PASSWORD }} + + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + # --- API: install, seed deterministic fixtures, boot on :2300 --- + - name: API deps + working-directory: services/api + run: yarn install --frozen-lockfile + + - name: Seed fixtures + working-directory: services/api + run: yarn fixtures:load + + - name: Start API + working-directory: services/api + run: yarn start & + + # --- Web: install, build, serve the production bundle on :2200 --- + - name: Web deps + working-directory: services/web + run: yarn install --frozen-lockfile + + - name: Build web + working-directory: services/web + run: yarn build + + - name: Serve web + working-directory: services/web + run: yarn static & + + - name: Wait for servers + run: | + npx --yes wait-on http://localhost:2300/1/status/check http://localhost:2200 --timeout 120000 + + # --- Playwright visual + conformance gate --- + - name: Install Chromium + working-directory: services/web + run: yarn playwright install --with-deps chromium + + - name: Run visual gate + working-directory: services/web + run: yarn test:visual + + - name: Upload diffs + report + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: visual-diffs + path: | + services/web/visual/.results/ + services/web/visual/.report/ + retention-days: 14 diff --git a/services/web/.gitignore b/services/web/.gitignore new file mode 100644 index 000000000..68ba9623e --- /dev/null +++ b/services/web/.gitignore @@ -0,0 +1,8 @@ +# Playwright visual-gate artifacts — all generated / per-platform, so not +# committed. Baselines are platform-specific (font rendering differs), so the +# canonical set is regenerated on the CI/linux image — see visual/README.md. +visual/.auth/ +visual/.results/ +visual/.report/ +visual/test-results/ +visual/baselines/ diff --git a/services/web/components.json b/services/web/components.json new file mode 100644 index 000000000..dd4cae458 --- /dev/null +++ b/services/web/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": false, + "tailwind": { + "config": "", + "css": "src/styles/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "ui": "@/components/ui", + "lib": "@/lib", + "utils": "@/lib/utils", + "hooks": "@/hooks" + } +} diff --git a/services/web/jsconfig.json b/services/web/jsconfig.json index f0482d3f7..6e3900088 100644 --- a/services/web/jsconfig.json +++ b/services/web/jsconfig.json @@ -8,7 +8,11 @@ "module": "ESNext", "moduleResolution": "bundler", "allowSyntheticDefaultImports": true, + "baseUrl": ".", "paths": { + "@/*": [ + "./src/*" + ], "helpers/*": [ "./src/helpers/*" ], diff --git a/services/web/package.json b/services/web/package.json index f4823d34b..7f9705caa 100644 --- a/services/web/package.json +++ b/services/web/package.json @@ -12,6 +12,8 @@ "static": "node ./serve/static", "lint": "eslint", "test": "vitest", + "test:visual": "playwright test -c visual/playwright.config.js", + "test:visual:update": "playwright test -c visual/playwright.config.js --update-snapshots", "generate": "cd ../../generator && yarn install && yarn generate", "postinstall": "cd serve && yarn install" }, @@ -20,43 +22,62 @@ "@bedrockio/config": "^3.0.0", "@bedrockio/logger": "^1.1.5", "@bedrockio/router": "^0.3.2", - "@mantine/core": "^8.3.10", - "@mantine/dates": "^8.3.10", - "@mantine/form": "^8.3.10", - "@mantine/hooks": "^8.3.10", - "@mantine/notifications": "^8.3.10", + "@hookform/resolvers": "^5.4.0", "@mdx-js/rollup": "^3.1.0", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-switch": "^1.2.6", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-tooltip": "^1.2.8", "@sentry/browser": "^9.15.0", "@simplewebauthn/browser": "^13.1.0", + "@tailwindcss/vite": "^4.3.0", "@uiw/react-json-view": "^2.0.0-alpha.37", "@vitejs/plugin-react": "^4.4.1", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", "dayjs": "^1.11.13", "hast-util-sanitize": "^4.0.0", "hast-util-to-mdast": "^8.4.1", + "input-otp": "^1.4.2", "less": "^4.3.0", "lodash-es": "^4.17.21", + "lucide-react": "^1.17.0", "postcss": "^8.5.3", - "postcss-preset-mantine": "^1.17.0", - "postcss-simple-vars": "^7.0.1", "prism-react-renderer": "^2.4.1", "prop-types": "^15.8.1", "qr.js": "^0.0.0", "react": "^19.1.1", "react-day-picker": "^9.5.0", "react-dom": "^19.1.1", - "react-dropzone": "^14.3.8", + "react-dropzone": "^15.0.0", "react-helmet-async": "^1.3.0", + "react-hook-form": "^7.77.0", "react-icons": "^5.5.0", "react-markdown": "^10.1.0", "rehype-autolink-headings": "^7.1.0", "rehype-slug": "^6.0.0", "remark-gfm": "^4.0.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "tailwindcss": "^4.3.0", + "tw-animate-css": "^1.4.0", "vite": "^6.3.5", - "vitest": "^3.1.3" + "vitest": "^3.1.3", + "zod": "^4.4.3" }, "devDependencies": { "@bedrockio/eslint-plugin": "^1.5.3", "@bedrockio/prettier-config": "^1.1.1", + "@playwright/test": "^1.60.0", "eslint": "^9.37.0", "eslint-plugin-mdx": "^3.6.2", "prettier": "^3.6.2", diff --git a/services/web/postcss.config.js b/services/web/postcss.config.js deleted file mode 100644 index 0d6062caa..000000000 --- a/services/web/postcss.config.js +++ /dev/null @@ -1,14 +0,0 @@ -export default { - plugins: { - 'postcss-preset-mantine': {}, - 'postcss-simple-vars': { - variables: { - 'mantine-breakpoint-xs': '36em', - 'mantine-breakpoint-sm': '48em', - 'mantine-breakpoint-md': '62em', - 'mantine-breakpoint-lg': '75em', - 'mantine-breakpoint-xl': '88em', - }, - }, - }, -}; diff --git a/services/web/src/Wrapper.js b/services/web/src/Wrapper.js index 5998617aa..358620b8b 100644 --- a/services/web/src/Wrapper.js +++ b/services/web/src/Wrapper.js @@ -1,9 +1,4 @@ -// TODO: remove? - -// part of integration with mantine import { BrowserRouter, Route, Routes } from '@bedrockio/router'; -import { MantineProvider } from '@mantine/core'; -import { Notifications } from '@mantine/notifications'; import dayjs from 'dayjs'; import customParseFormat from 'dayjs/plugin/customParseFormat'; import React, { Suspense } from 'react'; @@ -12,13 +7,16 @@ import { HelmetProvider } from 'react-helmet-async'; import SessionSwitch from 'helpers/SessionSwitch'; import { SessionProvider, useSession } from 'stores/session'; +import { ThemeProvider } from '@/components/ThemeProvider'; +import { Toaster } from '@/components/ui/sonner'; + import LoadingScreen from 'screens/Loading'; import Unsubscribe from 'screens/Unsubscribe'; import 'utils/sentry'; import { hasAccess } from 'utils/user'; -import { theme } from './theme'; +import './styles/globals.css'; dayjs.extend(customParseFormat); @@ -41,8 +39,8 @@ function AppSwitch() { export default function Wrapper() { return ( - - + + @@ -57,7 +55,7 @@ export default function Wrapper() { - + ); } diff --git a/services/web/src/components/Auth/Apple/DisableButton.js b/services/web/src/components/Auth/Apple/DisableButton.js index c06c5e232..f6d757804 100644 --- a/services/web/src/components/Auth/Apple/DisableButton.js +++ b/services/web/src/components/Auth/Apple/DisableButton.js @@ -1,10 +1,12 @@ -import { Button } from '@mantine/core'; import { noop } from 'lodash'; import PropTypes from 'prop-types'; import { useState } from 'react'; import { useSession } from 'stores/session'; +import { Button } from '@/components/ui/button'; +import { Spinner } from '@/components/ui/spinner'; + import { disable } from 'utils/auth/apple'; /** @@ -15,7 +17,7 @@ import { disable } from 'utils/auth/apple'; * @returns {JSX.Element} The DisableButton component. */ export default function DisableButton(props) { - const { onError = noop, onDisabled = noop } = props; + const { onError = noop, onDisabled = noop, ...rest } = props; const [loading, setLoading] = useState(false); const { user, updateUser } = useSession(); @@ -34,11 +36,12 @@ export default function DisableButton(props) { return ( ); diff --git a/services/web/src/components/Auth/Apple/SignInButton.js b/services/web/src/components/Auth/Apple/SignInButton.js index 68d9de827..ccb13a599 100644 --- a/services/web/src/components/Auth/Apple/SignInButton.js +++ b/services/web/src/components/Auth/Apple/SignInButton.js @@ -1,9 +1,10 @@ import { useNavigate } from '@bedrockio/router'; -import { ActionIcon, Button } from '@mantine/core'; import { FaApple } from 'react-icons/fa'; import { useSession } from 'stores/session'; +import { Button } from '@/components/ui/button'; + import { signInWithApple } from 'utils/auth/apple'; export default function AppleSignInButton({ @@ -34,19 +35,20 @@ export default function AppleSignInButton({ if (type === 'login') { return ( - - + ); } return ( - ); diff --git a/services/web/src/components/Auth/Federated.js b/services/web/src/components/Auth/Federated.js index 3f6fbca5f..f32c74d5a 100644 --- a/services/web/src/components/Auth/Federated.js +++ b/services/web/src/components/Auth/Federated.js @@ -1,5 +1,3 @@ -import { Divider, Group, Stack } from '@mantine/core'; - import { canShowAppleSignin } from 'utils/auth/apple'; import { canShowGoogleSignin } from 'utils/auth/google'; import { canShowPasskey } from 'utils/auth/passkey'; @@ -21,16 +19,23 @@ export default function Federated(props) { return null; } - const Wrapper = type === 'signup' ? Stack : Group; - return ( <> - - +
+
+ OR +
+
+
*]:flex-1 [&>*]:justify-center' + }> {showPasskey && } {showGoogle && } {showApple && } - +
); } diff --git a/services/web/src/components/Auth/Google/DisableButton.js b/services/web/src/components/Auth/Google/DisableButton.js index 4e0224442..979919efa 100644 --- a/services/web/src/components/Auth/Google/DisableButton.js +++ b/services/web/src/components/Auth/Google/DisableButton.js @@ -1,10 +1,12 @@ -import { Button } from '@mantine/core'; import { noop } from 'lodash'; import PropTypes from 'prop-types'; import { useState } from 'react'; import { useSession } from 'stores/session'; +import { Button } from '@/components/ui/button'; +import { Spinner } from '@/components/ui/spinner'; + import { disable } from 'utils/auth/google'; /** @@ -12,7 +14,7 @@ import { disable } from 'utils/auth/google'; * @param {object} props - Component props. * @param {Function} [props.onError=noop] - Callback function for errors. * @param {Function} [props.onDisabled=noop] - Callback function after successful disabling. - * @param {object} rest - Remaining props to pass to the Mantine Button. + * @param {object} rest - Remaining props to pass to the Button. * @returns {JSX.Element} */ export default function DisableButton({ @@ -42,12 +44,13 @@ export default function DisableButton({ return ( ); diff --git a/services/web/src/components/Auth/Google/SignInButton.js b/services/web/src/components/Auth/Google/SignInButton.js index 3217cce2b..4b97b944e 100644 --- a/services/web/src/components/Auth/Google/SignInButton.js +++ b/services/web/src/components/Auth/Google/SignInButton.js @@ -1,10 +1,11 @@ import { useNavigate } from '@bedrockio/router'; -import { ActionIcon, Button } from '@mantine/core'; import { useSession } from 'stores/session'; import logo from 'assets/google-logo.svg'; +import { Button } from '@/components/ui/button'; + import { signInWithGoogle } from 'utils/auth/google'; export default function GoogleSignInButton({ @@ -35,22 +36,20 @@ export default function GoogleSignInButton({ if (type === 'login') { return ( - Google - + ); } return ( - ); diff --git a/services/web/src/components/Auth/OptionalPassword.js b/services/web/src/components/Auth/OptionalPassword.js index a54ddc79d..a54a5ff6a 100644 --- a/services/web/src/components/Auth/OptionalPassword.js +++ b/services/web/src/components/Auth/OptionalPassword.js @@ -1,4 +1,4 @@ -import { PasswordInput } from '@mantine/core'; +import { PasswordInput } from '@/components/ui/password-input'; import { AUTH_TYPE } from 'utils/env'; diff --git a/services/web/src/components/Auth/PasskeyButton.js b/services/web/src/components/Auth/PasskeyButton.js index 33b73d802..48c4de693 100644 --- a/services/web/src/components/Auth/PasskeyButton.js +++ b/services/web/src/components/Auth/PasskeyButton.js @@ -1,10 +1,11 @@ import { useNavigate } from '@bedrockio/router'; -import { ActionIcon } from '@mantine/core'; import { noop } from 'lodash'; import { PiFingerprintBold } from 'react-icons/pi'; import { useSession } from 'stores/session'; +import { Button } from '@/components/ui/button'; + import { login } from 'utils/auth/passkey'; export default function PasskeyButton(props) { @@ -31,13 +32,13 @@ export default function PasskeyButton(props) { } return ( - - + ); } diff --git a/services/web/src/components/Authenticator.js b/services/web/src/components/Authenticator.js index 805ef29ff..1cc33b45b 100644 --- a/services/web/src/components/Authenticator.js +++ b/services/web/src/components/Authenticator.js @@ -1,14 +1,3 @@ -import { - Button, - Container, - Divider, - Group, - Loader, - PinInput, - Stack, - Text, -} from '@mantine/core'; - import { useEffect, useState } from 'react'; import { useSession } from 'stores/session'; @@ -17,6 +6,15 @@ import ErrorMessage from 'components/ErrorMessage'; import { ExternalLink } from 'components/Link'; import QRCode from 'components/QRCode'; +import { Button } from '@/components/ui/button'; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from '@/components/ui/input-otp'; +import { Separator } from '@/components/ui/separator'; +import { Spinner } from '@/components/ui/spinner'; + import { request } from 'utils/api'; export default function Authenticator({ onClose, onSuccess }) { @@ -85,14 +83,14 @@ export default function Authenticator({ onClose, onSuccess }) { /> ); } else { - return ; + return ; } }; return ( - - 1. Download an authenticator app. - +
+

1. Download an authenticator app.

+

We recommend{' '} Google Authenticator @@ -102,41 +100,41 @@ export default function Authenticator({ onClose, onSuccess }) { 1Password , or Authy. - - 2. Scan this barcode using your authenticator app - - {renderQrCode()} - - 3. Enter the security code generated by your app +

+

+ 2. Scan this barcode using your authenticator app +

+
{renderQrCode()}
+

+ 3. Enter the security code generated by your app +

- - - - - - - +
+
); } diff --git a/services/web/src/components/BackLink.js b/services/web/src/components/BackLink.js index 00f291104..0189b76cf 100644 --- a/services/web/src/components/BackLink.js +++ b/services/web/src/components/BackLink.js @@ -1,5 +1,5 @@ import { useNavigate } from '@bedrockio/router'; -import { NavLink } from '@mantine/core'; +import { ArrowLeft } from 'lucide-react'; export default function BackLink(props) { const { label = 'Back' } = props; @@ -11,5 +11,13 @@ export default function BackLink(props) { navigate.back(); } - return ; + return ( + + + {label} + + ); } diff --git a/services/web/src/components/Code/index.js b/services/web/src/components/Code/index.js index 07867131c..e28b1d7cf 100644 --- a/services/web/src/components/Code/index.js +++ b/services/web/src/components/Code/index.js @@ -1,4 +1,3 @@ -import { Paper } from '@mantine/core'; import { Highlight, themes } from 'prism-react-renderer'; import PropTypes from 'prop-types'; import { useMemo, useState } from 'react'; @@ -68,7 +67,7 @@ export default function Code({ code, language, scroll, action, ...rest }) { }, [language]); return ( - +
{action || ( @@ -82,7 +81,7 @@ export default function Code({ code, language, scroll, action, ...rest }) {
)}
-
+
); } diff --git a/services/web/src/components/ErrorBoundary.jsx b/services/web/src/components/ErrorBoundary.jsx index 59dae66e9..a53fa6565 100644 --- a/services/web/src/components/ErrorBoundary.jsx +++ b/services/web/src/components/ErrorBoundary.jsx @@ -1,8 +1,10 @@ -import { Button, Group, Paper, Stack, Text, Title } from '@mantine/core'; import PropTypes from 'prop-types'; import React from 'react'; import { PiArrowClockwiseBold } from 'react-icons/pi'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent } from '@/components/ui/card'; + class ErrorBoundary extends React.Component { constructor(props) { super(props); @@ -24,17 +26,16 @@ class ErrorBoundary extends React.Component { render() { if (this.state.hasError) { return ( - - - Something went wrong - + + +

Something went wrong

+

An error occurred while rendering this component. You can try to reload the page or continue using other parts of the application. - - - - - - + + + ); } return this.props.children; diff --git a/services/web/src/components/ErrorMessage.js b/services/web/src/components/ErrorMessage.js index 80fb49e89..4f232b0d0 100644 --- a/services/web/src/components/ErrorMessage.js +++ b/services/web/src/components/ErrorMessage.js @@ -2,15 +2,17 @@ // also captures and logs error stack for production // debugging. -import { Alert } from '@mantine/core'; import { useEffect } from 'react'; +import { Alert, AlertDescription } from '@/components/ui/alert'; +import { cn } from '@/lib/utils'; + import { ApiError } from 'utils/api'; import { CustomError } from 'utils/error'; import { captureError } from 'utils/sentry'; export default function ErrorMessage(props) { - const { error, ...rest } = props; + const { error, className } = props; useEffect(() => { if (canLogError(error)) { @@ -35,8 +37,8 @@ export default function ErrorMessage(props) { } return ( - - {error.message || 'An error occurred'} + + {error.message || 'An error occurred'} ); } diff --git a/services/web/src/components/Footer.js b/services/web/src/components/Footer.js index 7fd584664..867bfad75 100644 --- a/services/web/src/components/Footer.js +++ b/services/web/src/components/Footer.js @@ -1,47 +1,28 @@ -import { Group, Switch, Text, useMantineColorScheme } from '@mantine/core'; -import { PiMoonBold, PiSunBold } from 'react-icons/pi'; - import Logo from 'components/Logo'; +import { useTheme } from '@/components/ThemeProvider'; +import { Switch } from '@/components/ui/switch'; + export default function Footer() { - const { setColorScheme, colorScheme } = useMantineColorScheme(); + const { resolvedTheme, setTheme } = useTheme(); + const isDark = resolvedTheme === 'dark'; + + function onToggle(checked) { + setTheme(checked ? 'dark' : 'light'); + } + return ( -