mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-24 21:17:31 +00:00
feat: list allowed groups when hovering over allowed group count
This commit is contained in:
@@ -137,11 +137,7 @@ func (oc *OidcController) listClientsHandler(c *gin.Context) error {
|
||||
return err
|
||||
}
|
||||
clientDto.HasDarkLogo = client.HasDarkLogo()
|
||||
|
||||
clientDto.AllowedUserGroupsCount, err = oc.oidcService.GetAllowedGroupsCountOfClient(c, client.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
clientDto.AllowedUserGroupsCount = int64(len(client.AllowedUserGroups))
|
||||
clientsDto[i] = clientDto
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,8 @@ type OidcClientWithAllowedUserGroupsDto struct {
|
||||
|
||||
type OidcClientWithAllowedGroupsCountDto struct {
|
||||
OidcClientDto
|
||||
AllowedUserGroupsCount int64 `json:"allowedUserGroupsCount"`
|
||||
AllowedUserGroups []UserGroupMinimalDto `json:"allowedUserGroups"`
|
||||
AllowedUserGroupsCount int64 `json:"allowedUserGroupsCount"`
|
||||
}
|
||||
|
||||
type OidcClientUpdateDto struct {
|
||||
|
||||
@@ -124,6 +124,7 @@ func (s *OidcService) ListClients(ctx context.Context, name string, listRequestO
|
||||
query := s.db.
|
||||
WithContext(ctx).
|
||||
Preload("CreatedBy").
|
||||
Preload("AllowedUserGroups").
|
||||
Model(&model.OidcClient{})
|
||||
|
||||
if name != "" {
|
||||
@@ -627,22 +628,6 @@ func (s *OidcService) UpdateAllowedUserGroups(ctx context.Context, id string, in
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func (s *OidcService) GetAllowedGroupsCountOfClient(ctx context.Context, id string) (int64, error) {
|
||||
// We only perform select queries here, so we can rollback in all cases
|
||||
tx := s.db.Begin()
|
||||
defer func() {
|
||||
tx.Rollback()
|
||||
}()
|
||||
|
||||
client, err := s.getClientInternal(ctx, id, tx, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
count := tx.WithContext(ctx).Model(&client).Association("AllowedUserGroups").Count()
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (s *OidcService) ListAuthorizedClients(ctx context.Context, userID string, listRequestOptions utils.ListRequestOptions) ([]model.UserAuthorizedOidcClient, utils.PaginationResponse, error) {
|
||||
tx := s.db.Begin()
|
||||
defer func() {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import Scrollbar from './scroll-area-scrollbar.svelte';
|
||||
import Root from './scroll-area.svelte';
|
||||
|
||||
export {
|
||||
Root,
|
||||
Scrollbar,
|
||||
//,
|
||||
Root as ScrollArea,
|
||||
Scrollbar as ScrollAreaScrollbar
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui';
|
||||
import { cn, type WithoutChild } from '$lib/utils/style.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
orientation = 'vertical',
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChild<ScrollAreaPrimitive.ScrollbarProps> = $props();
|
||||
</script>
|
||||
|
||||
<ScrollAreaPrimitive.Scrollbar
|
||||
bind:ref
|
||||
data-slot="scroll-area-scrollbar"
|
||||
data-orientation={orientation}
|
||||
{orientation}
|
||||
class={cn(
|
||||
'data-horizontal:h-2.5 data-horizontal:flex-col data-horizontal:border-t data-horizontal:border-t-transparent data-vertical:h-full data-vertical:w-2.5 data-vertical:border-l data-vertical:border-l-transparent flex touch-none p-px transition-colors select-none',
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{@render children?.()}
|
||||
<ScrollAreaPrimitive.Thumb
|
||||
data-slot="scroll-area-thumb"
|
||||
class="rounded-full relative flex-1 bg-border"
|
||||
/>
|
||||
</ScrollAreaPrimitive.Scrollbar>
|
||||
@@ -0,0 +1,43 @@
|
||||
<script lang="ts">
|
||||
import { ScrollArea as ScrollAreaPrimitive } from 'bits-ui';
|
||||
import { cn, type WithoutChild } from '$lib/utils/style.js';
|
||||
import { Scrollbar } from './index.js';
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
viewportRef = $bindable(null),
|
||||
class: className,
|
||||
orientation = 'vertical',
|
||||
scrollbarXClasses = '',
|
||||
scrollbarYClasses = '',
|
||||
children,
|
||||
...restProps
|
||||
}: WithoutChild<ScrollAreaPrimitive.RootProps> & {
|
||||
orientation?: 'vertical' | 'horizontal' | 'both' | undefined;
|
||||
scrollbarXClasses?: string | undefined;
|
||||
scrollbarYClasses?: string | undefined;
|
||||
viewportRef?: HTMLElement | null;
|
||||
} = $props();
|
||||
</script>
|
||||
|
||||
<ScrollAreaPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="scroll-area"
|
||||
class={cn('relative', className)}
|
||||
{...restProps}
|
||||
>
|
||||
<ScrollAreaPrimitive.Viewport
|
||||
bind:ref={viewportRef}
|
||||
data-slot="scroll-area-viewport"
|
||||
class="cn-scroll-area-viewport size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
|
||||
>
|
||||
{@render children?.()}
|
||||
</ScrollAreaPrimitive.Viewport>
|
||||
{#if orientation === 'vertical' || orientation === 'both'}
|
||||
<Scrollbar orientation="vertical" class={scrollbarYClasses} />
|
||||
{/if}
|
||||
{#if orientation === 'horizontal' || orientation === 'both'}
|
||||
<Scrollbar orientation="horizontal" class={scrollbarXClasses} />
|
||||
{/if}
|
||||
<ScrollAreaPrimitive.Corner />
|
||||
</ScrollAreaPrimitive.Root>
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { UserGroup } from './user-group.type';
|
||||
import type { UserGroup, UserGroupMinimal } from './user-group.type';
|
||||
|
||||
export type OidcClientType = 'standard' | 'cimd';
|
||||
|
||||
@@ -75,6 +75,7 @@ export type OidcClientWithAllowedUserGroups = OidcClient & {
|
||||
};
|
||||
|
||||
export type OidcClientWithAllowedUserGroupsCount = OidcClient & {
|
||||
allowedUserGroups: UserGroupMinimal[];
|
||||
allowedUserGroupsCount: number;
|
||||
};
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
import { openConfirmDialog } from '$lib/components/confirm-dialog/';
|
||||
import ImageBox from '$lib/components/image-box.svelte';
|
||||
import AdvancedTable from '$lib/components/table/advanced-table.svelte';
|
||||
import { ScrollArea } from '$lib/components/ui/scroll-area';
|
||||
import * as Tooltip from '$lib/components/ui/tooltip';
|
||||
import { m } from '$lib/paraglide/messages';
|
||||
import OIDCService from '$lib/services/oidc-service';
|
||||
import type {
|
||||
@@ -44,7 +46,7 @@
|
||||
label: m.oidc_allowed_group_count(),
|
||||
column: 'allowedUserGroupsCount',
|
||||
sortable: true,
|
||||
value: (item) => (item.isGroupRestricted ? item.allowedUserGroupsCount : '-')
|
||||
cell: AllowedGroupCountCell
|
||||
},
|
||||
{
|
||||
label: m.restricted(),
|
||||
@@ -145,6 +147,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
{#snippet AllowedGroupCountCell({ item }: { item: OidcClientWithAllowedUserGroupsCount })}
|
||||
{#if !item.isGroupRestricted}
|
||||
-
|
||||
{:else if item.allowedUserGroups.length === 0}
|
||||
{item.allowedUserGroupsCount}
|
||||
{:else}
|
||||
<Tooltip.Provider>
|
||||
<Tooltip.Root>
|
||||
<Tooltip.Trigger class="cursor-default underline decoration-dotted underline-offset-4">
|
||||
{item.allowedUserGroupsCount}
|
||||
</Tooltip.Trigger>
|
||||
<Tooltip.Content side="right" class="flex-col items-start">
|
||||
<ScrollArea
|
||||
class="[&>[data-slot=scroll-area-viewport]]:max-h-48"
|
||||
scrollbarYClasses="[&>[data-slot=scroll-area-thumb]]:bg-background/40"
|
||||
>
|
||||
<div class="flex flex-col gap-0.5 pr-3">
|
||||
{#each item.allowedUserGroups as group (group.id)}
|
||||
<span>{group.friendlyName}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</Tooltip.Content>
|
||||
</Tooltip.Root>
|
||||
</Tooltip.Provider>
|
||||
{/if}
|
||||
{/snippet}
|
||||
|
||||
{#snippet LogoCell({ item }: { item: OidcClientWithAllowedUserGroupsCount })}
|
||||
{#if item.hasLogo}
|
||||
<ImageBox
|
||||
|
||||
Reference in New Issue
Block a user