@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,578 @@
1
+ /**
2
+ * Auth Layout Styles
3
+ *
4
+ * Minimal auth-specific styles.
5
+ * Uses design system variables from @djangocfg/ui-core.
6
+ */
7
+
8
+ /* ===== SPRING EASINGS ===== */
9
+
10
+ :root {
11
+ --spring-bounce: cubic-bezier(0.34, 1.56, 0.64, 1);
12
+ --spring-smooth: cubic-bezier(0.16, 1, 0.3, 1);
13
+ --spring-snappy: cubic-bezier(0.2, 0, 0, 1);
14
+ }
15
+
16
+ /* ===== LAYOUT ===== */
17
+
18
+ .auth-layout {
19
+ min-height: 100vh;
20
+ min-height: 100dvh;
21
+ display: flex;
22
+ flex-direction: column;
23
+ align-items: center;
24
+ justify-content: center;
25
+ padding: 1.5rem;
26
+ background: hsl(var(--background));
27
+ }
28
+
29
+ /* ===== CONTAINER ===== */
30
+
31
+ .auth-container {
32
+ width: 100%;
33
+ max-width: 400px;
34
+ display: flex;
35
+ flex-direction: column;
36
+ gap: 2rem;
37
+ padding: 2rem;
38
+ background: hsl(var(--card));
39
+ border: 1px solid hsl(var(--border));
40
+ border-radius: 1rem;
41
+ }
42
+
43
+ .auth-container[data-entering="true"] {
44
+ animation: authFadeIn 0.4s var(--spring-smooth) forwards;
45
+ }
46
+
47
+ @keyframes authFadeIn {
48
+ from {
49
+ opacity: 0;
50
+ transform: translateY(20px);
51
+ }
52
+ to {
53
+ opacity: 1;
54
+ transform: translateY(0);
55
+ }
56
+ }
57
+
58
+ /* ===== HEADER ===== */
59
+
60
+ .auth-header {
61
+ text-align: center;
62
+ display: flex;
63
+ flex-direction: column;
64
+ align-items: center;
65
+ gap: 0.5rem;
66
+ }
67
+
68
+ .auth-logo {
69
+ width: 64px;
70
+ height: 64px;
71
+ margin-bottom: 0.5rem;
72
+ object-fit: contain;
73
+ }
74
+
75
+ .auth-title {
76
+ font-size: 1.5rem;
77
+ font-weight: 600;
78
+ line-height: 1.2;
79
+ color: hsl(var(--foreground));
80
+ margin: 0;
81
+ }
82
+
83
+ .auth-subtitle {
84
+ font-size: 0.9375rem;
85
+ color: hsl(var(--muted-foreground));
86
+ margin: 0;
87
+ }
88
+
89
+ .auth-identifier {
90
+ font-weight: 500;
91
+ color: hsl(var(--foreground));
92
+ }
93
+
94
+ /* ===== FORM ===== */
95
+
96
+ .auth-form-group {
97
+ display: flex;
98
+ flex-direction: column;
99
+ gap: 1rem;
100
+ }
101
+
102
+ .auth-input {
103
+ width: 100%;
104
+ height: 3rem;
105
+ padding: 0 1rem;
106
+ font-size: 0.9375rem;
107
+ background: hsl(var(--input));
108
+ border: 1px solid hsl(var(--border));
109
+ border-radius: 0.5rem;
110
+ color: hsl(var(--foreground));
111
+ transition: border-color 0.2s, box-shadow 0.2s;
112
+ }
113
+
114
+ .auth-input:focus {
115
+ outline: none;
116
+ border-color: hsl(var(--ring));
117
+ box-shadow: 0 0 0 3px hsl(var(--ring) / 0.1);
118
+ }
119
+
120
+ .auth-input:disabled {
121
+ opacity: 0.5;
122
+ cursor: not-allowed;
123
+ }
124
+
125
+ .auth-input::placeholder {
126
+ color: hsl(var(--muted-foreground));
127
+ }
128
+
129
+ /* Phone input */
130
+ .auth-phone-input .PhoneInputInput {
131
+ height: 3rem;
132
+ padding: 0 1rem;
133
+ font-size: 0.9375rem;
134
+ background: hsl(var(--input));
135
+ border: 1px solid hsl(var(--border));
136
+ border-radius: 0.5rem;
137
+ color: hsl(var(--foreground));
138
+ }
139
+
140
+ .auth-phone-input .PhoneInputInput:focus {
141
+ outline: none;
142
+ border-color: hsl(var(--ring));
143
+ box-shadow: 0 0 0 3px hsl(var(--ring) / 0.1);
144
+ }
145
+
146
+ /* ===== BUTTONS ===== */
147
+
148
+ .auth-button {
149
+ width: 100%;
150
+ height: 3rem;
151
+ display: flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ gap: 0.5rem;
155
+ font-size: 0.9375rem;
156
+ font-weight: 500;
157
+ border-radius: 0.5rem;
158
+ border: none;
159
+ cursor: pointer;
160
+ transition: opacity 0.15s, transform 0.15s;
161
+ }
162
+
163
+ .auth-button:active:not(:disabled) {
164
+ transform: scale(0.98);
165
+ }
166
+
167
+ .auth-button:disabled {
168
+ opacity: 0.5;
169
+ cursor: not-allowed;
170
+ }
171
+
172
+ .auth-button-primary {
173
+ background: hsl(var(--primary));
174
+ color: hsl(var(--primary-foreground));
175
+ }
176
+
177
+ .auth-button-primary:hover:not(:disabled) {
178
+ opacity: 0.9;
179
+ }
180
+
181
+ .auth-button-secondary {
182
+ background: hsl(var(--secondary));
183
+ color: hsl(var(--secondary-foreground));
184
+ }
185
+
186
+ .auth-button-secondary:hover:not(:disabled) {
187
+ opacity: 0.9;
188
+ }
189
+
190
+ /* Loading state */
191
+ .auth-button-loading {
192
+ color: transparent !important;
193
+ position: relative;
194
+ }
195
+
196
+ .auth-button-loading::before {
197
+ content: "";
198
+ position: absolute;
199
+ width: 1.25rem;
200
+ height: 1.25rem;
201
+ border: 2px solid currentColor;
202
+ border-right-color: transparent;
203
+ border-radius: 50%;
204
+ animation: authSpin 0.6s linear infinite;
205
+ }
206
+
207
+ .auth-button-primary.auth-button-loading::before {
208
+ border-color: hsl(var(--primary-foreground));
209
+ border-right-color: transparent;
210
+ }
211
+
212
+ @keyframes authSpin {
213
+ to { transform: rotate(360deg); }
214
+ }
215
+
216
+ /* ===== DIVIDER ===== */
217
+
218
+ .auth-divider {
219
+ display: flex;
220
+ align-items: center;
221
+ gap: 1rem;
222
+ color: hsl(var(--muted-foreground));
223
+ font-size: 0.8125rem;
224
+ }
225
+
226
+ .auth-divider::before,
227
+ .auth-divider::after {
228
+ content: "";
229
+ flex: 1;
230
+ height: 1px;
231
+ background: hsl(var(--border));
232
+ }
233
+
234
+ /* ===== LINKS ===== */
235
+
236
+ .auth-link {
237
+ display: inline-flex;
238
+ align-items: center;
239
+ gap: 0.25rem;
240
+ font-size: 0.875rem;
241
+ color: hsl(var(--primary));
242
+ background: none;
243
+ border: none;
244
+ padding: 0;
245
+ cursor: pointer;
246
+ transition: opacity 0.15s;
247
+ }
248
+
249
+ .auth-link:hover {
250
+ opacity: 0.8;
251
+ }
252
+
253
+ .auth-link:disabled {
254
+ opacity: 0.5;
255
+ cursor: not-allowed;
256
+ }
257
+
258
+ .auth-actions {
259
+ display: flex;
260
+ justify-content: center;
261
+ gap: 1.5rem;
262
+ }
263
+
264
+ /* ===== ERROR ===== */
265
+
266
+ .auth-error {
267
+ padding: 0.75rem 1rem;
268
+ font-size: 0.875rem;
269
+ color: hsl(var(--destructive));
270
+ background: hsl(var(--destructive) / 0.1);
271
+ border: 1px solid hsl(var(--destructive) / 0.2);
272
+ border-radius: 0.5rem;
273
+ animation: authShake 0.4s ease;
274
+ }
275
+
276
+ @keyframes authShake {
277
+ 0%, 100% { transform: translateX(0); }
278
+ 20% { transform: translateX(-6px); }
279
+ 40% { transform: translateX(6px); }
280
+ 60% { transform: translateX(-4px); }
281
+ 80% { transform: translateX(4px); }
282
+ }
283
+
284
+ /* ===== FOOTER ===== */
285
+
286
+ .auth-footer {
287
+ display: flex;
288
+ justify-content: center;
289
+ gap: 0.75rem;
290
+ font-size: 0.8125rem;
291
+ color: hsl(var(--muted-foreground));
292
+ }
293
+
294
+ .auth-footer a {
295
+ color: inherit;
296
+ text-decoration: none;
297
+ }
298
+
299
+ .auth-footer a:hover {
300
+ color: hsl(var(--foreground));
301
+ }
302
+
303
+ .auth-footer-dot {
304
+ opacity: 0.3;
305
+ }
306
+
307
+ /* ===== TERMS ===== */
308
+
309
+ .auth-terms {
310
+ display: flex;
311
+ align-items: flex-start;
312
+ gap: 0.75rem;
313
+ font-size: 0.875rem;
314
+ color: hsl(var(--muted-foreground));
315
+ }
316
+
317
+ .auth-terms-checkbox {
318
+ margin-top: 2px;
319
+ flex-shrink: 0;
320
+ }
321
+
322
+ .auth-terms a {
323
+ color: hsl(var(--primary));
324
+ text-decoration: none;
325
+ }
326
+
327
+ .auth-terms a:hover {
328
+ opacity: 0.8;
329
+ }
330
+
331
+ /* ===== CHANNEL TOGGLE ===== */
332
+
333
+ .auth-channel-toggle {
334
+ display: flex;
335
+ padding: 4px;
336
+ background: hsl(var(--muted));
337
+ border-radius: 0.5rem;
338
+ gap: 4px;
339
+ }
340
+
341
+ .auth-channel-option {
342
+ flex: 1;
343
+ display: flex;
344
+ align-items: center;
345
+ justify-content: center;
346
+ gap: 0.375rem;
347
+ padding: 0.5rem 1rem;
348
+ font-size: 0.875rem;
349
+ font-weight: 500;
350
+ color: hsl(var(--muted-foreground));
351
+ background: transparent;
352
+ border: none;
353
+ border-radius: 0.375rem;
354
+ cursor: pointer;
355
+ transition: all 0.2s;
356
+ }
357
+
358
+ .auth-channel-option[data-active="true"] {
359
+ color: hsl(var(--foreground));
360
+ background: hsl(var(--card));
361
+ box-shadow: 0 1px 2px hsl(0 0% 0% / 0.05);
362
+ }
363
+
364
+ .auth-channel-option svg {
365
+ width: 1rem;
366
+ height: 1rem;
367
+ }
368
+
369
+ /* ===== OTP INPUT ===== */
370
+
371
+ .auth-otp-container {
372
+ display: flex;
373
+ justify-content: center;
374
+ }
375
+
376
+ .auth-otp-wrapper {
377
+ display: flex;
378
+ gap: 0.5rem;
379
+ }
380
+
381
+ .auth-otp-wrapper input {
382
+ width: 3rem;
383
+ height: 3.5rem;
384
+ font-size: 1.25rem;
385
+ font-weight: 500;
386
+ text-align: center;
387
+ background: hsl(var(--input));
388
+ border: 1px solid hsl(var(--border));
389
+ border-radius: 0.5rem;
390
+ color: hsl(var(--foreground));
391
+ transition: border-color 0.2s, box-shadow 0.2s;
392
+ }
393
+
394
+ .auth-otp-wrapper input:focus {
395
+ outline: none;
396
+ border-color: hsl(var(--ring));
397
+ box-shadow: 0 0 0 3px hsl(var(--ring) / 0.1);
398
+ }
399
+
400
+ .auth-otp-wrapper input:disabled {
401
+ opacity: 0.5;
402
+ }
403
+
404
+ /* ===== QR CODE ===== */
405
+
406
+ .auth-qr-container {
407
+ display: flex;
408
+ justify-content: center;
409
+ padding: 1rem 0;
410
+ }
411
+
412
+ .auth-qr {
413
+ padding: 1rem;
414
+ background: white;
415
+ border-radius: 0.5rem;
416
+ box-shadow: 0 2px 8px hsl(0 0% 0% / 0.08);
417
+ }
418
+
419
+ /* ===== SECRET CODE ===== */
420
+
421
+ .auth-secret-container {
422
+ display: flex;
423
+ flex-direction: column;
424
+ align-items: center;
425
+ gap: 0.5rem;
426
+ padding: 1rem;
427
+ }
428
+
429
+ .auth-secret {
430
+ padding: 0.5rem 1rem;
431
+ font-family: ui-monospace, monospace;
432
+ font-size: 0.8125rem;
433
+ letter-spacing: 0.05em;
434
+ background: hsl(var(--muted));
435
+ border-radius: 0.375rem;
436
+ color: hsl(var(--foreground));
437
+ word-break: break-all;
438
+ text-align: center;
439
+ }
440
+
441
+ /* ===== BACKUP CODES ===== */
442
+
443
+ .auth-backup-codes {
444
+ display: grid;
445
+ grid-template-columns: repeat(2, 1fr);
446
+ gap: 0.5rem;
447
+ padding: 1rem;
448
+ background: hsl(var(--muted) / 0.5);
449
+ border: 1px dashed hsl(var(--border));
450
+ border-radius: 0.5rem;
451
+ }
452
+
453
+ .auth-backup-code {
454
+ font-family: ui-monospace, monospace;
455
+ font-size: 0.8125rem;
456
+ text-align: center;
457
+ padding: 0.5rem;
458
+ color: hsl(var(--foreground));
459
+ }
460
+
461
+ /* ===== SUCCESS OVERLAY ===== */
462
+
463
+ .auth-success-overlay {
464
+ position: fixed;
465
+ inset: 0;
466
+ display: flex;
467
+ align-items: center;
468
+ justify-content: center;
469
+ background: hsl(var(--background));
470
+ z-index: 50;
471
+ animation: authFadeIn 0.3s ease-out;
472
+ }
473
+
474
+ .auth-success-content {
475
+ display: flex;
476
+ flex-direction: column;
477
+ align-items: center;
478
+ gap: 1.5rem;
479
+ }
480
+
481
+ .auth-success-logo {
482
+ width: 80px;
483
+ height: 80px;
484
+ object-fit: contain;
485
+ animation: authSuccessIn 0.5s var(--spring-bounce);
486
+ }
487
+
488
+ .auth-success-check {
489
+ width: 80px;
490
+ height: 80px;
491
+ display: flex;
492
+ align-items: center;
493
+ justify-content: center;
494
+ background: hsl(142 76% 36% / 0.1);
495
+ border-radius: 50%;
496
+ color: hsl(142 76% 36%);
497
+ animation: authSuccessIn 0.5s var(--spring-bounce);
498
+ }
499
+
500
+ @keyframes authSuccessIn {
501
+ from {
502
+ opacity: 0;
503
+ transform: scale(0.5);
504
+ }
505
+ to {
506
+ opacity: 1;
507
+ transform: scale(1);
508
+ }
509
+ }
510
+
511
+ .auth-success-check svg {
512
+ width: 40px;
513
+ height: 40px;
514
+ }
515
+
516
+ .auth-success-text {
517
+ font-size: 0.9375rem;
518
+ color: hsl(var(--muted-foreground));
519
+ }
520
+
521
+ /* ===== DEV NOTICE ===== */
522
+
523
+ .auth-dev-notice {
524
+ padding: 0.5rem 0.75rem;
525
+ font-size: 0.75rem;
526
+ text-align: center;
527
+ color: hsl(38 92% 40%);
528
+ background: hsl(38 92% 95%);
529
+ border-radius: 0.375rem;
530
+ border: 1px solid hsl(38 92% 80%);
531
+ }
532
+
533
+ .dark .auth-dev-notice {
534
+ color: hsl(38 92% 65%);
535
+ background: hsl(38 92% 15%);
536
+ border-color: hsl(38 92% 30%);
537
+ }
538
+
539
+ /* ===== INSTRUCTION ===== */
540
+
541
+ .auth-instruction {
542
+ font-size: 0.875rem;
543
+ color: hsl(var(--muted-foreground));
544
+ text-align: center;
545
+ margin: 0;
546
+ }
547
+
548
+ /* ===== RESPONSIVE ===== */
549
+
550
+ @media (max-width: 480px) {
551
+ .auth-layout {
552
+ padding: 1rem;
553
+ }
554
+
555
+ .auth-container {
556
+ padding: 1.5rem;
557
+ gap: 1.5rem;
558
+ }
559
+
560
+ .auth-title {
561
+ font-size: 1.25rem;
562
+ }
563
+
564
+ .auth-otp-wrapper input {
565
+ width: 2.75rem;
566
+ height: 3rem;
567
+ font-size: 1rem;
568
+ }
569
+ }
570
+
571
+ /* ===== REDUCED MOTION ===== */
572
+
573
+ @media (prefers-reduced-motion: reduce) {
574
+ *, *::before, *::after {
575
+ animation-duration: 0.01ms !important;
576
+ transition-duration: 0.01ms !important;
577
+ }
578
+ }
@@ -28,7 +28,7 @@ import {
28
28
  } from '@djangocfg/ui-core/components';
29
29
  import { zodResolver } from '@hookform/resolvers/zod';
30
30
 
31
- import { TwoFactorSetup } from '../AuthLayout/components/TwoFactorSetup';
31
+ import { SetupStep } from '../AuthLayout/components/steps/SetupStep';
32
32
  import { profileLogger } from '../../utils/logger';
33
33
 
34
34
  // ─────────────────────────────────────────────────────────────────────────────
@@ -314,7 +314,7 @@ const ProfileContent = ({
314
314
  if (show2FASetup) {
315
315
  return (
316
316
  <div className="container mx-auto px-4 py-8 max-w-lg">
317
- <TwoFactorSetup
317
+ <SetupStep
318
318
  onComplete={() => {
319
319
  setShow2FASetup(false);
320
320
  fetch2FAStatus();
@@ -22,7 +22,7 @@ import {
22
22
  OTPInput,
23
23
  } from '@djangocfg/ui-core/components';
24
24
 
25
- import { TwoFactorSetup } from '../../AuthLayout/components/TwoFactorSetup';
25
+ import { SetupStep } from '../../AuthLayout/components/steps/SetupStep';
26
26
 
27
27
  type ViewState = 'status' | 'setup' | 'disable';
28
28
 
@@ -102,7 +102,7 @@ export const TwoFactorSection: React.FC = () => {
102
102
  </CardDescription>
103
103
  </CardHeader>
104
104
  <CardContent>
105
- <TwoFactorSetup
105
+ <SetupStep
106
106
  onComplete={handleSetupComplete}
107
107
  onSkip={handleSetupSkip}
108
108
  />
@@ -1,114 +0,0 @@
1
- import { HelpCircle, Mail, MessageCircle } from 'lucide-react';
2
- import React from 'react';
3
-
4
- import { Button } from '@djangocfg/ui-core/components';
5
-
6
- import { useAuthFormContext } from '../context';
7
-
8
- import type { AuthHelpProps } from '../types';
9
-
10
- export const AuthHelp: React.FC<AuthHelpProps> = ({
11
- className = '',
12
- variant = 'default',
13
- }) => {
14
- const { supportUrl, channel } = useAuthFormContext();
15
-
16
- const getChannelIcon = () => {
17
- return channel === 'phone' ? (
18
- <MessageCircle className="w-4 h-4 text-muted-foreground" />
19
- ) : (
20
- <Mail className="w-4 h-4 text-muted-foreground" />
21
- );
22
- };
23
-
24
- const getHelpText = () => {
25
- return channel === 'phone' ? 'Check WhatsApp/SMS' : 'Check spam folder';
26
- };
27
-
28
- const getDetailedHelp = () => {
29
- if (channel === 'phone') {
30
- return {
31
- title: "Didn't receive the code?",
32
- tips: [
33
- '• Check your WhatsApp messages',
34
- '• Look for SMS messages',
35
- '• Ensure you have signal/internet',
36
- '• Wait a few minutes for delivery',
37
- ],
38
- };
39
- } else {
40
- return {
41
- title: "Didn't receive the email?",
42
- tips: [
43
- '• Check your spam or junk folder',
44
- '• Make sure you entered the correct email address',
45
- '• Wait a few minutes for the email to arrive',
46
- ],
47
- };
48
- }
49
- };
50
-
51
- if (variant === 'compact') {
52
- return (
53
- <div
54
- className={`flex items-center justify-between p-3 bg-muted/30 rounded-sm border border-border ${className}`}
55
- >
56
- <div className="flex items-center gap-2">
57
- {getChannelIcon()}
58
- <span className="text-sm text-muted-foreground">{getHelpText()}</span>
59
- </div>
60
- {supportUrl && (
61
- <Button
62
- asChild
63
- variant="ghost"
64
- size="sm"
65
- className="text-xs"
66
- >
67
- <a href={supportUrl} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1">
68
- <HelpCircle className="w-3 h-3" />
69
- Need help?
70
- </a>
71
- </Button>
72
- )}
73
- </div>
74
- );
75
- }
76
-
77
- const helpData = getDetailedHelp();
78
-
79
- return (
80
- <div
81
- className={`flex flex-col gap-3 p-3 bg-muted/30 rounded-sm border border-border ${className}`}
82
- >
83
- <div className="flex items-start gap-3">
84
- {getChannelIcon()}
85
- <div className="flex flex-col gap-1">
86
- <h4 className="text-sm font-medium text-foreground">{helpData.title}</h4>
87
- <div className="flex flex-col gap-0.5 text-xs text-muted-foreground">
88
- {helpData.tips.map((tip, index) => (
89
- <p key={index}>{tip}</p>
90
- ))}
91
- </div>
92
- </div>
93
- </div>
94
-
95
- {supportUrl && (
96
- <div className="flex items-center justify-between pt-2 border-t border-border">
97
- <span className="text-xs text-muted-foreground">Still having trouble?</span>
98
- <Button
99
- asChild
100
- variant="ghost"
101
- size="sm"
102
- className="text-xs h-7 px-2"
103
- >
104
- <a href={supportUrl} target="_blank" rel="noopener noreferrer" className="flex items-center gap-1">
105
- <HelpCircle className="w-3 h-3" />
106
- Get Help
107
- </a>
108
- </Button>
109
- </div>
110
- )}
111
- </div>
112
- );
113
- };
114
-