@nymphjs/tilmeld-setup 1.0.0-beta.11 → 1.0.0-beta.111
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 +442 -0
- package/README.md +6 -5
- package/app/index.ts +9 -3
- package/app/src/App.svelte +143 -71
- package/app/src/nymph.ts +1 -1
- package/app/src/routes/GroupEdit.svelte +638 -0
- package/app/src/routes/Groups.svelte +201 -0
- package/app/src/{Intro.svelte → routes/Intro.svelte} +14 -1
- package/app/src/routes/NotFound.svelte +9 -0
- package/app/src/routes/UserEdit.svelte +1202 -0
- package/app/src/routes/Users.svelte +201 -0
- package/dist/app/index.css +105 -0
- package/dist/app/index.d.ts +1 -0
- package/dist/app/index.js +41288 -3
- package/dist/app/smui.css +1 -29
- package/dist/app/src/nymph.d.ts +6 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +19 -18
- package/dist/index.js.map +1 -1
- package/jest.config.js +11 -2
- package/package.json +58 -54
- package/rollup.config.mjs +36 -0
- package/src/index.ts +28 -21
- package/src/type/locutus.d.ts +3 -0
- package/static/index.html +4 -1
- package/test.mjs +13 -12
- package/tsconfig.json +11 -1
- package/tsconfig.server.json +5 -3
- package/typedoc.json +5 -0
- package/app/src/GroupEdit.svelte +0 -570
- package/app/src/Groups.svelte +0 -159
- package/app/src/UserEdit.svelte +0 -902
- package/app/src/Users.svelte +0 -159
- package/dist/app/index.js.LICENSE.txt +0 -114
- package/dist/app/index.js.map +0 -1
- package/webpack.config.js +0 -38
package/app/src/GroupEdit.svelte
DELETED
|
@@ -1,570 +0,0 @@
|
|
|
1
|
-
<div style="display: flex; align-items: center; padding: 12px;">
|
|
2
|
-
<IconButton title="Back" on:click={() => dispatch('leave')}>
|
|
3
|
-
<Icon component={Svg} viewBox="0 0 24 24">
|
|
4
|
-
<path fill="currentColor" d={mdiArrowLeft} />
|
|
5
|
-
</Icon>
|
|
6
|
-
</IconButton>
|
|
7
|
-
<h2 style="margin: 0px 12px 0px;" class="mdc-typography--headline5">
|
|
8
|
-
Editing {entity.guid ? entity.name : 'New Group'}
|
|
9
|
-
</h2>
|
|
10
|
-
</div>
|
|
11
|
-
{#if entity.user}
|
|
12
|
-
{#await entity.user.$ready() then _user}
|
|
13
|
-
<div style="padding: 12px;" class="mdc-typography--subtitle1">
|
|
14
|
-
Generated primary group for {entity.user.name} ({entity.user.username})
|
|
15
|
-
</div>
|
|
16
|
-
{/await}
|
|
17
|
-
{/if}
|
|
18
|
-
{#if entity != null}
|
|
19
|
-
{#if clientConfig == null || user == null}
|
|
20
|
-
<section style="padding-top: 0;">
|
|
21
|
-
<div style="display: flex; justify-content: center; align-items: center;">
|
|
22
|
-
<CircularProgress style="height: 45px; width: 45px;" indeterminate />
|
|
23
|
-
</div>
|
|
24
|
-
</section>
|
|
25
|
-
{:else}
|
|
26
|
-
<TabBar
|
|
27
|
-
tabs={['General', 'Parent', 'Abilities']}
|
|
28
|
-
let:tab
|
|
29
|
-
bind:active={activeTab}
|
|
30
|
-
>
|
|
31
|
-
<Tab {tab}>
|
|
32
|
-
<Label>{tab}</Label>
|
|
33
|
-
</Tab>
|
|
34
|
-
</TabBar>
|
|
35
|
-
|
|
36
|
-
<section>
|
|
37
|
-
{#if activeTab === 'General'}
|
|
38
|
-
<LayoutGrid style="padding: 0;">
|
|
39
|
-
{#if entity.user != null}
|
|
40
|
-
<LayoutCell span={12}>
|
|
41
|
-
Some of these fields are not editable, since this group inherits
|
|
42
|
-
the values from its user.
|
|
43
|
-
</LayoutCell>
|
|
44
|
-
{/if}
|
|
45
|
-
<LayoutCell span={4}>
|
|
46
|
-
<div class="mdc-typography--headline6">GUID</div>
|
|
47
|
-
<code>{entity.guid}</code>
|
|
48
|
-
</LayoutCell>
|
|
49
|
-
<LayoutCell span={4}>
|
|
50
|
-
<FormField>
|
|
51
|
-
<Checkbox bind:checked={entity.enabled} />
|
|
52
|
-
<span slot="label">Enabled (Able to give abilities)</span>
|
|
53
|
-
</FormField>
|
|
54
|
-
</LayoutCell>
|
|
55
|
-
<LayoutCell span={4} style="text-align: end;">
|
|
56
|
-
<a href="https://en.gravatar.com/" target="_blank" rel="noreferrer">
|
|
57
|
-
<img src={avatar} alt="Avatar" title="Avatar by Gravatar" />
|
|
58
|
-
</a>
|
|
59
|
-
</LayoutCell>
|
|
60
|
-
{#if !clientConfig.emailUsernames}
|
|
61
|
-
<LayoutCell span={6}>
|
|
62
|
-
<Textfield
|
|
63
|
-
bind:value={entity.groupname}
|
|
64
|
-
label="Groupname"
|
|
65
|
-
type="text"
|
|
66
|
-
style="width: 100%;"
|
|
67
|
-
helperLine$style="width: 100%;"
|
|
68
|
-
invalid={groupnameVerified === false}
|
|
69
|
-
input$autocomplete="off"
|
|
70
|
-
input$autocapitalize="off"
|
|
71
|
-
input$spellcheck="false"
|
|
72
|
-
disabled={entity.user != null}
|
|
73
|
-
>
|
|
74
|
-
<HelperText persistent slot="helper">
|
|
75
|
-
{groupnameVerifiedMessage ?? ''}
|
|
76
|
-
</HelperText>
|
|
77
|
-
</Textfield>
|
|
78
|
-
</LayoutCell>
|
|
79
|
-
{/if}
|
|
80
|
-
<LayoutCell span={clientConfig.emailUsernames ? 12 : 6}>
|
|
81
|
-
<Textfield
|
|
82
|
-
bind:value={entity.email}
|
|
83
|
-
label="Email"
|
|
84
|
-
type="email"
|
|
85
|
-
style="width: 100%;"
|
|
86
|
-
helperLine$style="width: 100%;"
|
|
87
|
-
invalid={emailVerified === false}
|
|
88
|
-
input$autocomplete="off"
|
|
89
|
-
input$autocapitalize="off"
|
|
90
|
-
input$spellcheck="false"
|
|
91
|
-
disabled={entity.user != null}
|
|
92
|
-
>
|
|
93
|
-
<HelperText persistent slot="helper">
|
|
94
|
-
{emailVerifiedMessage ?? ''}
|
|
95
|
-
</HelperText>
|
|
96
|
-
</Textfield>
|
|
97
|
-
</LayoutCell>
|
|
98
|
-
<LayoutCell span={12}>
|
|
99
|
-
<Textfield
|
|
100
|
-
bind:value={entity.name}
|
|
101
|
-
label="Display Name"
|
|
102
|
-
type="text"
|
|
103
|
-
style="width: 100%;"
|
|
104
|
-
input$autocomplete="off"
|
|
105
|
-
disabled={entity.user != null}
|
|
106
|
-
/>
|
|
107
|
-
</LayoutCell>
|
|
108
|
-
<LayoutCell span={8}>
|
|
109
|
-
<Textfield
|
|
110
|
-
bind:value={entity.avatar}
|
|
111
|
-
label="Avatar"
|
|
112
|
-
type="text"
|
|
113
|
-
style="width: 100%;"
|
|
114
|
-
input$autocomplete="off"
|
|
115
|
-
disabled={entity.user != null}
|
|
116
|
-
/>
|
|
117
|
-
</LayoutCell>
|
|
118
|
-
<LayoutCell span={4}>
|
|
119
|
-
<Textfield
|
|
120
|
-
bind:value={entity.phone}
|
|
121
|
-
label="Phone"
|
|
122
|
-
type="tel"
|
|
123
|
-
style="width: 100%;"
|
|
124
|
-
input$autocomplete="off"
|
|
125
|
-
disabled={entity.user != null}
|
|
126
|
-
/>
|
|
127
|
-
</LayoutCell>
|
|
128
|
-
<LayoutCell span={12}>
|
|
129
|
-
<FormField>
|
|
130
|
-
<Checkbox bind:checked={entity.defaultPrimary} />
|
|
131
|
-
<span slot="label"
|
|
132
|
-
>Default primary group for newly registered users. <small
|
|
133
|
-
class="form-text text-muted"
|
|
134
|
-
>Setting this will unset any current default primary group.</small
|
|
135
|
-
></span
|
|
136
|
-
>
|
|
137
|
-
</FormField>
|
|
138
|
-
</LayoutCell>
|
|
139
|
-
<LayoutCell span={12}>
|
|
140
|
-
<FormField>
|
|
141
|
-
<Checkbox bind:checked={entity.defaultSecondary} />
|
|
142
|
-
<span slot="label"
|
|
143
|
-
>Default secondary group for newly registered{clientConfig.verifyEmail &&
|
|
144
|
-
clientConfig.unverifiedAccess
|
|
145
|
-
? ', verified'
|
|
146
|
-
: ''} users.</span
|
|
147
|
-
>
|
|
148
|
-
</FormField>
|
|
149
|
-
</LayoutCell>
|
|
150
|
-
{#if clientConfig.verifyEmail && clientConfig.unverifiedAccess}
|
|
151
|
-
<LayoutCell span={12}>
|
|
152
|
-
<FormField>
|
|
153
|
-
<Checkbox bind:checked={entity.unverifiedSecondary} />
|
|
154
|
-
<span slot="label"
|
|
155
|
-
>Default secondary group for newly registered, unverified
|
|
156
|
-
users.</span
|
|
157
|
-
>
|
|
158
|
-
</FormField>
|
|
159
|
-
</LayoutCell>
|
|
160
|
-
{/if}
|
|
161
|
-
</LayoutGrid>
|
|
162
|
-
{/if}
|
|
163
|
-
|
|
164
|
-
{#if activeTab === 'Parent'}
|
|
165
|
-
<h5 style="margin-top: 0;">Parent</h5>
|
|
166
|
-
|
|
167
|
-
<Paper
|
|
168
|
-
style="display: flex; justify-content: space-between; align-items: center;"
|
|
169
|
-
>
|
|
170
|
-
{#if !entity.parent}
|
|
171
|
-
No parent
|
|
172
|
-
{:else}
|
|
173
|
-
<span
|
|
174
|
-
>{entity.parent.name + ' (' + entity.parent.groupname + ')'}</span
|
|
175
|
-
>
|
|
176
|
-
|
|
177
|
-
<IconButton
|
|
178
|
-
on:click={() => {
|
|
179
|
-
delete entity.parent;
|
|
180
|
-
entity = entity;
|
|
181
|
-
}}
|
|
182
|
-
>
|
|
183
|
-
<Icon component={Svg} viewBox="0 0 24 24">
|
|
184
|
-
<path fill="currentColor" d={mdiMinus} />
|
|
185
|
-
</Icon>
|
|
186
|
-
</IconButton>
|
|
187
|
-
{/if}
|
|
188
|
-
</Paper>
|
|
189
|
-
|
|
190
|
-
<h6>Change Parent</h6>
|
|
191
|
-
|
|
192
|
-
<div class="solo-search-container solo-container">
|
|
193
|
-
<Paper class="solo-paper" elevation={1}>
|
|
194
|
-
<Icon class="solo-icon" component={Svg} viewBox="0 0 24 24">
|
|
195
|
-
<path fill="currentColor" d={mdiMagnify} />
|
|
196
|
-
</Icon>
|
|
197
|
-
<Input
|
|
198
|
-
bind:value={parentSearch}
|
|
199
|
-
on:keydown={parentSearchKeyDown}
|
|
200
|
-
placeholder="Parent Search"
|
|
201
|
-
class="solo-input"
|
|
202
|
-
/>
|
|
203
|
-
</Paper>
|
|
204
|
-
<IconButton
|
|
205
|
-
on:click={searchParents}
|
|
206
|
-
disabled={parentSearch === ''}
|
|
207
|
-
class="solo-fab"
|
|
208
|
-
title="Search"
|
|
209
|
-
>
|
|
210
|
-
<Icon component={Svg} viewBox="0 0 24 24">
|
|
211
|
-
<path fill="currentColor" d={mdiArrowRight} />
|
|
212
|
-
</Icon>
|
|
213
|
-
</IconButton>
|
|
214
|
-
</div>
|
|
215
|
-
|
|
216
|
-
{#if parentsSearching}
|
|
217
|
-
<div
|
|
218
|
-
style="display: flex; justify-content: center; align-items: center;"
|
|
219
|
-
>
|
|
220
|
-
<CircularProgress
|
|
221
|
-
style="height: 32px; width: 32px;"
|
|
222
|
-
indeterminate
|
|
223
|
-
/>
|
|
224
|
-
</div>
|
|
225
|
-
{:else if parents != null}
|
|
226
|
-
<DataTable table$aria-label="Parent list" style="width: 100%;">
|
|
227
|
-
<Head>
|
|
228
|
-
<Row>
|
|
229
|
-
{#if !clientConfig.emailUsernames}
|
|
230
|
-
<Cell>Groupname</Cell>
|
|
231
|
-
{/if}
|
|
232
|
-
<Cell>Name</Cell>
|
|
233
|
-
<Cell>Email</Cell>
|
|
234
|
-
<Cell>Enabled</Cell>
|
|
235
|
-
</Row>
|
|
236
|
-
</Head>
|
|
237
|
-
<Body>
|
|
238
|
-
{#each parents as curEntity (curEntity.guid)}
|
|
239
|
-
<Row
|
|
240
|
-
on:click={() => (entity.parent = curEntity)}
|
|
241
|
-
style="cursor: pointer;"
|
|
242
|
-
>
|
|
243
|
-
{#if !clientConfig.emailUsernames}
|
|
244
|
-
<Cell>{curEntity.groupname}</Cell>
|
|
245
|
-
{/if}
|
|
246
|
-
<Cell>{curEntity.name}</Cell>
|
|
247
|
-
<Cell>{curEntity.email}</Cell>
|
|
248
|
-
<Cell>{curEntity.enabled ? 'Yes' : 'No'}</Cell>
|
|
249
|
-
</Row>
|
|
250
|
-
{/each}
|
|
251
|
-
</Body>
|
|
252
|
-
</DataTable>
|
|
253
|
-
{/if}
|
|
254
|
-
{/if}
|
|
255
|
-
|
|
256
|
-
{#if activeTab === 'Abilities'}
|
|
257
|
-
<h5 style="margin-top: 0;">Abilities</h5>
|
|
258
|
-
|
|
259
|
-
<List nonInteractive>
|
|
260
|
-
{#if entity.abilities}
|
|
261
|
-
{#each entity.abilities as ability, index (ability)}
|
|
262
|
-
<Item>
|
|
263
|
-
<Text>
|
|
264
|
-
{ability}
|
|
265
|
-
</Text>
|
|
266
|
-
<Meta>
|
|
267
|
-
<IconButton
|
|
268
|
-
on:click={() => {
|
|
269
|
-
entity.abilities?.splice(index, 1);
|
|
270
|
-
entity = entity;
|
|
271
|
-
}}
|
|
272
|
-
>
|
|
273
|
-
<Icon component={Svg} viewBox="0 0 24 24">
|
|
274
|
-
<path fill="currentColor" d={mdiMinus} />
|
|
275
|
-
</Icon>
|
|
276
|
-
</IconButton>
|
|
277
|
-
</Meta>
|
|
278
|
-
</Item>
|
|
279
|
-
{:else}
|
|
280
|
-
<Item>
|
|
281
|
-
<Text>No abilities</Text>
|
|
282
|
-
</Item>
|
|
283
|
-
{/each}
|
|
284
|
-
{/if}
|
|
285
|
-
</List>
|
|
286
|
-
|
|
287
|
-
<h6>Add Ability</h6>
|
|
288
|
-
|
|
289
|
-
<div style="display: flex; align-items: center; flex-wrap: wrap;">
|
|
290
|
-
<Textfield
|
|
291
|
-
bind:value={ability}
|
|
292
|
-
label="Ability"
|
|
293
|
-
type="text"
|
|
294
|
-
style="width: 250px; max-width: 100%;"
|
|
295
|
-
on:keydown={abilityKeyDown}
|
|
296
|
-
/>
|
|
297
|
-
<IconButton on:click={addAbility}>
|
|
298
|
-
<Icon component={Svg} viewBox="0 0 24 24">
|
|
299
|
-
<path fill="currentColor" d={mdiPlus} />
|
|
300
|
-
</Icon>
|
|
301
|
-
</IconButton>
|
|
302
|
-
</div>
|
|
303
|
-
{/if}
|
|
304
|
-
|
|
305
|
-
{#if failureMessage}
|
|
306
|
-
<div class="tilmeld-failure">
|
|
307
|
-
{failureMessage}
|
|
308
|
-
</div>
|
|
309
|
-
{/if}
|
|
310
|
-
|
|
311
|
-
<div style="margin-top: 36px;">
|
|
312
|
-
<Button variant="raised" on:click={saveEntity} disabled={saving}>
|
|
313
|
-
<Label>Save Group</Label>
|
|
314
|
-
</Button>
|
|
315
|
-
{#if entity.guid}
|
|
316
|
-
<Button on:click={deleteEntity} disabled={saving}>
|
|
317
|
-
<Label>Delete</Label>
|
|
318
|
-
</Button>
|
|
319
|
-
{/if}
|
|
320
|
-
{#if success}
|
|
321
|
-
<span>Successfully saved!</span>
|
|
322
|
-
{/if}
|
|
323
|
-
</div>
|
|
324
|
-
</section>
|
|
325
|
-
{/if}
|
|
326
|
-
{/if}
|
|
327
|
-
|
|
328
|
-
<script lang="ts">
|
|
329
|
-
import { createEventDispatcher, onMount } from 'svelte';
|
|
330
|
-
import type {
|
|
331
|
-
AdminGroupData,
|
|
332
|
-
ClientConfig,
|
|
333
|
-
CurrentUserData,
|
|
334
|
-
} from '@nymphjs/tilmeld-client';
|
|
335
|
-
import type {
|
|
336
|
-
Group as GroupClass,
|
|
337
|
-
User as UserClass,
|
|
338
|
-
} from '@nymphjs/tilmeld-client';
|
|
339
|
-
import queryParser from '@nymphjs/query-parser';
|
|
340
|
-
import {
|
|
341
|
-
mdiArrowLeft,
|
|
342
|
-
mdiArrowRight,
|
|
343
|
-
mdiMagnify,
|
|
344
|
-
mdiMinus,
|
|
345
|
-
mdiPlus,
|
|
346
|
-
} from '@mdi/js';
|
|
347
|
-
import CircularProgress from '@smui/circular-progress';
|
|
348
|
-
import Tab from '@smui/tab';
|
|
349
|
-
import TabBar from '@smui/tab-bar';
|
|
350
|
-
import LayoutGrid, { Cell as LayoutCell } from '@smui/layout-grid';
|
|
351
|
-
import FormField from '@smui/form-field';
|
|
352
|
-
import Checkbox from '@smui/checkbox';
|
|
353
|
-
import List, { Item, Text, Meta } from '@smui/list';
|
|
354
|
-
import Paper from '@smui/paper';
|
|
355
|
-
import DataTable, { Head, Body, Row, Cell } from '@smui/data-table';
|
|
356
|
-
import Textfield, { Input } from '@smui/textfield';
|
|
357
|
-
import HelperText from '@smui/textfield/helper-text';
|
|
358
|
-
import IconButton from '@smui/icon-button';
|
|
359
|
-
import Button from '@smui/button';
|
|
360
|
-
import { Icon, Label, Svg } from '@smui/common';
|
|
361
|
-
|
|
362
|
-
import { nymph, Group, User } from './nymph';
|
|
363
|
-
|
|
364
|
-
const dispatch = createEventDispatcher();
|
|
365
|
-
|
|
366
|
-
export let entity: GroupClass & AdminGroupData;
|
|
367
|
-
|
|
368
|
-
let clientConfig: ClientConfig | undefined = undefined;
|
|
369
|
-
let user: (UserClass & CurrentUserData) | undefined = undefined;
|
|
370
|
-
let activeTab: 'General' | 'Parent' | 'Abilities' = 'General';
|
|
371
|
-
let parentSearch = '';
|
|
372
|
-
let ability = '';
|
|
373
|
-
let avatar = 'https://secure.gravatar.com/avatar/?d=mm&s=40';
|
|
374
|
-
let failureMessage: string | undefined = undefined;
|
|
375
|
-
let groupnameTimer: NodeJS.Timeout | undefined = undefined;
|
|
376
|
-
let groupnameVerified: boolean | undefined = undefined;
|
|
377
|
-
let groupnameVerifiedMessage: string | undefined = undefined;
|
|
378
|
-
let emailTimer: NodeJS.Timeout | undefined = undefined;
|
|
379
|
-
let emailVerified: boolean | undefined = undefined;
|
|
380
|
-
let emailVerifiedMessage: string | undefined = undefined;
|
|
381
|
-
let saving = false;
|
|
382
|
-
let success: boolean | undefined = undefined;
|
|
383
|
-
|
|
384
|
-
onMount(async () => {
|
|
385
|
-
user = (await User.current()) ?? undefined;
|
|
386
|
-
});
|
|
387
|
-
onMount(async () => {
|
|
388
|
-
clientConfig = await User.getClientConfig();
|
|
389
|
-
});
|
|
390
|
-
|
|
391
|
-
readyEntity();
|
|
392
|
-
function readyEntity() {
|
|
393
|
-
// Make sure all fields are defined.
|
|
394
|
-
if (entity.enabled == null) {
|
|
395
|
-
entity.enabled = false;
|
|
396
|
-
}
|
|
397
|
-
if (entity.groupname == null) {
|
|
398
|
-
entity.groupname = '';
|
|
399
|
-
}
|
|
400
|
-
if (entity.email == null) {
|
|
401
|
-
entity.email = '';
|
|
402
|
-
}
|
|
403
|
-
if (entity.name == null) {
|
|
404
|
-
entity.name = '';
|
|
405
|
-
}
|
|
406
|
-
if (entity.avatar == null) {
|
|
407
|
-
entity.avatar = '';
|
|
408
|
-
}
|
|
409
|
-
if (entity.phone == null) {
|
|
410
|
-
entity.phone = '';
|
|
411
|
-
}
|
|
412
|
-
if (entity.defaultPrimary == null) {
|
|
413
|
-
entity.defaultPrimary = false;
|
|
414
|
-
}
|
|
415
|
-
if (entity.defaultSecondary == null) {
|
|
416
|
-
entity.defaultSecondary = false;
|
|
417
|
-
}
|
|
418
|
-
if (entity.unverifiedSecondary == null) {
|
|
419
|
-
entity.unverifiedSecondary = false;
|
|
420
|
-
}
|
|
421
|
-
entity.$getAvatar().then((value) => {
|
|
422
|
-
avatar = value;
|
|
423
|
-
});
|
|
424
|
-
entity.$readyAll(1).then(() => {
|
|
425
|
-
entity = entity;
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
let parentsSearching = false;
|
|
430
|
-
let parents: (GroupClass & AdminGroupData)[] | undefined = undefined;
|
|
431
|
-
async function searchParents() {
|
|
432
|
-
parentsSearching = true;
|
|
433
|
-
failureMessage = undefined;
|
|
434
|
-
if (parentSearch.trim() == '') {
|
|
435
|
-
return;
|
|
436
|
-
}
|
|
437
|
-
try {
|
|
438
|
-
const query = queryParser({
|
|
439
|
-
query: parentSearch,
|
|
440
|
-
entityClass: Group,
|
|
441
|
-
defaultFields: ['groupname', 'name', 'email'],
|
|
442
|
-
qrefMap: {
|
|
443
|
-
User: {
|
|
444
|
-
class: User,
|
|
445
|
-
defaultFields: ['username', 'name', 'email'],
|
|
446
|
-
},
|
|
447
|
-
Group: {
|
|
448
|
-
class: Group,
|
|
449
|
-
defaultFields: ['groupname', 'name', 'email'],
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
});
|
|
453
|
-
parents = (await nymph.getEntities(...query)).filter((group) => {
|
|
454
|
-
return !group.$is(entity) && !group.$is(entity.parent);
|
|
455
|
-
});
|
|
456
|
-
} catch (e: any) {
|
|
457
|
-
failureMessage = e?.message;
|
|
458
|
-
}
|
|
459
|
-
parentsSearching = false;
|
|
460
|
-
}
|
|
461
|
-
function parentSearchKeyDown(event: CustomEvent | KeyboardEvent) {
|
|
462
|
-
event = event as KeyboardEvent;
|
|
463
|
-
if (event.key === 'Enter') searchParents();
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
let oldGroupname = entity.groupname;
|
|
467
|
-
$: if (entity.groupname !== oldGroupname) {
|
|
468
|
-
if (groupnameTimer) {
|
|
469
|
-
clearTimeout(groupnameTimer);
|
|
470
|
-
}
|
|
471
|
-
groupnameTimer = setTimeout(async () => {
|
|
472
|
-
if (entity.groupname === '') {
|
|
473
|
-
groupnameVerified = undefined;
|
|
474
|
-
groupnameVerifiedMessage = undefined;
|
|
475
|
-
return;
|
|
476
|
-
}
|
|
477
|
-
try {
|
|
478
|
-
const data = await entity.$checkGroupname();
|
|
479
|
-
groupnameVerified = data.result;
|
|
480
|
-
groupnameVerifiedMessage = data.message;
|
|
481
|
-
} catch (e: any) {
|
|
482
|
-
groupnameVerified = false;
|
|
483
|
-
groupnameVerifiedMessage = e?.message;
|
|
484
|
-
}
|
|
485
|
-
}, 400);
|
|
486
|
-
oldGroupname = entity.groupname;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
let oldEmail = entity.email;
|
|
490
|
-
$: if (entity.email !== oldEmail) {
|
|
491
|
-
if (emailTimer) {
|
|
492
|
-
clearTimeout(emailTimer);
|
|
493
|
-
}
|
|
494
|
-
emailTimer = setTimeout(async () => {
|
|
495
|
-
if (entity.email === '') {
|
|
496
|
-
emailVerified = undefined;
|
|
497
|
-
emailVerifiedMessage = undefined;
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
try {
|
|
501
|
-
const data = await entity.$checkEmail();
|
|
502
|
-
emailVerified = data.result;
|
|
503
|
-
emailVerifiedMessage = data.message;
|
|
504
|
-
} catch (e: any) {
|
|
505
|
-
emailVerified = false;
|
|
506
|
-
emailVerifiedMessage = e?.message;
|
|
507
|
-
}
|
|
508
|
-
}, 400);
|
|
509
|
-
oldEmail = entity.email;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
function addAbility() {
|
|
513
|
-
if (ability === '') {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
|
-
failureMessage = undefined;
|
|
517
|
-
if (ability === 'tilmeld/admin') {
|
|
518
|
-
failureMessage = "Groups aren't allowed to be Tilmeld admins.";
|
|
519
|
-
return;
|
|
520
|
-
}
|
|
521
|
-
if (ability === 'system/admin') {
|
|
522
|
-
failureMessage = "Groups aren't allowed to be system admins.";
|
|
523
|
-
return;
|
|
524
|
-
}
|
|
525
|
-
entity.abilities?.push(ability);
|
|
526
|
-
ability = '';
|
|
527
|
-
entity = entity;
|
|
528
|
-
}
|
|
529
|
-
function abilityKeyDown(event: CustomEvent | KeyboardEvent) {
|
|
530
|
-
event = event as KeyboardEvent;
|
|
531
|
-
if (event.key === 'Enter') addAbility();
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
async function saveEntity() {
|
|
535
|
-
saving = true;
|
|
536
|
-
failureMessage = undefined;
|
|
537
|
-
try {
|
|
538
|
-
if (await entity.$save()) {
|
|
539
|
-
success = true;
|
|
540
|
-
setTimeout(() => {
|
|
541
|
-
success = undefined;
|
|
542
|
-
}, 1000);
|
|
543
|
-
readyEntity();
|
|
544
|
-
} else {
|
|
545
|
-
failureMessage = 'Error saving group.';
|
|
546
|
-
}
|
|
547
|
-
} catch (e: any) {
|
|
548
|
-
console.log('error:', e);
|
|
549
|
-
failureMessage = e?.message;
|
|
550
|
-
}
|
|
551
|
-
saving = false;
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
async function deleteEntity() {
|
|
555
|
-
failureMessage = undefined;
|
|
556
|
-
if (confirm('Are you sure you want to delete this?')) {
|
|
557
|
-
saving = true;
|
|
558
|
-
try {
|
|
559
|
-
if (await entity.$delete()) {
|
|
560
|
-
dispatch('leave');
|
|
561
|
-
} else {
|
|
562
|
-
failureMessage = 'An error occurred.';
|
|
563
|
-
}
|
|
564
|
-
} catch (e: any) {
|
|
565
|
-
failureMessage = e?.message;
|
|
566
|
-
}
|
|
567
|
-
saving = false;
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
</script>
|