feat: add access profile selection support with device-specific profile persistence
Add SelectOwnProfile handler to allow users to choose from available access profiles. Store selected profile ID per device in settings table with device_access_profile category. Implement GetSelectedProfileID and SetSelectedProfileID repository methods using JSONB storage. Add ListSelectableProfiles to policy repository and service to query user/group/device-specific profiles ordered by priority. Filter gateway
This commit is contained in:
@@ -33,23 +33,27 @@ export function AppHeader({
|
||||
</div>
|
||||
|
||||
<div className="header-actions">
|
||||
{enrolled ? (
|
||||
<>
|
||||
<ActionButton disabled={syncing} onClick={onSync} variant="secondary">
|
||||
{syncing ? "Syncing..." : "Sync"}
|
||||
</ActionButton>
|
||||
<ActionButton onClick={onLogout} variant="ghost">
|
||||
Logout
|
||||
</ActionButton>
|
||||
</>
|
||||
) : null}
|
||||
<ActionButton
|
||||
disabled={!enrolled}
|
||||
onClick={onToggleConnection}
|
||||
variant={connected ? "danger" : "primary"}
|
||||
>
|
||||
{!enrolled ? "Provision first" : connected ? "Disconnect" : "Connect"}
|
||||
</ActionButton>
|
||||
<div className="header-actions-secondary">
|
||||
{enrolled ? (
|
||||
<>
|
||||
<ActionButton disabled={syncing} onClick={onSync} variant="secondary">
|
||||
{syncing ? "Syncing..." : "Sync"}
|
||||
</ActionButton>
|
||||
<ActionButton onClick={onLogout} variant="ghost">
|
||||
Logout
|
||||
</ActionButton>
|
||||
</>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="header-actions-primary">
|
||||
<ActionButton
|
||||
disabled={!enrolled}
|
||||
onClick={onToggleConnection}
|
||||
variant={connected ? "danger" : "primary"}
|
||||
>
|
||||
{!enrolled ? "Provision first" : connected ? "Disconnect" : "Connect"}
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -10,13 +10,34 @@ function ResourceListItem({ value }: { value: string }) {
|
||||
}
|
||||
|
||||
type ResourcePanelProps = {
|
||||
connected: boolean;
|
||||
profiles: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
destinations: string[];
|
||||
}>;
|
||||
resources: string[];
|
||||
profileLabel: string;
|
||||
selectedProfileId: string | null;
|
||||
selectingProfile: boolean;
|
||||
onSelectProfile: (profileId: string) => void;
|
||||
onReset: () => void;
|
||||
};
|
||||
|
||||
export function ResourcePanel({ resources, profileLabel, onReset }: ResourcePanelProps) {
|
||||
export function ResourcePanel({
|
||||
connected,
|
||||
profiles,
|
||||
resources,
|
||||
profileLabel,
|
||||
selectedProfileId,
|
||||
selectingProfile,
|
||||
onSelectProfile,
|
||||
onReset
|
||||
}: ResourcePanelProps) {
|
||||
const effectiveResources = resources.length > 0 ? resources : ["Keine Ressourcen zugewiesen"];
|
||||
const showSelector = profiles.length > 1;
|
||||
const selectedProfile = profiles.find((profile) => profile.id === selectedProfileId) ?? null;
|
||||
|
||||
return (
|
||||
<aside className="resource-panel">
|
||||
@@ -31,8 +52,29 @@ export function ResourcePanel({ resources, profileLabel, onReset }: ResourcePane
|
||||
<div className="resource-meta">
|
||||
<span className="resource-meta-label">Zugriffsprofil</span>
|
||||
<strong>{profileLabel}</strong>
|
||||
{selectedProfile?.description ? <small>{selectedProfile.description}</small> : null}
|
||||
</div>
|
||||
|
||||
{showSelector ? (
|
||||
<label className="resource-selector">
|
||||
<span className="resource-meta-label">Ressource auswählen</span>
|
||||
<select
|
||||
disabled={connected || selectingProfile}
|
||||
onChange={(event) => onSelectProfile(event.target.value)}
|
||||
value={selectedProfileId ?? profiles[0]?.id ?? ""}
|
||||
>
|
||||
{profiles.map((profile) => (
|
||||
<option key={profile.id} value={profile.id}>
|
||||
{profile.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<small>
|
||||
{connected ? "Auswahl ist nur getrennt möglich." : selectingProfile ? "Profil wird aktualisiert..." : "Auswahl wird vor dem Verbinden in die Config übernommen."}
|
||||
</small>
|
||||
</label>
|
||||
) : null}
|
||||
|
||||
<ul className="resource-list">
|
||||
{effectiveResources.map((resource) => (
|
||||
<ResourceListItem key={resource} value={resource} />
|
||||
|
||||
Reference in New Issue
Block a user