@sudobility/building_blocks 0.0.137 → 0.0.138
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.
- package/dist/__tests__/app-sitemap-page.test.d.ts +1 -0
- package/dist/__tests__/app-text-page.test.d.ts +1 -0
- package/dist/__tests__/appearance-settings.test.d.ts +1 -0
- package/dist/__tests__/global-settings-page.test.d.ts +1 -0
- package/dist/__tests__/login-page.test.d.ts +1 -0
- package/dist/components/breadcrumbs/app-breadcrumbs.d.ts +20 -0
- package/dist/components/footer/index.d.ts +1 -0
- package/dist/components/footer/shared.d.ts +15 -0
- package/dist/components/pages/index.d.ts +1 -1
- package/dist/components/pages/login-page.d.ts +66 -6
- package/dist/components/topbar/language-selector.d.ts +19 -0
- package/dist/firebase.js +6 -5
- package/dist/firebase.js.map +1 -1
- package/dist/index.js +287 -107
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
+
import { b, a, S, u } from "./SafeSubscriptionContext-CNuEKeqJ.js";
|
|
1
2
|
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
3
|
import React, { useState, useRef, useMemo, useCallback, useEffect } from "react";
|
|
4
|
+
import { SubscriptionLayout, SubscriptionTile, SegmentedControl } from "@sudobility/subscription-components";
|
|
5
|
+
import { useSubscriptionPeriods, useSubscriptionForPeriod, useSubscribable, useUserSubscription, refreshSubscription } from "@sudobility/subscription_lib";
|
|
3
6
|
import { TopbarProvider, Topbar, TopbarLeft, TopbarNavigation, TopbarLogo, Logo, TopbarCenter, TopbarRight, TopbarActions, TopbarMobileContent, Footer, FooterCompact, FooterCompactLeft, FooterVersion, FooterCopyright, FooterCompactRight, FooterGrid, FooterLinkSection, FooterLink, FooterBottom, FooterBrand, FooterSocialLinks, LayoutProvider, AspectFitView, Label, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, MasterListItem, Section, MasterDetailLayout } from "@sudobility/components";
|
|
4
7
|
import { clsx } from "clsx";
|
|
5
8
|
import { twMerge } from "tailwind-merge";
|
|
6
|
-
import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, ChevronRightIcon, EnvelopeIcon, DocumentTextIcon, CogIcon, HomeIcon } from "@heroicons/react/24/outline";
|
|
7
|
-
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
8
|
-
import { cva } from "class-variance-authority";
|
|
9
|
-
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
|
|
10
|
-
import { b, a, S, u } from "./SafeSubscriptionContext-CNuEKeqJ.js";
|
|
11
|
-
import { SubscriptionLayout, SubscriptionTile, SegmentedControl } from "@sudobility/subscription-components";
|
|
12
|
-
import { useSubscriptionPeriods, useSubscriptionForPeriod, useSubscribable, useUserSubscription, refreshSubscription } from "@sudobility/subscription_lib";
|
|
13
9
|
import i18n from "i18next";
|
|
14
10
|
import { default as default2 } from "i18next";
|
|
15
11
|
import { initReactI18next } from "react-i18next";
|
|
16
12
|
import Backend from "i18next-http-backend";
|
|
17
13
|
import LanguageDetector from "i18next-browser-languagedetector";
|
|
14
|
+
import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, LanguageIcon, ChevronRightIcon, EnvelopeIcon, DocumentTextIcon, CogIcon, HomeIcon } from "@heroicons/react/24/outline";
|
|
15
|
+
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
16
|
+
import { cva } from "class-variance-authority";
|
|
18
17
|
function cn(...inputs) {
|
|
19
18
|
return twMerge(clsx(inputs));
|
|
20
19
|
}
|
|
@@ -223,7 +222,7 @@ const LanguageSelector = ({
|
|
|
223
222
|
helperText && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: helperText })
|
|
224
223
|
] });
|
|
225
224
|
};
|
|
226
|
-
const DefaultLinkComponent$
|
|
225
|
+
const DefaultLinkComponent$1 = ({
|
|
227
226
|
href,
|
|
228
227
|
className,
|
|
229
228
|
children,
|
|
@@ -241,7 +240,7 @@ const AppTopBar = ({
|
|
|
241
240
|
renderAccountSection,
|
|
242
241
|
renderCenterSection,
|
|
243
242
|
renderMobileContent,
|
|
244
|
-
LinkComponent = DefaultLinkComponent$
|
|
243
|
+
LinkComponent = DefaultLinkComponent$1,
|
|
245
244
|
sticky = true,
|
|
246
245
|
variant = "default",
|
|
247
246
|
mobileMenuLabel = "Menu",
|
|
@@ -483,7 +482,11 @@ const ShareDropdown = ({
|
|
|
483
482
|
const [shareUrl, setShareUrl] = useState("");
|
|
484
483
|
const [isPreparingShare, setIsPreparingShare] = useState(false);
|
|
485
484
|
const [showCopiedFeedback, setShowCopiedFeedback] = useState(false);
|
|
486
|
-
|
|
485
|
+
const [focusedIndex, setFocusedIndex] = useState(-1);
|
|
486
|
+
const dropdownRef = useRef(null);
|
|
487
|
+
const triggerRef = useRef(null);
|
|
488
|
+
const itemRefs = useRef([]);
|
|
489
|
+
useEffect(() => {
|
|
487
490
|
const onBeforeShare = shareConfig.onBeforeShare;
|
|
488
491
|
if (onBeforeShare && !shareUrl) {
|
|
489
492
|
const prepareUrl = async () => {
|
|
@@ -502,26 +505,37 @@ const ShareDropdown = ({
|
|
|
502
505
|
prepareUrl();
|
|
503
506
|
}
|
|
504
507
|
}, [shareConfig, shareUrl]);
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
setShowCopiedFeedback(true);
|
|
510
|
-
setTimeout(() => {
|
|
511
|
-
setShowCopiedFeedback(false);
|
|
508
|
+
useEffect(() => {
|
|
509
|
+
if (!isOpen) return;
|
|
510
|
+
const handleClickOutside = (event) => {
|
|
511
|
+
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
512
512
|
setIsOpen(false);
|
|
513
|
-
|
|
514
|
-
|
|
513
|
+
setFocusedIndex(-1);
|
|
514
|
+
}
|
|
515
|
+
};
|
|
516
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
517
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
518
|
+
}, [isOpen]);
|
|
519
|
+
useEffect(() => {
|
|
520
|
+
if (!isOpen) return;
|
|
521
|
+
const handleKeyDown = (event) => {
|
|
522
|
+
var _a;
|
|
523
|
+
if (event.key === "Escape") {
|
|
524
|
+
setIsOpen(false);
|
|
525
|
+
setFocusedIndex(-1);
|
|
526
|
+
(_a = triggerRef.current) == null ? void 0 : _a.focus();
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
530
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
531
|
+
}, [isOpen]);
|
|
532
|
+
useEffect(() => {
|
|
533
|
+
var _a;
|
|
534
|
+
if (focusedIndex >= 0 && itemRefs.current[focusedIndex]) {
|
|
535
|
+
(_a = itemRefs.current[focusedIndex]) == null ? void 0 : _a.focus();
|
|
515
536
|
}
|
|
516
|
-
};
|
|
517
|
-
const
|
|
518
|
-
window.open(
|
|
519
|
-
platformUrl,
|
|
520
|
-
"_blank",
|
|
521
|
-
"noopener,noreferrer,width=600,height=400"
|
|
522
|
-
);
|
|
523
|
-
setIsOpen(false);
|
|
524
|
-
};
|
|
537
|
+
}, [focusedIndex]);
|
|
538
|
+
const url = shareUrl || (typeof window !== "undefined" ? window.location.href : "");
|
|
525
539
|
const sharePlatforms = [
|
|
526
540
|
{
|
|
527
541
|
name: "Twitter",
|
|
@@ -558,14 +572,73 @@ const ShareDropdown = ({
|
|
|
558
572
|
color: "text-gray-600"
|
|
559
573
|
}
|
|
560
574
|
];
|
|
561
|
-
|
|
575
|
+
const totalItems = sharePlatforms.length + 1;
|
|
576
|
+
const copyToClipboard = async () => {
|
|
577
|
+
try {
|
|
578
|
+
await navigator.clipboard.writeText(url);
|
|
579
|
+
setShowCopiedFeedback(true);
|
|
580
|
+
setTimeout(() => {
|
|
581
|
+
setShowCopiedFeedback(false);
|
|
582
|
+
setIsOpen(false);
|
|
583
|
+
}, 1500);
|
|
584
|
+
} catch {
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const handleSocialShare = (platformUrl) => {
|
|
588
|
+
window.open(
|
|
589
|
+
platformUrl,
|
|
590
|
+
"_blank",
|
|
591
|
+
"noopener,noreferrer,width=600,height=400"
|
|
592
|
+
);
|
|
593
|
+
setIsOpen(false);
|
|
594
|
+
};
|
|
595
|
+
const handleToggle = () => {
|
|
596
|
+
const next = !isOpen;
|
|
597
|
+
setIsOpen(next);
|
|
598
|
+
if (next) {
|
|
599
|
+
setFocusedIndex(0);
|
|
600
|
+
} else {
|
|
601
|
+
setFocusedIndex(-1);
|
|
602
|
+
}
|
|
603
|
+
};
|
|
604
|
+
const handleMenuKeyDown = useCallback(
|
|
605
|
+
(event) => {
|
|
606
|
+
switch (event.key) {
|
|
607
|
+
case "ArrowDown":
|
|
608
|
+
event.preventDefault();
|
|
609
|
+
setFocusedIndex((prev) => (prev + 1) % totalItems);
|
|
610
|
+
break;
|
|
611
|
+
case "ArrowUp":
|
|
612
|
+
event.preventDefault();
|
|
613
|
+
setFocusedIndex((prev) => (prev - 1 + totalItems) % totalItems);
|
|
614
|
+
break;
|
|
615
|
+
case "Home":
|
|
616
|
+
event.preventDefault();
|
|
617
|
+
setFocusedIndex(0);
|
|
618
|
+
break;
|
|
619
|
+
case "End":
|
|
620
|
+
event.preventDefault();
|
|
621
|
+
setFocusedIndex(totalItems - 1);
|
|
622
|
+
break;
|
|
623
|
+
case "Tab":
|
|
624
|
+
setIsOpen(false);
|
|
625
|
+
setFocusedIndex(-1);
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
},
|
|
629
|
+
[totalItems]
|
|
630
|
+
);
|
|
631
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", ref: dropdownRef, children: [
|
|
562
632
|
/* @__PURE__ */ jsx(
|
|
563
633
|
"button",
|
|
564
634
|
{
|
|
565
|
-
|
|
635
|
+
ref: triggerRef,
|
|
636
|
+
onClick: handleToggle,
|
|
566
637
|
disabled: isPreparingShare,
|
|
567
638
|
className: "flex items-center justify-center w-8 h-8 bg-blue-50 hover:bg-blue-100 dark:bg-blue-900/30 dark:hover:bg-blue-900/50 text-blue-600 dark:text-blue-400 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed",
|
|
568
|
-
|
|
639
|
+
"aria-label": "Share this page",
|
|
640
|
+
"aria-expanded": isOpen,
|
|
641
|
+
"aria-haspopup": "menu",
|
|
569
642
|
children: isPreparingShare ? /* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-blue-600 border-t-transparent rounded-full animate-spin" }) : /* @__PURE__ */ jsx(
|
|
570
643
|
"svg",
|
|
571
644
|
{
|
|
@@ -573,6 +646,7 @@ const ShareDropdown = ({
|
|
|
573
646
|
fill: "none",
|
|
574
647
|
stroke: "currentColor",
|
|
575
648
|
viewBox: "0 0 24 24",
|
|
649
|
+
"aria-hidden": "true",
|
|
576
650
|
children: /* @__PURE__ */ jsx(
|
|
577
651
|
"path",
|
|
578
652
|
{
|
|
@@ -586,35 +660,51 @@ const ShareDropdown = ({
|
|
|
586
660
|
)
|
|
587
661
|
}
|
|
588
662
|
),
|
|
589
|
-
isOpen && /* @__PURE__ */ jsxs(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
663
|
+
isOpen && /* @__PURE__ */ jsxs(
|
|
664
|
+
"div",
|
|
665
|
+
{
|
|
666
|
+
className: "absolute right-0 top-10 z-[999999] w-40 bg-white dark:bg-gray-800 rounded-lg shadow-xl border border-gray-200 dark:border-gray-700 py-1",
|
|
667
|
+
role: "menu",
|
|
668
|
+
"aria-label": "Share options",
|
|
669
|
+
onKeyDown: handleMenuKeyDown,
|
|
670
|
+
children: [
|
|
671
|
+
sharePlatforms.map((platform, index) => /* @__PURE__ */ jsx(
|
|
672
|
+
"button",
|
|
673
|
+
{
|
|
674
|
+
ref: (el) => {
|
|
675
|
+
itemRefs.current[index] = el;
|
|
676
|
+
},
|
|
677
|
+
onClick: () => handleSocialShare(platform.url),
|
|
678
|
+
className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors focus:bg-gray-50 dark:focus:bg-gray-700 focus:outline-none",
|
|
679
|
+
role: "menuitem",
|
|
680
|
+
tabIndex: focusedIndex === index ? 0 : -1,
|
|
681
|
+
children: /* @__PURE__ */ jsx("span", { className: `text-sm ${platform.color}`, children: platform.name })
|
|
682
|
+
},
|
|
683
|
+
platform.name
|
|
684
|
+
)),
|
|
685
|
+
/* @__PURE__ */ jsx(
|
|
686
|
+
"div",
|
|
687
|
+
{
|
|
688
|
+
className: "border-t border-gray-200 dark:border-gray-700 my-1",
|
|
689
|
+
role: "separator"
|
|
690
|
+
}
|
|
691
|
+
),
|
|
692
|
+
/* @__PURE__ */ jsx(
|
|
693
|
+
"button",
|
|
694
|
+
{
|
|
695
|
+
ref: (el) => {
|
|
696
|
+
itemRefs.current[sharePlatforms.length] = el;
|
|
697
|
+
},
|
|
698
|
+
onClick: copyToClipboard,
|
|
699
|
+
className: "w-full flex items-center px-3 py-1.5 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors focus:bg-gray-50 dark:focus:bg-gray-700 focus:outline-none",
|
|
700
|
+
role: "menuitem",
|
|
701
|
+
tabIndex: focusedIndex === sharePlatforms.length ? 0 : -1,
|
|
702
|
+
children: /* @__PURE__ */ jsx("span", { className: "text-sm text-gray-700 dark:text-gray-300", children: showCopiedFeedback ? "Copied!" : "Copy Link" })
|
|
703
|
+
}
|
|
704
|
+
)
|
|
705
|
+
]
|
|
706
|
+
}
|
|
707
|
+
)
|
|
618
708
|
] });
|
|
619
709
|
};
|
|
620
710
|
const TalkToFounderButton = ({
|
|
@@ -674,13 +764,13 @@ const AppBreadcrumbs = ({
|
|
|
674
764
|
] })
|
|
675
765
|
] }) }) });
|
|
676
766
|
};
|
|
677
|
-
const DefaultLinkComponent
|
|
767
|
+
const DefaultLinkComponent = ({
|
|
678
768
|
href,
|
|
679
769
|
className,
|
|
680
770
|
children,
|
|
681
771
|
onClick
|
|
682
772
|
}) => /* @__PURE__ */ jsx("a", { href, className, onClick, children });
|
|
683
|
-
function getCopyrightYear
|
|
773
|
+
function getCopyrightYear(startYear = 2025) {
|
|
684
774
|
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
685
775
|
if (currentYear === startYear) {
|
|
686
776
|
return String(startYear);
|
|
@@ -698,13 +788,13 @@ const AppFooter = ({
|
|
|
698
788
|
statusIndicator,
|
|
699
789
|
StatusIndicatorComponent,
|
|
700
790
|
links = [],
|
|
701
|
-
LinkComponent = DefaultLinkComponent
|
|
791
|
+
LinkComponent = DefaultLinkComponent,
|
|
702
792
|
sticky = true,
|
|
703
793
|
isNetworkOnline = true,
|
|
704
794
|
className,
|
|
705
795
|
onTrack
|
|
706
796
|
}) => {
|
|
707
|
-
const year = copyrightYear || getCopyrightYear
|
|
797
|
+
const year = copyrightYear || getCopyrightYear();
|
|
708
798
|
const track = useCallback(
|
|
709
799
|
(label, params) => {
|
|
710
800
|
onTrack == null ? void 0 : onTrack({
|
|
@@ -788,21 +878,6 @@ const AppFooter = ({
|
|
|
788
878
|
}
|
|
789
879
|
);
|
|
790
880
|
};
|
|
791
|
-
const DefaultLinkComponent = ({
|
|
792
|
-
href,
|
|
793
|
-
className,
|
|
794
|
-
children,
|
|
795
|
-
onClick
|
|
796
|
-
}) => /* @__PURE__ */ jsx("a", { href, className, onClick, children });
|
|
797
|
-
function getCopyrightYear(startYear = 2025) {
|
|
798
|
-
const currentYear = (/* @__PURE__ */ new Date()).getFullYear();
|
|
799
|
-
if (currentYear === startYear) {
|
|
800
|
-
return String(startYear);
|
|
801
|
-
} else if (currentYear > startYear) {
|
|
802
|
-
return `${startYear}-${currentYear}`;
|
|
803
|
-
}
|
|
804
|
-
return String(startYear);
|
|
805
|
-
}
|
|
806
881
|
function getGridColumnsClass(sectionCount, explicit) {
|
|
807
882
|
const cols = explicit || Math.min(sectionCount, 4);
|
|
808
883
|
switch (cols) {
|
|
@@ -1004,6 +1079,13 @@ const AppPageLayout = ({
|
|
|
1004
1079
|
mainClassName,
|
|
1005
1080
|
aspectRatio
|
|
1006
1081
|
}) => {
|
|
1082
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1083
|
+
if (!topBar) {
|
|
1084
|
+
console.warn(
|
|
1085
|
+
"[AppPageLayout] No topBar provided. The layout will render without a navigation bar. Pass an AppTopBar variant or custom component via the topBar prop."
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1007
1089
|
const content = aspectRatio ? /* @__PURE__ */ jsx(AspectFitView, { aspectRatio, children }) : children;
|
|
1008
1090
|
return /* @__PURE__ */ jsx(LayoutProvider, { mode: layoutMode, children: /* @__PURE__ */ jsxs("div", { className: cn(layoutVariants({ background }), className), children: [
|
|
1009
1091
|
/* @__PURE__ */ jsx("header", { children: topBar }),
|
|
@@ -1172,6 +1254,20 @@ const GlobalSettingsPage = ({
|
|
|
1172
1254
|
showAppearanceInfoBox = true,
|
|
1173
1255
|
onTrack
|
|
1174
1256
|
}) => {
|
|
1257
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1258
|
+
const ids = additionalSections.map((s) => s.id);
|
|
1259
|
+
const duplicateIds = ids.filter((id, i) => ids.indexOf(id) !== i);
|
|
1260
|
+
if (duplicateIds.length > 0) {
|
|
1261
|
+
console.warn(
|
|
1262
|
+
`[GlobalSettingsPage] Duplicate section IDs found: ${duplicateIds.join(", ")}. Each section must have a unique id.`
|
|
1263
|
+
);
|
|
1264
|
+
}
|
|
1265
|
+
if (ids.includes("appearance")) {
|
|
1266
|
+
console.warn(
|
|
1267
|
+
'[GlobalSettingsPage] additionalSections contains a section with id "appearance" which conflicts with the built-in Appearance section. Use a different id to avoid unexpected behavior.'
|
|
1268
|
+
);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1175
1271
|
const [selectedSection, setSelectedSection] = useState("appearance");
|
|
1176
1272
|
const [mobileView, setMobileView] = useState(
|
|
1177
1273
|
"navigation"
|
|
@@ -1545,17 +1641,82 @@ const USER_ACTION_ERROR_CODES = [
|
|
|
1545
1641
|
"auth/cancelled-popup-request",
|
|
1546
1642
|
"auth/user-cancelled"
|
|
1547
1643
|
];
|
|
1644
|
+
const colorVariantClasses = {
|
|
1645
|
+
primary: {
|
|
1646
|
+
title: "text-primary-600",
|
|
1647
|
+
inputFocus: "focus:ring-primary-500 focus:border-primary-500",
|
|
1648
|
+
submitButton: "bg-primary-600 hover:bg-primary-700 focus:ring-primary-500 disabled:bg-primary-300",
|
|
1649
|
+
googleButtonFocusRing: "focus:ring-primary-500",
|
|
1650
|
+
toggleLink: "text-primary-600 hover:text-primary-500"
|
|
1651
|
+
},
|
|
1652
|
+
blue: {
|
|
1653
|
+
title: "text-blue-600",
|
|
1654
|
+
inputFocus: "focus:ring-blue-500 focus:border-blue-500",
|
|
1655
|
+
submitButton: "bg-blue-600 hover:bg-blue-700 focus:ring-blue-500 disabled:bg-blue-300",
|
|
1656
|
+
googleButtonFocusRing: "focus:ring-blue-500",
|
|
1657
|
+
toggleLink: "text-blue-600 hover:text-blue-500"
|
|
1658
|
+
},
|
|
1659
|
+
indigo: {
|
|
1660
|
+
title: "text-indigo-600",
|
|
1661
|
+
inputFocus: "focus:ring-indigo-500 focus:border-indigo-500",
|
|
1662
|
+
submitButton: "bg-indigo-600 hover:bg-indigo-700 focus:ring-indigo-500 disabled:bg-indigo-300",
|
|
1663
|
+
googleButtonFocusRing: "focus:ring-indigo-500",
|
|
1664
|
+
toggleLink: "text-indigo-600 hover:text-indigo-500"
|
|
1665
|
+
},
|
|
1666
|
+
violet: {
|
|
1667
|
+
title: "text-violet-600",
|
|
1668
|
+
inputFocus: "focus:ring-violet-500 focus:border-violet-500",
|
|
1669
|
+
submitButton: "bg-violet-600 hover:bg-violet-700 focus:ring-violet-500 disabled:bg-violet-300",
|
|
1670
|
+
googleButtonFocusRing: "focus:ring-violet-500",
|
|
1671
|
+
toggleLink: "text-violet-600 hover:text-violet-500"
|
|
1672
|
+
},
|
|
1673
|
+
orange: {
|
|
1674
|
+
title: "text-orange-600",
|
|
1675
|
+
inputFocus: "focus:ring-orange-500 focus:border-orange-500",
|
|
1676
|
+
submitButton: "bg-orange-600 hover:bg-orange-700 focus:ring-orange-500 disabled:bg-orange-300",
|
|
1677
|
+
googleButtonFocusRing: "focus:ring-orange-500",
|
|
1678
|
+
toggleLink: "text-orange-600 hover:text-orange-500"
|
|
1679
|
+
},
|
|
1680
|
+
emerald: {
|
|
1681
|
+
title: "text-emerald-600",
|
|
1682
|
+
inputFocus: "focus:ring-emerald-500 focus:border-emerald-500",
|
|
1683
|
+
submitButton: "bg-emerald-600 hover:bg-emerald-700 focus:ring-emerald-500 disabled:bg-emerald-300",
|
|
1684
|
+
googleButtonFocusRing: "focus:ring-emerald-500",
|
|
1685
|
+
toggleLink: "text-emerald-600 hover:text-emerald-500"
|
|
1686
|
+
},
|
|
1687
|
+
rose: {
|
|
1688
|
+
title: "text-rose-600",
|
|
1689
|
+
inputFocus: "focus:ring-rose-500 focus:border-rose-500",
|
|
1690
|
+
submitButton: "bg-rose-600 hover:bg-rose-700 focus:ring-rose-500 disabled:bg-rose-300",
|
|
1691
|
+
googleButtonFocusRing: "focus:ring-rose-500",
|
|
1692
|
+
toggleLink: "text-rose-600 hover:text-rose-500"
|
|
1693
|
+
}
|
|
1694
|
+
};
|
|
1548
1695
|
function LoginPage({
|
|
1549
1696
|
appName,
|
|
1550
1697
|
logo,
|
|
1551
|
-
|
|
1698
|
+
onEmailSignIn,
|
|
1699
|
+
onEmailSignUp,
|
|
1700
|
+
onGoogleSignIn,
|
|
1552
1701
|
onSuccess,
|
|
1553
1702
|
onAuthError,
|
|
1554
1703
|
showGoogleSignIn = true,
|
|
1555
1704
|
showSignUp = true,
|
|
1556
1705
|
className = "",
|
|
1557
|
-
|
|
1706
|
+
colorVariant = "primary"
|
|
1558
1707
|
}) {
|
|
1708
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1709
|
+
if (showSignUp && !onEmailSignUp) {
|
|
1710
|
+
console.warn(
|
|
1711
|
+
"[LoginPage] showSignUp is true but onEmailSignUp handler is not provided. Sign-up will fall back to onEmailSignIn. Provide onEmailSignUp for proper account creation."
|
|
1712
|
+
);
|
|
1713
|
+
}
|
|
1714
|
+
if (showGoogleSignIn && !onGoogleSignIn) {
|
|
1715
|
+
console.warn(
|
|
1716
|
+
"[LoginPage] showGoogleSignIn is true but onGoogleSignIn handler is not provided. Google sign-in button will not be rendered. Provide onGoogleSignIn or set showGoogleSignIn to false."
|
|
1717
|
+
);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1559
1720
|
const [isSignUp, setIsSignUp] = useState(false);
|
|
1560
1721
|
const [email, setEmail] = useState("");
|
|
1561
1722
|
const [password, setPassword] = useState("");
|
|
@@ -1563,6 +1724,7 @@ function LoginPage({
|
|
|
1563
1724
|
const [isLoading, setIsLoading] = useState(false);
|
|
1564
1725
|
const [isGoogleSignInPending, setIsGoogleSignInPending] = useState(false);
|
|
1565
1726
|
const googleSignInStartTime = useRef(null);
|
|
1727
|
+
const colors = colorVariantClasses[colorVariant];
|
|
1566
1728
|
useEffect(() => {
|
|
1567
1729
|
const handleFocus = () => {
|
|
1568
1730
|
if (isGoogleSignInPending && googleSignInStartTime.current) {
|
|
@@ -1593,11 +1755,10 @@ function LoginPage({
|
|
|
1593
1755
|
setError(null);
|
|
1594
1756
|
setIsLoading(true);
|
|
1595
1757
|
try {
|
|
1596
|
-
if (
|
|
1597
|
-
|
|
1598
|
-
await createUserWithEmailAndPassword(auth, email, password);
|
|
1758
|
+
if (isSignUp && showSignUp && onEmailSignUp) {
|
|
1759
|
+
await onEmailSignUp(email, password);
|
|
1599
1760
|
} else {
|
|
1600
|
-
await
|
|
1761
|
+
await onEmailSignIn(email, password);
|
|
1601
1762
|
}
|
|
1602
1763
|
onSuccess();
|
|
1603
1764
|
} catch (err) {
|
|
@@ -1607,14 +1768,13 @@ function LoginPage({
|
|
|
1607
1768
|
}
|
|
1608
1769
|
};
|
|
1609
1770
|
const handleGoogleSignIn = async () => {
|
|
1771
|
+
if (!onGoogleSignIn) return;
|
|
1610
1772
|
setError(null);
|
|
1611
1773
|
setIsLoading(true);
|
|
1612
1774
|
setIsGoogleSignInPending(true);
|
|
1613
1775
|
googleSignInStartTime.current = Date.now();
|
|
1614
1776
|
try {
|
|
1615
|
-
|
|
1616
|
-
const provider = new GoogleAuthProvider();
|
|
1617
|
-
await signInWithPopup(auth, provider);
|
|
1777
|
+
await onGoogleSignIn();
|
|
1618
1778
|
onSuccess();
|
|
1619
1779
|
} catch (err) {
|
|
1620
1780
|
handleAuthError(err);
|
|
@@ -1628,27 +1788,31 @@ function LoginPage({
|
|
|
1628
1788
|
return /* @__PURE__ */ jsx(
|
|
1629
1789
|
"div",
|
|
1630
1790
|
{
|
|
1631
|
-
className:
|
|
1791
|
+
className: cn(
|
|
1792
|
+
"min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8",
|
|
1793
|
+
className
|
|
1794
|
+
),
|
|
1632
1795
|
children: /* @__PURE__ */ jsxs("div", { className: "max-w-md w-full space-y-8", children: [
|
|
1633
1796
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1634
1797
|
logo && /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-4", children: logo }),
|
|
1635
|
-
/* @__PURE__ */ jsx(
|
|
1636
|
-
"h1",
|
|
1637
|
-
{
|
|
1638
|
-
className: `text-center text-3xl font-bold text-${primaryColorClass}-600`,
|
|
1639
|
-
children: appName
|
|
1640
|
-
}
|
|
1641
|
-
),
|
|
1798
|
+
/* @__PURE__ */ jsx("h1", { className: cn("text-center text-3xl font-bold", colors.title), children: appName }),
|
|
1642
1799
|
/* @__PURE__ */ jsx("h2", { className: "mt-6 text-center text-2xl font-semibold text-gray-900", children: isSignUp && showSignUp ? text.createAccount : text.signInToAccount })
|
|
1643
1800
|
] }),
|
|
1644
1801
|
/* @__PURE__ */ jsxs("form", { className: "mt-8 space-y-6", onSubmit: handleSubmit, children: [
|
|
1645
|
-
error && /* @__PURE__ */ jsx(
|
|
1802
|
+
error && /* @__PURE__ */ jsx(
|
|
1803
|
+
"div",
|
|
1804
|
+
{
|
|
1805
|
+
className: "bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-md text-sm",
|
|
1806
|
+
role: "alert",
|
|
1807
|
+
children: error
|
|
1808
|
+
}
|
|
1809
|
+
),
|
|
1646
1810
|
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1647
1811
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1648
1812
|
/* @__PURE__ */ jsx(
|
|
1649
1813
|
"label",
|
|
1650
1814
|
{
|
|
1651
|
-
htmlFor: "email",
|
|
1815
|
+
htmlFor: "login-email",
|
|
1652
1816
|
className: "block text-sm font-medium text-gray-700",
|
|
1653
1817
|
children: text.emailLabel
|
|
1654
1818
|
}
|
|
@@ -1656,7 +1820,7 @@ function LoginPage({
|
|
|
1656
1820
|
/* @__PURE__ */ jsx(
|
|
1657
1821
|
"input",
|
|
1658
1822
|
{
|
|
1659
|
-
id: "email",
|
|
1823
|
+
id: "login-email",
|
|
1660
1824
|
name: "email",
|
|
1661
1825
|
type: "email",
|
|
1662
1826
|
autoComplete: "email",
|
|
@@ -1664,7 +1828,10 @@ function LoginPage({
|
|
|
1664
1828
|
value: email,
|
|
1665
1829
|
onChange: (e) => setEmail(e.target.value),
|
|
1666
1830
|
placeholder: text.emailPlaceholder,
|
|
1667
|
-
className:
|
|
1831
|
+
className: cn(
|
|
1832
|
+
"mt-1 appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none sm:text-sm",
|
|
1833
|
+
colors.inputFocus
|
|
1834
|
+
)
|
|
1668
1835
|
}
|
|
1669
1836
|
)
|
|
1670
1837
|
] }),
|
|
@@ -1672,7 +1839,7 @@ function LoginPage({
|
|
|
1672
1839
|
/* @__PURE__ */ jsx(
|
|
1673
1840
|
"label",
|
|
1674
1841
|
{
|
|
1675
|
-
htmlFor: "password",
|
|
1842
|
+
htmlFor: "login-password",
|
|
1676
1843
|
className: "block text-sm font-medium text-gray-700",
|
|
1677
1844
|
children: text.passwordLabel
|
|
1678
1845
|
}
|
|
@@ -1680,7 +1847,7 @@ function LoginPage({
|
|
|
1680
1847
|
/* @__PURE__ */ jsx(
|
|
1681
1848
|
"input",
|
|
1682
1849
|
{
|
|
1683
|
-
id: "password",
|
|
1850
|
+
id: "login-password",
|
|
1684
1851
|
name: "password",
|
|
1685
1852
|
type: "password",
|
|
1686
1853
|
autoComplete: isSignUp ? "new-password" : "current-password",
|
|
@@ -1688,7 +1855,10 @@ function LoginPage({
|
|
|
1688
1855
|
value: password,
|
|
1689
1856
|
onChange: (e) => setPassword(e.target.value),
|
|
1690
1857
|
placeholder: text.passwordPlaceholder,
|
|
1691
|
-
className:
|
|
1858
|
+
className: cn(
|
|
1859
|
+
"mt-1 appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm placeholder-gray-400 focus:outline-none sm:text-sm",
|
|
1860
|
+
colors.inputFocus
|
|
1861
|
+
)
|
|
1692
1862
|
}
|
|
1693
1863
|
)
|
|
1694
1864
|
] })
|
|
@@ -1698,7 +1868,10 @@ function LoginPage({
|
|
|
1698
1868
|
{
|
|
1699
1869
|
type: "submit",
|
|
1700
1870
|
disabled: isLoading,
|
|
1701
|
-
className:
|
|
1871
|
+
className: cn(
|
|
1872
|
+
"w-full inline-flex items-center justify-center font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 text-white px-4 py-2 text-sm",
|
|
1873
|
+
colors.submitButton
|
|
1874
|
+
),
|
|
1702
1875
|
children: [
|
|
1703
1876
|
isLoading && /* @__PURE__ */ jsxs(
|
|
1704
1877
|
"svg",
|
|
@@ -1707,6 +1880,7 @@ function LoginPage({
|
|
|
1707
1880
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1708
1881
|
fill: "none",
|
|
1709
1882
|
viewBox: "0 0 24 24",
|
|
1883
|
+
"aria-hidden": "true",
|
|
1710
1884
|
children: [
|
|
1711
1885
|
/* @__PURE__ */ jsx(
|
|
1712
1886
|
"circle",
|
|
@@ -1734,7 +1908,7 @@ function LoginPage({
|
|
|
1734
1908
|
]
|
|
1735
1909
|
}
|
|
1736
1910
|
) }),
|
|
1737
|
-
showGoogleSignIn && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1911
|
+
showGoogleSignIn && onGoogleSignIn && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1738
1912
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1739
1913
|
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t border-gray-300" }) }),
|
|
1740
1914
|
/* @__PURE__ */ jsx("div", { className: "relative flex justify-center text-sm", children: /* @__PURE__ */ jsx("span", { className: "px-2 bg-gray-50 text-gray-500", children: text.orContinueWith }) })
|
|
@@ -1745,7 +1919,10 @@ function LoginPage({
|
|
|
1745
1919
|
type: "button",
|
|
1746
1920
|
onClick: handleGoogleSignIn,
|
|
1747
1921
|
disabled: isLoading,
|
|
1748
|
-
className:
|
|
1922
|
+
className: cn(
|
|
1923
|
+
"w-full inline-flex items-center justify-center font-medium rounded-md transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2 bg-white text-gray-700 border border-gray-300 hover:bg-gray-50 disabled:bg-gray-100 px-4 py-2 text-sm",
|
|
1924
|
+
colors.googleButtonFocusRing
|
|
1925
|
+
),
|
|
1749
1926
|
children: [
|
|
1750
1927
|
isLoading ? /* @__PURE__ */ jsxs(
|
|
1751
1928
|
"svg",
|
|
@@ -1754,6 +1931,7 @@ function LoginPage({
|
|
|
1754
1931
|
xmlns: "http://www.w3.org/2000/svg",
|
|
1755
1932
|
fill: "none",
|
|
1756
1933
|
viewBox: "0 0 24 24",
|
|
1934
|
+
"aria-hidden": "true",
|
|
1757
1935
|
children: [
|
|
1758
1936
|
/* @__PURE__ */ jsx(
|
|
1759
1937
|
"circle",
|
|
@@ -1791,7 +1969,7 @@ function LoginPage({
|
|
|
1791
1969
|
{
|
|
1792
1970
|
type: "button",
|
|
1793
1971
|
onClick: () => setIsSignUp(!isSignUp),
|
|
1794
|
-
className:
|
|
1972
|
+
className: cn("font-medium", colors.toggleLink),
|
|
1795
1973
|
children: isSignUp ? text.signIn : text.signUp
|
|
1796
1974
|
}
|
|
1797
1975
|
)
|
|
@@ -2618,6 +2796,7 @@ export {
|
|
|
2618
2796
|
AuthStatus,
|
|
2619
2797
|
ChainType,
|
|
2620
2798
|
DEFAULT_LANGUAGES,
|
|
2799
|
+
DefaultLinkComponent,
|
|
2621
2800
|
FontSize,
|
|
2622
2801
|
GlobalSettingsPage,
|
|
2623
2802
|
LanguageSelector,
|
|
@@ -2628,6 +2807,7 @@ export {
|
|
|
2628
2807
|
S as SudobilityApp,
|
|
2629
2808
|
Theme,
|
|
2630
2809
|
cn,
|
|
2810
|
+
getCopyrightYear,
|
|
2631
2811
|
getI18n,
|
|
2632
2812
|
default2 as i18n,
|
|
2633
2813
|
initializeI18n,
|