Files
bambuddy/frontend
maziggy cecdf8f5a7 feat(auth): permission-delegated Settings + Group editor routes; fix group-edit cache stale-read (#1083)
Three intertwined changes, split by intent:

  1. Swap AdminRoute for PermissionRoute on /settings, /groups/new, and
     /groups/:id/edit. Admins retain full access; non-admin users whose
     group holds settings:read / groups:create / groups:update can now
     enter the respective pages instead of being silently redirected to
     the dashboard. SettingsPage's individual tabs and cards keep their
     existing per-action permission checks, so tabs a delegated user can't
     use stay hidden or disabled. AdminRoute had no other callers and is
     removed.

  2. Fix #1083: editing a custom group's permissions appeared to revert
     on reopen. The backend PATCH was persisting correctly — four new
     integration tests in test_groups_api.py (including a direct DB read
     after PATCH) confirm persistence, empty-list clear, preserve-on-
     absent, and 400 on bogus permission. The actual bug was a stale
     ['group', id] React Query cache: onSuccess invalidated ['groups']
     but not the detail key, so the 60s global staleTime served the pre-
     update body on re-mount. onSuccess now primes ['group', id] with the
     PATCH response body (invalidation is not enough — it races with the
     refetch). Frontend regression test added.

  3. Delegated users with settings:read but not settings:update no longer
     get an infinite loop of failed-save toasts on Settings. The debounced
     auto-save effect fires PATCH /settings whenever localSettings diverges
     from the server snapshot; without a permission gate this produced an
     endless 403 → toast → re-render → effect → 403 loop. Three gates now:
     the updateSetting callback short-circuits with a single toast before
     localSettings diverges, the effect safety-nets the same check in case
     any call site bypasses updateSetting, and the language <select> (the
     only direct api.updateSettings bypass in the file) now routes through
     updateMutation with the same guard. New settings.toast.noPermissionUpdate
     key translated in all 8 locales.

  Scoping note: an earlier iteration of change #3 included a
  localSettings rollback inside updateMutation.onError — removed in
  review because it would have discarded in-progress admin typing on
  any transient network/server error. The three up-front guards make
  the rollback unnecessary for the permission case (mutation never
  fires), and preserving typed-in values on transient failures is the
  right call for admins.
2026-04-22 19:10:18 +02:00
..
…
…

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])