mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-03-28 10:16:37 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d071694cd | ||
|
|
39e403d00f | ||
|
|
4e858420e9 | ||
|
|
2d78349b38 | ||
|
|
9ed2adb0f8 | ||
|
|
43790dc1be | ||
|
|
7fbc356d8d |
@@ -6,7 +6,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-22.04 # Using an older version because of https://github.com/actions/runner-images/issues/11471
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
packages: write
|
packages: write
|
||||||
@@ -20,7 +20,6 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
images: |
|
images: |
|
||||||
ghcr.io/${{ github.repository }}
|
ghcr.io/${{ github.repository }}
|
||||||
${{ github.repository }}
|
|
||||||
tags: |
|
tags: |
|
||||||
type=semver,pattern={{version}},prefix=v
|
type=semver,pattern={{version}},prefix=v
|
||||||
type=semver,pattern={{major}}.{{minor}},prefix=v
|
type=semver,pattern={{major}}.{{minor}},prefix=v
|
||||||
|
|||||||
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
|||||||
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.30.0...v) (2025-02-12)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* add ability to override the UI configuration with environment variables ([4e85842](https://github.com/pocket-id/pocket-id/commit/4e858420e9d9713e19f3b35c45c882403717f72f))
|
||||||
|
* add warning for only having one passkey configured ([#220](https://github.com/pocket-id/pocket-id/issues/220)) ([39e403d](https://github.com/pocket-id/pocket-id/commit/39e403d00f3870f9e960427653a1d9697da27a6f))
|
||||||
|
* display source in user and group table ([#225](https://github.com/pocket-id/pocket-id/issues/225)) ([9ed2adb](https://github.com/pocket-id/pocket-id/commit/9ed2adb0f8da13725fd9a4ef6a7798c377d13513))
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* user linking in ldap group sync ([#222](https://github.com/pocket-id/pocket-id/issues/222)) ([2d78349](https://github.com/pocket-id/pocket-id/commit/2d78349b381d7ca10f47d3c03cef685a576b1b49))
|
||||||
|
|
||||||
## [](https://github.com/pocket-id/pocket-id/compare/v0.29.0...v) (2025-02-08)
|
## [](https://github.com/pocket-id/pocket-id/compare/v0.29.0...v) (2025-02-08)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type EnvConfigSchema struct {
|
|||||||
Host string `env:"HOST"`
|
Host string `env:"HOST"`
|
||||||
MaxMindLicenseKey string `env:"MAXMIND_LICENSE_KEY"`
|
MaxMindLicenseKey string `env:"MAXMIND_LICENSE_KEY"`
|
||||||
GeoLiteDBPath string `env:"GEOLITE_DB_PATH"`
|
GeoLiteDBPath string `env:"GEOLITE_DB_PATH"`
|
||||||
|
UiConfigDisabled bool `env:"PUBLIC_UI_CONFIG_DISABLED"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var EnvConfig = &EnvConfigSchema{
|
var EnvConfig = &EnvConfigSchema{
|
||||||
@@ -38,6 +39,7 @@ var EnvConfig = &EnvConfigSchema{
|
|||||||
Host: "0.0.0.0",
|
Host: "0.0.0.0",
|
||||||
MaxMindLicenseKey: "",
|
MaxMindLicenseKey: "",
|
||||||
GeoLiteDBPath: "data/GeoLite2-City.mmdb",
|
GeoLiteDBPath: "data/GeoLite2-City.mmdb",
|
||||||
|
UiConfigDisabled: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|||||||
@@ -184,3 +184,10 @@ func (e *OidcAccessDeniedError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (e *OidcAccessDeniedError) HttpStatusCode() int { return http.StatusForbidden }
|
func (e *OidcAccessDeniedError) HttpStatusCode() int { return http.StatusForbidden }
|
||||||
|
|
||||||
|
type UiConfigDisabledError struct{}
|
||||||
|
|
||||||
|
func (e *UiConfigDisabledError) Error() string {
|
||||||
|
return "The configuration can't be changed since the UI configuration is disabled"
|
||||||
|
}
|
||||||
|
func (e *UiConfigDisabledError) HttpStatusCode() int { return http.StatusForbidden }
|
||||||
|
|||||||
@@ -188,12 +188,15 @@ var defaultDbConfig = model.AppConfig{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *AppConfigService) UpdateAppConfig(input dto.AppConfigUpdateDto) ([]model.AppConfigVariable, error) {
|
func (s *AppConfigService) UpdateAppConfig(input dto.AppConfigUpdateDto) ([]model.AppConfigVariable, error) {
|
||||||
var savedConfigVariables []model.AppConfigVariable
|
if common.EnvConfig.UiConfigDisabled {
|
||||||
|
return nil, &common.UiConfigDisabledError{}
|
||||||
|
}
|
||||||
|
|
||||||
tx := s.db.Begin()
|
tx := s.db.Begin()
|
||||||
rt := reflect.ValueOf(input).Type()
|
rt := reflect.ValueOf(input).Type()
|
||||||
rv := reflect.ValueOf(input)
|
rv := reflect.ValueOf(input)
|
||||||
|
|
||||||
|
var savedConfigVariables []model.AppConfigVariable
|
||||||
for i := 0; i < rt.NumField(); i++ {
|
for i := 0; i < rt.NumField(); i++ {
|
||||||
field := rt.Field(i)
|
field := rt.Field(i)
|
||||||
key := field.Tag.Get("json")
|
key := field.Tag.Get("json")
|
||||||
@@ -254,9 +257,13 @@ func (s *AppConfigService) ListAppConfig(showAll bool) ([]model.AppConfigVariabl
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the value to the default value if it is empty
|
|
||||||
for i := range configuration {
|
for i := range configuration {
|
||||||
if configuration[i].Value == "" && configuration[i].DefaultValue != "" {
|
if common.EnvConfig.UiConfigDisabled {
|
||||||
|
// Set the value to the environment variable if the UI config is disabled
|
||||||
|
configuration[i].Value = s.getConfigVariableFromEnvironmentVariable(configuration[i].Key, configuration[i].DefaultValue)
|
||||||
|
|
||||||
|
} else if configuration[i].Value == "" && configuration[i].DefaultValue != "" {
|
||||||
|
// Set the value to the default value if it is empty
|
||||||
configuration[i].Value = configuration[i].DefaultValue
|
configuration[i].Value = configuration[i].DefaultValue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,12 +362,25 @@ func (s *AppConfigService) LoadDbConfigFromDb() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if storedConfigVar.Value == "" && storedConfigVar.DefaultValue != "" {
|
if common.EnvConfig.UiConfigDisabled {
|
||||||
|
storedConfigVar.Value = s.getConfigVariableFromEnvironmentVariable(currentConfigVar.Key, storedConfigVar.DefaultValue)
|
||||||
|
} else if storedConfigVar.Value == "" && storedConfigVar.DefaultValue != "" {
|
||||||
storedConfigVar.Value = storedConfigVar.DefaultValue
|
storedConfigVar.Value = storedConfigVar.DefaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
dbConfigField.Set(reflect.ValueOf(storedConfigVar))
|
dbConfigField.Set(reflect.ValueOf(storedConfigVar))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *AppConfigService) getConfigVariableFromEnvironmentVariable(key, fallbackValue string) string {
|
||||||
|
environmentVariableName := utils.CamelCaseToScreamingSnakeCase(key)
|
||||||
|
|
||||||
|
if value, exists := os.LookupEnv(environmentVariableName); exists {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
return fallbackValue
|
||||||
|
}
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ func (s *LdapService) SyncGroups() error {
|
|||||||
singleMember := strings.Split(strings.Split(member, "=")[1], ",")[0]
|
singleMember := strings.Split(strings.Split(member, "=")[1], ",")[0]
|
||||||
|
|
||||||
var databaseUser model.User
|
var databaseUser model.User
|
||||||
s.db.Where("username = ?", singleMember).First(&databaseUser)
|
s.db.Where("username = ?", singleMember).Where("ldap_id IS NOT NULL").First(&databaseUser)
|
||||||
|
|
||||||
membersUserId = append(membersUserId, databaseUser.ID)
|
membersUserId = append(membersUserId, databaseUser.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -62,3 +64,12 @@ func CamelCaseToSnakeCase(s string) string {
|
|||||||
}
|
}
|
||||||
return string(result)
|
return string(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CamelCaseToScreamingSnakeCase(s string) string {
|
||||||
|
// Insert underscores before uppercase letters (except the first one)
|
||||||
|
re := regexp.MustCompile(`([a-z0-9])([A-Z])`)
|
||||||
|
snake := re.ReplaceAllString(s, `${1}_${2}`)
|
||||||
|
|
||||||
|
// Convert to uppercase
|
||||||
|
return strings.ToUpper(snake)
|
||||||
|
}
|
||||||
|
|||||||
9482
frontend/package-lock.json
generated
9482
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pocket-id-frontend",
|
"name": "pocket-id-frontend",
|
||||||
"version": "0.30.0",
|
"version": "0.31.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,17 +1,43 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { cn } from '$lib/utils/style.js';
|
||||||
|
import { LucideX } from 'lucide-svelte';
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import type { HTMLAttributes } from 'svelte/elements';
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
import { type Variant, alertVariants } from './index.js';
|
import { type Variant, alertVariants } from './index.js';
|
||||||
import { cn } from '$lib/utils/style.js';
|
|
||||||
|
|
||||||
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
type $$Props = HTMLAttributes<HTMLDivElement> & {
|
||||||
variant?: Variant;
|
variant?: Variant;
|
||||||
|
dismissibleId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
let className: $$Props['class'] = undefined;
|
let className: $$Props['class'] = undefined;
|
||||||
export let variant: $$Props['variant'] = 'default';
|
export let variant: $$Props['variant'] = 'default';
|
||||||
|
export let dismissibleId: $$Props['dismissibleId'] = undefined;
|
||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
|
let isVisible = !dismissibleId;
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (dismissibleId) {
|
||||||
|
const dismissedAlerts = JSON.parse(localStorage.getItem('dismissed-alerts') || '[]');
|
||||||
|
isVisible = !dismissedAlerts.includes(dismissibleId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function dismiss() {
|
||||||
|
if (dismissibleId) {
|
||||||
|
const dismissedAlerts = JSON.parse(localStorage.getItem('dismissed-alerts') || '[]');
|
||||||
|
localStorage.setItem('dismissed-alerts', JSON.stringify([...dismissedAlerts, dismissibleId]));
|
||||||
|
isVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class={cn(alertVariants({ variant }), className)} {...$$restProps} role="alert">
|
{#if isVisible}
|
||||||
<slot />
|
<div class={cn(alertVariants({ variant }), className)} {...$$restProps} role="alert">
|
||||||
</div>
|
<slot />
|
||||||
|
{#if dismissibleId}
|
||||||
|
<button on:click={dismiss} class="absolute top-0 right-0 m-3 text-black dark:text-white"><LucideX class="w-4" /></button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -62,9 +62,20 @@
|
|||||||
>Please add a passkey to prevent losing access to your account.</Alert.Description
|
>Please add a passkey to prevent losing access to your account.</Alert.Description
|
||||||
>
|
>
|
||||||
</Alert.Root>
|
</Alert.Root>
|
||||||
|
{:else if passkeys.length == 1}
|
||||||
|
<Alert.Root variant="warning" dismissibleId="single-passkey">
|
||||||
|
<LucideAlertTriangle class="size-4" />
|
||||||
|
<Alert.Title>Single Passkey Configured</Alert.Title>
|
||||||
|
<Alert.Description
|
||||||
|
>It is recommended to add more than one passkey to avoid loosing access to your account.</Alert.Description
|
||||||
|
>
|
||||||
|
</Alert.Root>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<fieldset disabled={!$appConfigStore.allowOwnAccountEdit || (!!account.ldapId && $appConfigStore.ldapEnabled)}>
|
<fieldset
|
||||||
|
disabled={!$appConfigStore.allowOwnAccountEdit ||
|
||||||
|
(!!account.ldapId && $appConfigStore.ldapEnabled)}
|
||||||
|
>
|
||||||
<Card.Root>
|
<Card.Root>
|
||||||
<Card.Header>
|
<Card.Header>
|
||||||
<Card.Title>Account Details</Card.Title>
|
<Card.Title>Account Details</Card.Title>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
import CollapsibleCard from '$lib/components/collapsible-card.svelte';
|
import CollapsibleCard from '$lib/components/collapsible-card.svelte';
|
||||||
import AppConfigService from '$lib/services/app-config-service';
|
import AppConfigService from '$lib/services/app-config-service';
|
||||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||||
@@ -13,6 +14,7 @@
|
|||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
let appConfig = $state(data.appConfig);
|
let appConfig = $state(data.appConfig);
|
||||||
|
|
||||||
|
const uiConfigDisabled = env.PUBLIC_UI_CONFIG_DISABLED === 'true';
|
||||||
const appConfigService = new AppConfigService();
|
const appConfigService = new AppConfigService();
|
||||||
|
|
||||||
async function updateAppConfig(updatedAppConfig: Partial<AllAppConfig>) {
|
async function updateAppConfig(updatedAppConfig: Partial<AllAppConfig>) {
|
||||||
@@ -55,26 +57,28 @@
|
|||||||
<title>Application Configuration</title>
|
<title>Application Configuration</title>
|
||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<CollapsibleCard id="application-configuration-general" title="General" defaultExpanded>
|
<fieldset class="flex flex-col gap-5" disabled={uiConfigDisabled}>
|
||||||
<AppConfigGeneralForm {appConfig} callback={updateAppConfig} />
|
<CollapsibleCard id="application-configuration-general" title="General" defaultExpanded>
|
||||||
</CollapsibleCard>
|
<AppConfigGeneralForm {appConfig} callback={updateAppConfig} />
|
||||||
|
</CollapsibleCard>
|
||||||
|
|
||||||
<CollapsibleCard
|
<CollapsibleCard
|
||||||
id="application-configuration-email"
|
id="application-configuration-email"
|
||||||
title="Email"
|
title="Email"
|
||||||
description="Enable email notifications to alert users when a login is detected from a new device or
|
description="Enable email notifications to alert users when a login is detected from a new device or
|
||||||
location."
|
location."
|
||||||
>
|
>
|
||||||
<AppConfigEmailForm {appConfig} callback={updateAppConfig} />
|
<AppConfigEmailForm {appConfig} callback={updateAppConfig} />
|
||||||
</CollapsibleCard>
|
</CollapsibleCard>
|
||||||
|
|
||||||
<CollapsibleCard
|
<CollapsibleCard
|
||||||
id="application-configuration-ldap"
|
id="application-configuration-ldap"
|
||||||
title="LDAP"
|
title="LDAP"
|
||||||
description="Configure LDAP settings to sync users and groups from an LDAP server."
|
description="Configure LDAP settings to sync users and groups from an LDAP server."
|
||||||
>
|
>
|
||||||
<AppConfigLdapForm {appConfig} callback={updateAppConfig} />
|
<AppConfigLdapForm {appConfig} callback={updateAppConfig} />
|
||||||
</CollapsibleCard>
|
</CollapsibleCard>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<CollapsibleCard id="application-configuration-images" title="Images">
|
<CollapsibleCard id="application-configuration-images" title="Images">
|
||||||
<UpdateApplicationImages callback={updateImages} />
|
<UpdateApplicationImages callback={updateImages} />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import AdvancedTable from '$lib/components/advanced-table.svelte';
|
import AdvancedTable from '$lib/components/advanced-table.svelte';
|
||||||
import { openConfirmDialog } from '$lib/components/confirm-dialog/';
|
import { openConfirmDialog } from '$lib/components/confirm-dialog/';
|
||||||
|
import { Badge } from '$lib/components/ui/badge/index';
|
||||||
import { Button } from '$lib/components/ui/button';
|
import { Button } from '$lib/components/ui/button';
|
||||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||||
import * as Table from '$lib/components/ui/table';
|
import * as Table from '$lib/components/ui/table';
|
||||||
@@ -35,7 +36,7 @@
|
|||||||
toast.success('User group deleted successfully');
|
toast.success('User group deleted successfully');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
axiosErrorToast(e);
|
axiosErrorToast(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -50,6 +51,7 @@
|
|||||||
{ label: 'Friendly Name', sortColumn: 'friendlyName' },
|
{ label: 'Friendly Name', sortColumn: 'friendlyName' },
|
||||||
{ label: 'Name', sortColumn: 'name' },
|
{ label: 'Name', sortColumn: 'name' },
|
||||||
{ label: 'User Count', sortColumn: 'userCount' },
|
{ label: 'User Count', sortColumn: 'userCount' },
|
||||||
|
...($appConfigStore.ldapEnabled ? [{ label: 'Source' }] : []),
|
||||||
{ label: 'Actions', hidden: true }
|
{ label: 'Actions', hidden: true }
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
@@ -57,6 +59,12 @@
|
|||||||
<Table.Cell>{item.friendlyName}</Table.Cell>
|
<Table.Cell>{item.friendlyName}</Table.Cell>
|
||||||
<Table.Cell>{item.name}</Table.Cell>
|
<Table.Cell>{item.name}</Table.Cell>
|
||||||
<Table.Cell>{item.userCount}</Table.Cell>
|
<Table.Cell>{item.userCount}</Table.Cell>
|
||||||
|
{#if $appConfigStore.ldapEnabled}
|
||||||
|
<Table.Cell>
|
||||||
|
<Badge variant={item.ldapId ? 'default' : 'outline'}>{item.ldapId ? 'LDAP' : 'Local'}</Badge
|
||||||
|
>
|
||||||
|
</Table.Cell>
|
||||||
|
{/if}
|
||||||
<Table.Cell class="flex justify-end">
|
<Table.Cell class="flex justify-end">
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger asChild let:builder>
|
<DropdownMenu.Trigger asChild let:builder>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu';
|
||||||
import * as Table from '$lib/components/ui/table';
|
import * as Table from '$lib/components/ui/table';
|
||||||
import UserService from '$lib/services/user-service';
|
import UserService from '$lib/services/user-service';
|
||||||
|
import appConfigStore from '$lib/stores/application-configuration-store';
|
||||||
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
import type { Paginated, SearchPaginationSortRequest } from '$lib/types/pagination.type';
|
||||||
import type { User } from '$lib/types/user.type';
|
import type { User } from '$lib/types/user.type';
|
||||||
import { axiosErrorToast } from '$lib/utils/error-util';
|
import { axiosErrorToast } from '$lib/utils/error-util';
|
||||||
@@ -14,7 +15,6 @@
|
|||||||
import Ellipsis from 'lucide-svelte/icons/ellipsis';
|
import Ellipsis from 'lucide-svelte/icons/ellipsis';
|
||||||
import { toast } from 'svelte-sonner';
|
import { toast } from 'svelte-sonner';
|
||||||
import OneTimeLinkModal from './one-time-link-modal.svelte';
|
import OneTimeLinkModal from './one-time-link-modal.svelte';
|
||||||
import appConfigStore from '$lib/stores/application-configuration-store';
|
|
||||||
|
|
||||||
let { users = $bindable() }: { users: Paginated<User> } = $props();
|
let { users = $bindable() }: { users: Paginated<User> } = $props();
|
||||||
let requestOptions: SearchPaginationSortRequest | undefined = $state();
|
let requestOptions: SearchPaginationSortRequest | undefined = $state();
|
||||||
@@ -49,30 +49,13 @@
|
|||||||
{requestOptions}
|
{requestOptions}
|
||||||
onRefresh={async (options) => (users = await userService.list(options))}
|
onRefresh={async (options) => (users = await userService.list(options))}
|
||||||
columns={[
|
columns={[
|
||||||
{
|
{ label: 'First name', sortColumn: 'firstName' },
|
||||||
label: 'First name',
|
{ label: 'Last name', sortColumn: 'lastName' },
|
||||||
sortColumn: 'firstName'
|
{ label: 'Email', sortColumn: 'email' },
|
||||||
},
|
{ label: 'Username', sortColumn: 'username' },
|
||||||
{
|
{ label: 'Role', sortColumn: 'isAdmin' },
|
||||||
label: 'Last name',
|
...($appConfigStore.ldapEnabled ? [{ label: 'Source' }] : []),
|
||||||
sortColumn: 'lastName'
|
{ label: 'Actions', hidden: true }
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Email',
|
|
||||||
sortColumn: 'email'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Username',
|
|
||||||
sortColumn: 'username'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Role',
|
|
||||||
sortColumn: 'isAdmin'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Actions',
|
|
||||||
hidden: true
|
|
||||||
}
|
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
{#snippet rows({ item })}
|
{#snippet rows({ item })}
|
||||||
@@ -80,9 +63,15 @@
|
|||||||
<Table.Cell>{item.lastName}</Table.Cell>
|
<Table.Cell>{item.lastName}</Table.Cell>
|
||||||
<Table.Cell>{item.email}</Table.Cell>
|
<Table.Cell>{item.email}</Table.Cell>
|
||||||
<Table.Cell>{item.username}</Table.Cell>
|
<Table.Cell>{item.username}</Table.Cell>
|
||||||
<Table.Cell class="hidden lg:table-cell">
|
<Table.Cell>
|
||||||
<Badge variant="outline">{item.isAdmin ? 'Admin' : 'User'}</Badge>
|
<Badge variant="outline">{item.isAdmin ? 'Admin' : 'User'}</Badge>
|
||||||
</Table.Cell>
|
</Table.Cell>
|
||||||
|
{#if $appConfigStore.ldapEnabled}
|
||||||
|
<Table.Cell>
|
||||||
|
<Badge variant={item.ldapId ? 'default' : 'outline'}>{item.ldapId ? 'LDAP' : 'Local'}</Badge
|
||||||
|
>
|
||||||
|
</Table.Cell>
|
||||||
|
{/if}
|
||||||
<Table.Cell>
|
<Table.Cell>
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger class={buttonVariants({ variant: 'ghost', size: 'icon' })}>
|
<DropdownMenu.Trigger class={buttonVariants({ variant: 'ghost', size: 'icon' })}>
|
||||||
|
|||||||
Reference in New Issue
Block a user