@oussemasahbeni/keycloakify-login-shadcn 250004.0.3 → 250004.0.8

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