@nymphjs/tilmeld-setup 1.0.0-beta.120 → 1.0.0-beta.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +4 -2
- package/app/src/App.svelte +28 -66
- package/app/src/routes/GroupEdit.svelte +26 -38
- package/app/src/routes/Groups.svelte +24 -22
- package/app/src/routes/UserEdit.svelte +35 -44
- package/app/src/routes/Users.svelte +24 -22
- package/dist/app/index.js +994 -383
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/package.json +16 -13
- package/src/index.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# [1.0.0-beta.122](https://forge.sciactive.com/sciactive/nymphjs/compare/v1.0.0-beta.121...v1.0.0-beta.122) (2026-09-22)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
- save and delete now throw on errors instead of returning false, refresh throws on db errors ([536db88](https://forge.sciactive.com/sciactive/nymphjs/commits/536db8804ac515cd3e1e9c30b104f674e267916a))
|
|
11
|
+
|
|
12
|
+
# [1.0.0-beta.121](https://forge.sciactive.com/sciactive/nymphjs/compare/v1.0.0-beta.120...v1.0.0-beta.121) (2026-09-17)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package @nymphjs/tilmeld-setup
|
|
15
|
+
|
|
6
16
|
# [1.0.0-beta.120](https://forge.sciactive.com/sciactive/nymphjs/compare/v1.0.0-beta.119...v1.0.0-beta.120) (2026-09-01)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @nymphjs/tilmeld-setup
|
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Tilmeld Setup App - User/Group System for Nymph
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Application Data and Access Framework for Node.js and the browser.
|
|
4
|
+
|
|
5
|
+
https://nymph.io/
|
|
4
6
|
|
|
5
7
|
The Tilmeld Setup App allows administrators to create, modify, and delete users and groups and configure how Tilmeld works. It also acts as the endpoint for email address verification.
|
|
6
8
|
|
|
@@ -73,7 +75,7 @@ app.listen(80);
|
|
|
73
75
|
|
|
74
76
|
# License
|
|
75
77
|
|
|
76
|
-
Copyright 2021-
|
|
78
|
+
Copyright 2021-2026 SciActive Inc
|
|
77
79
|
|
|
78
80
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
79
81
|
you may not use this file except in compliance with the License.
|
package/app/src/App.svelte
CHANGED
|
@@ -73,13 +73,12 @@
|
|
|
73
73
|
<Item
|
|
74
74
|
bind:this={section.component}
|
|
75
75
|
tag="a"
|
|
76
|
+
use={[link]}
|
|
76
77
|
nonInteractive={!('href' in section)}
|
|
77
|
-
href={'href' in section ?
|
|
78
|
+
href={'href' in section ? section.href : undefined}
|
|
78
79
|
activated={section.absolute
|
|
79
|
-
? `${
|
|
80
|
-
: `${
|
|
81
|
-
section.href ?? '!',
|
|
82
|
-
)}
|
|
80
|
+
? `${currentRoute}` === section.href
|
|
81
|
+
: `${currentRoute}/`.startsWith(section.href ?? '!')}
|
|
83
82
|
style={section.indent
|
|
84
83
|
? 'margin-left: ' + section.indent * 25 + 'px;'
|
|
85
84
|
: ''}
|
|
@@ -108,7 +107,7 @@
|
|
|
108
107
|
<main class="tilmeld-main-content" bind:this={mainContent}>
|
|
109
108
|
{#if tilmeldAdmin}
|
|
110
109
|
{#if $clientConfig}
|
|
111
|
-
<
|
|
110
|
+
<Router {routes} {onRouteLoaded} />
|
|
112
111
|
{:else}
|
|
113
112
|
Loading...
|
|
114
113
|
{/if}
|
|
@@ -132,10 +131,10 @@
|
|
|
132
131
|
|
|
133
132
|
<script lang="ts">
|
|
134
133
|
import type { Component } from 'svelte';
|
|
135
|
-
import { onMount } from 'svelte';
|
|
134
|
+
import { onMount, setContext } from 'svelte';
|
|
136
135
|
import type { Writable } from 'svelte/store';
|
|
137
136
|
import { writable } from 'svelte/store';
|
|
138
|
-
import
|
|
137
|
+
import Router, { link, type RouteDetailLoaded } from 'svelte-spa-router';
|
|
139
138
|
import type {
|
|
140
139
|
User as UserClass,
|
|
141
140
|
ClientConfig,
|
|
@@ -150,7 +149,7 @@
|
|
|
150
149
|
import Menu from '@smui/menu';
|
|
151
150
|
import { Icon } from '@smui/common';
|
|
152
151
|
|
|
153
|
-
import { User } from './nymph';
|
|
152
|
+
import { User } from './nymph.js';
|
|
154
153
|
import Intro from './routes/Intro.svelte';
|
|
155
154
|
import Users from './routes/Users.svelte';
|
|
156
155
|
import UserEdit from './routes/UserEdit.svelte';
|
|
@@ -172,6 +171,9 @@
|
|
|
172
171
|
let tilmeldAdmin: boolean | undefined = $state();
|
|
173
172
|
let accountOpen = $state(false);
|
|
174
173
|
|
|
174
|
+
setContext('clientConfigStore', clientConfig);
|
|
175
|
+
setContext('userStore', user);
|
|
176
|
+
|
|
175
177
|
$effect(() => {
|
|
176
178
|
if ($user) {
|
|
177
179
|
$user
|
|
@@ -184,65 +186,25 @@
|
|
|
184
186
|
}
|
|
185
187
|
});
|
|
186
188
|
|
|
187
|
-
|
|
188
|
-
let CurrentRoute: Component<any> = $state(Intro);
|
|
189
|
-
let params: any = $state({});
|
|
190
|
-
|
|
191
|
-
router.hooks({
|
|
192
|
-
before(done, match) {
|
|
193
|
-
if (mainContent) {
|
|
194
|
-
drawerOpen = false;
|
|
195
|
-
mainContent.scrollTop = 0;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
currentMatch = match;
|
|
199
|
-
|
|
200
|
-
done();
|
|
201
|
-
},
|
|
202
|
-
});
|
|
189
|
+
let currentRoute = $state('/');
|
|
203
190
|
|
|
204
|
-
|
|
205
|
-
'/':
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
'/
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
},
|
|
213
|
-
'/users/': () => {
|
|
214
|
-
CurrentRoute = Users;
|
|
215
|
-
params = {};
|
|
216
|
-
},
|
|
217
|
-
'/users/:query?': ({ data }: any) => {
|
|
218
|
-
CurrentRoute = Users;
|
|
219
|
-
params = data;
|
|
220
|
-
},
|
|
221
|
-
'/groups/edit/:guid': ({ data }: any) => {
|
|
222
|
-
CurrentRoute = GroupEdit;
|
|
223
|
-
params = data;
|
|
224
|
-
},
|
|
225
|
-
'/groups/': () => {
|
|
226
|
-
CurrentRoute = Groups;
|
|
227
|
-
params = {};
|
|
228
|
-
},
|
|
229
|
-
'/groups/:query?': ({ data }: any) => {
|
|
230
|
-
CurrentRoute = Groups;
|
|
231
|
-
params = data;
|
|
232
|
-
},
|
|
233
|
-
});
|
|
191
|
+
const routes = {
|
|
192
|
+
'/': Intro,
|
|
193
|
+
'/users/edit/:guid': UserEdit,
|
|
194
|
+
'/users/:query?': Users,
|
|
195
|
+
'/groups/edit/:guid': GroupEdit,
|
|
196
|
+
'/groups/:query?': Groups,
|
|
197
|
+
'*': NotFound,
|
|
198
|
+
};
|
|
234
199
|
|
|
235
|
-
|
|
236
|
-
if (
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
CurrentRoute = NotFound;
|
|
240
|
-
params = {};
|
|
200
|
+
function onRouteLoaded(detail: RouteDetailLoaded) {
|
|
201
|
+
if (mainContent) {
|
|
202
|
+
drawerOpen = false;
|
|
203
|
+
mainContent.scrollTop = 0;
|
|
241
204
|
}
|
|
242
|
-
});
|
|
243
205
|
|
|
244
|
-
|
|
245
|
-
|
|
206
|
+
currentRoute = `${detail?.route ?? '/'}`;
|
|
207
|
+
}
|
|
246
208
|
|
|
247
209
|
const sections: (
|
|
248
210
|
| {
|
|
@@ -266,12 +228,12 @@
|
|
|
266
228
|
},
|
|
267
229
|
{
|
|
268
230
|
name: 'Users',
|
|
269
|
-
href: 'users/',
|
|
231
|
+
href: '/users/',
|
|
270
232
|
indent: 0,
|
|
271
233
|
},
|
|
272
234
|
{
|
|
273
235
|
name: 'Groups',
|
|
274
|
-
href: 'groups/',
|
|
236
|
+
href: '/groups/',
|
|
275
237
|
indent: 0,
|
|
276
238
|
},
|
|
277
239
|
];
|
|
@@ -6,10 +6,7 @@
|
|
|
6
6
|
</section>
|
|
7
7
|
{:else}
|
|
8
8
|
<div style="display: flex; align-items: center; padding: 12px;">
|
|
9
|
-
<IconButton
|
|
10
|
-
title="Back"
|
|
11
|
-
onclick={() => router.navigate('', { historyAPIMethod: 'back' })}
|
|
12
|
-
>
|
|
9
|
+
<IconButton title="Back" onclick={() => pop()}>
|
|
13
10
|
<Icon tag="svg" viewBox="0 0 24 24">
|
|
14
11
|
<path fill="currentColor" d={mdiArrowLeft} />
|
|
15
12
|
</Icon>
|
|
@@ -23,7 +20,8 @@
|
|
|
23
20
|
{#await $entity.user.$wake() then _user}
|
|
24
21
|
<div style="padding: 12px;" class="mdc-typography--subtitle1">
|
|
25
22
|
Generated primary group for <a
|
|
26
|
-
href="
|
|
23
|
+
href="/users/edit/{encodeURIComponent($entity.user.guid || '')}"
|
|
24
|
+
use:link
|
|
27
25
|
>{$clientConfig.userFields.includes('name')
|
|
28
26
|
? $entity.user.name + ' (' + $entity.user.username + ')'
|
|
29
27
|
: $entity.user.username}</a
|
|
@@ -194,7 +192,8 @@
|
|
|
194
192
|
No parent
|
|
195
193
|
{:else}
|
|
196
194
|
<a
|
|
197
|
-
href="
|
|
195
|
+
href="/groups/edit/{encodeURIComponent($entity.parent.guid || '')}"
|
|
196
|
+
use:link
|
|
198
197
|
>{$clientConfig.userFields.includes('name')
|
|
199
198
|
? $entity.parent.name + ' (' + $entity.parent.groupname + ')'
|
|
200
199
|
: $entity.parent.groupname}</a
|
|
@@ -357,7 +356,8 @@
|
|
|
357
356
|
<script lang="ts">
|
|
358
357
|
import type { Writable } from 'svelte/store';
|
|
359
358
|
import { writable } from 'svelte/store';
|
|
360
|
-
import
|
|
359
|
+
import { getContext } from 'svelte';
|
|
360
|
+
import { link, pop, replace } from 'svelte-spa-router';
|
|
361
361
|
import type {
|
|
362
362
|
AdminGroupData,
|
|
363
363
|
ClientConfig,
|
|
@@ -390,19 +390,9 @@
|
|
|
390
390
|
import Button from '@smui/button';
|
|
391
391
|
import { Icon, Label } from '@smui/common';
|
|
392
392
|
|
|
393
|
-
import { nymph, Group, User } from '../nymph';
|
|
393
|
+
import { nymph, Group, User } from '../nymph.js';
|
|
394
394
|
|
|
395
|
-
let {
|
|
396
|
-
router,
|
|
397
|
-
params,
|
|
398
|
-
clientConfig,
|
|
399
|
-
user,
|
|
400
|
-
}: {
|
|
401
|
-
router: Navigo;
|
|
402
|
-
params: { guid: string };
|
|
403
|
-
clientConfig: Writable<ClientConfig | undefined>;
|
|
404
|
-
user: Writable<(UserClass & CurrentUserData) | null | undefined>;
|
|
405
|
-
} = $props();
|
|
395
|
+
let { params }: { params: { guid: string } } = $props();
|
|
406
396
|
|
|
407
397
|
let entity: Writable<GroupClass & AdminGroupData> = writable(
|
|
408
398
|
Group.factorySync(),
|
|
@@ -422,6 +412,13 @@
|
|
|
422
412
|
let success: boolean | undefined = $state();
|
|
423
413
|
let loading = $state(true);
|
|
424
414
|
|
|
415
|
+
const clientConfig =
|
|
416
|
+
getContext<Writable<ClientConfig | undefined>>('clientConfigStore');
|
|
417
|
+
const user =
|
|
418
|
+
getContext<Writable<(UserClass & CurrentUserData) | null | undefined>>(
|
|
419
|
+
'userStore',
|
|
420
|
+
);
|
|
421
|
+
|
|
425
422
|
$effect(() => {
|
|
426
423
|
if (params) {
|
|
427
424
|
handleGuidParam();
|
|
@@ -597,21 +594,15 @@
|
|
|
597
594
|
failureMessage = undefined;
|
|
598
595
|
const newEntity = $entity.guid == null;
|
|
599
596
|
try {
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
`/groups/edit/${encodeURIComponent($entity.guid || '')}`,
|
|
606
|
-
{ historyAPIMethod: 'replaceState' },
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
setTimeout(() => {
|
|
610
|
-
success = undefined;
|
|
611
|
-
}, 1000);
|
|
612
|
-
} else {
|
|
613
|
-
failureMessage = 'Error saving group.';
|
|
597
|
+
await $entity.$save();
|
|
598
|
+
await readyEntity();
|
|
599
|
+
success = true;
|
|
600
|
+
if (newEntity) {
|
|
601
|
+
replace(`/groups/edit/${encodeURIComponent($entity.guid || '')}`);
|
|
614
602
|
}
|
|
603
|
+
setTimeout(() => {
|
|
604
|
+
success = undefined;
|
|
605
|
+
}, 1000);
|
|
615
606
|
} catch (e: any) {
|
|
616
607
|
console.log('error:', e);
|
|
617
608
|
failureMessage = e?.message;
|
|
@@ -624,11 +615,8 @@
|
|
|
624
615
|
if (confirm('Are you sure you want to delete this?')) {
|
|
625
616
|
saving = true;
|
|
626
617
|
try {
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
} else {
|
|
630
|
-
failureMessage = 'An error occurred.';
|
|
631
|
-
}
|
|
618
|
+
await $entity.$delete();
|
|
619
|
+
pop();
|
|
632
620
|
} catch (e: any) {
|
|
633
621
|
failureMessage = e?.message;
|
|
634
622
|
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
{:else}
|
|
8
8
|
<div class="solo-search-container solo-container">
|
|
9
9
|
<Fab
|
|
10
|
-
href="
|
|
10
|
+
href="/groups/edit/+"
|
|
11
|
+
use={[link]}
|
|
11
12
|
color="primary"
|
|
12
13
|
mini
|
|
13
14
|
class="solo-fab"
|
|
@@ -74,27 +75,30 @@
|
|
|
74
75
|
{#if !$clientConfig.emailUsernames}
|
|
75
76
|
<Cell
|
|
76
77
|
><a
|
|
77
|
-
href="
|
|
78
|
+
href="/groups/edit/{encodeURIComponent(
|
|
78
79
|
curEntity.guid || '',
|
|
79
|
-
)}"
|
|
80
|
+
)}"
|
|
81
|
+
use:link>{curEntity.groupname}</a
|
|
80
82
|
></Cell
|
|
81
83
|
>
|
|
82
84
|
{/if}
|
|
83
85
|
{#if $clientConfig.userFields.includes('name')}
|
|
84
86
|
<Cell
|
|
85
87
|
><a
|
|
86
|
-
href="
|
|
88
|
+
href="/groups/edit/{encodeURIComponent(
|
|
87
89
|
curEntity.guid || '',
|
|
88
|
-
)}"
|
|
90
|
+
)}"
|
|
91
|
+
use:link>{curEntity.name}</a
|
|
89
92
|
></Cell
|
|
90
93
|
>
|
|
91
94
|
{/if}
|
|
92
95
|
{#if $clientConfig.userFields.includes('email')}
|
|
93
96
|
<Cell
|
|
94
97
|
><a
|
|
95
|
-
href="
|
|
98
|
+
href="/groups/edit/{encodeURIComponent(
|
|
96
99
|
curEntity.guid || '',
|
|
97
|
-
)}"
|
|
100
|
+
)}"
|
|
101
|
+
use:link>{curEntity.email}</a
|
|
98
102
|
></Cell
|
|
99
103
|
>
|
|
100
104
|
{/if}
|
|
@@ -119,7 +123,8 @@
|
|
|
119
123
|
|
|
120
124
|
<script lang="ts">
|
|
121
125
|
import type { Writable } from 'svelte/store';
|
|
122
|
-
import
|
|
126
|
+
import { getContext } from 'svelte';
|
|
127
|
+
import { link, push } from 'svelte-spa-router';
|
|
123
128
|
import queryParser from '@nymphjs/query-parser';
|
|
124
129
|
import type {
|
|
125
130
|
AdminGroupData,
|
|
@@ -138,23 +143,20 @@
|
|
|
138
143
|
import Fab from '@smui/fab';
|
|
139
144
|
import { Icon } from '@smui/common';
|
|
140
145
|
|
|
141
|
-
import { nymph, Group, User } from '../nymph';
|
|
146
|
+
import { nymph, Group, User } from '../nymph.js';
|
|
142
147
|
|
|
143
|
-
let {
|
|
144
|
-
router,
|
|
145
|
-
params,
|
|
146
|
-
clientConfig,
|
|
147
|
-
user,
|
|
148
|
-
}: {
|
|
149
|
-
router: Navigo;
|
|
150
|
-
params: { query?: string };
|
|
151
|
-
clientConfig: Writable<ClientConfig | undefined>;
|
|
152
|
-
user: Writable<(UserClass & CurrentUserData) | null | undefined>;
|
|
153
|
-
} = $props();
|
|
148
|
+
let { params }: { params: { query?: string } } = $props();
|
|
154
149
|
|
|
155
|
-
let entitySearch = $
|
|
150
|
+
let entitySearch = $derived(params.query ?? '');
|
|
156
151
|
let failureMessage: string | undefined = $state();
|
|
157
152
|
|
|
153
|
+
const clientConfig =
|
|
154
|
+
getContext<Writable<ClientConfig | undefined>>('clientConfigStore');
|
|
155
|
+
const user =
|
|
156
|
+
getContext<Writable<(UserClass & CurrentUserData) | null | undefined>>(
|
|
157
|
+
'userStore',
|
|
158
|
+
);
|
|
159
|
+
|
|
158
160
|
$effect(() => {
|
|
159
161
|
if (params) {
|
|
160
162
|
handleSearchParam();
|
|
@@ -192,7 +194,7 @@
|
|
|
192
194
|
let entitiesSearching = $state(false);
|
|
193
195
|
let entities: (GroupClass & AdminGroupData)[] | undefined = $state();
|
|
194
196
|
async function searchEntities() {
|
|
195
|
-
|
|
197
|
+
push(`/groups/${encodeURIComponent(entitySearch)}`);
|
|
196
198
|
}
|
|
197
199
|
function entitySearchKeyDown(event: CustomEvent | KeyboardEvent) {
|
|
198
200
|
event = event as KeyboardEvent;
|
|
@@ -6,10 +6,7 @@
|
|
|
6
6
|
</section>
|
|
7
7
|
{:else}
|
|
8
8
|
<div style="display: flex; align-items: center; padding: 12px;">
|
|
9
|
-
<IconButton
|
|
10
|
-
title="Back"
|
|
11
|
-
onclick={() => router.navigate('', { historyAPIMethod: 'back' })}
|
|
12
|
-
>
|
|
9
|
+
<IconButton title="Back" onclick={() => pop()}>
|
|
13
10
|
<Icon tag="svg" viewBox="0 0 24 24">
|
|
14
11
|
<path fill="currentColor" d={mdiArrowLeft} />
|
|
15
12
|
</Icon>
|
|
@@ -191,7 +188,9 @@
|
|
|
191
188
|
{#if !$entity.group}
|
|
192
189
|
No primary group
|
|
193
190
|
{:else}
|
|
194
|
-
<a
|
|
191
|
+
<a
|
|
192
|
+
href="/groups/edit/{encodeURIComponent($entity.group.guid || '')}"
|
|
193
|
+
use:link
|
|
195
194
|
>{$clientConfig.userFields.includes('name')
|
|
196
195
|
? $entity.group.name + ' (' + $entity.group.groupname + ')'
|
|
197
196
|
: $entity.group.groupname}</a
|
|
@@ -308,27 +307,30 @@
|
|
|
308
307
|
{#if !$clientConfig.emailUsernames}
|
|
309
308
|
<Cell
|
|
310
309
|
><a
|
|
311
|
-
href="
|
|
310
|
+
href="/groups/edit/{encodeURIComponent(
|
|
312
311
|
curEntity.guid || '',
|
|
313
|
-
)}"
|
|
312
|
+
)}"
|
|
313
|
+
use:link>{curEntity.groupname}</a
|
|
314
314
|
></Cell
|
|
315
315
|
>
|
|
316
316
|
{/if}
|
|
317
317
|
{#if $clientConfig.userFields.includes('name')}
|
|
318
318
|
<Cell
|
|
319
319
|
><a
|
|
320
|
-
href="
|
|
320
|
+
href="/groups/edit/{encodeURIComponent(
|
|
321
321
|
curEntity.guid || '',
|
|
322
|
-
)}"
|
|
322
|
+
)}"
|
|
323
|
+
use:link>{curEntity.name}</a
|
|
323
324
|
></Cell
|
|
324
325
|
>
|
|
325
326
|
{/if}
|
|
326
327
|
{#if $clientConfig.userFields.includes('email')}
|
|
327
328
|
<Cell
|
|
328
329
|
><a
|
|
329
|
-
href="
|
|
330
|
+
href="/groups/edit/{encodeURIComponent(
|
|
330
331
|
curEntity.guid || '',
|
|
331
|
-
)}"
|
|
332
|
+
)}"
|
|
333
|
+
use:link>{curEntity.email}</a
|
|
332
334
|
></Cell
|
|
333
335
|
>
|
|
334
336
|
{/if}
|
|
@@ -763,7 +765,8 @@
|
|
|
763
765
|
import { onMount } from 'svelte';
|
|
764
766
|
import type { Writable } from 'svelte/store';
|
|
765
767
|
import { writable } from 'svelte/store';
|
|
766
|
-
import
|
|
768
|
+
import { getContext } from 'svelte';
|
|
769
|
+
import { link, pop, replace } from 'svelte-spa-router';
|
|
767
770
|
import type {
|
|
768
771
|
AdminGroupData,
|
|
769
772
|
AdminUserData,
|
|
@@ -798,19 +801,9 @@
|
|
|
798
801
|
import Dialog, { Title, Content, Actions } from '@smui/dialog';
|
|
799
802
|
import { Icon, Label } from '@smui/common';
|
|
800
803
|
|
|
801
|
-
import { User, Group } from '../nymph';
|
|
804
|
+
import { User, Group } from '../nymph.js';
|
|
802
805
|
|
|
803
|
-
let {
|
|
804
|
-
router,
|
|
805
|
-
params,
|
|
806
|
-
clientConfig,
|
|
807
|
-
user,
|
|
808
|
-
}: {
|
|
809
|
-
router: Navigo;
|
|
810
|
-
params: { guid: string };
|
|
811
|
-
clientConfig: Writable<ClientConfig | undefined>;
|
|
812
|
-
user: Writable<(UserClass & CurrentUserData) | null | undefined>;
|
|
813
|
-
} = $props();
|
|
806
|
+
let { params }: { params: { guid: string } } = $props();
|
|
814
807
|
|
|
815
808
|
let entity: Writable<UserClass & AdminUserData> = writable(
|
|
816
809
|
User.factorySync(),
|
|
@@ -838,6 +831,13 @@
|
|
|
838
831
|
let success: boolean | undefined = $state();
|
|
839
832
|
let loading = $state(true);
|
|
840
833
|
|
|
834
|
+
const clientConfig =
|
|
835
|
+
getContext<Writable<ClientConfig | undefined>>('clientConfigStore');
|
|
836
|
+
const user =
|
|
837
|
+
getContext<Writable<(UserClass & CurrentUserData) | null | undefined>>(
|
|
838
|
+
'userStore',
|
|
839
|
+
);
|
|
840
|
+
|
|
841
841
|
$effect(() => {
|
|
842
842
|
if (params) {
|
|
843
843
|
handleGuidParam();
|
|
@@ -1138,22 +1138,16 @@
|
|
|
1138
1138
|
failureMessage = undefined;
|
|
1139
1139
|
const newEntity = $entity.guid == null;
|
|
1140
1140
|
try {
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
`/users/edit/${encodeURIComponent($entity.guid || '')}`,
|
|
1148
|
-
{ historyAPIMethod: 'replaceState' },
|
|
1149
|
-
);
|
|
1150
|
-
}
|
|
1151
|
-
setTimeout(() => {
|
|
1152
|
-
success = undefined;
|
|
1153
|
-
}, 1000);
|
|
1154
|
-
} else {
|
|
1155
|
-
failureMessage = 'Error saving user.';
|
|
1141
|
+
await $entity.$save();
|
|
1142
|
+
await readyEntity();
|
|
1143
|
+
success = true;
|
|
1144
|
+
passwordVerify = '';
|
|
1145
|
+
if (newEntity) {
|
|
1146
|
+
replace(`/users/edit/${encodeURIComponent($entity.guid || '')}`);
|
|
1156
1147
|
}
|
|
1148
|
+
setTimeout(() => {
|
|
1149
|
+
success = undefined;
|
|
1150
|
+
}, 1000);
|
|
1157
1151
|
} catch (e: any) {
|
|
1158
1152
|
console.log('error:', e);
|
|
1159
1153
|
failureMessage = e?.message;
|
|
@@ -1166,11 +1160,8 @@
|
|
|
1166
1160
|
if (confirm('Are you sure you want to delete this?')) {
|
|
1167
1161
|
saving = true;
|
|
1168
1162
|
try {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
} else {
|
|
1172
|
-
failureMessage = 'An error occurred.';
|
|
1173
|
-
}
|
|
1163
|
+
await $entity.$delete();
|
|
1164
|
+
pop();
|
|
1174
1165
|
} catch (e: any) {
|
|
1175
1166
|
failureMessage = e?.message;
|
|
1176
1167
|
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
{:else}
|
|
8
8
|
<div class="solo-search-container solo-container">
|
|
9
9
|
<Fab
|
|
10
|
-
href="
|
|
10
|
+
href="/users/edit/+"
|
|
11
|
+
use={[link]}
|
|
11
12
|
color="primary"
|
|
12
13
|
mini
|
|
13
14
|
class="solo-fab"
|
|
@@ -74,27 +75,30 @@
|
|
|
74
75
|
{#if !$clientConfig.emailUsernames}
|
|
75
76
|
<Cell
|
|
76
77
|
><a
|
|
77
|
-
href="
|
|
78
|
+
href="/users/edit/{encodeURIComponent(
|
|
78
79
|
curEntity.guid || '',
|
|
79
|
-
)}"
|
|
80
|
+
)}"
|
|
81
|
+
use:link>{curEntity.username}</a
|
|
80
82
|
></Cell
|
|
81
83
|
>
|
|
82
84
|
{/if}
|
|
83
85
|
{#if $clientConfig.userFields.includes('name')}
|
|
84
86
|
<Cell
|
|
85
87
|
><a
|
|
86
|
-
href="
|
|
88
|
+
href="/users/edit/{encodeURIComponent(
|
|
87
89
|
curEntity.guid || '',
|
|
88
|
-
)}"
|
|
90
|
+
)}"
|
|
91
|
+
use:link>{curEntity.name}</a
|
|
89
92
|
></Cell
|
|
90
93
|
>
|
|
91
94
|
{/if}
|
|
92
95
|
{#if $clientConfig.userFields.includes('email')}
|
|
93
96
|
<Cell
|
|
94
97
|
><a
|
|
95
|
-
href="
|
|
98
|
+
href="/users/edit/{encodeURIComponent(
|
|
96
99
|
curEntity.guid || '',
|
|
97
|
-
)}"
|
|
100
|
+
)}"
|
|
101
|
+
use:link>{curEntity.email}</a
|
|
98
102
|
></Cell
|
|
99
103
|
>
|
|
100
104
|
{/if}
|
|
@@ -119,7 +123,8 @@
|
|
|
119
123
|
|
|
120
124
|
<script lang="ts">
|
|
121
125
|
import type { Writable } from 'svelte/store';
|
|
122
|
-
import
|
|
126
|
+
import { getContext } from 'svelte';
|
|
127
|
+
import { link, push } from 'svelte-spa-router';
|
|
123
128
|
import queryParser from '@nymphjs/query-parser';
|
|
124
129
|
import type {
|
|
125
130
|
AdminUserData,
|
|
@@ -138,23 +143,20 @@
|
|
|
138
143
|
import Fab from '@smui/fab';
|
|
139
144
|
import { Icon } from '@smui/common';
|
|
140
145
|
|
|
141
|
-
import { nymph, User, Group } from '../nymph';
|
|
146
|
+
import { nymph, User, Group } from '../nymph.js';
|
|
142
147
|
|
|
143
|
-
let {
|
|
144
|
-
router,
|
|
145
|
-
params,
|
|
146
|
-
clientConfig,
|
|
147
|
-
user,
|
|
148
|
-
}: {
|
|
149
|
-
router: Navigo;
|
|
150
|
-
params: { query?: string };
|
|
151
|
-
clientConfig: Writable<ClientConfig | undefined>;
|
|
152
|
-
user: Writable<(UserClass & CurrentUserData) | null | undefined>;
|
|
153
|
-
} = $props();
|
|
148
|
+
let { params }: { params: { query?: string } } = $props();
|
|
154
149
|
|
|
155
|
-
let entitySearch = $
|
|
150
|
+
let entitySearch = $derived(params.query ?? '');
|
|
156
151
|
let failureMessage: string | undefined = $state();
|
|
157
152
|
|
|
153
|
+
const clientConfig =
|
|
154
|
+
getContext<Writable<ClientConfig | undefined>>('clientConfigStore');
|
|
155
|
+
const user =
|
|
156
|
+
getContext<Writable<(UserClass & CurrentUserData) | null | undefined>>(
|
|
157
|
+
'userStore',
|
|
158
|
+
);
|
|
159
|
+
|
|
158
160
|
$effect(() => {
|
|
159
161
|
if (params) {
|
|
160
162
|
handleSearchParam();
|
|
@@ -192,7 +194,7 @@
|
|
|
192
194
|
let entitiesSearching = $state(false);
|
|
193
195
|
let entities: (UserClass & AdminUserData)[] | undefined = $state();
|
|
194
196
|
async function searchEntities() {
|
|
195
|
-
|
|
197
|
+
push(`/users/${encodeURIComponent(entitySearch)}`);
|
|
196
198
|
}
|
|
197
199
|
function entitySearchKeyDown(event: CustomEvent | KeyboardEvent) {
|
|
198
200
|
event = event as KeyboardEvent;
|