@keycloakify/keycloak-account-ui 25.0.1-rc.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.
Files changed (349) hide show
  1. package/KeycloakAccountUi.d.ts +3 -0
  2. package/KeycloakAccountUi.js +29 -0
  3. package/KeycloakAccountUi.js.map +1 -0
  4. package/LICENSE +2 -0
  5. package/README.md +39 -0
  6. package/account-security/AccountRow.d.ts +8 -0
  7. package/account-security/AccountRow.js +36 -0
  8. package/account-security/AccountRow.js.map +1 -0
  9. package/account-security/DeviceActivity.d.ts +2 -0
  10. package/account-security/DeviceActivity.js +70 -0
  11. package/account-security/DeviceActivity.js.map +1 -0
  12. package/account-security/LinkedAccounts.d.ts +2 -0
  13. package/account-security/LinkedAccounts.js +25 -0
  14. package/account-security/LinkedAccounts.js.map +1 -0
  15. package/account-security/SigningIn.d.ts +2 -0
  16. package/account-security/SigningIn.js +65 -0
  17. package/account-security/SigningIn.js.map +1 -0
  18. package/api/constants.d.ts +2 -0
  19. package/api/constants.js +3 -0
  20. package/api/constants.js.map +1 -0
  21. package/api/methods.d.ts +25 -0
  22. package/api/methods.js +73 -0
  23. package/api/methods.js.map +1 -0
  24. package/api/parse-links.d.ts +5 -0
  25. package/api/parse-links.js +23 -0
  26. package/api/parse-links.js.map +1 -0
  27. package/api/parse-response.d.ts +3 -0
  28. package/api/parse-response.js +40 -0
  29. package/api/parse-response.js.map +1 -0
  30. package/api/representations.d.ts +202 -0
  31. package/api/representations.js +3 -0
  32. package/api/representations.js.map +1 -0
  33. package/api/request.d.ts +12 -0
  34. package/api/request.js +33 -0
  35. package/api/request.js.map +1 -0
  36. package/api.d.ts +13 -0
  37. package/api.js +52 -0
  38. package/api.js.map +1 -0
  39. package/applications/Applications.d.ts +2 -0
  40. package/applications/Applications.js +52 -0
  41. package/applications/Applications.js.map +1 -0
  42. package/components/datalist/EmptyRow.d.ts +5 -0
  43. package/components/datalist/EmptyRow.js +20 -0
  44. package/components/datalist/EmptyRow.js.map +1 -0
  45. package/components/page/Page.d.ts +7 -0
  46. package/components/page/Page.js +6 -0
  47. package/components/page/Page.js.map +1 -0
  48. package/content/ContentComponent.d.ts +2 -0
  49. package/content/ContentComponent.js +36 -0
  50. package/content/ContentComponent.js.map +1 -0
  51. package/content/fetchContent.d.ts +3 -0
  52. package/content/fetchContent.js +6 -0
  53. package/content/fetchContent.js.map +1 -0
  54. package/environment.d.ts +25 -0
  55. package/environment.js +3 -0
  56. package/environment.js.map +1 -0
  57. package/groups/Groups.d.ts +2 -0
  58. package/groups/Groups.js +45 -0
  59. package/groups/Groups.js.map +1 -0
  60. package/i18n.d.ts +4 -0
  61. package/i18n.js +31 -0
  62. package/i18n.js.map +1 -0
  63. package/oid4vci/Oid4Vci.d.ts +2 -0
  64. package/oid4vci/Oid4Vci.js +54 -0
  65. package/oid4vci/Oid4Vci.js.map +1 -0
  66. package/package.json +428 -0
  67. package/personal-info/PersonalInfo.d.ts +2 -0
  68. package/personal-info/PersonalInfo.js +63 -0
  69. package/personal-info/PersonalInfo.js.map +1 -0
  70. package/resources/EditTheResource.d.ts +8 -0
  71. package/resources/EditTheResource.js +36 -0
  72. package/resources/EditTheResource.js.map +1 -0
  73. package/resources/PermissionRequest.d.ts +7 -0
  74. package/resources/PermissionRequest.js +39 -0
  75. package/resources/PermissionRequest.js.map +1 -0
  76. package/resources/ResourceToolbar.d.ts +12 -0
  77. package/resources/ResourceToolbar.js +24 -0
  78. package/resources/ResourceToolbar.js.map +1 -0
  79. package/resources/Resources.d.ts +2 -0
  80. package/resources/Resources.js +13 -0
  81. package/resources/Resources.js.map +1 -0
  82. package/resources/ResourcesTab.d.ts +5 -0
  83. package/resources/ResourcesTab.js +103 -0
  84. package/resources/ResourcesTab.js.map +1 -0
  85. package/resources/ShareTheResource.d.ts +9 -0
  86. package/resources/ShareTheResource.js +70 -0
  87. package/resources/ShareTheResource.js.map +1 -0
  88. package/resources/SharedWith.d.ts +6 -0
  89. package/resources/SharedWith.js +8 -0
  90. package/resources/SharedWith.js.map +1 -0
  91. package/root/ErrorPage.d.ts +5 -0
  92. package/root/ErrorPage.js +29 -0
  93. package/root/ErrorPage.js.map +1 -0
  94. package/root/Header.d.ts +1 -0
  95. package/root/Header.js +31 -0
  96. package/root/Header.js.map +1 -0
  97. package/root/PageNav.d.ts +23 -0
  98. package/root/PageNav.js +44 -0
  99. package/root/PageNav.js.map +1 -0
  100. package/root/Root.d.ts +1 -0
  101. package/root/Root.js +12 -0
  102. package/root/Root.js.map +1 -0
  103. package/root/header.module.css +3 -0
  104. package/routes.d.ts +15 -0
  105. package/routes.js +69 -0
  106. package/routes.js.map +1 -0
  107. package/src/KeycloakAccountUi.tsx +36 -0
  108. package/src/account-security/AccountRow.tsx +136 -0
  109. package/src/account-security/DeviceActivity.tsx +253 -0
  110. package/src/account-security/LinkedAccounts.tsx +86 -0
  111. package/src/account-security/SigningIn.tsx +251 -0
  112. package/src/api/constants.ts +2 -0
  113. package/src/api/methods.ts +158 -0
  114. package/src/api/parse-links.ts +28 -0
  115. package/src/api/parse-response.ts +53 -0
  116. package/src/api/representations.ts +219 -0
  117. package/src/api/request.ts +74 -0
  118. package/src/api.ts +127 -0
  119. package/src/applications/Applications.tsx +282 -0
  120. package/src/components/datalist/EmptyRow.tsx +26 -0
  121. package/src/components/page/Page.tsx +27 -0
  122. package/src/content/ContentComponent.tsx +62 -0
  123. package/src/content/fetchContent.ts +13 -0
  124. package/src/environment.ts +31 -0
  125. package/src/global.d.ts +4 -0
  126. package/src/groups/Groups.tsx +142 -0
  127. package/src/i18n.ts +47 -0
  128. package/src/i18next.d.ts +10 -0
  129. package/src/oid4vci/Oid4Vci.tsx +136 -0
  130. package/src/personal-info/PersonalInfo.tsx +184 -0
  131. package/src/resources/EditTheResource.tsx +101 -0
  132. package/src/resources/PermissionRequest.tsx +129 -0
  133. package/src/resources/ResourceToolbar.tsx +87 -0
  134. package/src/resources/Resources.tsx +39 -0
  135. package/src/resources/ResourcesTab.tsx +410 -0
  136. package/src/resources/ShareTheResource.tsx +217 -0
  137. package/src/resources/SharedWith.tsx +26 -0
  138. package/src/root/ErrorPage.tsx +65 -0
  139. package/src/root/Header.tsx +62 -0
  140. package/src/root/PageNav.tsx +154 -0
  141. package/src/root/Root.tsx +20 -0
  142. package/src/root/header.module.css +3 -0
  143. package/src/routes.tsx +85 -0
  144. package/src/ui-shared/alerts/Alerts.tsx +86 -0
  145. package/src/ui-shared/buttons/FormSubmitButton.tsx +47 -0
  146. package/src/ui-shared/context/ErrorPage.tsx +60 -0
  147. package/src/ui-shared/context/HelpContext.tsx +30 -0
  148. package/src/ui-shared/context/KeycloakContext.tsx +97 -0
  149. package/src/ui-shared/context/environment.ts +50 -0
  150. package/src/ui-shared/continue-cancel/ContinueCancelModal.tsx +75 -0
  151. package/src/ui-shared/controls/FormErrorText.tsx +23 -0
  152. package/src/ui-shared/controls/FormLabel.tsx +40 -0
  153. package/src/ui-shared/controls/HelpItem.tsx +43 -0
  154. package/src/ui-shared/controls/NumberControl.tsx +89 -0
  155. package/src/ui-shared/controls/PasswordControl.tsx +71 -0
  156. package/src/ui-shared/controls/PasswordInput.tsx +50 -0
  157. package/src/ui-shared/controls/SwitchControl.tsx +67 -0
  158. package/src/ui-shared/controls/TextAreaControl.tsx +60 -0
  159. package/src/ui-shared/controls/TextControl.tsx +74 -0
  160. package/src/ui-shared/controls/keycloak-text-area/KeycloakTextArea.tsx +23 -0
  161. package/src/ui-shared/controls/select-control/SelectControl.tsx +75 -0
  162. package/src/ui-shared/controls/select-control/SingleSelectControl.tsx +107 -0
  163. package/src/ui-shared/controls/select-control/TypeaheadSelectControl.tsx +283 -0
  164. package/src/ui-shared/icons/IconMapper.tsx +63 -0
  165. package/src/ui-shared/index.ts +1 -0
  166. package/src/ui-shared/main.ts +62 -0
  167. package/src/ui-shared/masthead/DefaultAvatar.tsx +109 -0
  168. package/src/ui-shared/masthead/KeycloakDropdown.tsx +47 -0
  169. package/src/ui-shared/masthead/Masthead.tsx +145 -0
  170. package/src/ui-shared/scroll-form/FormPanel.tsx +29 -0
  171. package/src/ui-shared/scroll-form/FormTitle.tsx +28 -0
  172. package/src/ui-shared/scroll-form/ScrollForm.tsx +98 -0
  173. package/src/ui-shared/scroll-form/ScrollPanel.tsx +21 -0
  174. package/src/ui-shared/scroll-form/form-title.module.css +4 -0
  175. package/src/ui-shared/scroll-form/scroll-form.module.css +8 -0
  176. package/src/ui-shared/select/KeycloakSelect.tsx +49 -0
  177. package/src/ui-shared/select/SingleSelect.tsx +85 -0
  178. package/src/ui-shared/select/TypeaheadSelect.tsx +196 -0
  179. package/src/ui-shared/user-profile/LocaleSelector.tsx +51 -0
  180. package/src/ui-shared/user-profile/MultiInputComponent.tsx +146 -0
  181. package/src/ui-shared/user-profile/OptionsComponent.tsx +60 -0
  182. package/src/ui-shared/user-profile/SelectComponent.tsx +106 -0
  183. package/src/ui-shared/user-profile/TextAreaComponent.tsx +23 -0
  184. package/src/ui-shared/user-profile/TextComponent.tsx +30 -0
  185. package/src/ui-shared/user-profile/UserProfileFields.tsx +246 -0
  186. package/src/ui-shared/user-profile/UserProfileGroup.tsx +70 -0
  187. package/src/ui-shared/user-profile/utils.ts +161 -0
  188. package/src/ui-shared/utils/createNamedContext.ts +11 -0
  189. package/src/ui-shared/utils/isDefined.ts +3 -0
  190. package/src/ui-shared/utils/useRequiredContext.ts +24 -0
  191. package/src/ui-shared/utils/useStorageItem.ts +51 -0
  192. package/src/ui-shared/utils/useStoredState.ts +38 -0
  193. package/src/utils/formatDate.ts +18 -0
  194. package/src/utils/isRecord.ts +2 -0
  195. package/src/utils/joinPath.ts +22 -0
  196. package/src/utils/usePromise.ts +74 -0
  197. package/ui-shared/alerts/Alerts.d.ts +17 -0
  198. package/ui-shared/alerts/Alerts.js +27 -0
  199. package/ui-shared/alerts/Alerts.js.map +1 -0
  200. package/ui-shared/buttons/FormSubmitButton.d.ts +10 -0
  201. package/ui-shared/buttons/FormSubmitButton.js +26 -0
  202. package/ui-shared/buttons/FormSubmitButton.js.map +1 -0
  203. package/ui-shared/context/ErrorPage.d.ts +5 -0
  204. package/ui-shared/context/ErrorPage.js +24 -0
  205. package/ui-shared/context/ErrorPage.js.map +1 -0
  206. package/ui-shared/context/HelpContext.d.ts +9 -0
  207. package/ui-shared/context/HelpContext.js +14 -0
  208. package/ui-shared/context/HelpContext.js.map +1 -0
  209. package/ui-shared/context/KeycloakContext.d.ts +12 -0
  210. package/ui-shared/context/KeycloakContext.js +53 -0
  211. package/ui-shared/context/KeycloakContext.js.map +1 -0
  212. package/ui-shared/context/environment.d.ts +36 -0
  213. package/ui-shared/context/environment.js +27 -0
  214. package/ui-shared/context/environment.js.map +1 -0
  215. package/ui-shared/continue-cancel/ContinueCancelModal.d.ts +15 -0
  216. package/ui-shared/continue-cancel/ContinueCancelModal.js +27 -0
  217. package/ui-shared/continue-cancel/ContinueCancelModal.js.map +1 -0
  218. package/ui-shared/controls/FormErrorText.d.ts +5 -0
  219. package/ui-shared/controls/FormErrorText.js +19 -0
  220. package/ui-shared/controls/FormErrorText.js.map +1 -0
  221. package/ui-shared/controls/FormLabel.d.ts +13 -0
  222. package/ui-shared/controls/FormLabel.js +20 -0
  223. package/ui-shared/controls/FormLabel.js.map +1 -0
  224. package/ui-shared/controls/HelpItem.d.ts +9 -0
  225. package/ui-shared/controls/HelpItem.js +9 -0
  226. package/ui-shared/controls/HelpItem.js.map +1 -0
  227. package/ui-shared/controls/NumberControl.d.ts +13 -0
  228. package/ui-shared/controls/NumberControl.js +32 -0
  229. package/ui-shared/controls/NumberControl.js.map +1 -0
  230. package/ui-shared/controls/PasswordControl.d.ts +9 -0
  231. package/ui-shared/controls/PasswordControl.js +25 -0
  232. package/ui-shared/controls/PasswordControl.js.map +1 -0
  233. package/ui-shared/controls/PasswordInput.d.ts +7 -0
  234. package/ui-shared/controls/PasswordInput.js +25 -0
  235. package/ui-shared/controls/PasswordInput.js.map +1 -0
  236. package/ui-shared/controls/SwitchControl.d.ts +11 -0
  237. package/ui-shared/controls/SwitchControl.js +29 -0
  238. package/ui-shared/controls/SwitchControl.js.map +1 -0
  239. package/ui-shared/controls/TextAreaControl.d.ts +8 -0
  240. package/ui-shared/controls/TextAreaControl.js +12 -0
  241. package/ui-shared/controls/TextAreaControl.js.map +1 -0
  242. package/ui-shared/controls/TextControl.d.ts +10 -0
  243. package/ui-shared/controls/TextControl.js +24 -0
  244. package/ui-shared/controls/TextControl.js.map +1 -0
  245. package/ui-shared/controls/keycloak-text-area/KeycloakTextArea.d.ts +4 -0
  246. package/ui-shared/controls/keycloak-text-area/KeycloakTextArea.js +5 -0
  247. package/ui-shared/controls/keycloak-text-area/KeycloakTextArea.js.map +1 -0
  248. package/ui-shared/controls/select-control/SelectControl.d.ts +31 -0
  249. package/ui-shared/controls/select-control/SelectControl.js +28 -0
  250. package/ui-shared/controls/select-control/SelectControl.js.map +1 -0
  251. package/ui-shared/controls/select-control/SingleSelectControl.d.ts +3 -0
  252. package/ui-shared/controls/select-control/SingleSelectControl.js +41 -0
  253. package/ui-shared/controls/select-control/SingleSelectControl.js.map +1 -0
  254. package/ui-shared/controls/select-control/TypeaheadSelectControl.d.ts +3 -0
  255. package/ui-shared/controls/select-control/TypeaheadSelectControl.js +138 -0
  256. package/ui-shared/controls/select-control/TypeaheadSelectControl.js.map +1 -0
  257. package/ui-shared/icons/IconMapper.d.ts +5 -0
  258. package/ui-shared/icons/IconMapper.js +40 -0
  259. package/ui-shared/icons/IconMapper.js.map +1 -0
  260. package/ui-shared/index.d.ts +1 -0
  261. package/ui-shared/index.js +2 -0
  262. package/ui-shared/index.js.map +1 -0
  263. package/ui-shared/main.d.ts +31 -0
  264. package/ui-shared/main.js +29 -0
  265. package/ui-shared/main.js.map +1 -0
  266. package/ui-shared/masthead/DefaultAvatar.d.ts +7 -0
  267. package/ui-shared/masthead/DefaultAvatar.js +26 -0
  268. package/ui-shared/masthead/DefaultAvatar.js.map +1 -0
  269. package/ui-shared/masthead/KeycloakDropdown.d.ts +10 -0
  270. package/ui-shared/masthead/KeycloakDropdown.js +23 -0
  271. package/ui-shared/masthead/KeycloakDropdown.js.map +1 -0
  272. package/ui-shared/masthead/Masthead.d.ts +22 -0
  273. package/ui-shared/masthead/Masthead.js +55 -0
  274. package/ui-shared/masthead/Masthead.js.map +1 -0
  275. package/ui-shared/scroll-form/FormPanel.d.ts +8 -0
  276. package/ui-shared/scroll-form/FormPanel.js +9 -0
  277. package/ui-shared/scroll-form/FormPanel.js.map +1 -0
  278. package/ui-shared/scroll-form/FormTitle.d.ts +8 -0
  279. package/ui-shared/scroll-form/FormTitle.js +19 -0
  280. package/ui-shared/scroll-form/FormTitle.js.map +1 -0
  281. package/ui-shared/scroll-form/ScrollForm.d.ts +15 -0
  282. package/ui-shared/scroll-form/ScrollForm.js +38 -0
  283. package/ui-shared/scroll-form/ScrollForm.js.map +1 -0
  284. package/ui-shared/scroll-form/ScrollPanel.d.ts +7 -0
  285. package/ui-shared/scroll-form/ScrollPanel.js +18 -0
  286. package/ui-shared/scroll-form/ScrollPanel.js.map +1 -0
  287. package/ui-shared/select/KeycloakSelect.d.ts +30 -0
  288. package/ui-shared/select/KeycloakSelect.js +31 -0
  289. package/ui-shared/select/KeycloakSelect.js.map +1 -0
  290. package/ui-shared/select/SingleSelect.d.ts +4 -0
  291. package/ui-shared/select/SingleSelect.js +46 -0
  292. package/ui-shared/select/SingleSelect.js.map +1 -0
  293. package/ui-shared/select/TypeaheadSelect.d.ts +2 -0
  294. package/ui-shared/select/TypeaheadSelect.js +96 -0
  295. package/ui-shared/select/TypeaheadSelect.js.map +1 -0
  296. package/ui-shared/user-profile/LocaleSelector.d.ts +7 -0
  297. package/ui-shared/user-profile/LocaleSelector.js +28 -0
  298. package/ui-shared/user-profile/LocaleSelector.js.map +1 -0
  299. package/ui-shared/user-profile/MultiInputComponent.d.ts +15 -0
  300. package/ui-shared/user-profile/MultiInputComponent.js +61 -0
  301. package/ui-shared/user-profile/MultiInputComponent.js.map +1 -0
  302. package/ui-shared/user-profile/OptionsComponent.d.ts +2 -0
  303. package/ui-shared/user-profile/OptionsComponent.js +28 -0
  304. package/ui-shared/user-profile/OptionsComponent.js.map +1 -0
  305. package/ui-shared/user-profile/SelectComponent.d.ts +2 -0
  306. package/ui-shared/user-profile/SelectComponent.js +55 -0
  307. package/ui-shared/user-profile/SelectComponent.js.map +1 -0
  308. package/ui-shared/user-profile/TextAreaComponent.d.ts +2 -0
  309. package/ui-shared/user-profile/TextAreaComponent.js +11 -0
  310. package/ui-shared/user-profile/TextAreaComponent.js.map +1 -0
  311. package/ui-shared/user-profile/TextComponent.d.ts +2 -0
  312. package/ui-shared/user-profile/TextComponent.js +14 -0
  313. package/ui-shared/user-profile/TextComponent.js.map +1 -0
  314. package/ui-shared/user-profile/UserProfileFields.d.ts +39 -0
  315. package/ui-shared/user-profile/UserProfileFields.js +112 -0
  316. package/ui-shared/user-profile/UserProfileFields.js.map +1 -0
  317. package/ui-shared/user-profile/UserProfileGroup.d.ts +12 -0
  318. package/ui-shared/user-profile/UserProfileGroup.js +15 -0
  319. package/ui-shared/user-profile/UserProfileGroup.js.map +1 -0
  320. package/ui-shared/user-profile/utils.d.ts +32 -0
  321. package/ui-shared/user-profile/utils.js +79 -0
  322. package/ui-shared/user-profile/utils.js.map +1 -0
  323. package/ui-shared/utils/createNamedContext.d.ts +3 -0
  324. package/ui-shared/utils/createNamedContext.js +7 -0
  325. package/ui-shared/utils/createNamedContext.js.map +1 -0
  326. package/ui-shared/utils/isDefined.d.ts +1 -0
  327. package/ui-shared/utils/isDefined.js +4 -0
  328. package/ui-shared/utils/isDefined.js.map +1 -0
  329. package/ui-shared/utils/useRequiredContext.d.ts +9 -0
  330. package/ui-shared/utils/useRequiredContext.js +17 -0
  331. package/ui-shared/utils/useRequiredContext.js.map +1 -0
  332. package/ui-shared/utils/useStorageItem.d.ts +10 -0
  333. package/ui-shared/utils/useStorageItem.js +40 -0
  334. package/ui-shared/utils/useStorageItem.js.map +1 -0
  335. package/ui-shared/utils/useStoredState.d.ts +13 -0
  336. package/ui-shared/utils/useStoredState.js +21 -0
  337. package/ui-shared/utils/useStoredState.js.map +1 -0
  338. package/utils/formatDate.d.ts +4 -0
  339. package/utils/formatDate.js +12 -0
  340. package/utils/formatDate.js.map +1 -0
  341. package/utils/isRecord.d.ts +1 -0
  342. package/utils/isRecord.js +2 -0
  343. package/utils/isRecord.js.map +1 -0
  344. package/utils/joinPath.d.ts +1 -0
  345. package/utils/joinPath.js +18 -0
  346. package/utils/joinPath.js.map +1 -0
  347. package/utils/usePromise.d.ts +41 -0
  348. package/utils/usePromise.js +55 -0
  349. package/utils/usePromise.js.map +1 -0
package/root/Root.js ADDED
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { KeycloakProvider } from "../ui-shared";
3
+ import { Page, Spinner } from "@patternfly/react-core";
4
+ import { Suspense } from "react";
5
+ import { Outlet } from "react-router-dom";
6
+ import { environment } from "../environment";
7
+ import { Header } from "../root/Header";
8
+ import { PageNav } from "../root/PageNav";
9
+ export const Root = () => {
10
+ return (_jsx(KeycloakProvider, { environment: environment, children: _jsx(Page, { header: _jsx(Header, {}), sidebar: _jsx(PageNav, {}), isManagedSidebar: true, children: _jsx(Suspense, { fallback: _jsx(Spinner, {}), children: _jsx(Outlet, {}) }) }) }));
11
+ };
12
+ //# sourceMappingURL=Root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Root.js","sourceRoot":"","sources":["../src/root/Root.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4CAA4C,CAAC;AAC9E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,8CAA8C,CAAC;AAC3E,OAAO,EAAE,MAAM,EAAE,MAAM,8CAA8C,CAAC;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,+CAA+C,CAAC;AAExE,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE;IACvB,OAAO,CACL,KAAC,gBAAgB,IAAC,WAAW,EAAE,WAAW,YACxC,KAAC,IAAI,IAAC,MAAM,EAAE,KAAC,MAAM,KAAG,EAAE,OAAO,EAAE,KAAC,OAAO,KAAG,EAAE,gBAAgB,kBAC9D,KAAC,QAAQ,IAAC,QAAQ,EAAE,KAAC,OAAO,KAAG,YAC7B,KAAC,MAAM,KAAG,GACD,GACN,GACU,CACpB,CAAC;AACJ,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ .brand {
2
+ height: 35px;
3
+ }
package/routes.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { IndexRouteObject, RouteObject } from "react-router-dom";
2
+ export declare const DeviceActivityRoute: RouteObject;
3
+ export declare const LinkedAccountsRoute: RouteObject;
4
+ export declare const SigningInRoute: RouteObject;
5
+ export declare const ApplicationsRoute: RouteObject;
6
+ export declare const GroupsRoute: RouteObject;
7
+ export declare const ResourcesRoute: RouteObject;
8
+ export type ContentComponentParams = {
9
+ componentId: string;
10
+ };
11
+ export declare const ContentRoute: RouteObject;
12
+ export declare const PersonalInfoRoute: IndexRouteObject;
13
+ export declare const Oid4VciRoute: RouteObject;
14
+ export declare const RootRoute: RouteObject;
15
+ export declare const routes: RouteObject[];
package/routes.js ADDED
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { lazy } from "react";
3
+ import { environment } from "./environment";
4
+ import { ErrorPage } from "./root/ErrorPage";
5
+ import { Root } from "./root/Root";
6
+ const DeviceActivity = lazy(() => import("./account-security/DeviceActivity"));
7
+ const LinkedAccounts = lazy(() => import("./account-security/LinkedAccounts"));
8
+ const SigningIn = lazy(() => import("./account-security/SigningIn"));
9
+ const Applications = lazy(() => import("./applications/Applications"));
10
+ const Groups = lazy(() => import("./groups/Groups"));
11
+ const PersonalInfo = lazy(() => import("./personal-info/PersonalInfo"));
12
+ const Resources = lazy(() => import("./resources/Resources"));
13
+ const ContentComponent = lazy(() => import("./content/ContentComponent"));
14
+ const Oid4Vci = lazy(() => import("./oid4vci/Oid4Vci"));
15
+ export const DeviceActivityRoute = {
16
+ path: "account-security/device-activity",
17
+ element: _jsx(DeviceActivity, {}),
18
+ };
19
+ export const LinkedAccountsRoute = {
20
+ path: "account-security/linked-accounts",
21
+ element: _jsx(LinkedAccounts, {}),
22
+ };
23
+ export const SigningInRoute = {
24
+ path: "account-security/signing-in",
25
+ element: _jsx(SigningIn, {}),
26
+ };
27
+ export const ApplicationsRoute = {
28
+ path: "applications",
29
+ element: _jsx(Applications, {}),
30
+ };
31
+ export const GroupsRoute = {
32
+ path: "groups",
33
+ element: _jsx(Groups, {}),
34
+ };
35
+ export const ResourcesRoute = {
36
+ path: "resources",
37
+ element: _jsx(Resources, {}),
38
+ };
39
+ export const ContentRoute = {
40
+ path: "content/:componentId",
41
+ element: _jsx(ContentComponent, {}),
42
+ };
43
+ export const PersonalInfoRoute = {
44
+ index: true,
45
+ element: _jsx(PersonalInfo, {}),
46
+ };
47
+ export const Oid4VciRoute = {
48
+ path: "oid4vci",
49
+ element: _jsx(Oid4Vci, {}),
50
+ };
51
+ export const RootRoute = {
52
+ path: decodeURIComponent(new URL(environment.baseUrl).pathname),
53
+ element: _jsx(Root, {}),
54
+ errorElement: _jsx(ErrorPage, {}),
55
+ children: [
56
+ PersonalInfoRoute,
57
+ DeviceActivityRoute,
58
+ LinkedAccountsRoute,
59
+ SigningInRoute,
60
+ ApplicationsRoute,
61
+ GroupsRoute,
62
+ PersonalInfoRoute,
63
+ ResourcesRoute,
64
+ ContentRoute,
65
+ Oid4VciRoute,
66
+ ],
67
+ };
68
+ export const routes = [RootRoute];
69
+ //# sourceMappingURL=routes.js.map
package/routes.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["src/routes.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ import { useEffect, useReducer } from "react";
2
+ import "@patternfly/react-core/dist/styles/base.css";
3
+ import "@patternfly/patternfly/patternfly-addons.css";
4
+
5
+ import { createBrowserRouter, RouterProvider } from "react-router-dom";
6
+
7
+ import { i18n } from "@keycloakify/keycloak-account-ui/i18n";
8
+ import { routes } from "@keycloakify/keycloak-account-ui/routes";
9
+
10
+ const router = createBrowserRouter(routes);
11
+ const prI18nInitialized = i18n.init();
12
+
13
+ export default function KeycloakAccountUi() {
14
+ const [isI18nInitialized, setI18nInitialized] = useReducer(() => true, false);
15
+
16
+ useEffect(() => {
17
+ let isActive = true;
18
+
19
+ prI18nInitialized.then(() => {
20
+ if (!isActive) {
21
+ return;
22
+ }
23
+ setI18nInitialized();
24
+ });
25
+
26
+ return () => {
27
+ isActive = false;
28
+ };
29
+ }, []);
30
+
31
+ if (!isI18nInitialized) {
32
+ return null;
33
+ }
34
+
35
+ return <RouterProvider router={router} />;
36
+ }
@@ -0,0 +1,136 @@
1
+ import {
2
+ Button,
3
+ DataListAction,
4
+ DataListCell,
5
+ DataListItem,
6
+ DataListItemCells,
7
+ DataListItemRow,
8
+ Icon,
9
+ Label,
10
+ Split,
11
+ SplitItem,
12
+ } from "@patternfly/react-core";
13
+ import { LinkIcon, UnlinkIcon } from "@patternfly/react-icons";
14
+ import { useTranslation } from "react-i18next";
15
+ import {
16
+ IconMapper,
17
+ useAlerts,
18
+ useEnvironment,
19
+ } from "@keycloakify/keycloak-account-ui/ui-shared";
20
+ import { linkAccount, unLinkAccount } from "@keycloakify/keycloak-account-ui/api/methods";
21
+ import { LinkedAccountRepresentation } from "@keycloakify/keycloak-account-ui/api/representations";
22
+
23
+ type AccountRowProps = {
24
+ account: LinkedAccountRepresentation;
25
+ isLinked?: boolean;
26
+ refresh: () => void;
27
+ };
28
+
29
+ export const AccountRow = ({
30
+ account,
31
+ isLinked = false,
32
+ refresh,
33
+ }: AccountRowProps) => {
34
+ const { t } = useTranslation();
35
+ const context = useEnvironment();
36
+ const { addAlert, addError } = useAlerts();
37
+
38
+ const unLink = async (account: LinkedAccountRepresentation) => {
39
+ try {
40
+ await unLinkAccount(context, account);
41
+ addAlert(t("unLinkSuccess"));
42
+ refresh();
43
+ } catch (error) {
44
+ addError(t("unLinkError", { error }).toString());
45
+ }
46
+ };
47
+
48
+ const link = async (account: LinkedAccountRepresentation) => {
49
+ try {
50
+ const { accountLinkUri } = await linkAccount(context, account);
51
+ location.href = accountLinkUri;
52
+ } catch (error) {
53
+ addError(t("linkError", { error }).toString());
54
+ }
55
+ };
56
+
57
+ return (
58
+ <DataListItem
59
+ id={`${account.providerAlias}-idp`}
60
+ key={account.providerName}
61
+ aria-label={t("linkedAccounts")}
62
+ >
63
+ <DataListItemRow
64
+ key={account.providerName}
65
+ data-testid={`linked-accounts/${account.providerName}`}
66
+ >
67
+ <DataListItemCells
68
+ dataListCells={[
69
+ <DataListCell key="idp">
70
+ <Split>
71
+ <SplitItem className="pf-v5-u-mr-sm">
72
+ <IconMapper icon={account.providerName} />
73
+ </SplitItem>
74
+ <SplitItem className="pf-v5-u-my-xs" isFilled>
75
+ <span id={`${account.providerAlias}-idp-name`}>
76
+ {account.displayName}
77
+ </span>
78
+ </SplitItem>
79
+ </Split>
80
+ </DataListCell>,
81
+ <DataListCell key="label">
82
+ <Split>
83
+ <SplitItem className="pf-v5-u-my-xs" isFilled>
84
+ <span id={`${account.providerAlias}-idp-label`}>
85
+ <Label color={account.social ? "blue" : "green"}>
86
+ {t(account.social ? "socialLogin" : "systemDefined")}
87
+ </Label>
88
+ </span>
89
+ </SplitItem>
90
+ </Split>
91
+ </DataListCell>,
92
+ <DataListCell key="username" width={5}>
93
+ <Split>
94
+ <SplitItem className="pf-v5-u-my-xs" isFilled>
95
+ <span id={`${account.providerAlias}-idp-username`}>
96
+ {account.linkedUsername}
97
+ </span>
98
+ </SplitItem>
99
+ </Split>
100
+ </DataListCell>,
101
+ ]}
102
+ />
103
+ <DataListAction
104
+ aria-labelledby={t("link")}
105
+ aria-label={t("unLink")}
106
+ id="setPasswordAction"
107
+ >
108
+ {isLinked && (
109
+ <Button
110
+ id={`${account.providerAlias}-idp-unlink`}
111
+ variant="link"
112
+ onClick={() => unLink(account)}
113
+ >
114
+ <Icon size="sm">
115
+ <UnlinkIcon />
116
+ </Icon>{" "}
117
+ {t("unLink")}
118
+ </Button>
119
+ )}
120
+ {!isLinked && (
121
+ <Button
122
+ id={`${account.providerAlias}-idp-link`}
123
+ variant="link"
124
+ onClick={() => link(account)}
125
+ >
126
+ <Icon size="sm">
127
+ <LinkIcon />
128
+ </Icon>{" "}
129
+ {t("link")}
130
+ </Button>
131
+ )}
132
+ </DataListAction>
133
+ </DataListItemRow>
134
+ </DataListItem>
135
+ );
136
+ };
@@ -0,0 +1,253 @@
1
+ import {
2
+ Button,
3
+ DataList,
4
+ DataListContent,
5
+ DataListItem,
6
+ DataListItemRow,
7
+ DescriptionList,
8
+ DescriptionListDescription,
9
+ DescriptionListGroup,
10
+ DescriptionListTerm,
11
+ Grid,
12
+ GridItem,
13
+ Label,
14
+ Spinner,
15
+ Split,
16
+ SplitItem,
17
+ Title,
18
+ } from "@patternfly/react-core";
19
+ import {
20
+ DesktopIcon,
21
+ MobileAltIcon,
22
+ SyncAltIcon,
23
+ } from "@patternfly/react-icons";
24
+ import { useState } from "react";
25
+ import { useTranslation } from "react-i18next";
26
+ import {
27
+ ContinueCancelModal,
28
+ useAlerts,
29
+ useEnvironment,
30
+ } from "@keycloakify/keycloak-account-ui/ui-shared";
31
+ import { deleteSession, getDevices } from "@keycloakify/keycloak-account-ui/api/methods";
32
+ import {
33
+ ClientRepresentation,
34
+ DeviceRepresentation,
35
+ SessionRepresentation,
36
+ } from "@keycloakify/keycloak-account-ui/api/representations";
37
+ import { Page } from "@keycloakify/keycloak-account-ui/components/page/Page";
38
+ import { TFuncKey } from "@keycloakify/keycloak-account-ui/i18n";
39
+ import { formatDate } from "@keycloakify/keycloak-account-ui/utils/formatDate";
40
+ import { usePromise } from "@keycloakify/keycloak-account-ui/utils/usePromise";
41
+
42
+ export const DeviceActivity = () => {
43
+ const { t } = useTranslation();
44
+ const context = useEnvironment();
45
+ const { addAlert, addError } = useAlerts();
46
+
47
+ const [devices, setDevices] = useState<DeviceRepresentation[]>();
48
+ const [key, setKey] = useState(0);
49
+ const refresh = () => setKey(key + 1);
50
+
51
+ const moveCurrentToTop = (devices: DeviceRepresentation[]) => {
52
+ let currentDevice = devices[0];
53
+
54
+ const index = devices.findIndex((d) => d.current);
55
+ currentDevice = devices.splice(index, 1)[0];
56
+ devices.unshift(currentDevice);
57
+
58
+ const sessionIndex = currentDevice.sessions.findIndex((s) => s.current);
59
+ const currentSession = currentDevice.sessions.splice(sessionIndex, 1)[0];
60
+ currentDevice.sessions.unshift(currentSession);
61
+
62
+ setDevices(devices);
63
+ };
64
+
65
+ usePromise((signal) => getDevices({ signal, context }), moveCurrentToTop, [
66
+ key,
67
+ ]);
68
+
69
+ const signOutAll = async () => {
70
+ await deleteSession(context);
71
+ context.keycloak.logout();
72
+ };
73
+
74
+ const signOutSession = async (
75
+ session: SessionRepresentation,
76
+ device: DeviceRepresentation,
77
+ ) => {
78
+ try {
79
+ await deleteSession(context, session.id);
80
+ addAlert(
81
+ t("signedOutSession", { browser: session.browser, os: device.os }),
82
+ );
83
+ refresh();
84
+ } catch (error) {
85
+ addError(t("errorSignOutMessage", { error }).toString());
86
+ }
87
+ };
88
+
89
+ const makeClientsString = (clients: ClientRepresentation[]): string => {
90
+ let clientsString = "";
91
+ clients.forEach((client, index) => {
92
+ let clientName: string;
93
+ if (client.clientName !== "") {
94
+ clientName = t(client.clientName as TFuncKey);
95
+ } else {
96
+ clientName = client.clientId;
97
+ }
98
+
99
+ clientsString += clientName;
100
+
101
+ if (clients.length > index + 1) clientsString += ", ";
102
+ });
103
+
104
+ return clientsString;
105
+ };
106
+
107
+ if (!devices) {
108
+ return <Spinner />;
109
+ }
110
+
111
+ return (
112
+ <Page
113
+ title={t("deviceActivity")}
114
+ description={t("signedInDevicesExplanation")}
115
+ >
116
+ <Split hasGutter className="pf-v5-u-mb-lg">
117
+ <SplitItem isFilled>
118
+ <Title headingLevel="h2" size="xl">
119
+ {t("signedInDevices")}
120
+ </Title>
121
+ </SplitItem>
122
+ <SplitItem>
123
+ <Button
124
+ id="refresh-page"
125
+ variant="link"
126
+ onClick={() => refresh()}
127
+ icon={<SyncAltIcon />}
128
+ >
129
+ {t("refreshPage")}
130
+ </Button>
131
+
132
+ {(devices.length > 1 || devices[0].sessions.length > 1) && (
133
+ <ContinueCancelModal
134
+ buttonTitle={t("signOutAllDevices")}
135
+ modalTitle={t("signOutAllDevices")}
136
+ continueLabel={t("confirm")}
137
+ cancelLabel={t("cancel")}
138
+ onContinue={() => signOutAll()}
139
+ >
140
+ {t("signOutAllDevicesWarning")}
141
+ </ContinueCancelModal>
142
+ )}
143
+ </SplitItem>
144
+ </Split>
145
+ <DataList
146
+ className="signed-in-device-list"
147
+ aria-label={t("signedInDevices")}
148
+ >
149
+ <DataListItem aria-labelledby={`sessions-${key}`}>
150
+ {devices.map((device) =>
151
+ device.sessions.map((session, index) => (
152
+ <DataListItemRow key={device.id} data-testid={`row-${index}`}>
153
+ <DataListContent
154
+ aria-label="device-sessions-content"
155
+ className="pf-v5-u-flex-grow-1"
156
+ >
157
+ <Grid hasGutter>
158
+ <GridItem span={1} rowSpan={2}>
159
+ {device.mobile ? <MobileAltIcon /> : <DesktopIcon />}
160
+ </GridItem>
161
+ <GridItem sm={8} md={9} span={10}>
162
+ <span className="pf-v5-u-mr-md session-title">
163
+ {device.os.toLowerCase().includes("unknown")
164
+ ? t("unknownOperatingSystem")
165
+ : device.os}{" "}
166
+ {!device.osVersion.toLowerCase().includes("unknown") &&
167
+ device.osVersion}{" "}
168
+ / {session.browser}
169
+ </span>
170
+ {session.current && (
171
+ <Label color="green">{t("currentSession")}</Label>
172
+ )}
173
+ </GridItem>
174
+ <GridItem
175
+ className="pf-v5-u-text-align-right"
176
+ sm={3}
177
+ md={2}
178
+ span={1}
179
+ >
180
+ {!session.current && (
181
+ <ContinueCancelModal
182
+ buttonTitle={t("signOut")}
183
+ modalTitle={t("signOut")}
184
+ continueLabel={t("confirm")}
185
+ cancelLabel={t("cancel")}
186
+ buttonVariant="secondary"
187
+ onContinue={() => signOutSession(session, device)}
188
+ >
189
+ {t("signOutWarning")}
190
+ </ContinueCancelModal>
191
+ )}
192
+ </GridItem>
193
+ <GridItem span={11}>
194
+ <DescriptionList
195
+ className="signed-in-device-grid"
196
+ columnModifier={{ sm: "2Col", lg: "3Col" }}
197
+ cols={5}
198
+ rows={1}
199
+ >
200
+ <DescriptionListGroup>
201
+ <DescriptionListTerm>
202
+ {t("ipAddress")}
203
+ </DescriptionListTerm>
204
+ <DescriptionListDescription>
205
+ {session.ipAddress}
206
+ </DescriptionListDescription>
207
+ </DescriptionListGroup>
208
+ <DescriptionListGroup>
209
+ <DescriptionListTerm>
210
+ {t("lastAccessedOn")}
211
+ </DescriptionListTerm>
212
+ <DescriptionListDescription>
213
+ {formatDate(new Date(session.lastAccess * 1000))}
214
+ </DescriptionListDescription>
215
+ </DescriptionListGroup>
216
+ <DescriptionListGroup>
217
+ <DescriptionListTerm>
218
+ {t("clients")}
219
+ </DescriptionListTerm>
220
+ <DescriptionListDescription>
221
+ {makeClientsString(session.clients)}
222
+ </DescriptionListDescription>
223
+ </DescriptionListGroup>
224
+ <DescriptionListGroup>
225
+ <DescriptionListTerm>
226
+ {t("started")}
227
+ </DescriptionListTerm>
228
+ <DescriptionListDescription>
229
+ {formatDate(new Date(session.started * 1000))}
230
+ </DescriptionListDescription>
231
+ </DescriptionListGroup>
232
+ <DescriptionListGroup>
233
+ <DescriptionListTerm>
234
+ {t("expires")}
235
+ </DescriptionListTerm>
236
+ <DescriptionListDescription>
237
+ {formatDate(new Date(session.expires * 1000))}
238
+ </DescriptionListDescription>
239
+ </DescriptionListGroup>
240
+ </DescriptionList>
241
+ </GridItem>
242
+ </Grid>
243
+ </DataListContent>
244
+ </DataListItemRow>
245
+ )),
246
+ )}
247
+ </DataListItem>
248
+ </DataList>
249
+ </Page>
250
+ );
251
+ };
252
+
253
+ export default DeviceActivity;
@@ -0,0 +1,86 @@
1
+ import { DataList, Stack, StackItem, Title } from "@patternfly/react-core";
2
+ import { useMemo, useState } from "react";
3
+ import { useTranslation } from "react-i18next";
4
+ import { getLinkedAccounts } from "@keycloakify/keycloak-account-ui/api/methods";
5
+ import { LinkedAccountRepresentation } from "@keycloakify/keycloak-account-ui/api/representations";
6
+ import { EmptyRow } from "@keycloakify/keycloak-account-ui/components/datalist/EmptyRow";
7
+ import { Page } from "@keycloakify/keycloak-account-ui/components/page/Page";
8
+ import { usePromise } from "@keycloakify/keycloak-account-ui/utils/usePromise";
9
+ import { AccountRow } from "@keycloakify/keycloak-account-ui/account-security/AccountRow";
10
+ import { useEnvironment } from "@keycloakify/keycloak-account-ui/ui-shared";
11
+
12
+ export const LinkedAccounts = () => {
13
+ const { t } = useTranslation();
14
+ const context = useEnvironment();
15
+ const [accounts, setAccounts] = useState<LinkedAccountRepresentation[]>([]);
16
+
17
+ const [key, setKey] = useState(1);
18
+ const refresh = () => setKey(key + 1);
19
+
20
+ usePromise((signal) => getLinkedAccounts({ signal, context }), setAccounts, [
21
+ key,
22
+ ]);
23
+
24
+ const linkedAccounts = useMemo(
25
+ () => accounts.filter((account) => account.connected),
26
+ [accounts],
27
+ );
28
+
29
+ const unLinkedAccounts = useMemo(
30
+ () => accounts.filter((account) => !account.connected),
31
+ [accounts],
32
+ );
33
+
34
+ return (
35
+ <Page
36
+ title={t("linkedAccounts")}
37
+ description={t("linkedAccountsIntroMessage")}
38
+ >
39
+ <Stack hasGutter>
40
+ <StackItem>
41
+ <Title headingLevel="h2" className="pf-v5-u-mb-lg" size="xl">
42
+ {t("linkedLoginProviders")}
43
+ </Title>
44
+ <DataList id="linked-idps" aria-label={t("linkedLoginProviders")}>
45
+ {linkedAccounts.length > 0 ? (
46
+ linkedAccounts.map((account) => (
47
+ <AccountRow
48
+ key={account.providerName}
49
+ account={account}
50
+ isLinked
51
+ refresh={refresh}
52
+ />
53
+ ))
54
+ ) : (
55
+ <EmptyRow message={t("linkedEmpty")} />
56
+ )}
57
+ </DataList>
58
+ </StackItem>
59
+ <StackItem>
60
+ <Title
61
+ headingLevel="h2"
62
+ className="pf-v5-u-mt-xl pf-v5-u-mb-lg"
63
+ size="xl"
64
+ >
65
+ {t("unlinkedLoginProviders")}
66
+ </Title>
67
+ <DataList id="unlinked-idps" aria-label={t("unlinkedLoginProviders")}>
68
+ {unLinkedAccounts.length > 0 ? (
69
+ unLinkedAccounts.map((account) => (
70
+ <AccountRow
71
+ key={account.providerName}
72
+ account={account}
73
+ refresh={refresh}
74
+ />
75
+ ))
76
+ ) : (
77
+ <EmptyRow message={t("unlinkedEmpty")} />
78
+ )}
79
+ </DataList>
80
+ </StackItem>
81
+ </Stack>
82
+ </Page>
83
+ );
84
+ };
85
+
86
+ export default LinkedAccounts;