@sudobility/building_blocks 0.0.26 → 0.0.28
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.
|
@@ -2,3 +2,5 @@ export { AppSitemapPage } from './app-sitemap-page';
|
|
|
2
2
|
export type { AppSitemapPageProps, SitemapPageText, SitemapSection, SitemapLink, LanguageOption, QuickLink, } from './app-sitemap-page';
|
|
3
3
|
export { AppTextPage } from './app-text-page';
|
|
4
4
|
export type { AppTextPageProps, TextPageContent, TextSection, TextSectionWithContent, TextSectionWithList, TextSectionWithSubsections, TextPageContact, TextPageContactInfo, GdprNotice, } from './app-text-page';
|
|
5
|
+
export { LoginPage } from './login-page';
|
|
6
|
+
export type { LoginPageProps, LoginPageText } from './login-page';
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
import { type Auth } from 'firebase/auth';
|
|
3
|
+
/**
|
|
4
|
+
* Props for the LoginPage component
|
|
5
|
+
*/
|
|
6
|
+
export interface LoginPageProps {
|
|
7
|
+
/** Application name displayed as the main title */
|
|
8
|
+
appName: string;
|
|
9
|
+
/** Optional logo element to display above the title */
|
|
10
|
+
logo?: ReactNode;
|
|
11
|
+
/** Firebase Auth instance */
|
|
12
|
+
auth: Auth;
|
|
13
|
+
/** Callback fired on successful authentication */
|
|
14
|
+
onSuccess: () => void;
|
|
15
|
+
/** Whether to show Google sign-in option (default: true) */
|
|
16
|
+
showGoogleSignIn?: boolean;
|
|
17
|
+
/** Whether to show sign-up option (default: true) */
|
|
18
|
+
showSignUp?: boolean;
|
|
19
|
+
/** Custom className for the container */
|
|
20
|
+
className?: string;
|
|
21
|
+
/** Custom primary color class (default: 'primary') */
|
|
22
|
+
primaryColorClass?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Text content for the LoginPage
|
|
26
|
+
*/
|
|
27
|
+
export interface LoginPageText {
|
|
28
|
+
signIn: string;
|
|
29
|
+
signUp: string;
|
|
30
|
+
createAccount: string;
|
|
31
|
+
signInToAccount: string;
|
|
32
|
+
emailLabel: string;
|
|
33
|
+
emailPlaceholder: string;
|
|
34
|
+
passwordLabel: string;
|
|
35
|
+
passwordPlaceholder: string;
|
|
36
|
+
orContinueWith: string;
|
|
37
|
+
signInWithGoogle: string;
|
|
38
|
+
alreadyHaveAccount: string;
|
|
39
|
+
dontHaveAccount: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* A reusable login page component with email/password and Google sign-in support.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* import { LoginPage } from '@sudobility/building_blocks';
|
|
47
|
+
* import { getFirebaseAuth } from '@sudobility/auth_lib';
|
|
48
|
+
*
|
|
49
|
+
* function MyLoginPage() {
|
|
50
|
+
* const navigate = useNavigate();
|
|
51
|
+
* const auth = getFirebaseAuth();
|
|
52
|
+
*
|
|
53
|
+
* return (
|
|
54
|
+
* <LoginPage
|
|
55
|
+
* appName="My App"
|
|
56
|
+
* auth={auth}
|
|
57
|
+
* onSuccess={() => navigate('/')}
|
|
58
|
+
* />
|
|
59
|
+
* );
|
|
60
|
+
* }
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare function LoginPage({ appName, logo, auth, onSuccess, showGoogleSignIn, showSignUp, className, primaryColorClass, }: LoginPageProps): import("react/jsx-runtime").JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { twMerge } from "tailwind-merge";
|
|
|
6
6
|
import { ChevronDownIcon, CalendarDaysIcon, PaintBrushIcon, ChevronLeftIcon, LanguageIcon, ChevronRightIcon, EnvelopeIcon, DocumentTextIcon, CogIcon, HomeIcon } from "@heroicons/react/24/outline";
|
|
7
7
|
import { GRADIENT_CLASSES, textVariants } from "@sudobility/design";
|
|
8
8
|
import { cva } from "class-variance-authority";
|
|
9
|
+
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, GoogleAuthProvider, signInWithPopup } from "firebase/auth";
|
|
9
10
|
function cn(...inputs) {
|
|
10
11
|
return twMerge(clsx(inputs));
|
|
11
12
|
}
|
|
@@ -5146,6 +5147,275 @@ const AppTextPage = ({
|
|
|
5146
5147
|
}
|
|
5147
5148
|
return content;
|
|
5148
5149
|
};
|
|
5150
|
+
function GoogleIcon({ className }) {
|
|
5151
|
+
return /* @__PURE__ */ jsxs("svg", { className, viewBox: "0 0 24 24", "aria-hidden": "true", children: [
|
|
5152
|
+
/* @__PURE__ */ jsx(
|
|
5153
|
+
"path",
|
|
5154
|
+
{
|
|
5155
|
+
d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z",
|
|
5156
|
+
fill: "#4285F4"
|
|
5157
|
+
}
|
|
5158
|
+
),
|
|
5159
|
+
/* @__PURE__ */ jsx(
|
|
5160
|
+
"path",
|
|
5161
|
+
{
|
|
5162
|
+
d: "M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z",
|
|
5163
|
+
fill: "#34A853"
|
|
5164
|
+
}
|
|
5165
|
+
),
|
|
5166
|
+
/* @__PURE__ */ jsx(
|
|
5167
|
+
"path",
|
|
5168
|
+
{
|
|
5169
|
+
d: "M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z",
|
|
5170
|
+
fill: "#FBBC05"
|
|
5171
|
+
}
|
|
5172
|
+
),
|
|
5173
|
+
/* @__PURE__ */ jsx(
|
|
5174
|
+
"path",
|
|
5175
|
+
{
|
|
5176
|
+
d: "M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z",
|
|
5177
|
+
fill: "#EA4335"
|
|
5178
|
+
}
|
|
5179
|
+
)
|
|
5180
|
+
] });
|
|
5181
|
+
}
|
|
5182
|
+
const defaultText = {
|
|
5183
|
+
signIn: "Sign in",
|
|
5184
|
+
signUp: "Sign up",
|
|
5185
|
+
createAccount: "Create your account",
|
|
5186
|
+
signInToAccount: "Sign in to your account",
|
|
5187
|
+
emailLabel: "Email address",
|
|
5188
|
+
emailPlaceholder: "",
|
|
5189
|
+
passwordLabel: "Password",
|
|
5190
|
+
passwordPlaceholder: "",
|
|
5191
|
+
orContinueWith: "Or continue with",
|
|
5192
|
+
signInWithGoogle: "Sign in with Google",
|
|
5193
|
+
alreadyHaveAccount: "Already have an account?",
|
|
5194
|
+
dontHaveAccount: "Don't have an account?"
|
|
5195
|
+
};
|
|
5196
|
+
function LoginPage({
|
|
5197
|
+
appName,
|
|
5198
|
+
logo,
|
|
5199
|
+
auth,
|
|
5200
|
+
onSuccess,
|
|
5201
|
+
showGoogleSignIn = true,
|
|
5202
|
+
showSignUp = true,
|
|
5203
|
+
className = "",
|
|
5204
|
+
primaryColorClass = "primary"
|
|
5205
|
+
}) {
|
|
5206
|
+
const [isSignUp, setIsSignUp] = useState(false);
|
|
5207
|
+
const [email, setEmail] = useState("");
|
|
5208
|
+
const [password, setPassword] = useState("");
|
|
5209
|
+
const [error, setError] = useState(null);
|
|
5210
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
5211
|
+
const handleSubmit = async (e) => {
|
|
5212
|
+
e.preventDefault();
|
|
5213
|
+
setError(null);
|
|
5214
|
+
setIsLoading(true);
|
|
5215
|
+
try {
|
|
5216
|
+
if (!auth) throw new Error("Firebase not configured");
|
|
5217
|
+
if (isSignUp && showSignUp) {
|
|
5218
|
+
await createUserWithEmailAndPassword(auth, email, password);
|
|
5219
|
+
} else {
|
|
5220
|
+
await signInWithEmailAndPassword(auth, email, password);
|
|
5221
|
+
}
|
|
5222
|
+
onSuccess();
|
|
5223
|
+
} catch (err) {
|
|
5224
|
+
setError(err instanceof Error ? err.message : "Authentication failed");
|
|
5225
|
+
} finally {
|
|
5226
|
+
setIsLoading(false);
|
|
5227
|
+
}
|
|
5228
|
+
};
|
|
5229
|
+
const handleGoogleSignIn = async () => {
|
|
5230
|
+
setError(null);
|
|
5231
|
+
setIsLoading(true);
|
|
5232
|
+
try {
|
|
5233
|
+
if (!auth) throw new Error("Firebase not configured");
|
|
5234
|
+
const provider = new GoogleAuthProvider();
|
|
5235
|
+
await signInWithPopup(auth, provider);
|
|
5236
|
+
onSuccess();
|
|
5237
|
+
} catch (err) {
|
|
5238
|
+
setError(err instanceof Error ? err.message : "Google sign-in failed");
|
|
5239
|
+
} finally {
|
|
5240
|
+
setIsLoading(false);
|
|
5241
|
+
}
|
|
5242
|
+
};
|
|
5243
|
+
const text = defaultText;
|
|
5244
|
+
return /* @__PURE__ */ jsx(
|
|
5245
|
+
"div",
|
|
5246
|
+
{
|
|
5247
|
+
className: `min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4 sm:px-6 lg:px-8 ${className}`,
|
|
5248
|
+
children: /* @__PURE__ */ jsxs("div", { className: "max-w-md w-full space-y-8", children: [
|
|
5249
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5250
|
+
logo && /* @__PURE__ */ jsx("div", { className: "flex justify-center mb-4", children: logo }),
|
|
5251
|
+
/* @__PURE__ */ jsx(
|
|
5252
|
+
"h1",
|
|
5253
|
+
{
|
|
5254
|
+
className: `text-center text-3xl font-bold text-${primaryColorClass}-600`,
|
|
5255
|
+
children: appName
|
|
5256
|
+
}
|
|
5257
|
+
),
|
|
5258
|
+
/* @__PURE__ */ jsx("h2", { className: "mt-6 text-center text-2xl font-semibold text-gray-900", children: isSignUp && showSignUp ? text.createAccount : text.signInToAccount })
|
|
5259
|
+
] }),
|
|
5260
|
+
/* @__PURE__ */ jsxs("form", { className: "mt-8 space-y-6", onSubmit: handleSubmit, children: [
|
|
5261
|
+
error && /* @__PURE__ */ jsx("div", { className: "bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-md text-sm", children: error }),
|
|
5262
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
5263
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5264
|
+
/* @__PURE__ */ jsx(
|
|
5265
|
+
"label",
|
|
5266
|
+
{
|
|
5267
|
+
htmlFor: "email",
|
|
5268
|
+
className: "block text-sm font-medium text-gray-700",
|
|
5269
|
+
children: text.emailLabel
|
|
5270
|
+
}
|
|
5271
|
+
),
|
|
5272
|
+
/* @__PURE__ */ jsx(
|
|
5273
|
+
"input",
|
|
5274
|
+
{
|
|
5275
|
+
id: "email",
|
|
5276
|
+
name: "email",
|
|
5277
|
+
type: "email",
|
|
5278
|
+
autoComplete: "email",
|
|
5279
|
+
required: true,
|
|
5280
|
+
value: email,
|
|
5281
|
+
onChange: (e) => setEmail(e.target.value),
|
|
5282
|
+
placeholder: text.emailPlaceholder,
|
|
5283
|
+
className: `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 focus:ring-${primaryColorClass}-500 focus:border-${primaryColorClass}-500 sm:text-sm`
|
|
5284
|
+
}
|
|
5285
|
+
)
|
|
5286
|
+
] }),
|
|
5287
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
5288
|
+
/* @__PURE__ */ jsx(
|
|
5289
|
+
"label",
|
|
5290
|
+
{
|
|
5291
|
+
htmlFor: "password",
|
|
5292
|
+
className: "block text-sm font-medium text-gray-700",
|
|
5293
|
+
children: text.passwordLabel
|
|
5294
|
+
}
|
|
5295
|
+
),
|
|
5296
|
+
/* @__PURE__ */ jsx(
|
|
5297
|
+
"input",
|
|
5298
|
+
{
|
|
5299
|
+
id: "password",
|
|
5300
|
+
name: "password",
|
|
5301
|
+
type: "password",
|
|
5302
|
+
autoComplete: isSignUp ? "new-password" : "current-password",
|
|
5303
|
+
required: true,
|
|
5304
|
+
value: password,
|
|
5305
|
+
onChange: (e) => setPassword(e.target.value),
|
|
5306
|
+
placeholder: text.passwordPlaceholder,
|
|
5307
|
+
className: `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 focus:ring-${primaryColorClass}-500 focus:border-${primaryColorClass}-500 sm:text-sm`
|
|
5308
|
+
}
|
|
5309
|
+
)
|
|
5310
|
+
] })
|
|
5311
|
+
] }),
|
|
5312
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
|
|
5313
|
+
"button",
|
|
5314
|
+
{
|
|
5315
|
+
type: "submit",
|
|
5316
|
+
disabled: isLoading,
|
|
5317
|
+
className: `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-${primaryColorClass}-600 text-white hover:bg-${primaryColorClass}-700 focus:ring-${primaryColorClass}-500 disabled:bg-${primaryColorClass}-300 px-4 py-2 text-sm`,
|
|
5318
|
+
children: [
|
|
5319
|
+
isLoading && /* @__PURE__ */ jsxs(
|
|
5320
|
+
"svg",
|
|
5321
|
+
{
|
|
5322
|
+
className: "animate-spin -ml-1 mr-2 h-4 w-4",
|
|
5323
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5324
|
+
fill: "none",
|
|
5325
|
+
viewBox: "0 0 24 24",
|
|
5326
|
+
children: [
|
|
5327
|
+
/* @__PURE__ */ jsx(
|
|
5328
|
+
"circle",
|
|
5329
|
+
{
|
|
5330
|
+
className: "opacity-25",
|
|
5331
|
+
cx: "12",
|
|
5332
|
+
cy: "12",
|
|
5333
|
+
r: "10",
|
|
5334
|
+
stroke: "currentColor",
|
|
5335
|
+
strokeWidth: "4"
|
|
5336
|
+
}
|
|
5337
|
+
),
|
|
5338
|
+
/* @__PURE__ */ jsx(
|
|
5339
|
+
"path",
|
|
5340
|
+
{
|
|
5341
|
+
className: "opacity-75",
|
|
5342
|
+
fill: "currentColor",
|
|
5343
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
5344
|
+
}
|
|
5345
|
+
)
|
|
5346
|
+
]
|
|
5347
|
+
}
|
|
5348
|
+
),
|
|
5349
|
+
isSignUp && showSignUp ? text.signUp : text.signIn
|
|
5350
|
+
]
|
|
5351
|
+
}
|
|
5352
|
+
) }),
|
|
5353
|
+
showGoogleSignIn && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5354
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
5355
|
+
/* @__PURE__ */ jsx("div", { className: "absolute inset-0 flex items-center", children: /* @__PURE__ */ jsx("div", { className: "w-full border-t border-gray-300" }) }),
|
|
5356
|
+
/* @__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 }) })
|
|
5357
|
+
] }),
|
|
5358
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsxs(
|
|
5359
|
+
"button",
|
|
5360
|
+
{
|
|
5361
|
+
type: "button",
|
|
5362
|
+
onClick: handleGoogleSignIn,
|
|
5363
|
+
disabled: isLoading,
|
|
5364
|
+
className: `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 focus:ring-${primaryColorClass}-500 disabled:bg-gray-100 px-4 py-2 text-sm`,
|
|
5365
|
+
children: [
|
|
5366
|
+
isLoading ? /* @__PURE__ */ jsxs(
|
|
5367
|
+
"svg",
|
|
5368
|
+
{
|
|
5369
|
+
className: "animate-spin -ml-1 mr-2 h-5 w-5",
|
|
5370
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
5371
|
+
fill: "none",
|
|
5372
|
+
viewBox: "0 0 24 24",
|
|
5373
|
+
children: [
|
|
5374
|
+
/* @__PURE__ */ jsx(
|
|
5375
|
+
"circle",
|
|
5376
|
+
{
|
|
5377
|
+
className: "opacity-25",
|
|
5378
|
+
cx: "12",
|
|
5379
|
+
cy: "12",
|
|
5380
|
+
r: "10",
|
|
5381
|
+
stroke: "currentColor",
|
|
5382
|
+
strokeWidth: "4"
|
|
5383
|
+
}
|
|
5384
|
+
),
|
|
5385
|
+
/* @__PURE__ */ jsx(
|
|
5386
|
+
"path",
|
|
5387
|
+
{
|
|
5388
|
+
className: "opacity-75",
|
|
5389
|
+
fill: "currentColor",
|
|
5390
|
+
d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
5391
|
+
}
|
|
5392
|
+
)
|
|
5393
|
+
]
|
|
5394
|
+
}
|
|
5395
|
+
) : /* @__PURE__ */ jsx(GoogleIcon, { className: "h-5 w-5 mr-2" }),
|
|
5396
|
+
text.signInWithGoogle
|
|
5397
|
+
]
|
|
5398
|
+
}
|
|
5399
|
+
) })
|
|
5400
|
+
] })
|
|
5401
|
+
] }),
|
|
5402
|
+
showSignUp && /* @__PURE__ */ jsxs("p", { className: "text-center text-sm text-gray-600", children: [
|
|
5403
|
+
isSignUp ? text.alreadyHaveAccount : text.dontHaveAccount,
|
|
5404
|
+
" ",
|
|
5405
|
+
/* @__PURE__ */ jsx(
|
|
5406
|
+
"button",
|
|
5407
|
+
{
|
|
5408
|
+
type: "button",
|
|
5409
|
+
onClick: () => setIsSignUp(!isSignUp),
|
|
5410
|
+
className: `font-medium text-${primaryColorClass}-600 hover:text-${primaryColorClass}-500`,
|
|
5411
|
+
children: isSignUp ? text.signIn : text.signUp
|
|
5412
|
+
}
|
|
5413
|
+
)
|
|
5414
|
+
] })
|
|
5415
|
+
] })
|
|
5416
|
+
}
|
|
5417
|
+
);
|
|
5418
|
+
}
|
|
5149
5419
|
export {
|
|
5150
5420
|
AppBreadcrumbs,
|
|
5151
5421
|
AppFooter,
|
|
@@ -5165,6 +5435,7 @@ export {
|
|
|
5165
5435
|
FontSize,
|
|
5166
5436
|
GlobalSettingsPage,
|
|
5167
5437
|
LanguageSelector,
|
|
5438
|
+
LoginPage,
|
|
5168
5439
|
RTL_LANGUAGES,
|
|
5169
5440
|
Theme,
|
|
5170
5441
|
cn,
|