ldap-rest 0.0.1
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/LICENSE +661 -0
- package/README.md +48 -0
- package/bin/cleanup-external-users.mjs +219 -0
- package/bin/index.mjs +54 -0
- package/bin/sync-james.mjs +190 -0
- package/dist/abstract/ldapFlat.js +1 -0
- package/dist/abstract/plugin.js +1 -0
- package/dist/abstract/twakePlugin.js +1 -0
- package/dist/bin/index.js +1 -0
- package/dist/config/args.js +1 -0
- package/dist/lib/auth/base.js +1 -0
- package/dist/lib/authz/base.js +1 -0
- package/dist/lib/expressFormatedResponses.js +1 -0
- package/dist/lib/ldapActions.js +1 -0
- package/dist/lib/parseConfig.js +1 -0
- package/dist/lib/utils.js +1 -0
- package/dist/logger/winston.js +1 -0
- package/dist/plugins/auth/authzLinid1.js +1 -0
- package/dist/plugins/auth/authzPerBranch.js +1 -0
- package/dist/plugins/auth/crowdsec.js +1 -0
- package/dist/plugins/auth/llng.js +1 -0
- package/dist/plugins/auth/openidconnect.js +1 -0
- package/dist/plugins/auth/rateLimit.js +1 -0
- package/dist/plugins/auth/token.js +1 -0
- package/dist/plugins/configApi.js +1 -0
- package/dist/plugins/demo/helloworld.js +1 -0
- package/dist/plugins/ldap/bulkImport.js +1 -0
- package/dist/plugins/ldap/departmentSync.js +1 -0
- package/dist/plugins/ldap/externalUsersInGroups.js +1 -0
- package/dist/plugins/ldap/flatGeneric.js +1 -0
- package/dist/plugins/ldap/groups.js +1 -0
- package/dist/plugins/ldap/onChange.js +1 -0
- package/dist/plugins/ldap/organization.js +1 -0
- package/dist/plugins/ldap/trash.js +1 -0
- package/dist/plugins/priority.json.js +1 -0
- package/dist/plugins/static.js +1 -0
- package/dist/plugins/twake/calendarResources.js +1 -0
- package/dist/plugins/twake/james.js +1 -0
- package/dist/plugins/weblogs.js +1 -0
- package/dist/src/abstract/ldapFlat.d.ts +94 -0
- package/dist/src/abstract/plugin.d.ts +43 -0
- package/dist/src/abstract/twakePlugin.d.ts +90 -0
- package/dist/src/bin/index.d.ts +43 -0
- package/dist/src/browser/ldap-group-editor/LdapGroupEditor.d.ts +31 -0
- package/dist/src/browser/ldap-group-editor/api/GroupApiClient.d.ts +23 -0
- package/dist/src/browser/ldap-group-editor/components/CreateGroupModal.d.ts +19 -0
- package/dist/src/browser/ldap-group-editor/components/GroupPropertyEditor.d.ts +30 -0
- package/dist/src/browser/ldap-group-editor/components/GroupTree.d.ts +17 -0
- package/dist/src/browser/ldap-group-editor/components/MoveGroupModal.d.ts +20 -0
- package/dist/src/browser/ldap-group-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-group-editor/types.d.ts +58 -0
- package/dist/src/browser/ldap-resource-editor/api/ResourceApiClient.d.ts +53 -0
- package/dist/src/browser/ldap-resource-editor/cache/CacheManager.d.ts +65 -0
- package/dist/src/browser/ldap-resource-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-resource-editor/types.d.ts +78 -0
- package/dist/src/browser/ldap-tree-viewer/LdapTreeViewer.d.ts +26 -0
- package/dist/src/browser/ldap-tree-viewer/api/LdapApiClient.d.ts +30 -0
- package/dist/src/browser/ldap-tree-viewer/components/TreeNode.d.ts +17 -0
- package/dist/src/browser/ldap-tree-viewer/components/TreeRoot.d.ts +14 -0
- package/dist/src/browser/ldap-tree-viewer/index.d.ts +7 -0
- package/dist/src/browser/ldap-tree-viewer/store/Store.d.ts +18 -0
- package/dist/src/browser/ldap-tree-viewer/store/actions.d.ts +28 -0
- package/dist/src/browser/ldap-tree-viewer/store/reducers.d.ts +7 -0
- package/dist/src/browser/ldap-tree-viewer/types.d.ts +34 -0
- package/dist/src/browser/ldap-unit-editor/LdapUnitEditor.d.ts +27 -0
- package/dist/src/browser/ldap-unit-editor/api/UnitApiClient.d.ts +24 -0
- package/dist/src/browser/ldap-unit-editor/components/MoveUnitModal.d.ts +28 -0
- package/dist/src/browser/ldap-unit-editor/components/UnitPropertyEditor.d.ts +28 -0
- package/dist/src/browser/ldap-unit-editor/components/UnitTree.d.ts +18 -0
- package/dist/src/browser/ldap-unit-editor/index.d.ts +6 -0
- package/dist/src/browser/ldap-unit-editor/types.d.ts +55 -0
- package/dist/src/browser/ldap-user-editor/LdapUserEditor.d.ts +32 -0
- package/dist/src/browser/ldap-user-editor/api/UserApiClient.d.ts +55 -0
- package/dist/src/browser/ldap-user-editor/cache/CacheManager.d.ts +65 -0
- package/dist/src/browser/ldap-user-editor/components/MoveUserModal.d.ts +28 -0
- package/dist/src/browser/ldap-user-editor/components/PointerField.d.ts +22 -0
- package/dist/src/browser/ldap-user-editor/components/UserEditor.d.ts +46 -0
- package/dist/src/browser/ldap-user-editor/components/UserList.d.ts +24 -0
- package/dist/src/browser/ldap-user-editor/components/UserTree.d.ts +24 -0
- package/dist/src/browser/ldap-user-editor/index.d.ts +7 -0
- package/dist/src/browser/ldap-user-editor/types.d.ts +67 -0
- package/dist/src/browser/shared/components/Modal.d.ts +38 -0
- package/dist/src/browser/shared/components/StatusMessage.d.ts +48 -0
- package/dist/src/browser/shared/index.d.ts +10 -0
- package/dist/src/browser/shared/types/index.d.ts +65 -0
- package/dist/src/browser/shared/utils/dom.d.ts +33 -0
- package/dist/src/browser/shared/utils/form.d.ts +28 -0
- package/dist/src/browser/shared/utils/schema.d.ts +29 -0
- package/dist/src/config/args.d.ts +128 -0
- package/dist/src/config/schema.d.ts +18 -0
- package/dist/src/hooks.d.ts +76 -0
- package/dist/src/lib/auth/base.d.ts +9 -0
- package/dist/src/lib/authz/base.d.ts +63 -0
- package/dist/src/lib/expressFormatedResponses.d.ts +26 -0
- package/dist/src/lib/ldapActions.d.ts +91 -0
- package/dist/src/lib/parseConfig.d.ts +23 -0
- package/dist/src/lib/utils.d.ts +73 -0
- package/dist/src/logger/winston.d.ts +3 -0
- package/dist/src/plugins/auth/authzLinid1.d.ts +46 -0
- package/dist/src/plugins/auth/authzPerBranch.d.ts +59 -0
- package/dist/src/plugins/auth/crowdsec.d.ts +29 -0
- package/dist/src/plugins/auth/llng.d.ts +8 -0
- package/dist/src/plugins/auth/openidconnect.d.ts +11 -0
- package/dist/src/plugins/auth/rateLimit.d.ts +24 -0
- package/dist/src/plugins/auth/token.d.ts +17 -0
- package/dist/src/plugins/configApi.d.ts +24 -0
- package/dist/src/plugins/demo/helloworld.d.ts +12 -0
- package/dist/src/plugins/ldap/bulkImport.d.ts +48 -0
- package/dist/src/plugins/ldap/departmentSync.d.ts +35 -0
- package/dist/src/plugins/ldap/externalUsersInGroups.d.ts +21 -0
- package/dist/src/plugins/ldap/flatGeneric.d.ts +31 -0
- package/dist/src/plugins/ldap/groups.d.ts +95 -0
- package/dist/src/plugins/ldap/onChange.d.ts +32 -0
- package/dist/src/plugins/ldap/organization.d.ts +65 -0
- package/dist/src/plugins/ldap/trash.d.ts +52 -0
- package/dist/src/plugins/static.d.ts +7 -0
- package/dist/src/plugins/twake/calendarResources.d.ts +31 -0
- package/dist/src/plugins/twake/james.d.ts +83 -0
- package/dist/src/plugins/weblogs.d.ts +8 -0
- package/docs/DEVELOPER_GUIDE.md +298 -0
- package/docs/OPENAPI.md +440 -0
- package/docs/PLUGIN_DEPENDENCIES.md +612 -0
- package/docs/README.md +466 -0
- package/docs/api/REFERENCE.md +983 -0
- package/docs/api/REST_API.md +1237 -0
- package/docs/authentication.md +469 -0
- package/docs/authzLinid1.md +521 -0
- package/docs/authzPerBranch.md +596 -0
- package/docs/browser/LIBRARIES.md +1196 -0
- package/docs/examples/EXAMPLES.md +1659 -0
- package/docs/examples/twake-mailbox-type-nomenclature.ldif +37 -0
- package/docs/examples/twake-mailbox-type-schema-amendment.ldif +30 -0
- package/docs/jamesTeamMailboxes.md +343 -0
- package/docs/ldapBulkImport.md +722 -0
- package/docs/ldapExternalUsersInGroups.md +325 -0
- package/docs/ldapFlatGeneric.md +444 -0
- package/docs/ldapGroups.md +589 -0
- package/docs/ldapOrganizations.md +659 -0
- package/docs/ldapTrash.md +260 -0
- package/docs/linagora.png +0 -0
- package/docs/onChange.md +318 -0
- package/docs/plugins/DEVELOPMENT.md +1219 -0
- package/docs/presenterm-fr.md +721 -0
- package/docs/presenterm.md +721 -0
- package/docs/schemas/SCHEMAS.md +1212 -0
- package/docs/static.md +410 -0
- package/docs/twakeCalendarResources.md +148 -0
- package/docs/twakeJames.md +661 -0
- package/docs/weblogs.md +399 -0
- package/examples/web/ldap-group-editor.html +113 -0
- package/examples/web/ldap-tree-viewer.html +368 -0
- package/examples/web/ldap-unit-editor.html +108 -0
- package/examples/web/ldap-user-editor.html +412 -0
- package/package.json +458 -0
- package/static/browser/common-demo.css +450 -0
- package/static/browser/ldap-group-editor/LdapGroupEditor.js +207 -0
- package/static/browser/ldap-group-editor/LdapGroupEditor.js.map +1 -0
- package/static/browser/ldap-group-editor/api/GroupApiClient.js +112 -0
- package/static/browser/ldap-group-editor/api/GroupApiClient.js.map +1 -0
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js +212 -0
- package/static/browser/ldap-group-editor/components/CreateGroupModal.js.map +1 -0
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js +497 -0
- package/static/browser/ldap-group-editor/components/GroupPropertyEditor.js.map +1 -0
- package/static/browser/ldap-group-editor/components/GroupTree.js +153 -0
- package/static/browser/ldap-group-editor/components/GroupTree.js.map +1 -0
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js +235 -0
- package/static/browser/ldap-group-editor/components/MoveGroupModal.js.map +1 -0
- package/static/browser/ldap-group-editor/index.js +2 -0
- package/static/browser/ldap-group-editor/index.js.map +1 -0
- package/static/browser/ldap-group-editor/types.js +2 -0
- package/static/browser/ldap-group-editor/types.js.map +1 -0
- package/static/browser/ldap-group-editor.esm.js +1399 -0
- package/static/browser/ldap-group-editor.esm.js.map +1 -0
- package/static/browser/ldap-group-editor.js +1407 -0
- package/static/browser/ldap-group-editor.js.map +1 -0
- package/static/browser/ldap-group-editor.min.js +2 -0
- package/static/browser/ldap-group-editor.min.js.map +1 -0
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js +226 -0
- package/static/browser/ldap-resource-editor/api/ResourceApiClient.js.map +1 -0
- package/static/browser/ldap-resource-editor/cache/CacheManager.js +123 -0
- package/static/browser/ldap-resource-editor/cache/CacheManager.js.map +1 -0
- package/static/browser/ldap-resource-editor/index.js +2 -0
- package/static/browser/ldap-resource-editor/index.js.map +1 -0
- package/static/browser/ldap-resource-editor/types.js +2 -0
- package/static/browser/ldap-resource-editor/types.js.map +1 -0
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js +187 -0
- package/static/browser/ldap-tree-viewer/LdapTreeViewer.js.map +1 -0
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js +50 -0
- package/static/browser/ldap-tree-viewer/api/LdapApiClient.js.map +1 -0
- package/static/browser/ldap-tree-viewer/components/TreeNode.js +140 -0
- package/static/browser/ldap-tree-viewer/components/TreeNode.js.map +1 -0
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js +98 -0
- package/static/browser/ldap-tree-viewer/components/TreeRoot.js.map +1 -0
- package/static/browser/ldap-tree-viewer/index.js +2 -0
- package/static/browser/ldap-tree-viewer/index.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/Store.js +29 -0
- package/static/browser/ldap-tree-viewer/store/Store.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/actions.js +53 -0
- package/static/browser/ldap-tree-viewer/store/actions.js.map +1 -0
- package/static/browser/ldap-tree-viewer/store/reducers.js +79 -0
- package/static/browser/ldap-tree-viewer/store/reducers.js.map +1 -0
- package/static/browser/ldap-tree-viewer/types.js +2 -0
- package/static/browser/ldap-tree-viewer/types.js.map +1 -0
- package/static/browser/ldap-tree-viewer.css +2 -0
- package/static/browser/ldap-tree-viewer.css.map +1 -0
- package/static/browser/ldap-tree-viewer.esm.js +606 -0
- package/static/browser/ldap-tree-viewer.esm.js.map +1 -0
- package/static/browser/ldap-tree-viewer.js +614 -0
- package/static/browser/ldap-tree-viewer.js.map +1 -0
- package/static/browser/ldap-tree-viewer.min.js +2 -0
- package/static/browser/ldap-tree-viewer.min.js.map +1 -0
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js +119 -0
- package/static/browser/ldap-unit-editor/LdapUnitEditor.js.map +1 -0
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js +95 -0
- package/static/browser/ldap-unit-editor/api/UnitApiClient.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js +295 -0
- package/static/browser/ldap-unit-editor/components/MoveUnitModal.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js +321 -0
- package/static/browser/ldap-unit-editor/components/UnitPropertyEditor.js.map +1 -0
- package/static/browser/ldap-unit-editor/components/UnitTree.js +197 -0
- package/static/browser/ldap-unit-editor/components/UnitTree.js.map +1 -0
- package/static/browser/ldap-unit-editor/index.js +2 -0
- package/static/browser/ldap-unit-editor/index.js.map +1 -0
- package/static/browser/ldap-unit-editor/types.js +2 -0
- package/static/browser/ldap-unit-editor/types.js.map +1 -0
- package/static/browser/ldap-unit-editor.esm.js +1013 -0
- package/static/browser/ldap-unit-editor.esm.js.map +1 -0
- package/static/browser/ldap-unit-editor.js +1021 -0
- package/static/browser/ldap-unit-editor.js.map +1 -0
- package/static/browser/ldap-unit-editor.min.js +2 -0
- package/static/browser/ldap-unit-editor.min.js.map +1 -0
- package/static/browser/ldap-user-editor/LdapUserEditor.js +184 -0
- package/static/browser/ldap-user-editor/LdapUserEditor.js.map +1 -0
- package/static/browser/ldap-user-editor/api/UserApiClient.js +209 -0
- package/static/browser/ldap-user-editor/api/UserApiClient.js.map +1 -0
- package/static/browser/ldap-user-editor/cache/CacheManager.js +123 -0
- package/static/browser/ldap-user-editor/cache/CacheManager.js.map +1 -0
- package/static/browser/ldap-user-editor/components/MoveUserModal.js +302 -0
- package/static/browser/ldap-user-editor/components/MoveUserModal.js.map +1 -0
- package/static/browser/ldap-user-editor/components/PointerField.js +152 -0
- package/static/browser/ldap-user-editor/components/PointerField.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserEditor.js +608 -0
- package/static/browser/ldap-user-editor/components/UserEditor.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserList.js +181 -0
- package/static/browser/ldap-user-editor/components/UserList.js.map +1 -0
- package/static/browser/ldap-user-editor/components/UserTree.js +164 -0
- package/static/browser/ldap-user-editor/components/UserTree.js.map +1 -0
- package/static/browser/ldap-user-editor/index.js +2 -0
- package/static/browser/ldap-user-editor/index.js.map +1 -0
- package/static/browser/ldap-user-editor/types.js +2 -0
- package/static/browser/ldap-user-editor/types.js.map +1 -0
- package/static/browser/ldap-user-editor.css +2 -0
- package/static/browser/ldap-user-editor.css.map +1 -0
- package/static/browser/ldap-user-editor.esm.js +1897 -0
- package/static/browser/ldap-user-editor.esm.js.map +1 -0
- package/static/browser/ldap-user-editor.js +1905 -0
- package/static/browser/ldap-user-editor.js.map +1 -0
- package/static/browser/ldap-user-editor.min.js +2 -0
- package/static/browser/ldap-user-editor.min.js.map +1 -0
- package/static/browser/shared/components/Modal.js +121 -0
- package/static/browser/shared/components/Modal.js.map +1 -0
- package/static/browser/shared/components/StatusMessage.js +119 -0
- package/static/browser/shared/components/StatusMessage.js.map +1 -0
- package/static/browser/shared/index.js +7 -0
- package/static/browser/shared/index.js.map +1 -0
- package/static/browser/shared/types/index.js +37 -0
- package/static/browser/shared/types/index.js.map +1 -0
- package/static/browser/shared/utils/dom.js +78 -0
- package/static/browser/shared/utils/dom.js.map +1 -0
- package/static/browser/shared/utils/form.js +234 -0
- package/static/browser/shared/utils/form.js.map +1 -0
- package/static/browser/shared/utils/schema.js +121 -0
- package/static/browser/shared/utils/schema.js.map +1 -0
- package/static/browser/shared.esm.js +694 -0
- package/static/browser/shared.esm.js.map +1 -0
- package/static/browser/shared.js +723 -0
- package/static/browser/shared.js.map +1 -0
- package/static/browser/shared.min.js +2 -0
- package/static/browser/shared.min.js.map +1 -0
- package/static/schemas/ad/computers.json +92 -0
- package/static/schemas/ad/groups.json +55 -0
- package/static/schemas/ad/organizations.json +66 -0
- package/static/schemas/ad/users.json +116 -0
- package/static/schemas/standard/devices.json +72 -0
- package/static/schemas/standard/groups.json +54 -0
- package/static/schemas/standard/organizations.json +49 -0
- package/static/schemas/standard/users.json +86 -0
- package/static/schemas/twake/groups.json +75 -0
- package/static/schemas/twake/nomenclature/twakeAccountStatus.json +31 -0
- package/static/schemas/twake/nomenclature/twakeDeliveryMode.json +31 -0
- package/static/schemas/twake/nomenclature/twakeListType.json +31 -0
- package/static/schemas/twake/nomenclature/twakeMailboxType.json +31 -0
- package/static/schemas/twake/nomenclature/twakeTitle.json +31 -0
- package/static/schemas/twake/organizations.json +52 -0
- package/static/schemas/twake/positions.json +32 -0
- package/static/schemas/twake/users.json +112 -0
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM utility functions
|
|
3
|
+
* @module browser/shared/utils/dom
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Escape HTML special characters to prevent XSS attacks
|
|
7
|
+
* Reference: https://cheatsheetseries.owasp.org/cheatsheets/DOM_based_XSS_Prevention_Cheat_Sheet.html
|
|
8
|
+
*/
|
|
9
|
+
function escapeHtml(text) {
|
|
10
|
+
if (text == null)
|
|
11
|
+
return '';
|
|
12
|
+
const map = {
|
|
13
|
+
'&': '&',
|
|
14
|
+
'<': '<',
|
|
15
|
+
'>': '>',
|
|
16
|
+
'"': '"',
|
|
17
|
+
"'": ''',
|
|
18
|
+
'/': '/',
|
|
19
|
+
};
|
|
20
|
+
return String(text).replace(/[&<>"'/]/g, m => map[m]);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Convert camelCase or snake_case to Title Case
|
|
24
|
+
*/
|
|
25
|
+
function toTitleCase(text) {
|
|
26
|
+
const label = text.replace(/([A-Z])/g, ' $1').replace(/_/g, ' ');
|
|
27
|
+
return label.charAt(0).toUpperCase() + label.slice(1);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create an HTML element with attributes and children
|
|
31
|
+
*/
|
|
32
|
+
function createElement(tag, attributes = {}, children = []) {
|
|
33
|
+
const element = document.createElement(tag);
|
|
34
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
35
|
+
if (key === 'className') {
|
|
36
|
+
element.className = value;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
element.setAttribute(key, value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
for (const child of children) {
|
|
43
|
+
if (typeof child === 'string') {
|
|
44
|
+
element.appendChild(document.createTextNode(child));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
element.appendChild(child);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return element;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Set innerHTML safely (assumes content has been properly escaped)
|
|
54
|
+
*/
|
|
55
|
+
function setInnerHTML(element, html) {
|
|
56
|
+
element.innerHTML = html;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get element by ID with type safety
|
|
60
|
+
*/
|
|
61
|
+
function getElementByIdSafe(id) {
|
|
62
|
+
return document.getElementById(id);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Query selector with type safety
|
|
66
|
+
*/
|
|
67
|
+
function querySelectorSafe(selector, parent = document) {
|
|
68
|
+
return parent.querySelector(selector);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Query selector all with type safety
|
|
72
|
+
*/
|
|
73
|
+
function querySelectorAllSafe(selector, parent = document) {
|
|
74
|
+
return parent.querySelectorAll(selector);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Form utility functions
|
|
79
|
+
* @module browser/shared/utils/form
|
|
80
|
+
*/
|
|
81
|
+
/**
|
|
82
|
+
* Get user-friendly field label from field name
|
|
83
|
+
*/
|
|
84
|
+
function getFieldLabel(fieldName, attribute, customLabels) {
|
|
85
|
+
// Check custom labels first
|
|
86
|
+
if (customLabels && customLabels[fieldName]) {
|
|
87
|
+
return customLabels[fieldName];
|
|
88
|
+
}
|
|
89
|
+
// Common label mappings
|
|
90
|
+
const labelMap = {
|
|
91
|
+
ou: 'Unit Name',
|
|
92
|
+
l: 'Location',
|
|
93
|
+
telephoneNumber: 'Phone Number',
|
|
94
|
+
facsimileTelephoneNumber: 'Fax Number',
|
|
95
|
+
postalAddress: 'Postal Address',
|
|
96
|
+
twakeDepartmentPath: 'Department Path',
|
|
97
|
+
twakeLocalAdminLink: 'Local Administrators',
|
|
98
|
+
twakeMailboxType: 'Mailbox Type',
|
|
99
|
+
};
|
|
100
|
+
if (labelMap[fieldName]) {
|
|
101
|
+
return labelMap[fieldName];
|
|
102
|
+
}
|
|
103
|
+
// Convert to title case
|
|
104
|
+
return toTitleCase(fieldName);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Generate HTML for a form field based on schema attribute
|
|
108
|
+
*/
|
|
109
|
+
async function generateFormField(fieldName, attribute, options) {
|
|
110
|
+
const label = getFieldLabel(fieldName, attribute, options?.customLabels);
|
|
111
|
+
const required = attribute.required ? 'required' : '';
|
|
112
|
+
const labelClass = attribute.required ? 'required' : '';
|
|
113
|
+
let fieldHtml = '';
|
|
114
|
+
if (attribute.type === 'pointer') {
|
|
115
|
+
// Pointer field - load options and display select
|
|
116
|
+
const branch = attribute.branch?.[0];
|
|
117
|
+
if (branch && options?.pointerOptionsLoader) {
|
|
118
|
+
const pointerOptions = await options.pointerOptionsLoader(branch);
|
|
119
|
+
fieldHtml = `
|
|
120
|
+
<div class="form-group">
|
|
121
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
122
|
+
<select id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" class="form-select" ${required}>
|
|
123
|
+
<option value="">Select...</option>
|
|
124
|
+
${pointerOptions
|
|
125
|
+
.map(opt => `
|
|
126
|
+
<option value="${escapeHtml(opt.dn)}">${escapeHtml(opt.label)}</option>
|
|
127
|
+
`)
|
|
128
|
+
.join('')}
|
|
129
|
+
</select>
|
|
130
|
+
${fieldName === 'twakeMailboxType' ? '<div class="help-text">Select "mailingList" or "teamMailbox" to enable mail functionality</div>' : ''}
|
|
131
|
+
</div>
|
|
132
|
+
`;
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Fallback if no branch defined
|
|
136
|
+
fieldHtml = `
|
|
137
|
+
<div class="form-group">
|
|
138
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
139
|
+
<input type="text" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
|
|
140
|
+
<div class="help-text">Enter DN (Distinguished Name)</div>
|
|
141
|
+
</div>
|
|
142
|
+
`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
else if (attribute.type === 'array' &&
|
|
146
|
+
attribute.items?.type === 'pointer') {
|
|
147
|
+
// Array of pointers - will be handled dynamically
|
|
148
|
+
const branch = attribute.items.branch?.[0];
|
|
149
|
+
if (branch) {
|
|
150
|
+
fieldHtml = `
|
|
151
|
+
<div class="form-group">
|
|
152
|
+
<label class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
153
|
+
<div class="array-field pointer-array-field" data-field="${escapeHtml(fieldName)}" data-branch="${escapeHtml(branch)}">
|
|
154
|
+
<button type="button" class="btn btn-secondary add-pointer-item" data-field="${escapeHtml(fieldName)}">
|
|
155
|
+
<span class="material-icons">add</span>
|
|
156
|
+
Add ${escapeHtml(label)}
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
<input type="hidden" name="${escapeHtml(fieldName)}" id="${escapeHtml(fieldName)}-hidden" value="" />
|
|
160
|
+
</div>
|
|
161
|
+
`;
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
// Fallback
|
|
165
|
+
fieldHtml = `
|
|
166
|
+
<div class="form-group">
|
|
167
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
168
|
+
<textarea id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required}></textarea>
|
|
169
|
+
<div class="help-text">Enter one DN per line</div>
|
|
170
|
+
</div>
|
|
171
|
+
`;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (attribute.type === 'array' && attribute.items?.type === 'string') {
|
|
175
|
+
// Array field - use textarea
|
|
176
|
+
fieldHtml = `
|
|
177
|
+
<div class="form-group">
|
|
178
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
179
|
+
<textarea id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required}></textarea>
|
|
180
|
+
<div class="help-text">Enter one value per line</div>
|
|
181
|
+
</div>
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
else if (attribute.type === 'number' || attribute.type === 'integer') {
|
|
185
|
+
// Number field
|
|
186
|
+
fieldHtml = `
|
|
187
|
+
<div class="form-group">
|
|
188
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
189
|
+
<input type="number" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
|
|
190
|
+
</div>
|
|
191
|
+
`;
|
|
192
|
+
}
|
|
193
|
+
else if (attribute.type === 'string') {
|
|
194
|
+
// String field - use input with appropriate type
|
|
195
|
+
const type = fieldName.toLowerCase().includes('password') ||
|
|
196
|
+
fieldName.toLowerCase().includes('pwd')
|
|
197
|
+
? 'password'
|
|
198
|
+
: fieldName.toLowerCase().includes('mail')
|
|
199
|
+
? 'email'
|
|
200
|
+
: fieldName.toLowerCase().includes('phone') ||
|
|
201
|
+
fieldName.toLowerCase().includes('telephone')
|
|
202
|
+
? 'tel'
|
|
203
|
+
: 'text';
|
|
204
|
+
fieldHtml = `
|
|
205
|
+
<div class="form-group">
|
|
206
|
+
<label for="${escapeHtml(fieldName)}" class="${escapeHtml(labelClass)}">${escapeHtml(label)}</label>
|
|
207
|
+
<input type="${escapeHtml(type)}" id="${escapeHtml(fieldName)}" name="${escapeHtml(fieldName)}" ${required} />
|
|
208
|
+
</div>
|
|
209
|
+
`;
|
|
210
|
+
}
|
|
211
|
+
return fieldHtml;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Process form data and convert types based on schema
|
|
215
|
+
*/
|
|
216
|
+
function processFormData(formData, schema) {
|
|
217
|
+
const data = {};
|
|
218
|
+
// Collect form data
|
|
219
|
+
for (const [key, value] of formData) {
|
|
220
|
+
if (data[key]) {
|
|
221
|
+
// Multiple values for the same key - convert to array
|
|
222
|
+
if (!Array.isArray(data[key])) {
|
|
223
|
+
data[key] = [data[key]];
|
|
224
|
+
}
|
|
225
|
+
data[key].push(value);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
data[key] = value;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Process special field types based on schema
|
|
232
|
+
for (const [fieldName, attribute] of Object.entries(schema)) {
|
|
233
|
+
if (!data[fieldName])
|
|
234
|
+
continue;
|
|
235
|
+
if (attribute.type === 'array' && typeof data[fieldName] === 'string') {
|
|
236
|
+
// Split by newlines and filter empty lines
|
|
237
|
+
data[fieldName] = data[fieldName]
|
|
238
|
+
.split('\n')
|
|
239
|
+
.map(line => line.trim())
|
|
240
|
+
.filter(line => line.length > 0);
|
|
241
|
+
}
|
|
242
|
+
else if ((attribute.type === 'number' || attribute.type === 'integer') &&
|
|
243
|
+
typeof data[fieldName] === 'string') {
|
|
244
|
+
// Convert to number
|
|
245
|
+
data[fieldName] =
|
|
246
|
+
attribute.type === 'integer'
|
|
247
|
+
? parseInt(data[fieldName], 10)
|
|
248
|
+
: parseFloat(data[fieldName]);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// Clean up: remove empty strings, null values, and empty arrays
|
|
252
|
+
const cleanedData = {};
|
|
253
|
+
for (const [key, value] of Object.entries(data)) {
|
|
254
|
+
if (value === '' || value === null || value === undefined) {
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (Array.isArray(value) && value.length === 0) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
cleanedData[key] = value;
|
|
261
|
+
}
|
|
262
|
+
return cleanedData;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Collect values from pointer array fields
|
|
266
|
+
*/
|
|
267
|
+
function collectPointerArrayValues(container) {
|
|
268
|
+
const values = {};
|
|
269
|
+
const pointerArrayFields = container.querySelectorAll('.pointer-array-field');
|
|
270
|
+
pointerArrayFields.forEach(field => {
|
|
271
|
+
const fieldName = field.getAttribute('data-field');
|
|
272
|
+
if (!fieldName)
|
|
273
|
+
return;
|
|
274
|
+
const selects = field.querySelectorAll('.pointer-array-select');
|
|
275
|
+
const fieldValues = Array.from(selects)
|
|
276
|
+
.map(select => select.value)
|
|
277
|
+
.filter(val => val); // Remove empty values
|
|
278
|
+
if (fieldValues.length > 0) {
|
|
279
|
+
values[fieldName] = fieldValues;
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return values;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Group schema fields by category
|
|
286
|
+
*/
|
|
287
|
+
function groupSchemaFields(schema, categorizer) {
|
|
288
|
+
const groups = new Map();
|
|
289
|
+
for (const [fieldName, attribute] of Object.entries(schema)) {
|
|
290
|
+
if (attribute.fixed)
|
|
291
|
+
continue;
|
|
292
|
+
if (fieldName === 'objectClass')
|
|
293
|
+
continue;
|
|
294
|
+
const category = categorizer
|
|
295
|
+
? categorizer(fieldName, attribute)
|
|
296
|
+
: attribute.group || 'default';
|
|
297
|
+
if (category === null)
|
|
298
|
+
continue; // Skip this field
|
|
299
|
+
if (!groups.has(category)) {
|
|
300
|
+
groups.set(category, []);
|
|
301
|
+
}
|
|
302
|
+
groups.get(category).push([fieldName, attribute]);
|
|
303
|
+
}
|
|
304
|
+
return groups;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Schema utility functions
|
|
309
|
+
* @module browser/shared/utils/schema
|
|
310
|
+
*/
|
|
311
|
+
/**
|
|
312
|
+
* Cache for pointer options to avoid repeated API calls
|
|
313
|
+
*/
|
|
314
|
+
const pointerOptionsCache = new Map();
|
|
315
|
+
/**
|
|
316
|
+
* Load pointer options from API
|
|
317
|
+
*/
|
|
318
|
+
async function loadPointerOptions(branch, apiLoader, useCache = true) {
|
|
319
|
+
if (useCache && pointerOptionsCache.has(branch)) {
|
|
320
|
+
return pointerOptionsCache.get(branch);
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
const options = await apiLoader(branch);
|
|
324
|
+
if (useCache) {
|
|
325
|
+
pointerOptionsCache.set(branch, options);
|
|
326
|
+
}
|
|
327
|
+
return options;
|
|
328
|
+
}
|
|
329
|
+
catch (error) {
|
|
330
|
+
console.error('Failed to load pointer options for branch:', branch, error);
|
|
331
|
+
return [];
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Clear pointer options cache
|
|
336
|
+
*/
|
|
337
|
+
function clearPointerOptionsCache() {
|
|
338
|
+
pointerOptionsCache.clear();
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Replace placeholders in schema branches
|
|
342
|
+
*/
|
|
343
|
+
function replacePlaceholders(value, replacements) {
|
|
344
|
+
const replace = (str) => {
|
|
345
|
+
let result = str;
|
|
346
|
+
for (const [key, val] of Object.entries(replacements)) {
|
|
347
|
+
result = result.replace(new RegExp(key, 'g'), val);
|
|
348
|
+
}
|
|
349
|
+
return result;
|
|
350
|
+
};
|
|
351
|
+
if (Array.isArray(value)) {
|
|
352
|
+
return value.map(replace);
|
|
353
|
+
}
|
|
354
|
+
return replace(value);
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* Replace placeholders in schema attributes
|
|
358
|
+
*/
|
|
359
|
+
function replaceSchemaPlaceholders(schema, replacements) {
|
|
360
|
+
const processValue = (value) => {
|
|
361
|
+
if (typeof value === 'string') {
|
|
362
|
+
return replacePlaceholders(value, replacements);
|
|
363
|
+
}
|
|
364
|
+
if (Array.isArray(value)) {
|
|
365
|
+
return value.map(processValue);
|
|
366
|
+
}
|
|
367
|
+
if (value && typeof value === 'object') {
|
|
368
|
+
return processObject(value);
|
|
369
|
+
}
|
|
370
|
+
return value;
|
|
371
|
+
};
|
|
372
|
+
const processObject = (obj) => {
|
|
373
|
+
const result = {};
|
|
374
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
375
|
+
result[key] = processValue(value);
|
|
376
|
+
}
|
|
377
|
+
return result;
|
|
378
|
+
};
|
|
379
|
+
return processObject(schema);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Fetch organization path from API
|
|
383
|
+
*/
|
|
384
|
+
async function getOrganizationPath(orgDn, apiBaseUrl) {
|
|
385
|
+
try {
|
|
386
|
+
const response = await fetch(`${apiBaseUrl}/api/v1/ldap/organizations/${encodeURIComponent(orgDn)}`);
|
|
387
|
+
if (!response.ok) {
|
|
388
|
+
console.error('Failed to fetch organization:', response.status);
|
|
389
|
+
return orgDn;
|
|
390
|
+
}
|
|
391
|
+
const org = await response.json();
|
|
392
|
+
// Use twakeDepartmentPath if available (Twake schema)
|
|
393
|
+
if (org.twakeDepartmentPath) {
|
|
394
|
+
const pathValue = Array.isArray(org.twakeDepartmentPath)
|
|
395
|
+
? org.twakeDepartmentPath[0]
|
|
396
|
+
: org.twakeDepartmentPath;
|
|
397
|
+
return pathValue;
|
|
398
|
+
}
|
|
399
|
+
// Fallback to ou name for non-Twake schemas
|
|
400
|
+
if (org.ou) {
|
|
401
|
+
const ouValue = Array.isArray(org.ou) ? org.ou[0] : org.ou;
|
|
402
|
+
return ouValue;
|
|
403
|
+
}
|
|
404
|
+
// Last resort: use DN
|
|
405
|
+
return orgDn;
|
|
406
|
+
}
|
|
407
|
+
catch (error) {
|
|
408
|
+
console.error('Failed to get organization info:', error);
|
|
409
|
+
return orgDn;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Load schema from URL or object
|
|
414
|
+
*/
|
|
415
|
+
async function loadSchema(schemaSource) {
|
|
416
|
+
if (typeof schemaSource === 'string') {
|
|
417
|
+
const response = await fetch(schemaSource);
|
|
418
|
+
if (!response.ok) {
|
|
419
|
+
throw new Error(`Failed to load schema from ${schemaSource}`);
|
|
420
|
+
}
|
|
421
|
+
return (await response.json());
|
|
422
|
+
}
|
|
423
|
+
return schemaSource;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Reusable Modal component
|
|
428
|
+
* @module browser/shared/components/Modal
|
|
429
|
+
*/
|
|
430
|
+
class Modal {
|
|
431
|
+
constructor(options) {
|
|
432
|
+
this.overlay = null;
|
|
433
|
+
this.options = options;
|
|
434
|
+
this.overlay = getElementByIdSafe(options.id);
|
|
435
|
+
if (!this.overlay) {
|
|
436
|
+
throw new Error(`Modal overlay with id "${options.id}" not found`);
|
|
437
|
+
}
|
|
438
|
+
this.attachEventListeners();
|
|
439
|
+
}
|
|
440
|
+
attachEventListeners() {
|
|
441
|
+
if (!this.overlay)
|
|
442
|
+
return;
|
|
443
|
+
// Close button
|
|
444
|
+
const closeBtn = this.overlay.querySelector('.close-button');
|
|
445
|
+
if (closeBtn) {
|
|
446
|
+
closeBtn.addEventListener('click', () => this.close());
|
|
447
|
+
}
|
|
448
|
+
// Cancel button
|
|
449
|
+
const cancelBtn = this.overlay.querySelector('[data-modal-cancel]');
|
|
450
|
+
if (cancelBtn) {
|
|
451
|
+
cancelBtn.addEventListener('click', () => this.close());
|
|
452
|
+
}
|
|
453
|
+
// Click outside to close
|
|
454
|
+
this.overlay.addEventListener('click', e => {
|
|
455
|
+
if (e.target === this.overlay) {
|
|
456
|
+
this.close();
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
// Escape key to close
|
|
460
|
+
document.addEventListener('keydown', e => {
|
|
461
|
+
if (e.key === 'Escape' && this.isOpen()) {
|
|
462
|
+
this.close();
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
open() {
|
|
467
|
+
if (this.overlay) {
|
|
468
|
+
this.overlay.classList.add('active');
|
|
469
|
+
document.body.style.overflow = 'hidden'; // Prevent background scroll
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
close() {
|
|
473
|
+
if (this.overlay) {
|
|
474
|
+
this.overlay.classList.remove('active');
|
|
475
|
+
document.body.style.overflow = ''; // Restore scroll
|
|
476
|
+
if (this.options.onClose) {
|
|
477
|
+
this.options.onClose();
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
isOpen() {
|
|
482
|
+
return this.overlay?.classList.contains('active') || false;
|
|
483
|
+
}
|
|
484
|
+
getFormElement() {
|
|
485
|
+
return this.overlay?.querySelector('form') || null;
|
|
486
|
+
}
|
|
487
|
+
resetForm() {
|
|
488
|
+
const form = this.getFormElement();
|
|
489
|
+
if (form) {
|
|
490
|
+
form.reset();
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
setContent(html) {
|
|
494
|
+
const contentEl = this.overlay?.querySelector('[data-modal-content]');
|
|
495
|
+
if (contentEl) {
|
|
496
|
+
contentEl.innerHTML = html;
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
setTitle(title) {
|
|
500
|
+
const titleEl = this.overlay?.querySelector('.modal-header h2');
|
|
501
|
+
if (titleEl) {
|
|
502
|
+
titleEl.textContent = title;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Create modal HTML structure
|
|
507
|
+
*/
|
|
508
|
+
static createModalHTML(options) {
|
|
509
|
+
const formId = options.formId || `${options.id}-form`;
|
|
510
|
+
const submitLabel = options.submitLabel || 'Submit';
|
|
511
|
+
return `
|
|
512
|
+
<div class="modal-overlay" id="${escapeHtml(options.id)}">
|
|
513
|
+
<div class="modal">
|
|
514
|
+
<div class="modal-header">
|
|
515
|
+
<h2>${escapeHtml(options.title)}</h2>
|
|
516
|
+
<button class="close-button" type="button">
|
|
517
|
+
<span class="material-icons">close</span>
|
|
518
|
+
</button>
|
|
519
|
+
</div>
|
|
520
|
+
<form id="${escapeHtml(formId)}">
|
|
521
|
+
<div data-modal-content></div>
|
|
522
|
+
<div class="modal-footer">
|
|
523
|
+
<button type="button" class="secondary" data-modal-cancel>
|
|
524
|
+
Cancel
|
|
525
|
+
</button>
|
|
526
|
+
<button type="submit" class="primary">${escapeHtml(submitLabel)}</button>
|
|
527
|
+
</div>
|
|
528
|
+
</form>
|
|
529
|
+
</div>
|
|
530
|
+
</div>
|
|
531
|
+
`;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Create and inject modal into DOM
|
|
535
|
+
*/
|
|
536
|
+
static create(options) {
|
|
537
|
+
const html = Modal.createModalHTML(options);
|
|
538
|
+
document.body.insertAdjacentHTML('beforeend', html);
|
|
539
|
+
return new Modal(options);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Status message component for displaying notifications
|
|
545
|
+
* @module browser/shared/components/StatusMessage
|
|
546
|
+
*/
|
|
547
|
+
class StatusMessage {
|
|
548
|
+
constructor(options) {
|
|
549
|
+
this.container = null;
|
|
550
|
+
this.timeoutId = null;
|
|
551
|
+
this.options = {
|
|
552
|
+
duration: 5000,
|
|
553
|
+
dismissible: true,
|
|
554
|
+
...options,
|
|
555
|
+
};
|
|
556
|
+
this.container = getElementByIdSafe(options.containerId);
|
|
557
|
+
if (!this.container) {
|
|
558
|
+
throw new Error(`Status container with id "${options.containerId}" not found`);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Show a status message
|
|
563
|
+
*/
|
|
564
|
+
show(message, type = 'info') {
|
|
565
|
+
if (!this.container)
|
|
566
|
+
return;
|
|
567
|
+
// Clear any existing timeout
|
|
568
|
+
if (this.timeoutId !== null) {
|
|
569
|
+
window.clearTimeout(this.timeoutId);
|
|
570
|
+
this.timeoutId = null;
|
|
571
|
+
}
|
|
572
|
+
const iconMap = {
|
|
573
|
+
error: 'error_outline',
|
|
574
|
+
success: 'check_circle',
|
|
575
|
+
info: 'info',
|
|
576
|
+
warning: 'warning',
|
|
577
|
+
};
|
|
578
|
+
const icon = iconMap[type] || 'info';
|
|
579
|
+
let html = `
|
|
580
|
+
<div class="status-message ${escapeHtml(type)}">
|
|
581
|
+
<span class="material-icons">${icon}</span>
|
|
582
|
+
<span class="status-message-text">${escapeHtml(message)}</span>
|
|
583
|
+
`;
|
|
584
|
+
if (this.options.dismissible) {
|
|
585
|
+
html += `
|
|
586
|
+
<button class="status-message-close" type="button" aria-label="Close">
|
|
587
|
+
<span class="material-icons">close</span>
|
|
588
|
+
</button>
|
|
589
|
+
`;
|
|
590
|
+
}
|
|
591
|
+
html += '</div>';
|
|
592
|
+
this.container.innerHTML = html;
|
|
593
|
+
// Attach close button handler
|
|
594
|
+
if (this.options.dismissible) {
|
|
595
|
+
const closeBtn = this.container.querySelector('.status-message-close');
|
|
596
|
+
if (closeBtn) {
|
|
597
|
+
closeBtn.addEventListener('click', () => this.hide());
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
// Auto-hide after duration
|
|
601
|
+
if (this.options.duration && this.options.duration > 0) {
|
|
602
|
+
this.timeoutId = window.setTimeout(() => {
|
|
603
|
+
this.hide();
|
|
604
|
+
}, this.options.duration);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Show error message
|
|
609
|
+
*/
|
|
610
|
+
error(message) {
|
|
611
|
+
this.show(message, 'error');
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Show success message
|
|
615
|
+
*/
|
|
616
|
+
success(message) {
|
|
617
|
+
this.show(message, 'success');
|
|
618
|
+
}
|
|
619
|
+
/**
|
|
620
|
+
* Show info message
|
|
621
|
+
*/
|
|
622
|
+
info(message) {
|
|
623
|
+
this.show(message, 'info');
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Show warning message
|
|
627
|
+
*/
|
|
628
|
+
warning(message) {
|
|
629
|
+
this.show(message, 'warning');
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Hide the status message
|
|
633
|
+
*/
|
|
634
|
+
hide() {
|
|
635
|
+
if (this.container) {
|
|
636
|
+
this.container.innerHTML = '';
|
|
637
|
+
}
|
|
638
|
+
if (this.timeoutId !== null) {
|
|
639
|
+
window.clearTimeout(this.timeoutId);
|
|
640
|
+
this.timeoutId = null;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Get the container element
|
|
645
|
+
*/
|
|
646
|
+
getContainer() {
|
|
647
|
+
return this.container;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
/**
|
|
651
|
+
* Simple functional API for showing status messages
|
|
652
|
+
*/
|
|
653
|
+
function showStatus(containerId, message, type = 'info', duration = 5000) {
|
|
654
|
+
const statusMessage = new StatusMessage({ containerId, duration });
|
|
655
|
+
statusMessage.show(message, type);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Shared TypeScript types for browser components
|
|
660
|
+
* @module browser/shared/types
|
|
661
|
+
*/
|
|
662
|
+
const THEMES = {
|
|
663
|
+
purple: {
|
|
664
|
+
name: 'Purple',
|
|
665
|
+
primaryColor: '#6200ee',
|
|
666
|
+
primaryColorDark: '#3700b3',
|
|
667
|
+
gradientStart: '#667eea',
|
|
668
|
+
gradientEnd: '#764ba2',
|
|
669
|
+
},
|
|
670
|
+
blue: {
|
|
671
|
+
name: 'Blue',
|
|
672
|
+
primaryColor: '#185a9d',
|
|
673
|
+
primaryColorDark: '#0d3a6b',
|
|
674
|
+
gradientStart: '#43cea2',
|
|
675
|
+
gradientEnd: '#185a9d',
|
|
676
|
+
},
|
|
677
|
+
pink: {
|
|
678
|
+
name: 'Pink',
|
|
679
|
+
primaryColor: '#f5576c',
|
|
680
|
+
primaryColorDark: '#d43d50',
|
|
681
|
+
gradientStart: '#f093fb',
|
|
682
|
+
gradientEnd: '#f5576c',
|
|
683
|
+
},
|
|
684
|
+
green: {
|
|
685
|
+
name: 'Green',
|
|
686
|
+
primaryColor: '#00c853',
|
|
687
|
+
primaryColorDark: '#009624',
|
|
688
|
+
gradientStart: '#56ab2f',
|
|
689
|
+
gradientEnd: '#a8e063',
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
export { Modal, StatusMessage, THEMES, clearPointerOptionsCache, collectPointerArrayValues, createElement, escapeHtml, generateFormField, getElementByIdSafe, getFieldLabel, getOrganizationPath, groupSchemaFields, loadPointerOptions, loadSchema, processFormData, querySelectorAllSafe, querySelectorSafe, replacePlaceholders, replaceSchemaPlaceholders, setInnerHTML, showStatus, toTitleCase };
|
|
694
|
+
//# sourceMappingURL=shared.esm.js.map
|