@oussemasahbeni/keycloakify-login-shadcn 250004.0.2 → 250004.0.7

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 (174) hide show
  1. package/keycloak-theme/components/ui/alert.tsx +4 -4
  2. package/keycloak-theme/components/ui/dropdown-menu.tsx +5 -5
  3. package/keycloak-theme/components/ui/input.tsx +1 -1
  4. package/keycloak-theme/components/ui/select.tsx +4 -4
  5. package/keycloak-theme/components/ui/separator.tsx +1 -1
  6. package/keycloak-theme/login/KcContext.ts +23 -19
  7. package/keycloak-theme/login/KcPage.tsx +47 -60
  8. package/keycloak-theme/login/assets/img/auth-logo.svg +100 -100
  9. package/keycloak-theme/login/assets/img/shape.svg +71 -71
  10. package/keycloak-theme/login/components/LogoutOtherSessions.tsx +26 -26
  11. package/keycloak-theme/login/components/PasswordWrapper.tsx +35 -35
  12. package/keycloak-theme/login/components/Template/Template.tsx +227 -226
  13. package/keycloak-theme/login/components/Template/index.ts +1 -1
  14. package/keycloak-theme/login/components/Template/useInitializeTemplate.ts +61 -61
  15. package/keycloak-theme/login/components/UserProfileFormFields/AddRemoveButtonsMultiValuedAttribute.tsx +61 -61
  16. package/keycloak-theme/login/components/UserProfileFormFields/DO_MAKE_USER_CONFIRM_PASSWORD.ts +2 -2
  17. package/keycloak-theme/login/components/UserProfileFormFields/FieldErrors.tsx +28 -28
  18. package/keycloak-theme/login/components/UserProfileFormFields/GroupLabel.tsx +70 -70
  19. package/keycloak-theme/login/components/UserProfileFormFields/InputFieldByType.tsx +58 -58
  20. package/keycloak-theme/login/components/UserProfileFormFields/InputTag.tsx +116 -116
  21. package/keycloak-theme/login/components/UserProfileFormFields/InputTagSelects.tsx +135 -135
  22. package/keycloak-theme/login/components/UserProfileFormFields/SelectTag.tsx +114 -114
  23. package/keycloak-theme/login/components/UserProfileFormFields/TextareaTag.tsx +42 -42
  24. package/keycloak-theme/login/components/UserProfileFormFields/UserProfileFormFields.tsx +127 -127
  25. package/keycloak-theme/login/components/UserProfileFormFields/index.ts +1 -1
  26. package/keycloak-theme/login/i18n.ts +47 -51
  27. package/keycloak-theme/login/mocks/getKcContextMock.ts +22 -18
  28. package/keycloak-theme/login/pages/PageIndex.tsx +134 -134
  29. package/keycloak-theme/login/pages/code/Page.stories.tsx +62 -78
  30. package/keycloak-theme/login/pages/code/Page.tsx +89 -89
  31. package/keycloak-theme/login/pages/code/index.ts +3 -3
  32. package/keycloak-theme/login/pages/delete-account-confirm/Page.stories.tsx +39 -46
  33. package/keycloak-theme/login/pages/delete-account-confirm/Page.tsx +63 -63
  34. package/keycloak-theme/login/pages/delete-account-confirm/index.ts +3 -3
  35. package/keycloak-theme/login/pages/delete-credential/Page.stories.tsx +26 -30
  36. package/keycloak-theme/login/pages/delete-credential/Page.tsx +51 -51
  37. package/keycloak-theme/login/pages/delete-credential/index.ts +3 -3
  38. package/keycloak-theme/login/pages/error/Page.stories.tsx +47 -58
  39. package/keycloak-theme/login/pages/error/Page.tsx +42 -42
  40. package/keycloak-theme/login/pages/error/index.ts +3 -3
  41. package/keycloak-theme/login/pages/frontchannel-logout/Page.stories.tsx +25 -32
  42. package/keycloak-theme/login/pages/frontchannel-logout/Page.tsx +84 -84
  43. package/keycloak-theme/login/pages/frontchannel-logout/index.ts +3 -3
  44. package/keycloak-theme/login/pages/idp-review-user-profile/Page.stories.tsx +46 -58
  45. package/keycloak-theme/login/pages/idp-review-user-profile/Page.tsx +52 -52
  46. package/keycloak-theme/login/pages/idp-review-user-profile/index.ts +3 -3
  47. package/keycloak-theme/login/pages/info/Page.stories.tsx +50 -60
  48. package/keycloak-theme/login/pages/info/Page.tsx +92 -92
  49. package/keycloak-theme/login/pages/link-idp-action/Page.stories.tsx +32 -16
  50. package/keycloak-theme/login/pages/link-idp-action/Page.tsx +43 -43
  51. package/keycloak-theme/login/pages/link-idp-action/index.ts +3 -3
  52. package/keycloak-theme/login/pages/login/Form.tsx +242 -242
  53. package/keycloak-theme/login/pages/login/Info.tsx +29 -29
  54. package/keycloak-theme/login/pages/login/Page.stories.tsx +345 -365
  55. package/keycloak-theme/login/pages/login/Page.tsx +44 -44
  56. package/keycloak-theme/login/pages/login/SocialProviders.tsx +107 -107
  57. package/keycloak-theme/login/pages/login/index.ts +3 -3
  58. package/keycloak-theme/login/pages/login/providers/apple.svg +3 -3
  59. package/keycloak-theme/login/pages/login/providers/bitbucket.svg +11 -11
  60. package/keycloak-theme/login/pages/login/providers/discord.svg +4 -4
  61. package/keycloak-theme/login/pages/login/providers/facebook.svg +5 -5
  62. package/keycloak-theme/login/pages/login/providers/github.svg +3 -3
  63. package/keycloak-theme/login/pages/login/providers/gitlab.svg +7 -7
  64. package/keycloak-theme/login/pages/login/providers/google.svg +7 -7
  65. package/keycloak-theme/login/pages/login/providers/instagram.svg +31 -31
  66. package/keycloak-theme/login/pages/login/providers/linkedin.svg +3 -3
  67. package/keycloak-theme/login/pages/login/providers/microsoft.svg +6 -6
  68. package/keycloak-theme/login/pages/login/providers/oidc.svg +5 -5
  69. package/keycloak-theme/login/pages/login/providers/openshift.svg +7 -7
  70. package/keycloak-theme/login/pages/login/providers/paypal.svg +6 -6
  71. package/keycloak-theme/login/pages/login/providers/slack.svg +11 -11
  72. package/keycloak-theme/login/pages/login/providers/stackoverflow.svg +5 -5
  73. package/keycloak-theme/login/pages/login/providers/x.svg +3 -3
  74. package/keycloak-theme/login/pages/login/useProviderLogos.tsx +39 -39
  75. package/keycloak-theme/login/pages/login/useScript.tsx +62 -62
  76. package/keycloak-theme/login/pages/login-config-totp/Page.stories.tsx +45 -59
  77. package/keycloak-theme/login/pages/login-config-totp/Page.tsx +240 -240
  78. package/keycloak-theme/login/pages/login-config-totp/index.ts +3 -3
  79. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.stories.tsx +30 -34
  80. package/keycloak-theme/login/pages/login-idp-link-confirm/Page.tsx +43 -43
  81. package/keycloak-theme/login/pages/login-idp-link-confirm/index.ts +3 -3
  82. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.stories.tsx +16 -22
  83. package/keycloak-theme/login/pages/login-idp-link-confirm-override/Page.tsx +47 -47
  84. package/keycloak-theme/login/pages/login-idp-link-confirm-override/index.ts +3 -3
  85. package/keycloak-theme/login/pages/login-idp-link-email/Page.stories.tsx +54 -62
  86. package/keycloak-theme/login/pages/login-idp-link-email/Page.tsx +54 -54
  87. package/keycloak-theme/login/pages/login-idp-link-email/index.ts +3 -3
  88. package/keycloak-theme/login/pages/login-oauth-grant/Page.stories.tsx +39 -45
  89. package/keycloak-theme/login/pages/login-oauth-grant/Page.tsx +126 -126
  90. package/keycloak-theme/login/pages/login-oauth-grant/index.ts +3 -3
  91. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.stories.tsx +38 -48
  92. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/Page.tsx +58 -58
  93. package/keycloak-theme/login/pages/login-oauth2-device-verify-user-code/index.ts +3 -3
  94. package/keycloak-theme/login/pages/login-otp/Page.stories.tsx +82 -96
  95. package/keycloak-theme/login/pages/login-otp/Page.tsx +108 -108
  96. package/keycloak-theme/login/pages/login-otp/index.ts +3 -3
  97. package/keycloak-theme/login/pages/login-page-expired/Page.stories.tsx +28 -36
  98. package/keycloak-theme/login/pages/login-page-expired/Page.tsx +47 -47
  99. package/keycloak-theme/login/pages/login-page-expired/index.ts +3 -3
  100. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.stories.tsx +20 -0
  101. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/Page.tsx +233 -233
  102. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/index.ts +3 -3
  103. package/keycloak-theme/login/pages/login-passkeys-conditional-authenticate/useScript.tsx +63 -63
  104. package/keycloak-theme/login/pages/login-password/Page.stories.tsx +55 -56
  105. package/keycloak-theme/login/pages/login-password/Page.tsx +149 -149
  106. package/keycloak-theme/login/pages/login-password/index.ts +3 -3
  107. package/keycloak-theme/login/pages/login-password/useScript.tsx +63 -63
  108. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.stories.tsx +28 -36
  109. package/keycloak-theme/login/pages/login-recovery-authn-code-config/Page.tsx +181 -181
  110. package/keycloak-theme/login/pages/login-recovery-authn-code-config/index.ts +3 -3
  111. package/keycloak-theme/login/pages/login-recovery-authn-code-config/useScript.tsx +145 -145
  112. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.stories.tsx +16 -22
  113. package/keycloak-theme/login/pages/login-recovery-authn-code-input/Page.tsx +70 -70
  114. package/keycloak-theme/login/pages/login-recovery-authn-code-input/index.ts +3 -3
  115. package/keycloak-theme/login/pages/login-reset-otp/Page.stories.tsx +62 -74
  116. package/keycloak-theme/login/pages/login-reset-otp/Page.tsx +86 -86
  117. package/keycloak-theme/login/pages/login-reset-otp/index.ts +3 -3
  118. package/keycloak-theme/login/pages/login-reset-password/Form.tsx +68 -68
  119. package/keycloak-theme/login/pages/login-reset-password/Page.stories.tsx +44 -54
  120. package/keycloak-theme/login/pages/login-reset-password/Page.tsx +27 -27
  121. package/keycloak-theme/login/pages/login-reset-password/index.ts +3 -3
  122. package/keycloak-theme/login/pages/login-update-password/Page.stories.tsx +40 -50
  123. package/keycloak-theme/login/pages/login-update-password/Page.tsx +111 -111
  124. package/keycloak-theme/login/pages/login-update-password/index.ts +3 -3
  125. package/keycloak-theme/login/pages/login-update-profile/Page.stories.tsx +28 -36
  126. package/keycloak-theme/login/pages/login-update-profile/Page.tsx +68 -68
  127. package/keycloak-theme/login/pages/login-update-profile/index.ts +3 -3
  128. package/keycloak-theme/login/pages/login-username/Page.stories.tsx +32 -42
  129. package/keycloak-theme/login/pages/login-username/Page.tsx +246 -246
  130. package/keycloak-theme/login/pages/login-username/index.ts +3 -3
  131. package/keycloak-theme/login/pages/login-username/useScript.tsx +62 -62
  132. package/keycloak-theme/login/pages/login-verify-email/Page.stories.tsx +68 -80
  133. package/keycloak-theme/login/pages/login-verify-email/Page.tsx +38 -38
  134. package/keycloak-theme/login/pages/login-verify-email/index.ts +3 -3
  135. package/keycloak-theme/login/pages/login-x509-info/Page.stories.tsx +29 -37
  136. package/keycloak-theme/login/pages/login-x509-info/Page.tsx +75 -75
  137. package/keycloak-theme/login/pages/login-x509-info/index.ts +3 -3
  138. package/keycloak-theme/login/pages/logout-confirm/Page.stories.tsx +34 -42
  139. package/keycloak-theme/login/pages/logout-confirm/Page.tsx +53 -53
  140. package/keycloak-theme/login/pages/logout-confirm/index.ts +3 -3
  141. package/keycloak-theme/login/pages/register/Form.tsx +106 -106
  142. package/keycloak-theme/login/pages/register/Page.stories.tsx +23 -6
  143. package/keycloak-theme/login/pages/register/Page.tsx +26 -26
  144. package/keycloak-theme/login/pages/register/TermsAcceptance.tsx +56 -56
  145. package/keycloak-theme/login/pages/register/index.ts +3 -3
  146. package/keycloak-theme/login/pages/saml-post-form/Page.stories.tsx +16 -22
  147. package/keycloak-theme/login/pages/saml-post-form/Page.tsx +66 -66
  148. package/keycloak-theme/login/pages/saml-post-form/index.ts +3 -3
  149. package/keycloak-theme/login/pages/select-authenticator/Page.stories.tsx +83 -95
  150. package/keycloak-theme/login/pages/select-authenticator/Page.tsx +100 -100
  151. package/keycloak-theme/login/pages/select-authenticator/index.ts +3 -3
  152. package/keycloak-theme/login/pages/select-organization/Page.stories.tsx +62 -49
  153. package/keycloak-theme/login/pages/select-organization/Page.tsx +126 -126
  154. package/keycloak-theme/login/pages/select-organization/index.ts +3 -3
  155. package/keycloak-theme/login/pages/terms/Page.stories.tsx +15 -0
  156. package/keycloak-theme/login/pages/terms/Page.tsx +51 -51
  157. package/keycloak-theme/login/pages/terms/index.ts +3 -3
  158. package/keycloak-theme/login/pages/update-email/Page.stories.tsx +27 -35
  159. package/keycloak-theme/login/pages/update-email/Page.tsx +62 -62
  160. package/keycloak-theme/login/pages/update-email/index.ts +3 -3
  161. package/keycloak-theme/login/pages/webauthn-authenticate/Page.stories.tsx +112 -126
  162. package/keycloak-theme/login/pages/webauthn-authenticate/Page.tsx +202 -202
  163. package/keycloak-theme/login/pages/webauthn-authenticate/index.ts +3 -3
  164. package/keycloak-theme/login/pages/webauthn-authenticate/useScript.tsx +55 -55
  165. package/keycloak-theme/login/pages/webauthn-error/Page.stories.tsx +54 -66
  166. package/keycloak-theme/login/pages/webauthn-error/Page.tsx +73 -73
  167. package/keycloak-theme/login/pages/webauthn-error/index.ts +3 -3
  168. package/keycloak-theme/login/pages/webauthn-register/Page.stories.tsx +39 -49
  169. package/keycloak-theme/login/pages/webauthn-register/Page.tsx +78 -78
  170. package/keycloak-theme/login/pages/webauthn-register/index.ts +3 -3
  171. package/keycloak-theme/login/pages/webauthn-register/useScript.tsx +62 -62
  172. package/keycloak-theme/login/shared/getColorScheme.ts +45 -0
  173. package/keycloak-theme/login/styleLevelCustomization.tsx +36 -17
  174. package/package.json +6 -5
@@ -12,32 +12,26 @@ export default meta;
12
12
 
13
13
  type Story = StoryObj<typeof meta>;
14
14
 
15
- export const Default: Story = {
16
- render: () => <KcPageStory />
17
- };
15
+ export const Default: Story = {};
18
16
 
19
17
  export const Arabic: Story = {
20
- render: () => (
21
- <KcPageStory
22
- kcContext={{
23
- locale: {
24
- currentLanguageTag: "ar",
25
- rtl: true
26
- }
27
- }}
28
- />
29
- )
18
+ args: {
19
+ kcContext: {
20
+ locale: {
21
+ currentLanguageTag: "ar",
22
+ rtl: true
23
+ }
24
+ }
25
+ }
30
26
  };
31
27
  export const French: Story = {
32
- render: () => (
33
- <KcPageStory
34
- kcContext={{
35
- locale: {
36
- currentLanguageTag: "fr"
37
- }
38
- }}
39
- />
40
- )
28
+ args: {
29
+ kcContext: {
30
+ locale: {
31
+ currentLanguageTag: "fr"
32
+ }
33
+ }
34
+ }
41
35
  };
42
36
 
43
37
  /**
@@ -47,29 +41,27 @@ export const French: Story = {
47
41
  * - Key Aspect: Ensures that multiple OTP credentials are listed and selectable, and the correct credential is selected by default.
48
42
  */
49
43
  export const MultipleOtpCredentials: Story = {
50
- render: () => (
51
- <KcPageStory
52
- kcContext={{
53
- otpLogin: {
54
- userOtpCredentials: [
55
- { id: "credential1", userLabel: "Device 1" },
56
- { id: "credential2", userLabel: "Device 2" },
57
- { id: "credential3", userLabel: "Device 3" },
58
- { id: "credential4", userLabel: "Device 4" },
59
- { id: "credential5", userLabel: "Device 5" },
60
- { id: "credential6", userLabel: "Device 6" }
61
- ],
62
- selectedCredentialId: "credential1"
63
- },
64
- url: {
65
- loginAction: "/login-action"
66
- },
67
- messagesPerField: {
68
- existsError: () => false
69
- }
70
- }}
71
- />
72
- )
44
+ args: {
45
+ kcContext: {
46
+ otpLogin: {
47
+ userOtpCredentials: [
48
+ { id: "credential1", userLabel: "Device 1" },
49
+ { id: "credential2", userLabel: "Device 2" },
50
+ { id: "credential3", userLabel: "Device 3" },
51
+ { id: "credential4", userLabel: "Device 4" },
52
+ { id: "credential5", userLabel: "Device 5" },
53
+ { id: "credential6", userLabel: "Device 6" }
54
+ ],
55
+ selectedCredentialId: "credential1"
56
+ },
57
+ url: {
58
+ loginAction: "/login-action"
59
+ },
60
+ messagesPerField: {
61
+ existsError: () => false
62
+ }
63
+ }
64
+ }
73
65
  };
74
66
 
75
67
  /**
@@ -79,22 +71,20 @@ export const MultipleOtpCredentials: Story = {
79
71
  * - Key Aspect: Ensures that the OTP input displays error messages correctly and the error is visible.
80
72
  */
81
73
  export const WithOtpError: Story = {
82
- render: () => (
83
- <KcPageStory
84
- kcContext={{
85
- otpLogin: {
86
- userOtpCredentials: []
87
- },
88
- url: {
89
- loginAction: "/login-action"
90
- },
91
- messagesPerField: {
92
- existsError: (field: string) => field === "totp",
93
- get: () => "Invalid OTP code"
94
- }
95
- }}
96
- />
97
- )
74
+ args: {
75
+ kcContext: {
76
+ otpLogin: {
77
+ userOtpCredentials: []
78
+ },
79
+ url: {
80
+ loginAction: "/login-action"
81
+ },
82
+ messagesPerField: {
83
+ existsError: (field: string) => field === "totp",
84
+ get: () => "Invalid OTP code"
85
+ }
86
+ }
87
+ }
98
88
  };
99
89
 
100
90
  /**
@@ -104,21 +94,19 @@ export const WithOtpError: Story = {
104
94
  * - Key Aspect: Ensures that the component handles cases where there are no user OTP credentials, and the user is only prompted for the OTP code.
105
95
  */
106
96
  export const NoOtpCredentials: Story = {
107
- render: () => (
108
- <KcPageStory
109
- kcContext={{
110
- otpLogin: {
111
- userOtpCredentials: []
112
- },
113
- url: {
114
- loginAction: "/login-action"
115
- },
116
- messagesPerField: {
117
- existsError: () => false
118
- }
119
- }}
120
- />
121
- )
97
+ args: {
98
+ kcContext: {
99
+ otpLogin: {
100
+ userOtpCredentials: []
101
+ },
102
+ url: {
103
+ loginAction: "/login-action"
104
+ },
105
+ messagesPerField: {
106
+ existsError: () => false
107
+ }
108
+ }
109
+ }
122
110
  };
123
111
 
124
112
  /**
@@ -128,24 +116,22 @@ export const NoOtpCredentials: Story = {
128
116
  * - Key Aspect: Ensures that the component can handle both multiple OTP credentials and display an error message simultaneously.
129
117
  */
130
118
  export const WithErrorAndMultipleOtpCredentials: Story = {
131
- render: () => (
132
- <KcPageStory
133
- kcContext={{
134
- otpLogin: {
135
- userOtpCredentials: [
136
- { id: "credential1", userLabel: "Device 1" },
137
- { id: "credential2", userLabel: "Device 2" }
138
- ],
139
- selectedCredentialId: "credential1"
140
- },
141
- url: {
142
- loginAction: "/login-action"
143
- },
144
- messagesPerField: {
145
- existsError: (field: string) => field === "totp",
146
- get: () => "Invalid OTP code"
147
- }
148
- }}
149
- />
150
- )
119
+ args: {
120
+ kcContext: {
121
+ otpLogin: {
122
+ userOtpCredentials: [
123
+ { id: "credential1", userLabel: "Device 1" },
124
+ { id: "credential2", userLabel: "Device 2" }
125
+ ],
126
+ selectedCredentialId: "credential1"
127
+ },
128
+ url: {
129
+ loginAction: "/login-action"
130
+ },
131
+ messagesPerField: {
132
+ existsError: (field: string) => field === "totp",
133
+ get: () => "Invalid OTP code"
134
+ }
135
+ }
136
+ }
151
137
  };
@@ -1,108 +1,108 @@
1
- import { Button } from "@/components/ui/button";
2
- import { FieldError } from '@/components/ui/field';
3
- import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
4
- import { Label } from "@/components/ui/label";
5
- import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
6
- import { useI18n } from '@/login/i18n';
7
- import { useKcContext } from '@/login/KcContext';
8
- import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
9
- import { kcSanitize } from "keycloakify/lib/kcSanitize";
10
- import { useState } from 'react';
11
- import { MdOutlineDevices } from "react-icons/md";
12
- import { assert } from "tsafe/assert";
13
- import { Template } from "../../components/Template";
14
-
15
- export function Page() {
16
-
17
- const { kcContext } = useKcContext();
18
-
19
- assert(kcContext.pageId === "login-otp.ftl");
20
-
21
- const { msg, msgStr } = useI18n();
22
-
23
- const [isSubmitting, setIsSubmitting] = useState(false);
24
- return (
25
- <Template
26
- displayMessage={!kcContext.messagesPerField.existsError("totp")}
27
- headerNode={msg("doLogIn")}
28
- >
29
- <form
30
- id="kc-otp-login-form"
31
- className="space-y-6"
32
- action={kcContext.url.loginAction}
33
- onSubmit={() => {
34
- setIsSubmitting(true);
35
- return true;
36
- }}
37
- method="post"
38
- >
39
- {kcContext.otpLogin.userOtpCredentials.length > 1 && (
40
- <div className="space-y-3">
41
- <RadioGroup
42
- name="selectedCredentialId"
43
- defaultValue={kcContext.otpLogin.selectedCredentialId}
44
- className="space-y-2"
45
- >
46
- {kcContext.otpLogin.userOtpCredentials.map((otpCredential, index) => (
47
- <div
48
- key={otpCredential.id}
49
- className="flex items-center space-x-3 p-3 border rounded-lg"
50
- >
51
- <RadioGroupItem
52
- value={otpCredential.id}
53
- id={`kc-otp-credential-${index}`}
54
- />
55
- <Label
56
- htmlFor={`kc-otp-credential-${index}`}
57
- className="flex items-center space-x-2 cursor-pointer flex-1"
58
- >
59
- <MdOutlineDevices />
60
- <span className="text-sm font-medium">
61
- {otpCredential.userLabel}
62
- </span>
63
- </Label>
64
- </div>
65
- ))}
66
- </RadioGroup>
67
- </div>
68
- )}
69
-
70
- <div className="space-y-2">
71
- <Label htmlFor="otp" className="text-sm font-medium block">
72
- {msg("loginOtpOneTime")}
73
- </Label>
74
- <div className="flex w-72 ">
75
- <InputOTP
76
- maxLength={6}
77
- pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
78
- name="otp"
79
- autoFocus
80
- >
81
- <InputOTPGroup>
82
- <InputOTPSlot index={0} className="h-12 w-12 text-lg" />
83
- <InputOTPSlot index={1} className="h-12 w-12 text-lg" />
84
- <InputOTPSlot index={2} className="h-12 w-12 text-lg" />
85
- <InputOTPSlot index={3} className="h-12 w-12 text-lg" />
86
- <InputOTPSlot index={4} className="h-12 w-12 text-lg" />
87
- <InputOTPSlot index={5} className="h-12 w-12 text-lg" />
88
- </InputOTPGroup>
89
- </InputOTP>
90
- </div>
91
- {kcContext.messagesPerField.existsError("totp") && (
92
- <FieldError id="input-error-otp-code">
93
- <span
94
- dangerouslySetInnerHTML={{
95
- __html: kcSanitize(kcContext.messagesPerField.get("totp"))
96
- }}
97
- />
98
- </FieldError>
99
- )}
100
- </div>
101
-
102
- <Button className="w-full" name="login" id="kc-login" type="submit" disabled={isSubmitting}>
103
- {msgStr("doLogIn")}
104
- </Button>
105
- </form>
106
- </Template>
107
- );
108
- }
1
+ import { Button } from "@/components/ui/button";
2
+ import { FieldError } from '@/components/ui/field';
3
+ import { InputOTP, InputOTPGroup, InputOTPSlot } from "@/components/ui/input-otp";
4
+ import { Label } from "@/components/ui/label";
5
+ import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
6
+ import { useI18n } from '@/login/i18n';
7
+ import { useKcContext } from '@/login/KcContext';
8
+ import { REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp";
9
+ import { kcSanitize } from "keycloakify/lib/kcSanitize";
10
+ import { useState } from 'react';
11
+ import { MdOutlineDevices } from "react-icons/md";
12
+ import { assert } from "tsafe/assert";
13
+ import { Template } from "../../components/Template";
14
+
15
+ export function Page() {
16
+
17
+ const { kcContext } = useKcContext();
18
+
19
+ assert(kcContext.pageId === "login-otp.ftl");
20
+
21
+ const { msg, msgStr } = useI18n();
22
+
23
+ const [isSubmitting, setIsSubmitting] = useState(false);
24
+ return (
25
+ <Template
26
+ displayMessage={!kcContext.messagesPerField.existsError("totp")}
27
+ headerNode={msg("doLogIn")}
28
+ >
29
+ <form
30
+ id="kc-otp-login-form"
31
+ className="space-y-6"
32
+ action={kcContext.url.loginAction}
33
+ onSubmit={() => {
34
+ setIsSubmitting(true);
35
+ return true;
36
+ }}
37
+ method="post"
38
+ >
39
+ {kcContext.otpLogin.userOtpCredentials.length > 1 && (
40
+ <div className="space-y-3">
41
+ <RadioGroup
42
+ name="selectedCredentialId"
43
+ defaultValue={kcContext.otpLogin.selectedCredentialId}
44
+ className="space-y-2"
45
+ >
46
+ {kcContext.otpLogin.userOtpCredentials.map((otpCredential, index) => (
47
+ <div
48
+ key={otpCredential.id}
49
+ className="flex items-center space-x-3 p-3 border rounded-lg"
50
+ >
51
+ <RadioGroupItem
52
+ value={otpCredential.id}
53
+ id={`kc-otp-credential-${index}`}
54
+ />
55
+ <Label
56
+ htmlFor={`kc-otp-credential-${index}`}
57
+ className="flex items-center space-x-2 cursor-pointer flex-1"
58
+ >
59
+ <MdOutlineDevices />
60
+ <span className="text-sm font-medium">
61
+ {otpCredential.userLabel}
62
+ </span>
63
+ </Label>
64
+ </div>
65
+ ))}
66
+ </RadioGroup>
67
+ </div>
68
+ )}
69
+
70
+ <div className="space-y-2">
71
+ <Label htmlFor="otp" className="text-sm font-medium block">
72
+ {msg("loginOtpOneTime")}
73
+ </Label>
74
+ <div className="flex w-72 ">
75
+ <InputOTP
76
+ maxLength={6}
77
+ pattern={REGEXP_ONLY_DIGITS_AND_CHARS}
78
+ name="otp"
79
+ autoFocus
80
+ >
81
+ <InputOTPGroup>
82
+ <InputOTPSlot index={0} className="h-12 w-12 text-lg" />
83
+ <InputOTPSlot index={1} className="h-12 w-12 text-lg" />
84
+ <InputOTPSlot index={2} className="h-12 w-12 text-lg" />
85
+ <InputOTPSlot index={3} className="h-12 w-12 text-lg" />
86
+ <InputOTPSlot index={4} className="h-12 w-12 text-lg" />
87
+ <InputOTPSlot index={5} className="h-12 w-12 text-lg" />
88
+ </InputOTPGroup>
89
+ </InputOTP>
90
+ </div>
91
+ {kcContext.messagesPerField.existsError("totp") && (
92
+ <FieldError id="input-error-otp-code">
93
+ <span
94
+ dangerouslySetInnerHTML={{
95
+ __html: kcSanitize(kcContext.messagesPerField.get("totp"))
96
+ }}
97
+ />
98
+ </FieldError>
99
+ )}
100
+ </div>
101
+
102
+ <Button className="w-full" name="login" id="kc-login" type="submit" disabled={isSubmitting}>
103
+ {msgStr("doLogIn")}
104
+ </Button>
105
+ </form>
106
+ </Template>
107
+ );
108
+ }
@@ -1,3 +1,3 @@
1
- import { Page } from "./Page";
2
-
3
- export default Page;
1
+ import { Page } from "./Page";
2
+
3
+ export default Page;
@@ -12,32 +12,26 @@ export default meta;
12
12
 
13
13
  type Story = StoryObj<typeof meta>;
14
14
 
15
- export const Default: Story = {
16
- render: () => <KcPageStory />
17
- };
15
+ export const Default: Story = {};
18
16
 
19
17
  export const Arabic: Story = {
20
- render: () => (
21
- <KcPageStory
22
- kcContext={{
23
- locale: {
24
- currentLanguageTag: "ar",
25
- rtl: true
26
- }
27
- }}
28
- />
29
- )
18
+ args: {
19
+ kcContext: {
20
+ locale: {
21
+ currentLanguageTag: "ar",
22
+ rtl: true
23
+ }
24
+ }
25
+ }
30
26
  };
31
27
  export const French: Story = {
32
- render: () => (
33
- <KcPageStory
34
- kcContext={{
35
- locale: {
36
- currentLanguageTag: "fr"
37
- }
38
- }}
39
- />
40
- )
28
+ args: {
29
+ kcContext: {
30
+ locale: {
31
+ currentLanguageTag: "fr"
32
+ }
33
+ }
34
+ }
41
35
  };
42
36
 
43
37
  /**
@@ -47,18 +41,16 @@ export const French: Story = {
47
41
  * - Key Aspect: Ensures that error messages are displayed correctly in addition to the page expiration notice.
48
42
  */
49
43
  export const WithErrorMessage: Story = {
50
- render: () => (
51
- <KcPageStory
52
- kcContext={{
53
- url: {
54
- loginRestartFlowUrl: "/mock-restart-flow",
55
- loginAction: "/mock-continue-login"
56
- },
57
- message: {
58
- type: "error",
59
- summary: "An error occurred while processing your session."
60
- }
61
- }}
62
- />
63
- )
44
+ args: {
45
+ kcContext: {
46
+ url: {
47
+ loginRestartFlowUrl: "/mock-restart-flow",
48
+ loginAction: "/mock-continue-login"
49
+ },
50
+ message: {
51
+ type: "error",
52
+ summary: "An error occurred while processing your session."
53
+ }
54
+ }
55
+ }
64
56
  };
@@ -1,47 +1,47 @@
1
- import { Alert, AlertDescription } from "@/components/ui/alert";
2
- import { useI18n } from '@/login/i18n';
3
- import { useKcContext } from '@/login/KcContext';
4
- import { assert } from 'tsafe/assert';
5
- import { Template } from "../../components/Template";
6
-
7
- export function Page() {
8
- const { kcContext } = useKcContext();
9
- assert(kcContext.pageId === "login-page-expired.ftl");
10
-
11
- const { msg } = useI18n();
12
-
13
- return (
14
- <Template
15
- headerNode={msg("pageExpiredTitle")}
16
- >
17
- <Alert variant="warning" className="my-6">
18
- <AlertDescription>
19
- <div className="space-y-3 text-sm leading-relaxed">
20
- <p>
21
- {msg("pageExpiredMsg1")}{" "}
22
- <a
23
- id="loginRestartLink"
24
- href={kcContext.url.loginRestartFlowUrl}
25
- className="text-primary dark:text-white hover:text-primary/80 underline underline-offset-2 font-medium"
26
- >
27
- {msg("doClickHere")}
28
- </a>
29
- .
30
- </p>
31
- <p>
32
- {msg("pageExpiredMsg2")}{" "}
33
- <a
34
- id="loginContinueLink"
35
- href={kcContext.url.loginAction}
36
- className="text-primary dark:text-white hover:text-primary/80 underline underline-offset-2 font-medium"
37
- >
38
- {msg("doClickHere")}
39
- </a>
40
- .
41
- </p>
42
- </div>
43
- </AlertDescription>
44
- </Alert>
45
- </Template>
46
- );
47
- }
1
+ import { Alert, AlertDescription } from "@/components/ui/alert";
2
+ import { useI18n } from '@/login/i18n';
3
+ import { useKcContext } from '@/login/KcContext';
4
+ import { assert } from 'tsafe/assert';
5
+ import { Template } from "../../components/Template";
6
+
7
+ export function Page() {
8
+ const { kcContext } = useKcContext();
9
+ assert(kcContext.pageId === "login-page-expired.ftl");
10
+
11
+ const { msg } = useI18n();
12
+
13
+ return (
14
+ <Template
15
+ headerNode={msg("pageExpiredTitle")}
16
+ >
17
+ <Alert variant="warning" className="my-6">
18
+ <AlertDescription>
19
+ <div className="space-y-3 text-sm leading-relaxed">
20
+ <p>
21
+ {msg("pageExpiredMsg1")}{" "}
22
+ <a
23
+ id="loginRestartLink"
24
+ href={kcContext.url.loginRestartFlowUrl}
25
+ className="text-primary dark:text-white hover:text-primary/80 underline underline-offset-2 font-medium"
26
+ >
27
+ {msg("doClickHere")}
28
+ </a>
29
+ .
30
+ </p>
31
+ <p>
32
+ {msg("pageExpiredMsg2")}{" "}
33
+ <a
34
+ id="loginContinueLink"
35
+ href={kcContext.url.loginAction}
36
+ className="text-primary dark:text-white hover:text-primary/80 underline underline-offset-2 font-medium"
37
+ >
38
+ {msg("doClickHere")}
39
+ </a>
40
+ .
41
+ </p>
42
+ </div>
43
+ </AlertDescription>
44
+ </Alert>
45
+ </Template>
46
+ );
47
+ }
@@ -1,3 +1,3 @@
1
- import { Page } from "./Page";
2
-
3
- export default Page;
1
+ import { Page } from "./Page";
2
+
3
+ export default Page;
@@ -14,3 +14,23 @@ export default meta;
14
14
  type Story = StoryObj<typeof meta>;
15
15
 
16
16
  export const Default: Story = {};
17
+
18
+ export const Arabic: Story = {
19
+ args: {
20
+ kcContext: {
21
+ locale: {
22
+ currentLanguageTag: "ar",
23
+ rtl: true
24
+ }
25
+ }
26
+ }
27
+ };
28
+ export const French: Story = {
29
+ args: {
30
+ kcContext: {
31
+ locale: {
32
+ currentLanguageTag: "fr"
33
+ }
34
+ }
35
+ }
36
+ };