@isoftdata/svelte-user-configuration 1.0.0
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/README.md +29 -0
- package/dist/DeactivateUserModal.svelte +46 -0
- package/dist/DeactivateUserModal.svelte.d.ts +24 -0
- package/dist/PasswordRecoveryModal.svelte +58 -0
- package/dist/PasswordRecoveryModal.svelte.d.ts +20 -0
- package/dist/PasswordSetModal.svelte +78 -0
- package/dist/PasswordSetModal.svelte.d.ts +23 -0
- package/dist/PermissionList.svelte +199 -0
- package/dist/PermissionList.svelte.d.ts +37 -0
- package/dist/UserAccountInfo.svelte +268 -0
- package/dist/UserAccountInfo.svelte.d.ts +39 -0
- package/dist/UserConfiguration.svelte +87 -0
- package/dist/UserConfiguration.svelte.d.ts +48 -0
- package/dist/UserGroupMembership.svelte +39 -0
- package/dist/UserGroupMembership.svelte.d.ts +32 -0
- package/dist/UserSiteAccess.svelte +37 -0
- package/dist/UserSiteAccess.svelte.d.ts +33 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +6 -0
- package/dist/util.d.ts +45 -0
- package/dist/util.js +5 -0
- package/package.json +72 -0
package/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Svelte UserConfiguration
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npm i @isoftdata/svelte-user-configuration
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Props
|
|
10
|
+
| Name | Type | Description | Default Value |
|
|
11
|
+
|------|------|-------------|---------------|
|
|
12
|
+
| | | | |
|
|
13
|
+
| | | | |
|
|
14
|
+
| | | | |
|
|
15
|
+
|
|
16
|
+
## Slots
|
|
17
|
+
* default - A brief description of the slot, if there is one
|
|
18
|
+
|
|
19
|
+
## Events
|
|
20
|
+
* exampleEvent - A brief description of the event, if there are any
|
|
21
|
+
|
|
22
|
+
## Example
|
|
23
|
+
```svelte
|
|
24
|
+
<script lang="ts">
|
|
25
|
+
import UserConfiguration from '@isoftdata/svelte-user-configuration'
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<UserConfiguration prop='foo' />
|
|
29
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
<script>import { getContext, tick } from "svelte";
|
|
2
|
+
import { klona } from "klona";
|
|
3
|
+
import Modal from "@isoftdata/svelte-modal";
|
|
4
|
+
import TextArea from "@isoftdata/svelte-textarea";
|
|
5
|
+
let show = false;
|
|
6
|
+
let userAccount;
|
|
7
|
+
let lockNotes;
|
|
8
|
+
let textarea;
|
|
9
|
+
export let deactivateUser = void 0;
|
|
10
|
+
export async function open(userAccountCtx) {
|
|
11
|
+
userAccount = klona(userAccountCtx);
|
|
12
|
+
lockNotes = userAccount.lockNotes;
|
|
13
|
+
await tick();
|
|
14
|
+
show = true;
|
|
15
|
+
await tick();
|
|
16
|
+
textarea?.select();
|
|
17
|
+
}
|
|
18
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<Modal
|
|
22
|
+
title={translate('common:deactivate', 'Deactivate {{userName}}', { userName: userAccount?.name })}
|
|
23
|
+
confirmButtonText={translate('common:deactivate', 'Deactivate')}
|
|
24
|
+
confirmButtonColor="danger"
|
|
25
|
+
cancelButtonText={translate('common:close', 'Close')}
|
|
26
|
+
confirmButtonType="submit"
|
|
27
|
+
confirmButtonFormId="deactivateUserModalForm"
|
|
28
|
+
on:close={() => (show = false)}
|
|
29
|
+
bind:show
|
|
30
|
+
>
|
|
31
|
+
<form
|
|
32
|
+
id="deactivateUserModalForm"
|
|
33
|
+
on:submit={e => {
|
|
34
|
+
e.preventDefault()
|
|
35
|
+
userAccount && deactivateUser ? deactivateUser({ id: userAccount.id, lockNotes }) : undefined
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<TextArea
|
|
39
|
+
bind:textarea
|
|
40
|
+
label={translate('configuration.user.accountInfo.lockNote', 'Lock Notes')}
|
|
41
|
+
labelClass="py-0 mb-2"
|
|
42
|
+
style="min-height:83px;"
|
|
43
|
+
bind:value={lockNotes}
|
|
44
|
+
/>
|
|
45
|
+
</form>
|
|
46
|
+
</Modal>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { UserAccount } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
deactivateUser?: ((user: {
|
|
6
|
+
id: UserAccount["id"];
|
|
7
|
+
lockNotes: UserAccount["lockNotes"];
|
|
8
|
+
}) => void | Promise<void>) | undefined;
|
|
9
|
+
open?: (userAccountCtx: UserAccount) => Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
events: {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
};
|
|
14
|
+
slots: {};
|
|
15
|
+
exports?: {} | undefined;
|
|
16
|
+
bindings?: string | undefined;
|
|
17
|
+
};
|
|
18
|
+
export type DeactivateUserModalProps = typeof __propDef.props;
|
|
19
|
+
export type DeactivateUserModalEvents = typeof __propDef.events;
|
|
20
|
+
export type DeactivateUserModalSlots = typeof __propDef.slots;
|
|
21
|
+
export default class DeactivateUserModal extends SvelteComponent<DeactivateUserModalProps, DeactivateUserModalEvents, DeactivateUserModalSlots> {
|
|
22
|
+
get open(): (userAccountCtx: UserAccount) => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<script>import { getContext } from "svelte";
|
|
2
|
+
import Modal from "@isoftdata/svelte-modal";
|
|
3
|
+
import Input from "@isoftdata/svelte-input";
|
|
4
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
5
|
+
export let sendPasswordRecoveryToken = void 0;
|
|
6
|
+
let resetDate = void 0;
|
|
7
|
+
let email = void 0;
|
|
8
|
+
let show = false;
|
|
9
|
+
export function open(passwordRecoveryEmail, lastResetDate) {
|
|
10
|
+
email = passwordRecoveryEmail;
|
|
11
|
+
resetDate = lastResetDate;
|
|
12
|
+
show = true;
|
|
13
|
+
}
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<Modal
|
|
17
|
+
bind:show
|
|
18
|
+
title={translate('configuration.user.accountInfo.passwordRecovery.header', 'Password Recovery')}
|
|
19
|
+
confirmButtonText={translate('configuration.user.accountInfo.passwordManagementModal.requestResetToken', 'Request Reset Token')}
|
|
20
|
+
confirmButtonIcon="check"
|
|
21
|
+
confirmButtonDisabled={!email}
|
|
22
|
+
on:close={() => (show = false)}
|
|
23
|
+
confirmButtonType="submit"
|
|
24
|
+
confirmButtonFormId="passwordRecoveryForm"
|
|
25
|
+
>
|
|
26
|
+
<form
|
|
27
|
+
id="passwordRecoveryForm"
|
|
28
|
+
on:submit={async event => {
|
|
29
|
+
event.preventDefault()
|
|
30
|
+
try {
|
|
31
|
+
await sendPasswordRecoveryToken?.(email)
|
|
32
|
+
show = false
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error(err)
|
|
35
|
+
}
|
|
36
|
+
}}
|
|
37
|
+
>
|
|
38
|
+
<div class="form-row">
|
|
39
|
+
<Input
|
|
40
|
+
label={translate('configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate', 'Last Password Set Date')}
|
|
41
|
+
value={resetDate?.toLocaleString() ?? ''}
|
|
42
|
+
labelParentClass="col-12 col-md-6 form-group mb-0"
|
|
43
|
+
readonly
|
|
44
|
+
/>
|
|
45
|
+
<Input
|
|
46
|
+
label={translate('configuration.user.accountInfo.passwordRecoveryModal.passwordRecoveryEmail', 'Password Recovery Email')}
|
|
47
|
+
value={email ?? ''}
|
|
48
|
+
labelParentClass="col-12 col-md-6 form-group mb-0"
|
|
49
|
+
readonly
|
|
50
|
+
/>
|
|
51
|
+
{#if !email}
|
|
52
|
+
<div class="alert alert-danger mt-3 mb-0">
|
|
53
|
+
{translate('configuration.user.accountInfo.passwordRecovery.noRecoveryEmail', 'No recovery email has been set for this user.')}
|
|
54
|
+
</div>
|
|
55
|
+
{/if}
|
|
56
|
+
</div>
|
|
57
|
+
</form>
|
|
58
|
+
</Modal>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
declare const __propDef: {
|
|
3
|
+
props: {
|
|
4
|
+
sendPasswordRecoveryToken?: ((email: string | undefined) => void | Promise<void>) | undefined;
|
|
5
|
+
open?: (passwordRecoveryEmail: string | undefined, lastResetDate: Date | undefined) => void;
|
|
6
|
+
};
|
|
7
|
+
events: {
|
|
8
|
+
[evt: string]: CustomEvent<any>;
|
|
9
|
+
};
|
|
10
|
+
slots: {};
|
|
11
|
+
exports?: {} | undefined;
|
|
12
|
+
bindings?: string | undefined;
|
|
13
|
+
};
|
|
14
|
+
export type PasswordRecoveryModalProps = typeof __propDef.props;
|
|
15
|
+
export type PasswordRecoveryModalEvents = typeof __propDef.events;
|
|
16
|
+
export type PasswordRecoveryModalSlots = typeof __propDef.slots;
|
|
17
|
+
export default class PasswordRecoveryModal extends SvelteComponent<PasswordRecoveryModalProps, PasswordRecoveryModalEvents, PasswordRecoveryModalSlots> {
|
|
18
|
+
get open(): (passwordRecoveryEmail: string | undefined, lastResetDate: Date | undefined) => void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script>import { tick, getContext } from "svelte";
|
|
2
|
+
import Input from "@isoftdata/svelte-input";
|
|
3
|
+
import Modal from "@isoftdata/svelte-modal";
|
|
4
|
+
import PasswordFields from "@isoftdata/svelte-password-fields";
|
|
5
|
+
import { klona } from "klona";
|
|
6
|
+
let show = false;
|
|
7
|
+
let newPassword = "";
|
|
8
|
+
let confirmPassword = "";
|
|
9
|
+
let passwordMismatch = false;
|
|
10
|
+
let lastResetDate = void 0;
|
|
11
|
+
let input = void 0;
|
|
12
|
+
let passwordRecoveryEmail = void 0;
|
|
13
|
+
let error = void 0;
|
|
14
|
+
export let confirmPasswordSet = void 0;
|
|
15
|
+
export async function open(userAccount) {
|
|
16
|
+
error = void 0;
|
|
17
|
+
newPassword = "";
|
|
18
|
+
confirmPassword = "";
|
|
19
|
+
lastResetDate = klona(userAccount?.lastPasswordResetDate);
|
|
20
|
+
passwordRecoveryEmail = userAccount?.recoveryEmail;
|
|
21
|
+
show = true;
|
|
22
|
+
await tick();
|
|
23
|
+
input?.focus();
|
|
24
|
+
}
|
|
25
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<Modal
|
|
29
|
+
bind:show
|
|
30
|
+
modalSize="sm"
|
|
31
|
+
title={translate('configuration.user.accountInfo.setPasswordModal.header', 'Set Password')}
|
|
32
|
+
confirmButtonText={translate('configuration.user.accountInfo.passwordManagementModal.setPassword', 'Set Password')}
|
|
33
|
+
confirmButtonIcon="check"
|
|
34
|
+
confirmButtonDisabled={passwordMismatch || !newPassword || !confirmPassword}
|
|
35
|
+
on:close={() => (show = false)}
|
|
36
|
+
confirmButtonType="submit"
|
|
37
|
+
confirmButtonFormId="setPasswordForm"
|
|
38
|
+
>
|
|
39
|
+
<form
|
|
40
|
+
id="setPasswordForm"
|
|
41
|
+
on:submit={async e => {
|
|
42
|
+
e.preventDefault()
|
|
43
|
+
if (!passwordMismatch && confirmPasswordSet) {
|
|
44
|
+
try {
|
|
45
|
+
await confirmPasswordSet({ password: newPassword })
|
|
46
|
+
show = false
|
|
47
|
+
} catch (err) {
|
|
48
|
+
console.error(err)
|
|
49
|
+
if (err instanceof Error) {
|
|
50
|
+
error = err
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
<Input
|
|
57
|
+
label={translate('configuration.user.accountInfo.passwordManagementModal.lastPasswordSetDate', 'Last Password Set Date')}
|
|
58
|
+
tabindex={-1}
|
|
59
|
+
value={lastResetDate ? lastResetDate.toLocaleString() : ''}
|
|
60
|
+
labelParentClass="form-group mb-0"
|
|
61
|
+
readonly
|
|
62
|
+
/>
|
|
63
|
+
<PasswordFields
|
|
64
|
+
bind:password={newPassword}
|
|
65
|
+
bind:confirmPassword
|
|
66
|
+
bind:passwordMismatch
|
|
67
|
+
bind:passwordInput={input}
|
|
68
|
+
columnClass="col-12"
|
|
69
|
+
passwordLabel={translate('configuration.user.accountInfo.passwordManagementModal.newPassword', 'New Password')}
|
|
70
|
+
confirmPasswordLabel={translate('configuration.user.accountInfo.passwordManagementModal.confirmNewPassword', 'Confirm New Password')}
|
|
71
|
+
/>
|
|
72
|
+
{#if !passwordRecoveryEmail}
|
|
73
|
+
<div class="alert alert-danger mt-3 mb-0">
|
|
74
|
+
{translate('configuration.user.accountInfo.passwordRecovery.noRecoveryEmail', 'No recovery email has been set for this user.')}
|
|
75
|
+
</div>
|
|
76
|
+
{/if}
|
|
77
|
+
</form>
|
|
78
|
+
</Modal>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { UserAccount } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
confirmPasswordSet?: ((ctx: {
|
|
6
|
+
password: string;
|
|
7
|
+
}) => void | Promise<void>) | undefined;
|
|
8
|
+
open?: (userAccount?: UserAccount) => Promise<void>;
|
|
9
|
+
};
|
|
10
|
+
events: {
|
|
11
|
+
[evt: string]: CustomEvent<any>;
|
|
12
|
+
};
|
|
13
|
+
slots: {};
|
|
14
|
+
exports?: {} | undefined;
|
|
15
|
+
bindings?: string | undefined;
|
|
16
|
+
};
|
|
17
|
+
export type PasswordSetModalProps = typeof __propDef.props;
|
|
18
|
+
export type PasswordSetModalEvents = typeof __propDef.events;
|
|
19
|
+
export type PasswordSetModalSlots = typeof __propDef.slots;
|
|
20
|
+
export default class PasswordSetModal extends SvelteComponent<PasswordSetModalProps, PasswordSetModalEvents, PasswordSetModalSlots> {
|
|
21
|
+
get open(): (userAccount?: UserAccount) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<script>import { getContext } from "svelte";
|
|
2
|
+
import camelCase from "just-camel-case";
|
|
3
|
+
import Icon from "@isoftdata/svelte-icon";
|
|
4
|
+
import Button from "@isoftdata/svelte-button";
|
|
5
|
+
import Select from "@isoftdata/svelte-select";
|
|
6
|
+
import { getEventValue } from "@isoftdata/browser-event";
|
|
7
|
+
import { Table, Td } from "@isoftdata/svelte-table";
|
|
8
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
9
|
+
export let permissions;
|
|
10
|
+
export let siteLabel = "Site";
|
|
11
|
+
export let permissionValueChange = void 0;
|
|
12
|
+
export let icon = "user-lock";
|
|
13
|
+
export let cardHeaderTitle = translate("common:permissions", "Permissions");
|
|
14
|
+
let computedPermissions = [];
|
|
15
|
+
let selectedPermissionIds = /* @__PURE__ */ new Set();
|
|
16
|
+
let selectedPermissionValue = null;
|
|
17
|
+
const permissionColumns = getColumns(permissions);
|
|
18
|
+
const permissionValueList = {
|
|
19
|
+
NONE: { label: translate("common:permissionLevel.none", "None"), value: "NONE", color: "danger" },
|
|
20
|
+
SITE: { label: translate(`common:permissionLevel.${siteLabel.toLowerCase()}`, siteLabel), value: "SITE", color: "primary" },
|
|
21
|
+
GLOBAL: { label: translate("common:permissionLevel.global", "Global"), value: "GLOBAL", color: "success" }
|
|
22
|
+
};
|
|
23
|
+
function getColumns(permissions2) {
|
|
24
|
+
const hasGroupValue = permissions2.every((permission) => !!permission.groupValue);
|
|
25
|
+
let columns = [
|
|
26
|
+
{ property: "value", name: hasGroupValue ? translate("common:user", "User") : translate("common:permission", "Permission"), width: "1rem" },
|
|
27
|
+
{ property: "category", name: translate("common:category", "Category") },
|
|
28
|
+
{ property: "displayName", name: translate("common:name", "Name") }
|
|
29
|
+
];
|
|
30
|
+
if (hasGroupValue) {
|
|
31
|
+
columns.splice(
|
|
32
|
+
1,
|
|
33
|
+
0,
|
|
34
|
+
{ property: "groupValue", name: translate("common:group", "Group"), align: "center", width: "1rem", minWidth: "25%" },
|
|
35
|
+
{ property: "computedValue", name: translate("common:access", "Access"), align: "center", width: "1rem", minWidth: "25%" }
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return columns;
|
|
39
|
+
}
|
|
40
|
+
function selectPermissions(permissionId) {
|
|
41
|
+
selectedPermissionIds.has(permissionId) ? selectedPermissionIds.delete(permissionId) : selectedPermissionIds.add(permissionId);
|
|
42
|
+
selectedPermissionIds = selectedPermissionIds;
|
|
43
|
+
}
|
|
44
|
+
function selectAndDeselectAllPermissions() {
|
|
45
|
+
if (selectedPermissionIds.size === computedPermissions.length) {
|
|
46
|
+
selectedPermissionIds.clear();
|
|
47
|
+
selectedPermissionValue = null;
|
|
48
|
+
} else {
|
|
49
|
+
selectedPermissionIds = new Set(permissions.map((permission) => permission.id));
|
|
50
|
+
}
|
|
51
|
+
selectedPermissionIds = selectedPermissionIds;
|
|
52
|
+
}
|
|
53
|
+
const getButtonColor = (optionValue, permissionValue) => optionValue === permissionValue ? permissionValueList[optionValue].color : "outline-secondary";
|
|
54
|
+
const isValidPermissionValue = (value) => Object.keys(permissionValueList).includes(value);
|
|
55
|
+
async function updatePermissionValues(event, permissionIds) {
|
|
56
|
+
const newValue = getEventValue(event);
|
|
57
|
+
if (isValidPermissionValue(newValue)) {
|
|
58
|
+
for (const permission of permissions) {
|
|
59
|
+
if (permissionIds.has(permission.id)) {
|
|
60
|
+
permission.value = newValue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
permissions = permissions;
|
|
64
|
+
await permissionValueChange?.({
|
|
65
|
+
value: newValue,
|
|
66
|
+
permissionIds: Array.from(permissionIds)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function getComputedPermissions(permissions2) {
|
|
71
|
+
return permissions2.map((permission) => {
|
|
72
|
+
const { value, groupValue } = permission;
|
|
73
|
+
let computedValue = value === "NONE" && groupValue || value === siteLabel.toUpperCase() && groupValue === "GLOBAL" ? groupValue : value;
|
|
74
|
+
return {
|
|
75
|
+
...permission,
|
|
76
|
+
displayName: translate(`permissions:${camelCase(permission.codeName)}.displayName`, permission.displayName),
|
|
77
|
+
category: translate(`permissions:categories.${camelCase(permission.category)}`, permission.category),
|
|
78
|
+
computedValue
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
$: computedPermissions = getComputedPermissions(permissions);
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<div
|
|
86
|
+
class="card"
|
|
87
|
+
{...$$restProps}
|
|
88
|
+
>
|
|
89
|
+
<div class="card-header">
|
|
90
|
+
<h5 class="mb-0">
|
|
91
|
+
<Icon
|
|
92
|
+
{icon}
|
|
93
|
+
class="mr-1"
|
|
94
|
+
/>
|
|
95
|
+
{cardHeaderTitle}
|
|
96
|
+
</h5>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="card-body">
|
|
99
|
+
<Table
|
|
100
|
+
class="mb-0"
|
|
101
|
+
parentClass="mh-60vh overflow-y-auto"
|
|
102
|
+
filterPlaceholder={translate('common:filterPermissions', 'Filter Permissions')}
|
|
103
|
+
filterColumnClass="col-6 col-lg-4 align-self-end"
|
|
104
|
+
rows={computedPermissions}
|
|
105
|
+
columns={permissionColumns}
|
|
106
|
+
responsive
|
|
107
|
+
filterEnabled
|
|
108
|
+
let:row={permission}
|
|
109
|
+
>
|
|
110
|
+
<tr
|
|
111
|
+
on:click={() => selectPermissions(permission.id)}
|
|
112
|
+
class:table-primary={selectedPermissionIds.has(permission.id)}
|
|
113
|
+
class="cursor-pointer"
|
|
114
|
+
>
|
|
115
|
+
<Td property="value">
|
|
116
|
+
<div
|
|
117
|
+
class="btn-group btn-group-toggle"
|
|
118
|
+
data-toggle="buttons"
|
|
119
|
+
>
|
|
120
|
+
{#each Object.values(permissionValueList) as permissionValue}
|
|
121
|
+
<label class="btn btn-sm btn-{getButtonColor(permissionValue.value, permission.value)} cursor-pointer">
|
|
122
|
+
<input
|
|
123
|
+
type="radio"
|
|
124
|
+
autocomplete="off"
|
|
125
|
+
name={permission.value}
|
|
126
|
+
value={permissionValue.value}
|
|
127
|
+
on:change={e => updatePermissionValues(e, new Set([permission.id]))}
|
|
128
|
+
/>
|
|
129
|
+
{permissionValue.label}
|
|
130
|
+
</label>
|
|
131
|
+
{/each}
|
|
132
|
+
</div>
|
|
133
|
+
</Td>
|
|
134
|
+
{#if permission.groupValue && permission.computedValue}
|
|
135
|
+
<Td
|
|
136
|
+
property="groupValue"
|
|
137
|
+
class="px-4"
|
|
138
|
+
>
|
|
139
|
+
<span
|
|
140
|
+
style="width: 5em;"
|
|
141
|
+
class="badge badge-pill badge-{permissionValueList[permission.groupValue].color}">{permissionValueList[permission.groupValue].label}</span
|
|
142
|
+
>
|
|
143
|
+
</Td>
|
|
144
|
+
<Td
|
|
145
|
+
property="computedValue"
|
|
146
|
+
class="px-4"
|
|
147
|
+
>
|
|
148
|
+
<span
|
|
149
|
+
style="width: 5em;"
|
|
150
|
+
class="badge badge-pill badge-{permissionValueList[permission.computedValue].color}">{permissionValueList[permission.computedValue].label}</span
|
|
151
|
+
>
|
|
152
|
+
</Td>
|
|
153
|
+
{/if}
|
|
154
|
+
<Td property="category">{permission.category}</Td>
|
|
155
|
+
<Td property="displayName">{permission.displayName}</Td>
|
|
156
|
+
</tr>
|
|
157
|
+
</Table>
|
|
158
|
+
<slot></slot>
|
|
159
|
+
</div>
|
|
160
|
+
<div class="card-footer">
|
|
161
|
+
<div class="d-flex justify-content-between align-items-end">
|
|
162
|
+
<Select
|
|
163
|
+
label="{translate('common:setSelectedTo', 'Set Selected To')}:"
|
|
164
|
+
bind:value={selectedPermissionValue}
|
|
165
|
+
showEmptyOption={selectedPermissionValue === null}
|
|
166
|
+
emptyValue={null}
|
|
167
|
+
emptyText={translate('common:selectValue', 'Select a Value')}
|
|
168
|
+
labelParentClass="form-inline mr-2"
|
|
169
|
+
labelClass="mr-2 p-0"
|
|
170
|
+
disabled={selectedPermissionIds.size === 0}
|
|
171
|
+
on:change={e => {
|
|
172
|
+
updatePermissionValues(e, selectedPermissionIds)
|
|
173
|
+
selectedPermissionIds = new Set() //clear the Set and trigger reactivity
|
|
174
|
+
selectedPermissionValue = null
|
|
175
|
+
}}
|
|
176
|
+
>
|
|
177
|
+
{#each Object.values(permissionValueList) as permission}
|
|
178
|
+
<option value={permission.value}>{permission.label}</option>
|
|
179
|
+
{/each}
|
|
180
|
+
</Select>
|
|
181
|
+
<Button
|
|
182
|
+
class="mb-1"
|
|
183
|
+
outline
|
|
184
|
+
size="sm"
|
|
185
|
+
iconClass="check-double"
|
|
186
|
+
on:click={selectAndDeselectAllPermissions}
|
|
187
|
+
>
|
|
188
|
+
<span class:d-none={selectedPermissionIds.size !== computedPermissions.length}>{translate('common:deselectAll', 'Deselect All')}</span>
|
|
189
|
+
<span class:d-none={selectedPermissionIds.size === computedPermissions.length}>{translate('common:selectAll', 'Select All')}</span>
|
|
190
|
+
</Button>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
|
|
195
|
+
<style>
|
|
196
|
+
.cursor-pointer {
|
|
197
|
+
cursor: pointer;
|
|
198
|
+
}
|
|
199
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { IconName, SiteLabel, HTMLDivAttributes } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: HTMLDivAttributes & {
|
|
5
|
+
permissions: Array<{
|
|
6
|
+
id: number;
|
|
7
|
+
displayName: string;
|
|
8
|
+
category: string;
|
|
9
|
+
value: "NONE" | "SITE" | "GLOBAL";
|
|
10
|
+
/** Ractive version of this component didn't require `codeName`, but the translation key for permissions requires it, so we can't translate without it.*/
|
|
11
|
+
codeName: string;
|
|
12
|
+
/** Providing this value will cause the Group and Access columns to be rendered*/
|
|
13
|
+
groupValue?: "NONE" | "SITE" | "GLOBAL";
|
|
14
|
+
}>;
|
|
15
|
+
siteLabel?: SiteLabel;
|
|
16
|
+
permissionValueChange?: ((change: {
|
|
17
|
+
value: "NONE" | "SITE" | "GLOBAL";
|
|
18
|
+
permissionIds: Array<number>;
|
|
19
|
+
}) => void | Promise<void>) | undefined;
|
|
20
|
+
icon?: IconName;
|
|
21
|
+
cardHeaderTitle?: string;
|
|
22
|
+
};
|
|
23
|
+
events: {
|
|
24
|
+
[evt: string]: CustomEvent<any>;
|
|
25
|
+
};
|
|
26
|
+
slots: {
|
|
27
|
+
default: {};
|
|
28
|
+
};
|
|
29
|
+
exports?: {} | undefined;
|
|
30
|
+
bindings?: string | undefined;
|
|
31
|
+
};
|
|
32
|
+
export type PermissionListProps = typeof __propDef.props;
|
|
33
|
+
export type PermissionListEvents = typeof __propDef.events;
|
|
34
|
+
export type PermissionListSlots = typeof __propDef.slots;
|
|
35
|
+
export default class PermissionList extends SvelteComponent<PermissionListProps, PermissionListEvents, PermissionListSlots> {
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
<script>import { getContext } from "svelte";
|
|
2
|
+
import Icon from "@isoftdata/svelte-icon";
|
|
3
|
+
import Input from "@isoftdata/svelte-input";
|
|
4
|
+
import Button from "@isoftdata/svelte-button";
|
|
5
|
+
import TextArea from "@isoftdata/svelte-textarea";
|
|
6
|
+
import PasswordSetModal from "./PasswordSetModal.svelte";
|
|
7
|
+
import PasswordFields from "@isoftdata/svelte-password-fields";
|
|
8
|
+
import DeactivateUserModal from "./DeactivateUserModal.svelte";
|
|
9
|
+
import PasswordRecoveryModal from "./PasswordRecoveryModal.svelte";
|
|
10
|
+
export let userAccount;
|
|
11
|
+
export let icon = "user";
|
|
12
|
+
export let canToggleActive = true;
|
|
13
|
+
export let canEditAccountInfo = true;
|
|
14
|
+
export let isCreatingNewUser = false;
|
|
15
|
+
export let deactivateUser = void 0;
|
|
16
|
+
export let hasPermissionToChangePassword = false;
|
|
17
|
+
export let confirmPasswordSet = void 0;
|
|
18
|
+
export let accountInfoChanged = void 0;
|
|
19
|
+
export let error = void 0;
|
|
20
|
+
export let success = void 0;
|
|
21
|
+
export let sendPasswordRecoveryToken = void 0;
|
|
22
|
+
export let generateNewActivationPIN = () => Promise.resolve();
|
|
23
|
+
let isLoading = false;
|
|
24
|
+
let passwordSetModal;
|
|
25
|
+
let activationPINExpiration = "";
|
|
26
|
+
let deactivateUserModal;
|
|
27
|
+
let passwordRecoveryModal;
|
|
28
|
+
let activationPINInput;
|
|
29
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
30
|
+
async function getNewActivationPIN(sendEmail = false) {
|
|
31
|
+
let confirmationMessage = sendEmail ? translate("configuration.user.permissions.sendNewActivationPINMessage", "Are you sure you want to send a new activation PIN? The user will receive an email with the new activation PIN.") : translate("configuration.user.permissions.generateNewActivationPINMessage", "Are you sure you want to generate a new activation PIN?");
|
|
32
|
+
if (confirm(confirmationMessage)) {
|
|
33
|
+
try {
|
|
34
|
+
isLoading = true;
|
|
35
|
+
await generateNewActivationPIN(userAccount.name, !!userAccount.workEmail);
|
|
36
|
+
let successMessage = sendEmail ? translate("configuration.user.permissions.activationPINSent", "Email with new activation PIN has been sent successfully.") : translate("configuration.user.permissions.activationPINGenerated", "Activation PIN generated successfully.");
|
|
37
|
+
let successHeading = sendEmail ? translate("configuration.messageHeading.activationPINSent", "New Activation PIN Sent!") : translate("configuration.messageHeading.activationPINGenerated", "New Activation PIN Generated!");
|
|
38
|
+
await success?.({ heading: successHeading, message: successMessage });
|
|
39
|
+
isLoading = false;
|
|
40
|
+
await accountInfoChanged?.();
|
|
41
|
+
} catch (err) {
|
|
42
|
+
console.log(err);
|
|
43
|
+
if (err instanceof Error) {
|
|
44
|
+
await error?.({ heading: translate("configuration.messageHeading.failedToGenerateNewPIN", "Failed To Generate New PIN"), message: err.message });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function copyTextToClipboard() {
|
|
50
|
+
if (activationPINInput) {
|
|
51
|
+
navigator.clipboard.writeText(activationPINInput.value);
|
|
52
|
+
await success?.({
|
|
53
|
+
heading: translate("configuration.messageHeading.activationPINCopied", "Activation PIN Copied!"),
|
|
54
|
+
message: translate("configuration.user.permissions.activationPINCopied", "Activation PIN copied to clipboard successfully.")
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function reactivateUserAccount() {
|
|
59
|
+
if (confirm(translate("configuration.user.permissions.reactivateUserConfirmation", "Are you sure you want to reactivate this user?"))) {
|
|
60
|
+
userAccount.status = "ACTIVE";
|
|
61
|
+
userAccount.lockNotes = null;
|
|
62
|
+
await accountInfoChanged?.();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
async function deactivateUserAccount(lockNotes) {
|
|
66
|
+
userAccount.status = "DEACTIVATED";
|
|
67
|
+
userAccount.lockNotes = lockNotes;
|
|
68
|
+
await accountInfoChanged?.();
|
|
69
|
+
}
|
|
70
|
+
$: workEmail = userAccount.workEmail;
|
|
71
|
+
$: status = userAccount.status;
|
|
72
|
+
$: activationPIN = userAccount.activationPIN;
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
<div
|
|
76
|
+
class="card"
|
|
77
|
+
{...$$restProps}
|
|
78
|
+
>
|
|
79
|
+
<fieldset disabled={!canEditAccountInfo}>
|
|
80
|
+
<div class="card-header">
|
|
81
|
+
<div class="d-flex justify-content-between align-items-center">
|
|
82
|
+
<h5 class="mb-0">
|
|
83
|
+
<Icon
|
|
84
|
+
{icon}
|
|
85
|
+
class="mr-1"
|
|
86
|
+
/>
|
|
87
|
+
{isCreatingNewUser ? translate('configuration.user.creatingNewAccountInfoHeader', 'New Account') : translate('configuration.user.accountInfoHeader', 'Account')}
|
|
88
|
+
</h5>
|
|
89
|
+
{#if status === 'ACTIVE'}
|
|
90
|
+
<Button
|
|
91
|
+
size="xs"
|
|
92
|
+
outline
|
|
93
|
+
color="danger"
|
|
94
|
+
iconClass="xmark"
|
|
95
|
+
on:click={() => deactivateUserModal.open(userAccount)}
|
|
96
|
+
disabled={!canEditAccountInfo || !canToggleActive}
|
|
97
|
+
>
|
|
98
|
+
<span>{translate('common:deactivate', 'Deactivate')}</span>
|
|
99
|
+
</Button>
|
|
100
|
+
{:else if status === 'DEACTIVATED' || status === 'LOCKED'}
|
|
101
|
+
<Button
|
|
102
|
+
size="sm"
|
|
103
|
+
outline
|
|
104
|
+
color="success"
|
|
105
|
+
iconClass="check"
|
|
106
|
+
on:click={() => reactivateUserAccount()}
|
|
107
|
+
disabled={!canEditAccountInfo || !canToggleActive}
|
|
108
|
+
>
|
|
109
|
+
<span>{translate('common:activate', 'Activate')}</span>
|
|
110
|
+
</Button>
|
|
111
|
+
{/if}
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
<div class="card-body">
|
|
115
|
+
<div class="form-row">
|
|
116
|
+
{#if !isCreatingNewUser}
|
|
117
|
+
{#if status === 'PENDING_ACTIVATION'}
|
|
118
|
+
<div class="col-12">
|
|
119
|
+
{#if activationPIN && workEmail}
|
|
120
|
+
<div class="alert alert-info mb-0">
|
|
121
|
+
{translate('configuration.user.accountInfo.activationPINSent', 'An activation PIN has been sent to {{email}}.', { email: workEmail })}
|
|
122
|
+
</div>
|
|
123
|
+
{:else}
|
|
124
|
+
<label for="activationPIN">{translate('configuration.user.activationPIN', 'Activation PIN')}</label>
|
|
125
|
+
<div class="input-group input-group-sm">
|
|
126
|
+
<input
|
|
127
|
+
bind:this={activationPINInput}
|
|
128
|
+
type="text"
|
|
129
|
+
class="form-control"
|
|
130
|
+
placeholder="###-###"
|
|
131
|
+
value={activationPIN}
|
|
132
|
+
readonly
|
|
133
|
+
/>
|
|
134
|
+
<div class="input-group-append">
|
|
135
|
+
<!-- When this button is hit, the function will call an API endpoint that will write the new PIN into the db -->
|
|
136
|
+
<!-- Therefore, this button will ignore the save function, as we need the new PIN to display it here -->
|
|
137
|
+
<Button
|
|
138
|
+
size="sm"
|
|
139
|
+
outline
|
|
140
|
+
{isLoading}
|
|
141
|
+
iconClass="refresh"
|
|
142
|
+
on:click={() => getNewActivationPIN()}
|
|
143
|
+
title={translate('configuration.user.generateNewActivationPIN', 'Generate New PIN')}
|
|
144
|
+
/>
|
|
145
|
+
<Button
|
|
146
|
+
size="sm"
|
|
147
|
+
outline
|
|
148
|
+
iconClass="copy"
|
|
149
|
+
on:click={() => copyTextToClipboard()}
|
|
150
|
+
disabled={!activationPIN}
|
|
151
|
+
title={translate('configuration.user.copyActivationPIN', 'Copy Activation PIN')}
|
|
152
|
+
/>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
{/if}
|
|
156
|
+
{#if activationPIN}
|
|
157
|
+
<small class="text-danger">{translate('configuratioin.user.activationPINExpireText', 'Activation PIN expires on')} {activationPINExpiration}</small>
|
|
158
|
+
{/if}
|
|
159
|
+
</div>
|
|
160
|
+
{:else}
|
|
161
|
+
<div class="col-12">
|
|
162
|
+
<Button
|
|
163
|
+
size="sm"
|
|
164
|
+
outline
|
|
165
|
+
iconClass="paper-plane"
|
|
166
|
+
on:click={() => passwordRecoveryModal.open(userAccount.recoveryEmail, userAccount.lastPasswordResetDate)}
|
|
167
|
+
>
|
|
168
|
+
{translate('configuration.user.sendResetToken', 'Send Reset Token')}...
|
|
169
|
+
</Button>
|
|
170
|
+
{#if hasPermissionToChangePassword}
|
|
171
|
+
<Button
|
|
172
|
+
size="sm"
|
|
173
|
+
outline
|
|
174
|
+
iconClass="key"
|
|
175
|
+
on:click={() => passwordSetModal.open(userAccount)}
|
|
176
|
+
>
|
|
177
|
+
{translate('configuration.user.setPassword', 'Set Password')}...
|
|
178
|
+
</Button>
|
|
179
|
+
{/if}
|
|
180
|
+
</div>
|
|
181
|
+
{/if}
|
|
182
|
+
{/if}
|
|
183
|
+
<div class="col-12">
|
|
184
|
+
<Input
|
|
185
|
+
id="usernameInput"
|
|
186
|
+
label={translate('configuration.user.accountInfo.username', 'Username')}
|
|
187
|
+
bind:value={userAccount.name}
|
|
188
|
+
maxlength={320}
|
|
189
|
+
required={isCreatingNewUser}
|
|
190
|
+
validation={{
|
|
191
|
+
validator: value => {
|
|
192
|
+
return value.length > 320 ? 'Username must be less than 320 characters.' : true
|
|
193
|
+
},
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="col-6">
|
|
198
|
+
<Input
|
|
199
|
+
label={translate('configuration.user.accountInfo.firstName', 'First Name')}
|
|
200
|
+
bind:value={userAccount.firstName}
|
|
201
|
+
maxlength={100}
|
|
202
|
+
/>
|
|
203
|
+
</div>
|
|
204
|
+
<div class="col-6">
|
|
205
|
+
<Input
|
|
206
|
+
label={translate('configuration.user.accountInfo.lastName', 'Last Name')}
|
|
207
|
+
bind:value={userAccount.lastName}
|
|
208
|
+
maxlength={100}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
211
|
+
<div class="col-12">
|
|
212
|
+
<Input
|
|
213
|
+
label={translate('configuration.user.accountInfo.workEmail', 'Work Email')}
|
|
214
|
+
bind:value={userAccount.workEmail}
|
|
215
|
+
autocomplete="email"
|
|
216
|
+
type="email"
|
|
217
|
+
inputmode="email"
|
|
218
|
+
/>
|
|
219
|
+
{#if workEmail && !isCreatingNewUser && status === 'PENDING_ACTIVATION'}
|
|
220
|
+
<Button
|
|
221
|
+
size="sm"
|
|
222
|
+
outline
|
|
223
|
+
iconClass="paper-plane"
|
|
224
|
+
on:click={() => getNewActivationPIN(true)}
|
|
225
|
+
>
|
|
226
|
+
{translate('configuration.user.sendNewActivationPIN', 'Send New Activation PIN')}
|
|
227
|
+
</Button>
|
|
228
|
+
{/if}
|
|
229
|
+
</div>
|
|
230
|
+
<div class="col-12">
|
|
231
|
+
{#if !isCreatingNewUser}
|
|
232
|
+
<TextArea
|
|
233
|
+
label={translate('configuration.user.accountInfo.lockNote', 'Lock Note')}
|
|
234
|
+
labelClass="py-0 mb-2"
|
|
235
|
+
style="min-height:83px;"
|
|
236
|
+
bind:value={userAccount.lockNotes}
|
|
237
|
+
readonly
|
|
238
|
+
/>
|
|
239
|
+
{:else if hasPermissionToChangePassword}
|
|
240
|
+
<!-- TODO: When we migrate to Svelte 5, switch this and the other usage of PasswordFields to use a snippet-->
|
|
241
|
+
<PasswordFields
|
|
242
|
+
bind:password={userAccount.newPassword}
|
|
243
|
+
columnClass="col-12"
|
|
244
|
+
passwordLabel={translate('configuration.user.accountInfo.passwordManagementModal.newPassword', 'New Password')}
|
|
245
|
+
confirmPasswordLabel={translate('configuration.user.accountInfo.passwordManagementModal.confirmNewPassword', 'Confirm New Password')}
|
|
246
|
+
/>
|
|
247
|
+
{/if}
|
|
248
|
+
</div>
|
|
249
|
+
</div>
|
|
250
|
+
<slot></slot>
|
|
251
|
+
</div>
|
|
252
|
+
</fieldset>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<PasswordRecoveryModal
|
|
256
|
+
bind:this={passwordRecoveryModal}
|
|
257
|
+
{sendPasswordRecoveryToken}
|
|
258
|
+
/>
|
|
259
|
+
|
|
260
|
+
<DeactivateUserModal
|
|
261
|
+
bind:this={deactivateUserModal}
|
|
262
|
+
{deactivateUser}
|
|
263
|
+
/>
|
|
264
|
+
|
|
265
|
+
<PasswordSetModal
|
|
266
|
+
bind:this={passwordSetModal}
|
|
267
|
+
{confirmPasswordSet}
|
|
268
|
+
/>
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { UserAccount, ConfirmPasswordSetFn, DeactivateUserFn, HTMLDivAttributes, IconName } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: HTMLDivAttributes & {
|
|
5
|
+
userAccount: UserAccount;
|
|
6
|
+
canEditAccountInfo?: boolean;
|
|
7
|
+
canToggleActive?: boolean;
|
|
8
|
+
isCreatingNewUser?: boolean;
|
|
9
|
+
hasPermissionToChangePassword?: boolean;
|
|
10
|
+
generateNewActivationPIN?: (userName: string, hasWorkEmail: boolean) => Promise<void>;
|
|
11
|
+
confirmPasswordSet?: ConfirmPasswordSetFn;
|
|
12
|
+
deactivateUser?: DeactivateUserFn;
|
|
13
|
+
success?: ((info: {
|
|
14
|
+
heading: string;
|
|
15
|
+
message: string;
|
|
16
|
+
}) => void | Promise<void>) | undefined;
|
|
17
|
+
error?: ((info: {
|
|
18
|
+
heading: string;
|
|
19
|
+
message: string;
|
|
20
|
+
}) => void | Promise<void>) | undefined;
|
|
21
|
+
accountInfoChanged?: (() => void | Promise<void>) | undefined;
|
|
22
|
+
sendPasswordRecoveryToken?: ((email: string | undefined) => void | Promise<void>) | undefined;
|
|
23
|
+
icon?: IconName;
|
|
24
|
+
};
|
|
25
|
+
events: {
|
|
26
|
+
[evt: string]: CustomEvent<any>;
|
|
27
|
+
};
|
|
28
|
+
slots: {
|
|
29
|
+
default: {};
|
|
30
|
+
};
|
|
31
|
+
exports?: {} | undefined;
|
|
32
|
+
bindings?: string | undefined;
|
|
33
|
+
};
|
|
34
|
+
export type UserAccountInfoProps = typeof __propDef.props;
|
|
35
|
+
export type UserAccountInfoEvents = typeof __propDef.events;
|
|
36
|
+
export type UserAccountInfoSlots = typeof __propDef.slots;
|
|
37
|
+
export default class UserAccountInfo extends SvelteComponent<UserAccountInfoProps, UserAccountInfoEvents, UserAccountInfoSlots> {
|
|
38
|
+
}
|
|
39
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script>import UserSiteAccess from "../lib/UserSiteAccess.svelte";
|
|
2
|
+
import PermissionList from "../lib/PermissionList.svelte";
|
|
3
|
+
import UserAccountInfo from "../lib/UserAccountInfo.svelte";
|
|
4
|
+
import UserGroupMembership from "../lib/UserGroupMembership.svelte";
|
|
5
|
+
export let siteLabel = "Site";
|
|
6
|
+
export let userAccountInfoIcon = "user";
|
|
7
|
+
export let groupMembershipIcon = "users";
|
|
8
|
+
export let permissionListIcon = "user-lock";
|
|
9
|
+
export let userSiteAccessIcon = "industry-windows";
|
|
10
|
+
export let permissionListCardHeaderTitle = "Permissions";
|
|
11
|
+
export let userAccount;
|
|
12
|
+
export let canToggleActive = true;
|
|
13
|
+
export let isCreatingNewUser = false;
|
|
14
|
+
export let canEditAccountInfo = true;
|
|
15
|
+
export let hasPermissionToChangePassword = false;
|
|
16
|
+
export let error = void 0;
|
|
17
|
+
export let success = void 0;
|
|
18
|
+
export let deactivateUser = void 0;
|
|
19
|
+
export let confirmPasswordSet = void 0;
|
|
20
|
+
export let accountInfoChanged = void 0;
|
|
21
|
+
export let sendPasswordRecoveryToken = void 0;
|
|
22
|
+
export let generateNewActivationPIN = () => Promise.resolve();
|
|
23
|
+
export let userSites;
|
|
24
|
+
export let canEditSiteAccess = true;
|
|
25
|
+
export let siteAccessChange = void 0;
|
|
26
|
+
export let groupMembership;
|
|
27
|
+
export let canEditGroupMembership = true;
|
|
28
|
+
export let groupMembershipChange = void 0;
|
|
29
|
+
export let permissions;
|
|
30
|
+
export let permissionValueChange = void 0;
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<div class="form-row">
|
|
34
|
+
<div class="col-12 col-lg-4 mb-2">
|
|
35
|
+
<UserAccountInfo
|
|
36
|
+
{userAccount}
|
|
37
|
+
{canToggleActive}
|
|
38
|
+
{canEditAccountInfo}
|
|
39
|
+
{isCreatingNewUser}
|
|
40
|
+
{hasPermissionToChangePassword}
|
|
41
|
+
{error}
|
|
42
|
+
{success}
|
|
43
|
+
{deactivateUser}
|
|
44
|
+
{accountInfoChanged}
|
|
45
|
+
{confirmPasswordSet}
|
|
46
|
+
{sendPasswordRecoveryToken}
|
|
47
|
+
{generateNewActivationPIN}
|
|
48
|
+
icon={userAccountInfoIcon}
|
|
49
|
+
style="height: 500px;"
|
|
50
|
+
/>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="col-12 col-lg-4 mb-2">
|
|
53
|
+
<UserSiteAccess
|
|
54
|
+
{siteLabel}
|
|
55
|
+
{userSites}
|
|
56
|
+
{canEditSiteAccess}
|
|
57
|
+
{siteAccessChange}
|
|
58
|
+
icon={userSiteAccessIcon}
|
|
59
|
+
style="height: 500px;"
|
|
60
|
+
>
|
|
61
|
+
<slot name="siteAccess"></slot>
|
|
62
|
+
</UserSiteAccess>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="col-12 col-lg-4 mb-2">
|
|
65
|
+
<UserGroupMembership
|
|
66
|
+
{groupMembership}
|
|
67
|
+
{canEditGroupMembership}
|
|
68
|
+
{groupMembershipChange}
|
|
69
|
+
icon={groupMembershipIcon}
|
|
70
|
+
style="height: 500px;"
|
|
71
|
+
>
|
|
72
|
+
<slot name="userGroupMembership"></slot>
|
|
73
|
+
</UserGroupMembership>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
<div class="form-row">
|
|
77
|
+
<div class="col-12">
|
|
78
|
+
<PermissionList
|
|
79
|
+
{permissions}
|
|
80
|
+
{permissionValueChange}
|
|
81
|
+
icon={permissionListIcon}
|
|
82
|
+
cardHeaderTitle={permissionListCardHeaderTitle}
|
|
83
|
+
>
|
|
84
|
+
<slot name="permissionList"></slot>
|
|
85
|
+
</PermissionList>
|
|
86
|
+
</div>
|
|
87
|
+
</div>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { ErrorFn, IconName, UserSites, SuccessFn, SiteLabel, UserAccount, Permissions, GroupMembership, CanToggleActive, DeactivateUserFn, CanEditSiteAccess, IsCreatingNewUser, SiteAccessChangeFn, CanEditAccountInfo, ConfirmPasswordSetFn, AccountInfoChangedFn, CanEditGroupMembership, GroupMembershipChangeFn, PermissionValueChangeFn, GenerateNewActivationPINFn, SendPasswordRecoveryTokenFn, HasPermissionToChangePassword, PermissionListCardHeaderTitle } from '../lib/util';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: {
|
|
5
|
+
siteLabel?: SiteLabel;
|
|
6
|
+
userAccountInfoIcon?: IconName;
|
|
7
|
+
groupMembershipIcon?: IconName;
|
|
8
|
+
permissionListIcon?: IconName;
|
|
9
|
+
userSiteAccessIcon?: IconName;
|
|
10
|
+
permissionListCardHeaderTitle?: PermissionListCardHeaderTitle;
|
|
11
|
+
userAccount: UserAccount;
|
|
12
|
+
canToggleActive?: CanToggleActive;
|
|
13
|
+
isCreatingNewUser?: IsCreatingNewUser;
|
|
14
|
+
canEditAccountInfo?: CanEditAccountInfo;
|
|
15
|
+
hasPermissionToChangePassword?: HasPermissionToChangePassword;
|
|
16
|
+
error?: ErrorFn;
|
|
17
|
+
success?: SuccessFn;
|
|
18
|
+
deactivateUser?: DeactivateUserFn;
|
|
19
|
+
confirmPasswordSet?: ConfirmPasswordSetFn;
|
|
20
|
+
accountInfoChanged?: AccountInfoChangedFn;
|
|
21
|
+
sendPasswordRecoveryToken?: SendPasswordRecoveryTokenFn;
|
|
22
|
+
generateNewActivationPIN?: GenerateNewActivationPINFn;
|
|
23
|
+
userSites: UserSites;
|
|
24
|
+
canEditSiteAccess?: CanEditSiteAccess;
|
|
25
|
+
siteAccessChange?: SiteAccessChangeFn;
|
|
26
|
+
groupMembership: GroupMembership;
|
|
27
|
+
canEditGroupMembership?: CanEditGroupMembership;
|
|
28
|
+
groupMembershipChange?: GroupMembershipChangeFn;
|
|
29
|
+
permissions: Permissions;
|
|
30
|
+
permissionValueChange?: PermissionValueChangeFn;
|
|
31
|
+
};
|
|
32
|
+
events: {
|
|
33
|
+
[evt: string]: CustomEvent<any>;
|
|
34
|
+
};
|
|
35
|
+
slots: {
|
|
36
|
+
siteAccess: {};
|
|
37
|
+
userGroupMembership: {};
|
|
38
|
+
permissionList: {};
|
|
39
|
+
};
|
|
40
|
+
exports?: {} | undefined;
|
|
41
|
+
bindings?: string | undefined;
|
|
42
|
+
};
|
|
43
|
+
export type UserConfigurationProps = typeof __propDef.props;
|
|
44
|
+
export type UserConfigurationEvents = typeof __propDef.events;
|
|
45
|
+
export type UserConfigurationSlots = typeof __propDef.slots;
|
|
46
|
+
export default class UserConfiguration extends SvelteComponent<UserConfigurationProps, UserConfigurationEvents, UserConfigurationSlots> {
|
|
47
|
+
}
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
<script>import { getContext } from "svelte";
|
|
2
|
+
import Icon from "@isoftdata/svelte-icon";
|
|
3
|
+
import Checkbox from "@isoftdata/svelte-checkbox";
|
|
4
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
5
|
+
export let groupMembership;
|
|
6
|
+
export let canEditGroupMembership = true;
|
|
7
|
+
export let groupMembershipChange = void 0;
|
|
8
|
+
export let icon = "users";
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<div
|
|
12
|
+
class="card"
|
|
13
|
+
{...$$restProps}
|
|
14
|
+
>
|
|
15
|
+
<div class="card-header">
|
|
16
|
+
<h5 class="mb-0">
|
|
17
|
+
<Icon {icon} />
|
|
18
|
+
{translate('configuration.user.groupMembership', 'Group Membership')}
|
|
19
|
+
</h5>
|
|
20
|
+
</div>
|
|
21
|
+
<ul
|
|
22
|
+
class="list-group list-group-flush h-100"
|
|
23
|
+
style="overflow-y: auto;"
|
|
24
|
+
>
|
|
25
|
+
{#each groupMembership as { name, isMember }, i}
|
|
26
|
+
<li class="list-group-item">
|
|
27
|
+
<Checkbox
|
|
28
|
+
label={name}
|
|
29
|
+
bind:checked={isMember}
|
|
30
|
+
disabled={!canEditGroupMembership}
|
|
31
|
+
on:change={async () => {
|
|
32
|
+
await groupMembershipChange?.({ id: i, name, isMember })
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</li>
|
|
36
|
+
{/each}
|
|
37
|
+
</ul>
|
|
38
|
+
<slot></slot>
|
|
39
|
+
</div>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { IconName, HTMLDivAttributes } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: HTMLDivAttributes & {
|
|
5
|
+
groupMembership: Array<{
|
|
6
|
+
id: number;
|
|
7
|
+
name: string;
|
|
8
|
+
isMember: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
canEditGroupMembership?: boolean;
|
|
11
|
+
groupMembershipChange?: ((groupMembership: {
|
|
12
|
+
id: number;
|
|
13
|
+
name: string;
|
|
14
|
+
isMember: boolean;
|
|
15
|
+
}) => void | Promise<void>) | undefined;
|
|
16
|
+
icon?: IconName;
|
|
17
|
+
};
|
|
18
|
+
events: {
|
|
19
|
+
[evt: string]: CustomEvent<any>;
|
|
20
|
+
};
|
|
21
|
+
slots: {
|
|
22
|
+
default: {};
|
|
23
|
+
};
|
|
24
|
+
exports?: {} | undefined;
|
|
25
|
+
bindings?: string | undefined;
|
|
26
|
+
};
|
|
27
|
+
export type UserGroupMembershipProps = typeof __propDef.props;
|
|
28
|
+
export type UserGroupMembershipEvents = typeof __propDef.events;
|
|
29
|
+
export type UserGroupMembershipSlots = typeof __propDef.slots;
|
|
30
|
+
export default class UserGroupMembership extends SvelteComponent<UserGroupMembershipProps, UserGroupMembershipEvents, UserGroupMembershipSlots> {
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script>import { getContext } from "svelte";
|
|
2
|
+
import Checkbox from "@isoftdata/svelte-checkbox";
|
|
3
|
+
import Icon from "@isoftdata/svelte-icon";
|
|
4
|
+
const { t: translate } = getContext("i18next") || { t: (_key, fallback) => fallback };
|
|
5
|
+
export let userSites;
|
|
6
|
+
export let siteLabel = "Site";
|
|
7
|
+
export let canEditSiteAccess = true;
|
|
8
|
+
export let siteAccessChange = void 0;
|
|
9
|
+
export let icon = "industry-windows";
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
class="card"
|
|
14
|
+
{...$$restProps}
|
|
15
|
+
>
|
|
16
|
+
<div class="card-header">
|
|
17
|
+
<h5 class="mb-0"><Icon {icon} /> {translate(`user.configuration.${siteLabel}Access`, `${siteLabel} Access`)}</h5>
|
|
18
|
+
</div>
|
|
19
|
+
<ul
|
|
20
|
+
class="list-group list-group-flush h-100"
|
|
21
|
+
style="overflow-y: auto"
|
|
22
|
+
>
|
|
23
|
+
{#each userSites as { code, name }, i}
|
|
24
|
+
<li class="list-group-item">
|
|
25
|
+
<Checkbox
|
|
26
|
+
label="{code} - {name}"
|
|
27
|
+
bind:checked={userSites[i].isAuthorized}
|
|
28
|
+
disabled={!canEditSiteAccess}
|
|
29
|
+
on:change={async () => {
|
|
30
|
+
await siteAccessChange?.(userSites[i])
|
|
31
|
+
}}
|
|
32
|
+
/>
|
|
33
|
+
</li>
|
|
34
|
+
{/each}
|
|
35
|
+
</ul>
|
|
36
|
+
<slot></slot>
|
|
37
|
+
</div>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { SvelteComponent } from "svelte";
|
|
2
|
+
import type { IconName, HTMLDivAttributes, SiteLabel } from './';
|
|
3
|
+
declare const __propDef: {
|
|
4
|
+
props: HTMLDivAttributes & {
|
|
5
|
+
userSites: Array<{
|
|
6
|
+
code: string;
|
|
7
|
+
name: string;
|
|
8
|
+
isAuthorized: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
siteLabel?: SiteLabel;
|
|
11
|
+
canEditSiteAccess?: boolean;
|
|
12
|
+
siteAccessChange?: ((siteAccess: {
|
|
13
|
+
code: string;
|
|
14
|
+
name: string;
|
|
15
|
+
isAuthorized: boolean;
|
|
16
|
+
}) => void | Promise<void>) | undefined;
|
|
17
|
+
icon?: IconName;
|
|
18
|
+
};
|
|
19
|
+
events: {
|
|
20
|
+
[evt: string]: CustomEvent<any>;
|
|
21
|
+
};
|
|
22
|
+
slots: {
|
|
23
|
+
default: {};
|
|
24
|
+
};
|
|
25
|
+
exports?: {} | undefined;
|
|
26
|
+
bindings?: string | undefined;
|
|
27
|
+
};
|
|
28
|
+
export type UserSiteAccessProps = typeof __propDef.props;
|
|
29
|
+
export type UserSiteAccessEvents = typeof __propDef.events;
|
|
30
|
+
export type UserSiteAccessSlots = typeof __propDef.slots;
|
|
31
|
+
export default class UserSiteAccess extends SvelteComponent<UserSiteAccessProps, UserSiteAccessEvents, UserSiteAccessSlots> {
|
|
32
|
+
}
|
|
33
|
+
export {};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { default as default, default as UserConfiguration } from './UserConfiguration.svelte';
|
|
2
|
+
export { default as UserAccountInfo } from './UserAccountInfo.svelte';
|
|
3
|
+
export { default as UserGroupMembership } from './UserGroupMembership.svelte';
|
|
4
|
+
export { default as PermissionList } from './PermissionList.svelte';
|
|
5
|
+
export { default as UserSiteAccess } from './UserSiteAccess.svelte';
|
|
6
|
+
export * from './util';
|
|
7
|
+
export type * from './util';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as default, default as UserConfiguration } from './UserConfiguration.svelte';
|
|
2
|
+
export { default as UserAccountInfo } from './UserAccountInfo.svelte';
|
|
3
|
+
export { default as UserGroupMembership } from './UserGroupMembership.svelte';
|
|
4
|
+
export { default as PermissionList } from './PermissionList.svelte';
|
|
5
|
+
export { default as UserSiteAccess } from './UserSiteAccess.svelte';
|
|
6
|
+
export * from './util';
|
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
import DeactivateUserModal from './DeactivateUserModal.svelte';
|
|
4
|
+
import PasswordSetModal from './PasswordSetModal.svelte';
|
|
5
|
+
import UserAccountInfo from './UserAccountInfo.svelte';
|
|
6
|
+
import UserSiteAccess from './UserSiteAccess.svelte';
|
|
7
|
+
import UserGroupMembership from './UserGroupMembership.svelte';
|
|
8
|
+
import type PermissionList from './PermissionList.svelte';
|
|
9
|
+
export interface UserAccount {
|
|
10
|
+
id: number;
|
|
11
|
+
name: string;
|
|
12
|
+
workEmail: string;
|
|
13
|
+
firstName: string;
|
|
14
|
+
lastName: string;
|
|
15
|
+
lockNotes: string | null;
|
|
16
|
+
recoveryEmail: string;
|
|
17
|
+
activationPIN: string;
|
|
18
|
+
status: 'ACTIVE' | 'DEACTIVATED' | 'LOCKED' | 'PENDING_ACTIVATION';
|
|
19
|
+
newPassword?: string;
|
|
20
|
+
lastPasswordResetDate?: Date;
|
|
21
|
+
lastAccessDate?: Date;
|
|
22
|
+
}
|
|
23
|
+
export type { IconName } from '@fortawesome/fontawesome-common-types';
|
|
24
|
+
export type HTMLDivAttributes = HTMLAttributes<HTMLDivElement>;
|
|
25
|
+
export type SiteLabel = string;
|
|
26
|
+
export type GenerateNewActivationPINFn = ComponentProps<UserAccountInfo>['generateNewActivationPIN'];
|
|
27
|
+
export type SuccessFn = ComponentProps<UserAccountInfo>['success'];
|
|
28
|
+
export type ErrorFn = ComponentProps<UserAccountInfo>['error'];
|
|
29
|
+
export type AccountInfoChangedFn = ComponentProps<UserAccountInfo>['accountInfoChanged'];
|
|
30
|
+
export type SendPasswordRecoveryTokenFn = ComponentProps<UserAccountInfo>['sendPasswordRecoveryToken'];
|
|
31
|
+
export type CanEditAccountInfo = ComponentProps<UserAccountInfo>['canEditAccountInfo'];
|
|
32
|
+
export type CanToggleActive = ComponentProps<UserAccountInfo>['canToggleActive'];
|
|
33
|
+
export type IsCreatingNewUser = ComponentProps<UserAccountInfo>['isCreatingNewUser'];
|
|
34
|
+
export type HasPermissionToChangePassword = ComponentProps<UserAccountInfo>['hasPermissionToChangePassword'];
|
|
35
|
+
export type ConfirmPasswordSetFn = ComponentProps<PasswordSetModal>['confirmPasswordSet'];
|
|
36
|
+
export type DeactivateUserFn = ComponentProps<DeactivateUserModal>['deactivateUser'];
|
|
37
|
+
export type UserSites = ComponentProps<UserSiteAccess>['userSites'];
|
|
38
|
+
export type CanEditSiteAccess = ComponentProps<UserSiteAccess>['canEditSiteAccess'];
|
|
39
|
+
export type SiteAccessChangeFn = ComponentProps<UserSiteAccess>['siteAccessChange'];
|
|
40
|
+
export type GroupMembership = ComponentProps<UserGroupMembership>['groupMembership'];
|
|
41
|
+
export type GroupMembershipChangeFn = ComponentProps<UserGroupMembership>['groupMembershipChange'];
|
|
42
|
+
export type CanEditGroupMembership = ComponentProps<UserGroupMembership>['canEditGroupMembership'];
|
|
43
|
+
export type Permissions = ComponentProps<PermissionList>['permissions'];
|
|
44
|
+
export type PermissionValueChangeFn = ComponentProps<PermissionList>['permissionValueChange'];
|
|
45
|
+
export type PermissionListCardHeaderTitle = ComponentProps<PermissionList>['cardHeaderTitle'];
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import DeactivateUserModal from './DeactivateUserModal.svelte';
|
|
2
|
+
import PasswordSetModal from './PasswordSetModal.svelte';
|
|
3
|
+
import UserAccountInfo from './UserAccountInfo.svelte';
|
|
4
|
+
import UserSiteAccess from './UserSiteAccess.svelte';
|
|
5
|
+
import UserGroupMembership from './UserGroupMembership.svelte';
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@isoftdata/svelte-user-configuration",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"dev": "vite dev",
|
|
6
|
+
"build": "vite build && npm run package",
|
|
7
|
+
"preview": "vite preview",
|
|
8
|
+
"package": "svelte-kit sync && svelte-package && publint",
|
|
9
|
+
"prepublishOnly": "npm run package",
|
|
10
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
11
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
12
|
+
"lint": "prettier --check . && eslint .",
|
|
13
|
+
"format": "prettier --write ."
|
|
14
|
+
},
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"svelte": "./dist/index.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": [
|
|
22
|
+
"**/*.css"
|
|
23
|
+
],
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"!dist/**/*.test.*",
|
|
27
|
+
"!dist/**/*.spec.*"
|
|
28
|
+
],
|
|
29
|
+
"peerDependencies": {
|
|
30
|
+
"svelte": "^4.0.0 || ^5.16.3"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@fortawesome/fontawesome-common-types": "^6.7.2",
|
|
34
|
+
"@isoftdata/prettier-config": "^1.0.3",
|
|
35
|
+
"@sveltejs/adapter-auto": "^3.0.0",
|
|
36
|
+
"@sveltejs/kit": "^2.0.0",
|
|
37
|
+
"@sveltejs/package": "^2.0.0",
|
|
38
|
+
"@sveltejs/vite-plugin-svelte": "^3.0.0",
|
|
39
|
+
"@types/eslint": "^9.6.0",
|
|
40
|
+
"eslint": "^9.0.0",
|
|
41
|
+
"eslint-config-prettier": "^9.1.0",
|
|
42
|
+
"eslint-plugin-svelte": "^2.36.0",
|
|
43
|
+
"globals": "^15.0.0",
|
|
44
|
+
"i18next": "^24.2.1",
|
|
45
|
+
"prettier": "^3.1.1",
|
|
46
|
+
"prettier-plugin-svelte": "^3.1.2",
|
|
47
|
+
"publint": "^0.2.0",
|
|
48
|
+
"svelte": "^4.2.7",
|
|
49
|
+
"svelte-check": "^4.0.0",
|
|
50
|
+
"type-fest": "^4.33.0",
|
|
51
|
+
"typescript": "^5.0.0",
|
|
52
|
+
"typescript-eslint": "^8.0.0",
|
|
53
|
+
"vite": "^5.0.11"
|
|
54
|
+
},
|
|
55
|
+
"svelte": "./dist/index.js",
|
|
56
|
+
"types": "./dist/index.d.ts",
|
|
57
|
+
"type": "module",
|
|
58
|
+
"prettier": "@isoftdata/prettier-config",
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"@isoftdata/browser-event": "^2.3.2",
|
|
61
|
+
"@isoftdata/svelte-button": "^1.4.1",
|
|
62
|
+
"@isoftdata/svelte-checkbox": "^1.5.1",
|
|
63
|
+
"@isoftdata/svelte-input": "^1.6.0",
|
|
64
|
+
"@isoftdata/svelte-modal": "^1.3.1",
|
|
65
|
+
"@isoftdata/svelte-password-fields": "^1.3.0",
|
|
66
|
+
"@isoftdata/svelte-select": "^1.5.0",
|
|
67
|
+
"@isoftdata/svelte-table": "^1.16.0",
|
|
68
|
+
"@isoftdata/svelte-textarea": "^1.2.0",
|
|
69
|
+
"just-camel-case": "^6.2.0",
|
|
70
|
+
"klona": "^2.0.6"
|
|
71
|
+
}
|
|
72
|
+
}
|