keycloakify 11.10.0 → 11.11.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 (144) hide show
  1. package/bin/main.js +2 -1
  2. package/bin/shared/constants.d.ts +1 -1
  3. package/bin/shared/constants.js +2 -1
  4. package/bin/shared/constants.js.map +1 -1
  5. package/login/DefaultPage.js +3 -0
  6. package/login/DefaultPage.js.map +1 -1
  7. package/login/KcContext/KcContext.d.ts +10 -1
  8. package/login/KcContext/KcContext.js.map +1 -1
  9. package/login/KcContext/getKcContextMock.d.ts +1 -1
  10. package/login/KcContext/kcContextMocks.d.ts +1 -1
  11. package/login/KcContext/kcContextMocks.js +20 -1
  12. package/login/KcContext/kcContextMocks.js.map +1 -1
  13. package/login/i18n/messages_defaultSet/ar.d.ts +1 -0
  14. package/login/i18n/messages_defaultSet/ar.js +2 -1
  15. package/login/i18n/messages_defaultSet/ar.js.map +1 -1
  16. package/login/i18n/messages_defaultSet/ca.d.ts +1 -0
  17. package/login/i18n/messages_defaultSet/ca.js +2 -1
  18. package/login/i18n/messages_defaultSet/ca.js.map +1 -1
  19. package/login/i18n/messages_defaultSet/cs.d.ts +1 -0
  20. package/login/i18n/messages_defaultSet/cs.js +2 -1
  21. package/login/i18n/messages_defaultSet/cs.js.map +1 -1
  22. package/login/i18n/messages_defaultSet/da.d.ts +1 -0
  23. package/login/i18n/messages_defaultSet/da.js +2 -1
  24. package/login/i18n/messages_defaultSet/da.js.map +1 -1
  25. package/login/i18n/messages_defaultSet/de.d.ts +1 -0
  26. package/login/i18n/messages_defaultSet/de.js +2 -1
  27. package/login/i18n/messages_defaultSet/de.js.map +1 -1
  28. package/login/i18n/messages_defaultSet/el.d.ts +1 -0
  29. package/login/i18n/messages_defaultSet/el.js +2 -1
  30. package/login/i18n/messages_defaultSet/el.js.map +1 -1
  31. package/login/i18n/messages_defaultSet/en.d.ts +1 -0
  32. package/login/i18n/messages_defaultSet/en.js +2 -1
  33. package/login/i18n/messages_defaultSet/en.js.map +1 -1
  34. package/login/i18n/messages_defaultSet/es.d.ts +1 -0
  35. package/login/i18n/messages_defaultSet/es.js +2 -1
  36. package/login/i18n/messages_defaultSet/es.js.map +1 -1
  37. package/login/i18n/messages_defaultSet/fa.d.ts +1 -0
  38. package/login/i18n/messages_defaultSet/fa.js +2 -1
  39. package/login/i18n/messages_defaultSet/fa.js.map +1 -1
  40. package/login/i18n/messages_defaultSet/fi.d.ts +1 -0
  41. package/login/i18n/messages_defaultSet/fi.js +2 -1
  42. package/login/i18n/messages_defaultSet/fi.js.map +1 -1
  43. package/login/i18n/messages_defaultSet/fr.d.ts +1 -0
  44. package/login/i18n/messages_defaultSet/fr.js +2 -1
  45. package/login/i18n/messages_defaultSet/fr.js.map +1 -1
  46. package/login/i18n/messages_defaultSet/hu.d.ts +1 -0
  47. package/login/i18n/messages_defaultSet/hu.js +2 -1
  48. package/login/i18n/messages_defaultSet/hu.js.map +1 -1
  49. package/login/i18n/messages_defaultSet/index.d.ts +10 -0
  50. package/login/i18n/messages_defaultSet/it.d.ts +1 -0
  51. package/login/i18n/messages_defaultSet/it.js +2 -1
  52. package/login/i18n/messages_defaultSet/it.js.map +1 -1
  53. package/login/i18n/messages_defaultSet/ja.d.ts +1 -0
  54. package/login/i18n/messages_defaultSet/ja.js +2 -1
  55. package/login/i18n/messages_defaultSet/ja.js.map +1 -1
  56. package/login/i18n/messages_defaultSet/ka.d.ts +1 -0
  57. package/login/i18n/messages_defaultSet/ka.js +2 -1
  58. package/login/i18n/messages_defaultSet/ka.js.map +1 -1
  59. package/login/i18n/messages_defaultSet/lt.d.ts +1 -0
  60. package/login/i18n/messages_defaultSet/lt.js +2 -1
  61. package/login/i18n/messages_defaultSet/lt.js.map +1 -1
  62. package/login/i18n/messages_defaultSet/lv.d.ts +1 -0
  63. package/login/i18n/messages_defaultSet/lv.js +2 -1
  64. package/login/i18n/messages_defaultSet/lv.js.map +1 -1
  65. package/login/i18n/messages_defaultSet/nl.d.ts +1 -0
  66. package/login/i18n/messages_defaultSet/nl.js +2 -1
  67. package/login/i18n/messages_defaultSet/nl.js.map +1 -1
  68. package/login/i18n/messages_defaultSet/no.d.ts +1 -0
  69. package/login/i18n/messages_defaultSet/no.js +2 -1
  70. package/login/i18n/messages_defaultSet/no.js.map +1 -1
  71. package/login/i18n/messages_defaultSet/pl.d.ts +1 -0
  72. package/login/i18n/messages_defaultSet/pl.js +2 -1
  73. package/login/i18n/messages_defaultSet/pl.js.map +1 -1
  74. package/login/i18n/messages_defaultSet/pt-BR.d.ts +1 -0
  75. package/login/i18n/messages_defaultSet/pt-BR.js +2 -1
  76. package/login/i18n/messages_defaultSet/pt-BR.js.map +1 -1
  77. package/login/i18n/messages_defaultSet/pt.d.ts +1 -0
  78. package/login/i18n/messages_defaultSet/pt.js +2 -1
  79. package/login/i18n/messages_defaultSet/pt.js.map +1 -1
  80. package/login/i18n/messages_defaultSet/ru.d.ts +1 -0
  81. package/login/i18n/messages_defaultSet/ru.js +2 -1
  82. package/login/i18n/messages_defaultSet/ru.js.map +1 -1
  83. package/login/i18n/messages_defaultSet/sk.d.ts +1 -0
  84. package/login/i18n/messages_defaultSet/sk.js +2 -1
  85. package/login/i18n/messages_defaultSet/sk.js.map +1 -1
  86. package/login/i18n/messages_defaultSet/sv.d.ts +1 -0
  87. package/login/i18n/messages_defaultSet/sv.js +2 -1
  88. package/login/i18n/messages_defaultSet/sv.js.map +1 -1
  89. package/login/i18n/messages_defaultSet/th.d.ts +1 -0
  90. package/login/i18n/messages_defaultSet/th.js +2 -1
  91. package/login/i18n/messages_defaultSet/th.js.map +1 -1
  92. package/login/i18n/messages_defaultSet/tr.d.ts +1 -0
  93. package/login/i18n/messages_defaultSet/tr.js +2 -1
  94. package/login/i18n/messages_defaultSet/tr.js.map +1 -1
  95. package/login/i18n/messages_defaultSet/uk.d.ts +1 -0
  96. package/login/i18n/messages_defaultSet/uk.js +2 -1
  97. package/login/i18n/messages_defaultSet/uk.js.map +1 -1
  98. package/login/i18n/messages_defaultSet/zh-CN.d.ts +1 -0
  99. package/login/i18n/messages_defaultSet/zh-CN.js +2 -1
  100. package/login/i18n/messages_defaultSet/zh-CN.js.map +1 -1
  101. package/login/i18n/messages_defaultSet/zh-TW.d.ts +1 -0
  102. package/login/i18n/messages_defaultSet/zh-TW.js +2 -1
  103. package/login/i18n/messages_defaultSet/zh-TW.js.map +1 -1
  104. package/login/pages/SelectOrganization.d.ts +7 -0
  105. package/login/pages/SelectOrganization.js +33 -0
  106. package/login/pages/SelectOrganization.js.map +1 -0
  107. package/package.json +6 -1
  108. package/src/bin/shared/constants.ts +2 -1
  109. package/src/login/DefaultPage.tsx +3 -0
  110. package/src/login/KcContext/KcContext.ts +12 -1
  111. package/src/login/KcContext/kcContextMocks.ts +23 -0
  112. package/src/login/i18n/messages_defaultSet/ar.ts +2 -1
  113. package/src/login/i18n/messages_defaultSet/ca.ts +2 -1
  114. package/src/login/i18n/messages_defaultSet/cs.ts +2 -1
  115. package/src/login/i18n/messages_defaultSet/da.ts +2 -1
  116. package/src/login/i18n/messages_defaultSet/de.ts +2 -1
  117. package/src/login/i18n/messages_defaultSet/el.ts +2 -1
  118. package/src/login/i18n/messages_defaultSet/en.ts +2 -1
  119. package/src/login/i18n/messages_defaultSet/es.ts +2 -1
  120. package/src/login/i18n/messages_defaultSet/fa.ts +2 -1
  121. package/src/login/i18n/messages_defaultSet/fi.ts +2 -1
  122. package/src/login/i18n/messages_defaultSet/fr.ts +2 -1
  123. package/src/login/i18n/messages_defaultSet/hu.ts +2 -1
  124. package/src/login/i18n/messages_defaultSet/it.ts +2 -1
  125. package/src/login/i18n/messages_defaultSet/ja.ts +2 -1
  126. package/src/login/i18n/messages_defaultSet/ka.ts +2 -1
  127. package/src/login/i18n/messages_defaultSet/lt.ts +2 -1
  128. package/src/login/i18n/messages_defaultSet/lv.ts +2 -1
  129. package/src/login/i18n/messages_defaultSet/nl.ts +2 -1
  130. package/src/login/i18n/messages_defaultSet/no.ts +2 -1
  131. package/src/login/i18n/messages_defaultSet/pl.ts +2 -1
  132. package/src/login/i18n/messages_defaultSet/pt-BR.ts +2 -1
  133. package/src/login/i18n/messages_defaultSet/pt.ts +2 -1
  134. package/src/login/i18n/messages_defaultSet/ru.ts +2 -1
  135. package/src/login/i18n/messages_defaultSet/sk.ts +2 -1
  136. package/src/login/i18n/messages_defaultSet/sv.ts +2 -1
  137. package/src/login/i18n/messages_defaultSet/th.ts +2 -1
  138. package/src/login/i18n/messages_defaultSet/tr.ts +2 -1
  139. package/src/login/i18n/messages_defaultSet/uk.ts +2 -1
  140. package/src/login/i18n/messages_defaultSet/zh-CN.ts +2 -1
  141. package/src/login/i18n/messages_defaultSet/zh-TW.ts +2 -1
  142. package/src/login/pages/SelectOrganization.tsx +69 -0
  143. package/stories/login/pages/SelectOrganization.stories.tsx +18 -0
  144. package/vite-plugin/index.js +2 -1
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useRef, useState } from "react";
3
+ import { getKcClsx } from "../../login/lib/kcClsx";
4
+ export default function SelectOrganization(props) {
5
+ var _a;
6
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
7
+ const { kcClsx } = getKcClsx({
8
+ doUseDefaultCss,
9
+ classes
10
+ });
11
+ const { url, user } = kcContext;
12
+ const { msg } = i18n;
13
+ const [isSubmitting, setIsSubmitting] = useState(false);
14
+ const formRef = useRef(null);
15
+ const organizationInputRef = useRef(null);
16
+ const onOrganizationClick = (organizationAlias) => (event) => {
17
+ event.preventDefault();
18
+ if (!organizationInputRef.current || !formRef.current) {
19
+ return;
20
+ }
21
+ organizationInputRef.current.value = organizationAlias;
22
+ setIsSubmitting(true);
23
+ if (typeof formRef.current.requestSubmit === "function") {
24
+ formRef.current.requestSubmit();
25
+ return;
26
+ }
27
+ formRef.current.submit();
28
+ };
29
+ const organizations = (_a = user.organizations) !== null && _a !== void 0 ? _a : [];
30
+ const shouldDisplayGrid = organizations.length > 3;
31
+ return (_jsx(Template, Object.assign({ kcContext: kcContext, i18n: i18n, doUseDefaultCss: doUseDefaultCss, classes: classes, headerNode: null }, { children: _jsxs("form", Object.assign({ ref: formRef, action: url.loginAction, className: "form-vertical", method: "post" }, { children: [_jsxs("div", Object.assign({ id: "kc-user-organizations", className: kcClsx("kcFormGroupClass") }, { children: [_jsx("h2", { children: msg("organization.select") }), _jsx("ul", Object.assign({ className: kcClsx("kcFormSocialAccountListClass", shouldDisplayGrid && "kcFormSocialAccountListGridClass") }, { children: organizations.map(({ alias, name }) => (_jsx("li", { children: _jsx("button", Object.assign({ id: `organization-${alias}`, className: kcClsx("kcFormSocialAccountListButtonClass", shouldDisplayGrid && "kcFormSocialAccountGridItem"), type: "button", onClick: onOrganizationClick(alias), disabled: isSubmitting }, { children: _jsx("span", Object.assign({ className: kcClsx("kcFormSocialAccountNameClass") }, { children: name !== null && name !== void 0 ? name : alias })) })) }, alias))) }))] })), _jsx("input", { ref: organizationInputRef, type: "hidden", name: "kc.org" })] })) })));
32
+ }
33
+ //# sourceMappingURL=SelectOrganization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectOrganization.js","sourceRoot":"","sources":["../../src/login/pages/SelectOrganization.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAc,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAKzD,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,KAAiF;;IACxH,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,eAAe,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC;IAEtE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;QACzB,eAAe;QACf,OAAO;KACV,CAAC,CAAC;IAEH,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC;IAEhC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;IAErB,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,MAAM,CAAkB,IAAI,CAAC,CAAC;IAC9C,MAAM,oBAAoB,GAAG,MAAM,CAAmB,IAAI,CAAC,CAAC;IAE5D,MAAM,mBAAmB,GAAG,CAAC,iBAAyB,EAAE,EAAE,CAAC,CAAC,KAAoC,EAAE,EAAE;QAChG,KAAK,CAAC,cAAc,EAAE,CAAC;QAEvB,IAAI,CAAC,oBAAoB,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;YACnD,OAAO;SACV;QAED,oBAAoB,CAAC,OAAO,CAAC,KAAK,GAAG,iBAAiB,CAAC;QACvD,eAAe,CAAC,IAAI,CAAC,CAAC;QAEtB,IAAI,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa,KAAK,UAAU,EAAE;YACrD,OAAO,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAChC,OAAO;SACV;QAED,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,aAAa,GAAG,MAAA,IAAI,CAAC,aAAa,mCAAI,EAAE,CAAC;IAC/C,MAAM,iBAAiB,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,CAAC;IAEnD,OAAO,CACH,KAAC,QAAQ,kBAAC,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,eAAe,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,gBAC5G,8BAAM,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,WAAW,EAAE,SAAS,EAAC,eAAe,EAAC,MAAM,EAAC,MAAM,iBAChF,6BAAK,EAAE,EAAC,uBAAuB,EAAC,SAAS,EAAE,MAAM,CAAC,kBAAkB,CAAC,iBACjE,uBAAK,GAAG,CAAC,qBAAqB,CAAC,GAAM,EACrC,2BAAI,SAAS,EAAE,MAAM,CAAC,8BAA8B,EAAE,iBAAiB,IAAI,kCAAkC,CAAC,gBACzG,aAAa,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CACpC,uBACI,+BACI,EAAE,EAAE,gBAAgB,KAAK,EAAE,EAC3B,SAAS,EAAE,MAAM,CAAC,oCAAoC,EAAE,iBAAiB,IAAI,6BAA6B,CAAC,EAC3G,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,EACnC,QAAQ,EAAE,YAAY,gBAEtB,6BAAM,SAAS,EAAE,MAAM,CAAC,8BAA8B,CAAC,gBAAG,IAAI,aAAJ,IAAI,cAAJ,IAAI,GAAI,KAAK,IAAQ,IAC1E,IATJ,KAAK,CAUT,CACR,CAAC,IACD,KACH,EACN,gBAAO,GAAG,EAAE,oBAAoB,EAAE,IAAI,EAAC,QAAQ,EAAC,IAAI,EAAC,QAAQ,GAAG,KAC7D,IACA,CACd,CAAC;AACN,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "keycloakify",
3
- "version": "11.10.0",
3
+ "version": "11.11.0",
4
4
  "description": "Framework to create custom Keycloak UIs",
5
5
  "repository": {
6
6
  "type": "git",
@@ -475,6 +475,9 @@
475
475
  "login/pages/SelectAuthenticator.d.ts",
476
476
  "login/pages/SelectAuthenticator.js",
477
477
  "login/pages/SelectAuthenticator.js.map",
478
+ "login/pages/SelectOrganization.d.ts",
479
+ "login/pages/SelectOrganization.js",
480
+ "login/pages/SelectOrganization.js.map",
478
481
  "login/pages/Terms.d.ts",
479
482
  "login/pages/Terms.js",
480
483
  "login/pages/Terms.js.map",
@@ -866,6 +869,7 @@
866
869
  "src/login/pages/Register.tsx",
867
870
  "src/login/pages/SamlPostForm.tsx",
868
871
  "src/login/pages/SelectAuthenticator.tsx",
872
+ "src/login/pages/SelectOrganization.tsx",
869
873
  "src/login/pages/Terms.tsx",
870
874
  "src/login/pages/UpdateEmail.tsx",
871
875
  "src/login/pages/WebauthnAuthenticate.tsx",
@@ -948,6 +952,7 @@
948
952
  "stories/login/pages/Register.stories.tsx",
949
953
  "stories/login/pages/SamlPostForm.stories.tsx",
950
954
  "stories/login/pages/SelectAuthenticator.stories.tsx",
955
+ "stories/login/pages/SelectOrganization.stories.tsx",
951
956
  "stories/login/pages/Terms.stories.tsx",
952
957
  "stories/login/pages/UpdateEmail.stories.tsx",
953
958
  "stories/login/pages/WebauthnAuthenticate.stories.tsx",
@@ -53,7 +53,8 @@ export const LOGIN_THEME_PAGE_IDS = [
53
53
  "login-x509-info.ftl",
54
54
  "webauthn-error.ftl",
55
55
  "login-passkeys-conditional-authenticate.ftl",
56
- "login-idp-link-confirm-override.ftl"
56
+ "login-idp-link-confirm-override.ftl",
57
+ "select-organization.ftl"
57
58
  ] as const;
58
59
 
59
60
  export const ACCOUNT_THEME_PAGE_IDS = [
@@ -43,6 +43,7 @@ const LoginX509Info = lazy(() => import("keycloakify/login/pages/LoginX509Info")
43
43
  const WebauthnError = lazy(() => import("keycloakify/login/pages/WebauthnError"));
44
44
  const LoginPasskeysConditionalAuthenticate = lazy(() => import("keycloakify/login/pages/LoginPasskeysConditionalAuthenticate"));
45
45
  const LoginIdpLinkConfirmOverride = lazy(() => import("keycloakify/login/pages/LoginIdpLinkConfirmOverride"));
46
+ const SelectOrganization = lazy(() => import("keycloakify/login/pages/SelectOrganization"));
46
47
 
47
48
  type DefaultPageProps = PageProps<KcContext, I18n> & {
48
49
  UserProfileFormFields: LazyOrNot<(props: UserProfileFormFieldsProps) => JSX.Element>;
@@ -128,6 +129,8 @@ export default function DefaultPage(props: DefaultPageProps) {
128
129
  return <LoginPasskeysConditionalAuthenticate kcContext={kcContext} {...rest} />;
129
130
  case "login-idp-link-confirm-override.ftl":
130
131
  return <LoginIdpLinkConfirmOverride kcContext={kcContext} {...rest} />;
132
+ case "select-organization.ftl":
133
+ return <SelectOrganization kcContext={kcContext} {...rest} />;
131
134
  }
132
135
  assert<Equals<typeof kcContext, never>>(false);
133
136
  })()}
@@ -61,7 +61,8 @@ export type KcContext =
61
61
  | KcContext.LoginX509Info
62
62
  | KcContext.WebauthnError
63
63
  | KcContext.LoginPasskeysConditionalAuthenticate
64
- | KcContext.LoginIdpLinkConfirmOverride;
64
+ | KcContext.LoginIdpLinkConfirmOverride
65
+ | KcContext.SelectOrganization;
65
66
 
66
67
  assert<KcContext["themeType"] extends ThemeType ? true : false>();
67
68
 
@@ -620,6 +621,16 @@ export declare namespace KcContext {
620
621
  };
621
622
  idpDisplayName: string;
622
623
  };
624
+
625
+ export type SelectOrganization = Common & {
626
+ pageId: "select-organization.ftl";
627
+ user: {
628
+ organizations: {
629
+ alias: string;
630
+ name?: string;
631
+ }[];
632
+ };
633
+ };
623
634
  }
624
635
 
625
636
  export type UserProfile = {
@@ -623,6 +623,29 @@ export const kcContextMocks = [
623
623
  loginRestartFlowUrl: "#"
624
624
  },
625
625
  idpDisplayName: "Google"
626
+ }),
627
+ id<KcContext.SelectOrganization>({
628
+ pageId: "select-organization.ftl",
629
+ ...kcContextCommonMock,
630
+ user: {
631
+ organizations: [
632
+ {
633
+ alias: "acme-inc",
634
+ name: "Acme Incorporated"
635
+ },
636
+ {
637
+ alias: "northwind-traders",
638
+ name: "Northwind Traders"
639
+ },
640
+ {
641
+ alias: "contoso-labs",
642
+ name: "Contoso Labs"
643
+ },
644
+ {
645
+ alias: "shared-services"
646
+ }
647
+ ]
648
+ }
626
649
  })
627
650
  ];
628
651
 
@@ -478,7 +478,8 @@ const messages = {
478
478
  selectAnOption: "اختر خيارًا",
479
479
  remove: "إزالة",
480
480
  addValue: "أضف قيمة",
481
- languages: "اللغات"
481
+ languages: "اللغات",
482
+ "organization.select": "اختر مؤسسة للمتابعة:"
482
483
  };
483
484
 
484
485
  export default messages;
@@ -510,7 +510,8 @@ const messages = {
510
510
  selectAnOption: "Selecciona una opció",
511
511
  remove: "Elimina",
512
512
  addValue: "Afegeix valor",
513
- languages: "Idiomes"
513
+ languages: "Idiomes",
514
+ "organization.select": "Selecciona una organització per continuar:"
514
515
  };
515
516
 
516
517
  export default messages;
@@ -487,7 +487,8 @@ const messages = {
487
487
  selectAnOption: "Vyberte možnost",
488
488
  remove: "Odstranit",
489
489
  addValue: "Přidat hodnotu",
490
- languages: "Jazyky"
490
+ languages: "Jazyky",
491
+ "organization.select": "Vyberte organizaci pro pokračování:"
491
492
  };
492
493
 
493
494
  export default messages;
@@ -349,7 +349,8 @@ const messages = {
349
349
  selectAnOption: "Vælg en mulighed",
350
350
  remove: "Fjern",
351
351
  addValue: "Tilføj værdi",
352
- languages: "Sprog"
352
+ languages: "Sprog",
353
+ "organization.select": "Vælg en organisation for at fortsætte:"
353
354
  };
354
355
 
355
356
  export default messages;
@@ -536,7 +536,8 @@ const messages = {
536
536
  selectAnOption: "Wählen Sie eine Option",
537
537
  remove: "Entfernen",
538
538
  addValue: "Wert hinzufügen",
539
- languages: "Sprachen"
539
+ languages: "Sprachen",
540
+ "organization.select": "Wählen Sie eine Organisation, um fortzufahren:"
540
541
  };
541
542
 
542
543
  export default messages;
@@ -505,7 +505,8 @@ const messages = {
505
505
  selectAnOption: "Επιλέξτε μια επιλογή",
506
506
  remove: "Αφαίρεση",
507
507
  addValue: "Προσθήκη τιμής",
508
- languages: "Γλώσσες"
508
+ languages: "Γλώσσες",
509
+ "organization.select": "Επιλέξτε έναν οργανισμό για να συνεχίσετε:"
509
510
  };
510
511
 
511
512
  export default messages;
@@ -521,7 +521,8 @@ const messages = {
521
521
  selectAnOption: "Select an option",
522
522
  remove: "Remove",
523
523
  addValue: "Add value",
524
- languages: "Languages"
524
+ languages: "Languages",
525
+ "organization.select": "Select an organization to proceed:"
525
526
  };
526
527
 
527
528
  export default messages;
@@ -518,7 +518,8 @@ const messages = {
518
518
  selectAnOption: "Selecciona una opción",
519
519
  remove: "Eliminar",
520
520
  addValue: "Añadir valor",
521
- languages: "Idiomas"
521
+ languages: "Idiomas",
522
+ "organization.select": "Selecciona una organización para continuar:"
522
523
  };
523
524
 
524
525
  export default messages;
@@ -473,7 +473,8 @@ const messages = {
473
473
  selectAnOption: "یک گزینه انتخاب کنید",
474
474
  remove: "حذف",
475
475
  addValue: "افزودن مقدار",
476
- languages: "زبان‌ها"
476
+ languages: "زبان‌ها",
477
+ "organization.select": "یک سازمان را برای ادامه انتخاب کنید:"
477
478
  };
478
479
 
479
480
  export default messages;
@@ -419,7 +419,8 @@ const messages = {
419
419
  selectAnOption: "Valitse vaihtoehto",
420
420
  remove: "Poista",
421
421
  addValue: "Lisää arvo",
422
- languages: "Kielet"
422
+ languages: "Kielet",
423
+ "organization.select": "Valitse organisaatio jatkaaksesi:"
423
424
  };
424
425
 
425
426
  export default messages;
@@ -451,7 +451,8 @@ const messages = {
451
451
  selectAnOption: "Sélectionnez une option",
452
452
  remove: "Supprimer",
453
453
  addValue: "Ajouter une valeur",
454
- languages: "Langues"
454
+ languages: "Langues",
455
+ "organization.select": "Sélectionnez une organisation pour continuer :"
455
456
  };
456
457
 
457
458
  export default messages;
@@ -505,7 +505,8 @@ const messages = {
505
505
  selectAnOption: "Válasszon egy lehetőséget",
506
506
  remove: "Eltávolítás",
507
507
  addValue: "Érték hozzáadása",
508
- languages: "Nyelvek"
508
+ languages: "Nyelvek",
509
+ "organization.select": "Válasszon egy szervezetet a folytatáshoz:"
509
510
  };
510
511
 
511
512
  export default messages;
@@ -359,7 +359,8 @@ const messages = {
359
359
  selectAnOption: "Seleziona un'opzione",
360
360
  remove: "Rimuovi",
361
361
  addValue: "Aggiungi valore",
362
- languages: "Lingue"
362
+ languages: "Lingue",
363
+ "organization.select": "Seleziona un'organizzazione per continuare:"
363
364
  };
364
365
 
365
366
  export default messages;
@@ -362,7 +362,8 @@ const messages = {
362
362
  selectAnOption: "オプションを選択",
363
363
  remove: "削除",
364
364
  addValue: "値を追加",
365
- languages: "言語"
365
+ languages: "言語",
366
+ "organization.select": "続行する組織を選択してください:"
366
367
  };
367
368
 
368
369
  export default messages;
@@ -530,7 +530,8 @@ const messages = {
530
530
  selectAnOption: "აირჩიეთ ვარიანტი",
531
531
  remove: "წაშალეთ",
532
532
  addValue: "დაამატეთ მნიშვნელობა",
533
- languages: "ენები"
533
+ languages: "ენები",
534
+ "organization.select": "აირჩიეთ ორგანიზაცია გაგრძელებისთვის:"
534
535
  };
535
536
 
536
537
  export default messages;
@@ -226,7 +226,8 @@ const messages = {
226
226
  selectAnOption: "Pasirinkite parinktį",
227
227
  remove: "Pašalinti",
228
228
  addValue: "Pridėti reikšmę",
229
- languages: "Kalbos"
229
+ languages: "Kalbos",
230
+ "organization.select": "Pasirinkite organizaciją, kad galėtumėte tęsti:"
230
231
  };
231
232
 
232
233
  export default messages;
@@ -222,7 +222,8 @@ const messages = {
222
222
  selectAnOption: "Izvēlieties opciju",
223
223
  remove: "Noņemt",
224
224
  addValue: "Pievienot vērtību",
225
- languages: "Valodas"
225
+ languages: "Valodas",
226
+ "organization.select": "Izvēlieties organizāciju, lai turpinātu:"
226
227
  };
227
228
 
228
229
  export default messages;
@@ -317,7 +317,8 @@ const messages = {
317
317
  selectAnOption: "Selecteer een optie",
318
318
  remove: "Verwijderen",
319
319
  addValue: "Waarde toevoegen",
320
- languages: "Talen"
320
+ languages: "Talen",
321
+ "organization.select": "Selecteer een organisatie om door te gaan:"
321
322
  };
322
323
 
323
324
  export default messages;
@@ -221,7 +221,8 @@ const messages = {
221
221
  selectAnOption: "Velg et alternativ",
222
222
  remove: "Fjern",
223
223
  addValue: "Legg til verdi",
224
- languages: "Språk"
224
+ languages: "Språk",
225
+ "organization.select": "Velg en organisasjon for å fortsette:"
225
226
  };
226
227
 
227
228
  export default messages;
@@ -321,7 +321,8 @@ const messages = {
321
321
  selectAnOption: "Wybierz opcję",
322
322
  remove: "Usuń",
323
323
  addValue: "Dodaj wartość",
324
- languages: "Języki"
324
+ languages: "Języki",
325
+ "organization.select": "Wybierz organizację, aby kontynuować:"
325
326
  };
326
327
 
327
328
  export default messages;
@@ -391,7 +391,8 @@ const messages = {
391
391
  selectAnOption: "Selecione uma opção",
392
392
  remove: "Remover",
393
393
  addValue: "Adicionar valor",
394
- languages: "Idiomas"
394
+ languages: "Idiomas",
395
+ "organization.select": "Selecione uma organização para continuar:"
395
396
  };
396
397
 
397
398
  export default messages;
@@ -512,7 +512,8 @@ const messages = {
512
512
  selectAnOption: "Selecione uma opção",
513
513
  remove: "Remover",
514
514
  addValue: "Adicionar valor",
515
- languages: "Idiomas"
515
+ languages: "Idiomas",
516
+ "organization.select": "Selecione uma organização para continuar:"
516
517
  };
517
518
 
518
519
  export default messages;
@@ -252,7 +252,8 @@ const messages = {
252
252
  selectAnOption: "Выберите вариант",
253
253
  remove: "Удалить",
254
254
  addValue: "Добавить значение",
255
- languages: "Языки"
255
+ languages: "Языки",
256
+ "organization.select": "Выберите организацию, чтобы продолжить:"
256
257
  };
257
258
 
258
259
  export default messages;
@@ -496,7 +496,8 @@ const messages = {
496
496
  selectAnOption: "Vyberte možnosť",
497
497
  remove: "Odstrániť",
498
498
  addValue: "Pridať hodnotu",
499
- languages: "Jazyky"
499
+ languages: "Jazyky",
500
+ "organization.select": "Vyberte organizáciu, aby ste pokračovali:"
500
501
  };
501
502
 
502
503
  export default messages;
@@ -222,7 +222,8 @@ const messages = {
222
222
  selectAnOption: "Välj ett alternativ",
223
223
  remove: "Ta bort",
224
224
  addValue: "Lägg till värde",
225
- languages: "Språk"
225
+ languages: "Språk",
226
+ "organization.select": "Välj en organisation för att fortsätta:"
226
227
  };
227
228
 
228
229
  export default messages;
@@ -484,7 +484,8 @@ const messages = {
484
484
  selectAnOption: "เลือกตัวเลือก",
485
485
  remove: "ลบ",
486
486
  addValue: "เพิ่มค่า",
487
- languages: "ภาษา"
487
+ languages: "ภาษา",
488
+ "organization.select": "เลือกองค์กรเพื่อดำเนินการต่อ:"
488
489
  };
489
490
 
490
491
  export default messages;
@@ -299,7 +299,8 @@ const messages = {
299
299
  selectAnOption: "Bir seçenek seçin",
300
300
  remove: "Kaldır",
301
301
  addValue: "Değer ekle",
302
- languages: "Diller"
302
+ languages: "Diller",
303
+ "organization.select": "Devam etmek için bir organizasyon seçin:"
303
304
  };
304
305
 
305
306
  export default messages;
@@ -510,7 +510,8 @@ const messages = {
510
510
  selectAnOption: "Виберіть опцію",
511
511
  remove: "Видалити",
512
512
  addValue: "Додати значення",
513
- languages: "Мови"
513
+ languages: "Мови",
514
+ "organization.select": "Виберіть організацію, щоб продовжити:"
514
515
  };
515
516
 
516
517
  export default messages;
@@ -455,7 +455,8 @@ const messages = {
455
455
  selectAnOption: "选择一个选项",
456
456
  remove: "移除",
457
457
  addValue: "添加值",
458
- languages: "语言"
458
+ languages: "语言",
459
+ "organization.select": "选择一个组织以继续:"
459
460
  };
460
461
 
461
462
  export default messages;
@@ -464,7 +464,8 @@ const messages = {
464
464
  selectAnOption: "選擇一個選項",
465
465
  remove: "移除",
466
466
  addValue: "添加值",
467
- languages: "語言"
467
+ languages: "語言",
468
+ "organization.select": "選擇一個組織以繼續:"
468
469
  };
469
470
 
470
471
  export default messages;
@@ -0,0 +1,69 @@
1
+ import { MouseEvent, useRef, useState } from "react";
2
+ import { getKcClsx } from "keycloakify/login/lib/kcClsx";
3
+ import type { PageProps } from "keycloakify/login/pages/PageProps";
4
+ import type { KcContext } from "../KcContext";
5
+ import type { I18n } from "../i18n";
6
+
7
+ export default function SelectOrganization(props: PageProps<Extract<KcContext, { pageId: "select-organization.ftl" }>, I18n>) {
8
+ const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
9
+
10
+ const { kcClsx } = getKcClsx({
11
+ doUseDefaultCss,
12
+ classes
13
+ });
14
+
15
+ const { url, user } = kcContext;
16
+
17
+ const { msg } = i18n;
18
+
19
+ const [isSubmitting, setIsSubmitting] = useState(false);
20
+ const formRef = useRef<HTMLFormElement>(null);
21
+ const organizationInputRef = useRef<HTMLInputElement>(null);
22
+
23
+ const onOrganizationClick = (organizationAlias: string) => (event: MouseEvent<HTMLButtonElement>) => {
24
+ event.preventDefault();
25
+
26
+ if (!organizationInputRef.current || !formRef.current) {
27
+ return;
28
+ }
29
+
30
+ organizationInputRef.current.value = organizationAlias;
31
+ setIsSubmitting(true);
32
+
33
+ if (typeof formRef.current.requestSubmit === "function") {
34
+ formRef.current.requestSubmit();
35
+ return;
36
+ }
37
+
38
+ formRef.current.submit();
39
+ };
40
+
41
+ const organizations = user.organizations ?? [];
42
+ const shouldDisplayGrid = organizations.length > 3;
43
+
44
+ return (
45
+ <Template kcContext={kcContext} i18n={i18n} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={null}>
46
+ <form ref={formRef} action={url.loginAction} className="form-vertical" method="post">
47
+ <div id="kc-user-organizations" className={kcClsx("kcFormGroupClass")}>
48
+ <h2>{msg("organization.select")}</h2>
49
+ <ul className={kcClsx("kcFormSocialAccountListClass", shouldDisplayGrid && "kcFormSocialAccountListGridClass")}>
50
+ {organizations.map(({ alias, name }) => (
51
+ <li key={alias}>
52
+ <button
53
+ id={`organization-${alias}`}
54
+ className={kcClsx("kcFormSocialAccountListButtonClass", shouldDisplayGrid && "kcFormSocialAccountGridItem")}
55
+ type="button"
56
+ onClick={onOrganizationClick(alias)}
57
+ disabled={isSubmitting}
58
+ >
59
+ <span className={kcClsx("kcFormSocialAccountNameClass")}>{name ?? alias}</span>
60
+ </button>
61
+ </li>
62
+ ))}
63
+ </ul>
64
+ </div>
65
+ <input ref={organizationInputRef} type="hidden" name="kc.org" />
66
+ </form>
67
+ </Template>
68
+ );
69
+ }
@@ -0,0 +1,18 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { createKcPageStory } from "../KcPageStory";
4
+
5
+ const { KcPageStory } = createKcPageStory({ pageId: "select-organization.ftl" });
6
+
7
+ const meta = {
8
+ title: "login/select-organization.ftl",
9
+ component: KcPageStory
10
+ } satisfies Meta<typeof KcPageStory>;
11
+
12
+ export default meta;
13
+
14
+ type Story = StoryObj<typeof meta>;
15
+
16
+ export const Default: Story = {
17
+ render: () => <KcPageStory />
18
+ };
@@ -1076,7 +1076,8 @@ const LOGIN_THEME_PAGE_IDS = [
1076
1076
  "login-x509-info.ftl",
1077
1077
  "webauthn-error.ftl",
1078
1078
  "login-passkeys-conditional-authenticate.ftl",
1079
- "login-idp-link-confirm-override.ftl"
1079
+ "login-idp-link-confirm-override.ftl",
1080
+ "select-organization.ftl"
1080
1081
  ];
1081
1082
  const ACCOUNT_THEME_PAGE_IDS = [
1082
1083
  "password.ftl",