user management keyed (267 -> 214)
Accounts, permissions and the create-user form. The confirm dialogs for removing an account and resetting a password were multi-line JSX the scanner cannot see, so they were converted on sight rather than from its list. Cancel comes from ui.button.cancel in all four places it appears here.
This commit is contained in:
+16
-10
@@ -6,6 +6,7 @@ import { useClient } from 'hooks/useClient';
|
||||
import { PERMISSIONS_QUERY_KEY } from 'hooks/usePermissions';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { useTranslation } from '@/lib/i18n';
|
||||
|
||||
// What each ROLE may reach. Not each user — see the table comment in schema/permissions.ts for why.
|
||||
//
|
||||
@@ -37,6 +38,7 @@ type Level = 'none' | 'read' | 'write';
|
||||
const PERMISSIONS_KEY = ['ROLE_PERMISSIONS'];
|
||||
|
||||
export const PermissionsSection = () => {
|
||||
const { t, tUi } = useTranslation();
|
||||
const client = useClient();
|
||||
const queryClient = useQueryClient();
|
||||
const [role, setRole] = useState<string | null>(null);
|
||||
@@ -78,7 +80,7 @@ export const PermissionsSection = () => {
|
||||
// The owner may be editing their own view's inputs — and anyone already signed in needs the dock to
|
||||
// catch up without a reload.
|
||||
await queryClient.invalidateQueries({ queryKey: PERMISSIONS_QUERY_KEY });
|
||||
toast.success(`Saved what ${activeRole}s can reach`);
|
||||
toast.success(t('settings.users.permissionsSaved', { role: activeRole }));
|
||||
} catch (ex) {
|
||||
toast.error(ex instanceof Error ? ex.message : 'Could not save');
|
||||
} finally {
|
||||
@@ -89,12 +91,12 @@ export const PermissionsSection = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center gap-2 p-6 text-sm text-muted-foreground">
|
||||
<Loader2 className="h-4 w-4 animate-spin" /> Loading permissions…
|
||||
<Loader2 className="h-4 w-4 animate-spin" /> {t('settings.users.loadingPermissions')}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (isError || !data) {
|
||||
return <div className="p-6 text-sm text-destructive">Could not load permissions.</div>;
|
||||
return <div className="p-6 text-sm text-destructive">{t('settings.users.permissionsFailed')}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -103,7 +105,11 @@ export const PermissionsSection = () => {
|
||||
hides two of them behind a click and gives no sense of "which one am I editing" at a glance. Real
|
||||
buttons, because switching role mutates a draft rather than navigating. */}
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-1 rounded-lg border p-1" role="tablist" aria-label="Role">
|
||||
<div
|
||||
className="flex items-center gap-1 rounded-lg border p-1"
|
||||
role="tablist"
|
||||
aria-label={t('settings.users.role')}
|
||||
>
|
||||
{data.roles.map((r) => (
|
||||
<button
|
||||
key={r}
|
||||
@@ -128,9 +134,9 @@ export const PermissionsSection = () => {
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Everything is denied unless granted here. <strong>Read</strong> allows viewing, plus changes to things that are
|
||||
only ever the person’s own — their favourites, their playlists, their devices.
|
||||
<strong> Full</strong> allows everything within the app.
|
||||
{t('settings.users.deniedUnlessGranted')} <strong>{t('settings.users.read')}</strong> allows viewing, plus
|
||||
changes to things that are only ever the person’s own — their favourites, their playlists, their devices.
|
||||
<strong> {t('settings.users.full')}</strong> allows everything within the app.
|
||||
</p>
|
||||
|
||||
<div className="divide-y rounded-lg border">
|
||||
@@ -151,9 +157,9 @@ export const PermissionsSection = () => {
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="none">No access</SelectItem>
|
||||
<SelectItem value="read">Read</SelectItem>
|
||||
<SelectItem value="write">Full</SelectItem>
|
||||
<SelectItem value="none">{t('settings.users.noAccess')}</SelectItem>
|
||||
<SelectItem value="read">{t('settings.users.read')}</SelectItem>
|
||||
<SelectItem value="write">{t('settings.users.full')}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user