@djangocfg/layouts 2.1.109 → 2.1.110

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 (38) hide show
  1. package/package.json +14 -14
  2. package/src/layouts/AuthLayout/AuthLayout.tsx +92 -20
  3. package/src/layouts/AuthLayout/components/index.ts +11 -7
  4. package/src/layouts/AuthLayout/components/oauth/index.ts +0 -1
  5. package/src/layouts/AuthLayout/components/shared/AuthButton.tsx +35 -0
  6. package/src/layouts/AuthLayout/components/shared/AuthContainer.tsx +56 -0
  7. package/src/layouts/AuthLayout/components/shared/AuthDivider.tsx +22 -0
  8. package/src/layouts/AuthLayout/components/shared/AuthError.tsx +26 -0
  9. package/src/layouts/AuthLayout/components/shared/AuthFooter.tsx +47 -0
  10. package/src/layouts/AuthLayout/components/shared/AuthHeader.tsx +53 -0
  11. package/src/layouts/AuthLayout/components/shared/AuthLink.tsx +41 -0
  12. package/src/layouts/AuthLayout/components/shared/AuthOTPInput.tsx +42 -0
  13. package/src/layouts/AuthLayout/components/shared/ChannelToggle.tsx +48 -0
  14. package/src/layouts/AuthLayout/components/shared/TermsCheckbox.tsx +57 -0
  15. package/src/layouts/AuthLayout/components/shared/index.ts +21 -0
  16. package/src/layouts/AuthLayout/components/steps/IdentifierStep.tsx +171 -0
  17. package/src/layouts/AuthLayout/components/steps/OTPStep.tsx +114 -0
  18. package/src/layouts/AuthLayout/components/steps/SetupStep/SetupComplete.tsx +70 -0
  19. package/src/layouts/AuthLayout/components/steps/SetupStep/SetupLoading.tsx +24 -0
  20. package/src/layouts/AuthLayout/components/steps/SetupStep/SetupQRCode.tsx +125 -0
  21. package/src/layouts/AuthLayout/components/steps/SetupStep/index.tsx +91 -0
  22. package/src/layouts/AuthLayout/components/steps/TwoFactorStep.tsx +92 -0
  23. package/src/layouts/AuthLayout/components/steps/index.ts +6 -0
  24. package/src/layouts/AuthLayout/constants.ts +24 -0
  25. package/src/layouts/AuthLayout/content.ts +78 -0
  26. package/src/layouts/AuthLayout/hooks/index.ts +1 -0
  27. package/src/layouts/AuthLayout/hooks/useCopyToClipboard.ts +37 -0
  28. package/src/layouts/AuthLayout/index.ts +9 -5
  29. package/src/layouts/AuthLayout/styles/auth.css +578 -0
  30. package/src/layouts/ProfileLayout/ProfileLayout.tsx +2 -2
  31. package/src/layouts/ProfileLayout/components/TwoFactorSection.tsx +2 -2
  32. package/src/layouts/AuthLayout/components/AuthHelp.tsx +0 -114
  33. package/src/layouts/AuthLayout/components/AuthSuccess.tsx +0 -101
  34. package/src/layouts/AuthLayout/components/IdentifierForm.tsx +0 -322
  35. package/src/layouts/AuthLayout/components/OTPForm.tsx +0 -174
  36. package/src/layouts/AuthLayout/components/TwoFactorForm.tsx +0 -140
  37. package/src/layouts/AuthLayout/components/TwoFactorSetup.tsx +0 -286
  38. package/src/layouts/AuthLayout/components/oauth/OAuthProviders.tsx +0 -56
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+
5
+ import { Input } from '@djangocfg/ui-core/components';
6
+
7
+ import { AUTH } from '../../constants';
8
+ import { AUTH_CONTENT } from '../../content';
9
+ import { useAuthFormContext } from '../../context';
10
+ import {
11
+ AuthButton,
12
+ AuthContainer,
13
+ AuthError,
14
+ AuthHeader,
15
+ AuthLink,
16
+ AuthOTPInput,
17
+ } from '../shared';
18
+
19
+ /**
20
+ * TwoFactorStep - Apple-style 2FA verification
21
+ *
22
+ * Minimal design with:
23
+ * - TOTP code input (default)
24
+ * - Backup code input (alternate)
25
+ * - Clean toggle between modes
26
+ */
27
+ export const TwoFactorStep: React.FC = () => {
28
+ const {
29
+ twoFactorCode,
30
+ useBackupCode,
31
+ error,
32
+ is2FALoading,
33
+ twoFactorWarning,
34
+ setTwoFactorCode,
35
+ handle2FASubmit,
36
+ handleUseBackupCode,
37
+ handleUseTOTP,
38
+ } = useAuthFormContext();
39
+
40
+ const content = AUTH_CONTENT.twoFactor;
41
+ const subtitle = useBackupCode ? content.subtitle.backup : content.subtitle.totp;
42
+
43
+ return (
44
+ <AuthContainer step="2fa">
45
+ <AuthHeader title={content.title} subtitle={subtitle} />
46
+
47
+ <form onSubmit={handle2FASubmit} className="auth-form-group">
48
+ {useBackupCode ? (
49
+ <Input
50
+ type="text"
51
+ value={twoFactorCode}
52
+ onChange={(e) => setTwoFactorCode(e.target.value.toUpperCase())}
53
+ placeholder={content.placeholder.backup}
54
+ disabled={is2FALoading}
55
+ maxLength={AUTH.BACKUP_CODE_MAX_LENGTH}
56
+ autoComplete="off"
57
+ autoFocus
58
+ className="auth-input font-mono tracking-widest text-center"
59
+ />
60
+ ) : (
61
+ <AuthOTPInput
62
+ value={twoFactorCode}
63
+ onChange={setTwoFactorCode}
64
+ disabled={is2FALoading}
65
+ />
66
+ )}
67
+
68
+ {twoFactorWarning && (
69
+ <div className="auth-dev-notice">{twoFactorWarning}</div>
70
+ )}
71
+
72
+ <AuthError message={error} />
73
+
74
+ <AuthButton
75
+ loading={is2FALoading}
76
+ disabled={!useBackupCode && twoFactorCode.length !== AUTH.OTP_LENGTH}
77
+ >
78
+ {content.button}
79
+ </AuthButton>
80
+
81
+ <div className="auth-actions">
82
+ <AuthLink
83
+ onClick={useBackupCode ? handleUseTOTP : handleUseBackupCode}
84
+ disabled={is2FALoading}
85
+ >
86
+ {useBackupCode ? content.toggle.toTotp : content.toggle.toBackup}
87
+ </AuthLink>
88
+ </div>
89
+ </form>
90
+ </AuthContainer>
91
+ );
92
+ };
@@ -0,0 +1,6 @@
1
+ export { IdentifierStep } from './IdentifierStep';
2
+ export { OTPStep } from './OTPStep';
3
+ export { TwoFactorStep } from './TwoFactorStep';
4
+ export { SetupStep } from './SetupStep';
5
+
6
+ export type { SetupStepProps } from './SetupStep';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * AuthLayout Constants
3
+ *
4
+ * All magic numbers extracted for clarity and maintainability.
5
+ */
6
+
7
+ export const AUTH = {
8
+ // Input lengths
9
+ OTP_LENGTH: 6,
10
+ BACKUP_CODE_MAX_LENGTH: 12,
11
+
12
+ // Timing (ms)
13
+ ANIMATION_DURATION: 400,
14
+ AUTO_SUBMIT_DELAY: 100,
15
+ COPY_FEEDBACK_DURATION: 2000,
16
+ REDIRECT_DELAY: 1500,
17
+ ANIMATION_START_DELAY: 50,
18
+
19
+ // Sizes (px)
20
+ QR_CODE_SIZE: 160,
21
+
22
+ // Routes
23
+ DEFAULT_REDIRECT: '/dashboard',
24
+ } as const;
@@ -0,0 +1,78 @@
1
+ /**
2
+ * AuthLayout Content
3
+ *
4
+ * All UI strings centralized for easy updates and i18n.
5
+ */
6
+
7
+ export const AUTH_CONTENT = {
8
+ identifier: {
9
+ title: 'Sign in',
10
+ subtitle: {
11
+ email: 'Enter your email to continue',
12
+ phone: 'Enter your phone number to continue',
13
+ },
14
+ placeholder: {
15
+ email: 'email@example.com',
16
+ phone: 'Enter your phone number',
17
+ },
18
+ button: 'Continue',
19
+ oauth: {
20
+ github: 'Continue with GitHub',
21
+ },
22
+ },
23
+
24
+ otp: {
25
+ title: 'Enter code',
26
+ subtitle: 'We sent a code to',
27
+ button: 'Verify',
28
+ resend: 'Resend code',
29
+ changeIdentifier: (channel: string) => `Change ${channel}`,
30
+ },
31
+
32
+ twoFactor: {
33
+ title: 'Two-factor authentication',
34
+ subtitle: {
35
+ totp: 'Enter the 6-digit code from your authenticator',
36
+ backup: 'Enter one of your backup codes',
37
+ },
38
+ placeholder: {
39
+ backup: 'XXXXXXXX',
40
+ },
41
+ button: 'Verify',
42
+ toggle: {
43
+ toBackup: 'Use backup code',
44
+ toTotp: 'Use authenticator app',
45
+ },
46
+ },
47
+
48
+ setup: {
49
+ loading: {
50
+ title: 'Setting up...',
51
+ button: 'Loading',
52
+ },
53
+ qrCode: {
54
+ title: 'Set up 2FA',
55
+ subtitle: 'Scan this QR code with your authenticator app',
56
+ manualEntry: "Can't scan? Enter manually",
57
+ confirmPrompt: 'Enter the 6-digit code to confirm',
58
+ button: 'Enable 2FA',
59
+ skip: 'Skip for now',
60
+ },
61
+ complete: {
62
+ title: '2FA enabled',
63
+ subtitle: 'Save these backup codes securely',
64
+ instruction: 'Each code can only be used once',
65
+ copyAll: 'Copy all codes',
66
+ copied: 'Copied',
67
+ done: 'Done',
68
+ },
69
+ },
70
+
71
+ success: {
72
+ message: 'Signed in successfully',
73
+ },
74
+
75
+ dev: {
76
+ anyCodeWorks: 'Dev Mode: Any code works',
77
+ },
78
+ } as const;
@@ -0,0 +1 @@
1
+ export { useCopyToClipboard } from './useCopyToClipboard';
@@ -0,0 +1,37 @@
1
+ 'use client';
2
+
3
+ import { useCallback, useState } from 'react';
4
+
5
+ import { AUTH } from '../constants';
6
+
7
+ interface UseCopyToClipboardResult {
8
+ copied: boolean;
9
+ copy: (text: string) => Promise<void>;
10
+ }
11
+
12
+ /**
13
+ * useCopyToClipboard - Copy text with temporary feedback state
14
+ *
15
+ * @param duration - How long to show "copied" state (default: 2000ms)
16
+ */
17
+ export const useCopyToClipboard = (
18
+ duration = AUTH.COPY_FEEDBACK_DURATION
19
+ ): UseCopyToClipboardResult => {
20
+ const [copied, setCopied] = useState(false);
21
+
22
+ const copy = useCallback(
23
+ async (text: string) => {
24
+ try {
25
+ await navigator.clipboard.writeText(text);
26
+ setCopied(true);
27
+ setTimeout(() => setCopied(false), duration);
28
+ } catch {
29
+ // Clipboard API not available
30
+ console.warn('Clipboard API not available');
31
+ }
32
+ },
33
+ [duration]
34
+ );
35
+
36
+ return { copied, copy };
37
+ };
@@ -9,16 +9,20 @@ export type { AuthLayoutProps } from './AuthLayout';
9
9
  // Context and hooks
10
10
  export { AuthFormProvider, useAuthFormContext } from './context';
11
11
 
12
- // Components
12
+ // Apple-style step components
13
13
  export {
14
- IdentifierForm,
15
- OTPForm,
16
- AuthHelp,
17
- OAuthProviders,
14
+ IdentifierStep,
15
+ OTPStep,
16
+ TwoFactorStep,
17
+ SetupStep,
18
+ type SetupStepProps,
18
19
  OAuthCallback,
19
20
  type OAuthCallbackProps,
20
21
  } from './components';
21
22
 
23
+ // Shared UI components
24
+ export * from './components/shared';
25
+
22
26
  // Types (re-exported from @djangocfg/api/auth)
23
27
  export type {
24
28
  AuthChannel,