diff --git a/web/src/pages/AdminUserDetail.tsx b/web/src/pages/AdminUserDetail.tsx index a2a47faa..41879235 100644 --- a/web/src/pages/AdminUserDetail.tsx +++ b/web/src/pages/AdminUserDetail.tsx @@ -1,4 +1,4 @@ -import { useMemo, useState } from "react"; +import { useId, useMemo, useState } from "react"; import type { FormEvent } from "react"; import { useParams, Link } from "react-router"; import { @@ -66,6 +66,7 @@ import { playbackQualityValueFromPreset, type PlaybackQualityPreset, } from "@/lib/playback-quality"; +import { PERMISSION_METADATA_CURATION } from "@/lib/permissions"; import { RegistrySettingControl } from "@/components/settings/RegistrySettingControl"; import { formatSettingValue, getSettingDefinition } from "@/lib/settingsManifest"; import { @@ -275,6 +276,14 @@ function OverviewTab({ user }: { user: AdminUser }) {
+ void const [password, setPassword] = useState(""); const [role, setRole] = useState(user.role); const [enabled, setEnabled] = useState(user.enabled); + const [permissions, setPermissions] = useState(user.permissions ?? []); const [libraryIDs, setLibraryIDs] = useState(user.library_ids); const [maxStreams, setMaxStreams] = useState(user.max_streams); const [maxTranscodes, setMaxTranscodes] = useState(user.max_transcodes); @@ -889,6 +913,7 @@ function EditUserForm({ user, onClose }: { user: AdminUser; onClose: () => void const [downloadTranscodeAllowed, setDownloadTranscodeAllowed] = useState( user.download_transcode_allowed, ); + const metadataCurationId = useId(); const updateMutation = useUpdateUser(); function handleSubmit(e: FormEvent) { @@ -897,6 +922,7 @@ function EditUserForm({ user, onClose }: { user: AdminUser; onClose: () => void username, email, role, + permissions, enabled, library_ids: libraryIDs, max_streams: maxStreams, @@ -982,6 +1008,23 @@ function EditUserForm({ user, onClose }: { user: AdminUser; onClose: () => void value={libraryIDs} onChange={setLibraryIDs} /> +
+
+ +

+ Edit, refresh, and rematch metadata within assigned libraries. +

+
+ + setPermissions((current) => + setAssignedPermission(current, PERMISSION_METADATA_CURATION, checked), + ) + } + /> +
diff --git a/web/src/pages/AdminUsers.tsx b/web/src/pages/AdminUsers.tsx index 5c277324..8d490466 100644 --- a/web/src/pages/AdminUsers.tsx +++ b/web/src/pages/AdminUsers.tsx @@ -59,6 +59,7 @@ import { playbackQualityValueFromPreset, type PlaybackQualityPreset, } from "@/lib/playback-quality"; +import { PERMISSION_METADATA_CURATION } from "@/lib/permissions"; const PAGE_SIZE_OPTIONS = ["25", "50", "100"] as const; type UserSortField = "username" | "email" | "role" | "enabled" | "created_at" | "last_active_at"; @@ -514,6 +515,20 @@ function formatRelativeTime(value?: string | null, fallback = "-") { return fallback; } +function hasAssignedPermission(permissions: string[] | undefined, permission: string) { + return Array.isArray(permissions) && permissions.includes(permission); +} + +function setAssignedPermission(permissions: string[], permission: string, enabled: boolean) { + const next = new Set(permissions); + if (enabled) { + next.add(permission); + } else { + next.delete(permission); + } + return Array.from(next).sort(); +} + function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => void }) { const { data: settings } = useAdminServerSettings(); const { data: libraries = [] } = useAdminLibraries(); @@ -522,6 +537,7 @@ function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => vo const [password, setPassword] = useState(""); const [role, setRole] = useState(user?.role ?? "user"); const [enabled, setEnabled] = useState(user?.enabled ?? true); + const [permissions, setPermissions] = useState(user?.permissions ?? []); const [libraryIDs, setLibraryIDs] = useState(user?.library_ids ?? null); const [maxStreams, setMaxStreams] = useState( user?.max_streams ?? Number(settings?.["defaults.max_streams"] ?? "6"), @@ -549,6 +565,7 @@ function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => vo const passwordId = useId(); const roleId = useId(); const enabledId = useId(); + const metadataCurationId = useId(); const downloadAllowedId = useId(); const downloadTranscodeAllowedId = useId(); const maxStreamsId = useId(); @@ -566,6 +583,7 @@ function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => vo username, email, role, + permissions, enabled, library_ids: libraryIDs, max_streams: maxStreams, @@ -583,6 +601,7 @@ function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => vo email, password, role, + permissions, create_default_profile: true, max_streams: maxStreams, max_transcodes: maxTranscodes, @@ -682,6 +701,23 @@ function UserForm({ user, onClose }: { user: AdminUser | null; onClose: () => vo value={libraryIDs} onChange={setLibraryIDs} /> +
+
+ +

+ Edit, refresh, and rematch metadata within assigned libraries. +

+
+ + setPermissions((current) => + setAssignedPermission(current, PERMISSION_METADATA_CURATION, checked), + ) + } + /> +