@myandgo/signin 0.1.0 → 0.2.1

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.
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+ import { WheelProduct } from "./BrandWheel.js";
3
+ export type AuthMethod = "google" | "microsoft" | "email" | "magic";
4
+ export interface SignInCardProps {
5
+ /** Theme + "Sign in to <Product>". Omit for the neutral MyAndGo card. */
6
+ productId?: string;
7
+ /** Product catalogue for theming/logo lookup (default WHEEL_PRODUCTS). */
8
+ products?: WheelProduct[];
9
+ /** Where shield logos live: `${logoBase}<id>-logo.png`. Default "/". */
10
+ logoBase?: string;
11
+ /** Override the derived accent colour. */
12
+ accent?: string;
13
+ /** Host wires real auth. All optional — a missing handler hides its control. */
14
+ onOAuth?: (provider: "google" | "microsoft") => void;
15
+ onEmailAuth?: (args: {
16
+ email: string;
17
+ password: string;
18
+ isSignUp: boolean;
19
+ }) => void | Promise<void>;
20
+ onMagicLink?: (email: string) => void | Promise<void>;
21
+ onForgotPassword?: (email: string) => void;
22
+ /** Disable the submit button + show a working state. */
23
+ busy?: boolean;
24
+ /** Show the sign-in/create-account toggle (default true). */
25
+ allowSignUp?: boolean;
26
+ /** Show the "email link instead" option (default true, requires onMagicLink). */
27
+ showMagicLink?: boolean;
28
+ /** localStorage key for the "Last used" badge (default myandgo_last_auth_method). */
29
+ lastMethodKey?: string;
30
+ /** Support address surfaced in the trouble-helper. */
31
+ supportEmail?: string;
32
+ /** Optional footer (privacy/terms links etc.) rendered under the card. */
33
+ footer?: React.ReactNode;
34
+ /** Optional toast hook for lightweight confirmations (magic sent, reset sent). */
35
+ onToast?: (msg: string) => void;
36
+ }
37
+ export declare function SignInCard({ productId, products, logoBase, accent: accentOverride, onOAuth, onEmailAuth, onMagicLink, onForgotPassword, busy, allowSignUp, showMagicLink, lastMethodKey, supportEmail, footer, onToast, }: SignInCardProps): React.JSX.Element;
@@ -0,0 +1,87 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { WHEEL_PRODUCTS } from "./BrandWheel.js";
4
+ /**
5
+ * The canonical MyAndGo fleet sign-in card. Portable by construction: inline
6
+ * styles (no Tailwind), inline SVG icons (no lucide), no router / framer / toast
7
+ * coupling. The host wires the REAL auth via callbacks; the card owns the layout,
8
+ * per-product theming, the "Last used" badge, and the "Trouble signing in?" helper
9
+ * so every product's sign-in looks and behaves identically.
10
+ *
11
+ * <SignInCard
12
+ * productId="bookandgo"
13
+ * onOAuth={(p) => startOAuth(p)}
14
+ * onEmailAuth={({email,password,isSignUp}) => ...}
15
+ * onMagicLink={(email) => ...}
16
+ * />
17
+ */
18
+ const LAST_METHOD_DEFAULT = "myandgo_last_auth_method";
19
+ const PLATFORM_ACCENT = "#0097b2";
20
+ // Yellow (#FFCD00) is unreadable as an accent on white — fall back to platform cyan.
21
+ const accentFor = (p, override) => override ?? (p && p.colour.toUpperCase() !== "#FFCD00" ? p.colour : PLATFORM_ACCENT);
22
+ const readLast = (key) => {
23
+ try {
24
+ return localStorage.getItem(key) ?? null;
25
+ }
26
+ catch {
27
+ return null;
28
+ }
29
+ };
30
+ const writeLast = (key, m) => { try {
31
+ localStorage.setItem(key, m);
32
+ }
33
+ catch { /* ignore */ } };
34
+ // ── inline icons (no lucide dependency) ──────────────────────────────────────
35
+ const GoogleIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 18, height: 18, "aria-hidden": true, children: [_jsx("path", { fill: "#4285F4", d: "M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" }), _jsx("path", { fill: "#34A853", 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.84A11 11 0 0012 23z" }), _jsx("path", { fill: "#FBBC05", d: "M5.84 14.1a6.6 6.6 0 010-4.2V7.06H2.18a11 11 0 000 9.88l3.66-2.84z" }), _jsx("path", { fill: "#EA4335", d: "M12 4.75c1.62 0 3.06.56 4.21 1.64l3.15-3.15A11 11 0 002.18 7.06L5.84 9.9C6.71 7.3 9.14 4.75 12 4.75z" })] }));
36
+ const MsIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 18, height: 18, "aria-hidden": true, children: [_jsx("path", { fill: "#F25022", d: "M2 2h9.5v9.5H2z" }), _jsx("path", { fill: "#7FBA00", d: "M12.5 2H22v9.5h-9.5z" }), _jsx("path", { fill: "#00A4EF", d: "M2 12.5h9.5V22H2z" }), _jsx("path", { fill: "#FFB900", d: "M12.5 12.5H22V22h-9.5z" })] }));
37
+ const stroke = { fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", strokeLinejoin: "round" };
38
+ const MailIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 16, height: 16, ...stroke, children: [_jsx("rect", { x: "2", y: "4", width: "20", height: "16", rx: "2" }), _jsx("path", { d: "m22 7-10 6L2 7" })] }));
39
+ const LockIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 16, height: 16, ...stroke, children: [_jsx("rect", { x: "3", y: "11", width: "18", height: "11", rx: "2" }), _jsx("path", { d: "M7 11V7a5 5 0 0 1 10 0v4" })] }));
40
+ const EyeIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 16, height: 16, ...stroke, children: [_jsx("path", { d: "M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] }));
41
+ const EyeOffIcon = () => (_jsxs("svg", { viewBox: "0 0 24 24", width: 16, height: 16, ...stroke, children: [_jsx("path", { d: "M9.9 4.24A9.1 9.1 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68M6.6 6.6C3.6 8.3 2 11 2 11s3.5 7 10 7a9.3 9.3 0 0 0 5.4-1.6M1 1l22 22" }), _jsx("path", { d: "M9.9 9.9a3 3 0 0 0 4.2 4.2" })] }));
42
+ const ChevronIcon = ({ open }) => (_jsx("svg", { viewBox: "0 0 24 24", width: 16, height: 16, ...stroke, style: { transition: "transform .2s", transform: open ? "rotate(180deg)" : "none" }, children: _jsx("path", { d: "m6 9 6 6 6-6" }) }));
43
+ // ── little style helpers ─────────────────────────────────────────────────────
44
+ const label = { display: "block", marginBottom: 4, fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".04em", color: "#64748b" };
45
+ const field = { width: "100%", boxSizing: "border-box", borderRadius: 12, border: "1px solid #e2e8f0", padding: "10px 12px 10px 34px", fontSize: 14, outline: "none", color: "#0f172a" };
46
+ const iconWrap = { position: "absolute", left: 11, top: "50%", transform: "translateY(-50%)", color: "#94a3b8", display: "flex" };
47
+ function LastUsed({ show }) {
48
+ if (!show)
49
+ return null;
50
+ return _jsx("span", { style: { position: "absolute", top: -8, left: "50%", transform: "translateX(-50%)", borderRadius: 999, background: PLATFORM_ACCENT, padding: "1px 8px", fontSize: 10, fontWeight: 600, color: "#fff", boxShadow: "0 1px 2px rgba(0,0,0,.15)", whiteSpace: "nowrap" }, children: "Last used" });
51
+ }
52
+ export function SignInCard({ productId, products = WHEEL_PRODUCTS, logoBase = "/", accent: accentOverride, onOAuth, onEmailAuth, onMagicLink, onForgotPassword, busy = false, allowSignUp = true, showMagicLink = true, lastMethodKey = LAST_METHOD_DEFAULT, supportEmail, footer, onToast, }) {
53
+ const product = products.find((p) => p.id === productId);
54
+ const accent = accentFor(product, accentOverride);
55
+ const logo = product ? (product.logo ?? `${logoBase}${product.id}-logo.png`) : `${logoBase}andgo-logo.png`;
56
+ const [email, setEmail] = useState("");
57
+ const [password, setPassword] = useState("");
58
+ const [showPw, setShowPw] = useState(false);
59
+ const [isSignUp, setIsSignUp] = useState(false);
60
+ const [magicSent, setMagicSent] = useState(false);
61
+ const [troubleOpen, setTroubleOpen] = useState(false);
62
+ const [last, setLast] = useState(null);
63
+ const [focused, setFocused] = useState(null);
64
+ useEffect(() => { setLast(readLast(lastMethodKey)); }, [lastMethodKey]);
65
+ const mark = (m) => { writeLast(lastMethodKey, m); setLast(m); };
66
+ const doOAuth = (p) => { mark(p); onOAuth?.(p); };
67
+ const doSubmit = (e) => { e.preventDefault(); mark("email"); onEmailAuth?.({ email, password, isSignUp }); };
68
+ const doMagic = async () => {
69
+ if (!email) {
70
+ onToast?.("Enter your email first");
71
+ return;
72
+ }
73
+ mark("magic");
74
+ await onMagicLink?.(email);
75
+ setMagicSent(true);
76
+ onToast?.("Sign-in link sent — check your inbox.");
77
+ };
78
+ const fieldStyle = (name) => ({ ...field, borderColor: focused === name ? accent : "#e2e8f0", boxShadow: focused === name ? `0 0 0 3px ${accent}22` : "none", transition: "border-color .15s, box-shadow .15s" });
79
+ const oauthBtn = { position: "relative", display: "flex", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 8, border: "1px solid #e2e8f0", padding: "10px 0", fontSize: 14, fontWeight: 600, color: "#334155", background: "#fff", cursor: "pointer" };
80
+ return (_jsxs("div", { style: { width: "100%", maxWidth: 384, margin: "0 auto", fontFamily: "Inter, system-ui, -apple-system, sans-serif" }, children: [_jsxs("div", { style: { marginBottom: 28, textAlign: "center" }, children: [_jsx("img", { src: logo, alt: "", style: { width: 48, height: 48, margin: "0 auto 12px", objectFit: "contain" } }), _jsx("h1", { style: { fontSize: 20, fontWeight: 700, letterSpacing: "-0.01em", color: "#0f172a", margin: 0 }, children: product ? _jsxs(_Fragment, { children: ["Sign in to ", _jsx("span", { style: { color: accent }, children: product.name })] }) : _jsxs(_Fragment, { children: ["My", _jsx("span", { style: { color: accent }, children: "AndGo" })] }) }), _jsx("p", { style: { marginTop: 4, fontSize: 14, color: "#64748b" }, children: product ? "with your MyAndGo account" : "Sign in to your account" })] }), _jsxs("div", { style: { borderRadius: 16, border: "1px solid #e2e8f0", background: "#fff", padding: 24, boxShadow: "0 1px 2px rgba(0,0,0,.04)" }, children: [onOAuth && (_jsxs("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }, children: [_jsxs("button", { type: "button", onClick: () => doOAuth("google"), style: oauthBtn, children: [_jsx(LastUsed, { show: last === "google" }), _jsx(GoogleIcon, {}), " Google"] }), _jsxs("button", { type: "button", onClick: () => doOAuth("microsoft"), style: oauthBtn, children: [_jsx(LastUsed, { show: last === "microsoft" }), _jsx(MsIcon, {}), " Microsoft"] })] })), onOAuth && onEmailAuth && (_jsxs("div", { style: { display: "flex", alignItems: "center", gap: 12, margin: "20px 0", fontSize: 12, color: "#94a3b8" }, children: [_jsx("span", { style: { height: 1, flex: 1, background: "#e2e8f0" } }), " or sign in with email ", _jsx("span", { style: { height: 1, flex: 1, background: "#e2e8f0" } })] })), onEmailAuth && (_jsxs("form", { onSubmit: doSubmit, style: { display: "flex", flexDirection: "column", gap: 12 }, children: [_jsxs("div", { children: [_jsx("label", { style: label, children: "Email" }), _jsxs("div", { style: { position: "relative" }, children: [_jsx("span", { style: iconWrap, children: _jsx(MailIcon, {}) }), _jsx("input", { type: "email", required: true, value: email, onChange: (e) => setEmail(e.target.value), onFocus: () => setFocused("email"), onBlur: () => setFocused(null), placeholder: "you@company.com", style: fieldStyle("email") })] })] }), _jsxs("div", { children: [_jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }, children: [_jsx("label", { style: { ...label, marginBottom: 0 }, children: "Password" }), onForgotPassword && _jsx("button", { type: "button", onClick: () => { if (!email) {
81
+ onToast?.("Enter your email first");
82
+ return;
83
+ } onForgotPassword(email); onToast?.("Password reset link sent if the email exists."); }, style: { fontSize: 12, fontWeight: 500, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }, children: "Forgot password?" })] }), _jsxs("div", { style: { position: "relative" }, children: [_jsx("span", { style: iconWrap, children: _jsx(LockIcon, {}) }), _jsx("input", { type: showPw ? "text" : "password", required: true, value: password, onChange: (e) => setPassword(e.target.value), onFocus: () => setFocused("pw"), onBlur: () => setFocused(null), placeholder: "Your password", style: { ...fieldStyle("pw"), paddingRight: 36 } }), _jsx("button", { type: "button", onClick: () => setShowPw((s) => !s), style: { position: "absolute", right: 10, top: "50%", transform: "translateY(-50%)", color: "#94a3b8", background: "none", border: "none", cursor: "pointer", display: "flex" }, children: showPw ? _jsx(EyeOffIcon, {}) : _jsx(EyeIcon, {}) })] })] }), _jsxs("button", { type: "submit", disabled: busy, style: { position: "relative", display: "flex", width: "100%", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 8, padding: "12px 0", fontSize: 14, fontWeight: 600, color: "#fff", background: accent, border: "none", cursor: busy ? "default" : "pointer", opacity: busy ? 0.6 : 1, boxShadow: "0 1px 2px rgba(0,0,0,.08)" }, children: [_jsx(LastUsed, { show: last === "email" }), busy ? "Signing in…" : isSignUp ? "Create account" : "Sign in"] })] })), showMagicLink && onMagicLink && (_jsx("div", { style: { marginTop: 20, borderTop: "1px solid #f1f5f9", paddingTop: 16, textAlign: "center" }, children: magicSent
84
+ ? _jsx("p", { style: { fontSize: 14, color: "#475569", margin: 0 }, children: "Check your inbox for a sign-in link." })
85
+ : _jsxs("button", { type: "button", onClick: doMagic, style: { position: "relative", fontSize: 14, fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer" }, children: [_jsx(LastUsed, { show: last === "magic" }), "Sign in with an email link instead"] }) }))] }), allowSignUp && onEmailAuth && (_jsxs("p", { style: { marginTop: 20, textAlign: "center", fontSize: 14, color: "#64748b" }, children: [isSignUp ? "Already have an account?" : "Don't have an account?", " ", _jsx("button", { type: "button", onClick: () => setIsSignUp((s) => !s), style: { fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }, children: isSignUp ? "Sign in" : "Create one" })] })), _jsxs("div", { style: { marginTop: 16, borderRadius: 12, border: "1px solid #e2e8f0", background: "#fff", overflow: "hidden" }, children: [_jsxs("button", { type: "button", onClick: () => setTroubleOpen((o) => !o), style: { display: "flex", width: "100%", alignItems: "center", justifyContent: "space-between", padding: "12px 16px", fontSize: 14, fontWeight: 500, color: "#334155", background: "none", border: "none", cursor: "pointer" }, children: ["Trouble signing in? ", _jsx(ChevronIcon, { open: troubleOpen })] }), troubleOpen && (_jsxs("div", { style: { padding: "0 16px 16px", display: "flex", flexDirection: "column", gap: 8, fontSize: 14 }, children: [_jsx("p", { style: { color: "#64748b", margin: 0 }, children: "If Google or Microsoft sign-in keeps failing, your browser is likely blocking a cookie our sign-in needs. Three things that almost always help:" }), onMagicLink && (_jsxs("div", { style: { borderRadius: 8, background: "#f8fafc", padding: 12 }, children: [_jsx("button", { type: "button", onClick: doMagic, style: { fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }, children: "Email me a one-time sign-in link" }), _jsx("p", { style: { margin: "2px 0 0", fontSize: 12, color: "#64748b" }, children: "Skips the cookie requirement entirely. Arrives in seconds." })] })), _jsxs("div", { style: { borderRadius: 8, background: "#f8fafc", padding: 12 }, children: [_jsx("p", { style: { margin: 0, fontWeight: 600, color: "#334155" }, children: "Try a different browser" }), _jsx("p", { style: { margin: "2px 0 0", fontSize: 12, color: "#64748b" }, children: "Safari and some privacy extensions block cross-site cookies by default. Chrome, Firefox or Edge in a fresh window usually work." })] }), _jsxs("div", { style: { borderRadius: 8, background: "#f8fafc", padding: 12 }, children: [_jsx("p", { style: { margin: 0, fontWeight: 600, color: "#334155" }, children: "Still stuck?" }), _jsx("p", { style: { margin: "2px 0 0", fontSize: 12, color: "#64748b" }, children: supportEmail ? _jsxs(_Fragment, { children: ["Email ", _jsx("a", { href: `mailto:${supportEmail}`, style: { color: accent }, children: supportEmail }), " and we'll sort it."] }) : "Email support and we'll sort it." })] })] }))] }), footer && _jsx("div", { style: { marginTop: 24, display: "flex", justifyContent: "center", gap: 16, fontSize: 12, color: "#94a3b8" }, children: footer })] }));
86
+ }
87
+ //# sourceMappingURL=SignInCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SignInCard.js","sourceRoot":"","sources":["../src/SignInCard.tsx"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export { BrandWheel, BrandWheelLoader, WHEEL_PRODUCTS } from "./BrandWheel.js";
2
2
  export type { WheelProduct, BrandWheelProps } from "./BrandWheel.js";
3
+ export { SignInCard } from "./SignInCard.js";
4
+ export type { SignInCardProps, AuthMethod } from "./SignInCard.js";
package/dist/index.js CHANGED
@@ -1,5 +1,8 @@
1
1
  // @myandgo/signin — the MyAndGo fleet sign-in kit. Portable (inline styles, no
2
- // Tailwind or router coupling); theme per product. v0.1: the brand wheel + the
3
- // post-auth loader. The sign-in card + badge + trouble-helper land in v0.2.
2
+ // Tailwind or router coupling); theme per product.
3
+ // v0.1: the brand wheel + the post-auth loader.
4
+ // v0.2: the sign-in card (OAuth + email + magic-link), the "Last used" badge,
5
+ // and the "Trouble signing in?" helper — the full drop-in sign-in surface.
4
6
  export { BrandWheel, BrandWheelLoader, WHEEL_PRODUCTS } from "./BrandWheel.js";
7
+ export { SignInCard } from "./SignInCard.js";
5
8
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,+EAA+E;AAC/E,4EAA4E;AAC5E,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mDAAmD;AACnD,kDAAkD;AAClD,gFAAgF;AAChF,mFAAmF;AACnF,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE/E,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -1,17 +1,44 @@
1
1
  {
2
2
  "name": "@myandgo/signin",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "MyAndGo fleet sign-in kit — the brand wheel + sign-in card, badge, trouble-helper and post-auth loader. Portable (inline styles, no Tailwind/router coupling); theme per product.",
5
5
  "license": "MIT",
6
- "repository": { "type": "git", "url": "git+https://github.com/andycarey1969/andgo-signin.git" },
7
- "keywords": ["signin", "auth", "andgo", "myandgo", "brand-wheel"],
8
- "publishConfig": { "access": "public" },
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/andycarey1969/andgo-signin.git"
9
+ },
10
+ "keywords": [
11
+ "signin",
12
+ "auth",
13
+ "andgo",
14
+ "myandgo",
15
+ "brand-wheel"
16
+ ],
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
9
20
  "type": "module",
10
21
  "main": "dist/index.js",
11
22
  "module": "dist/index.js",
12
23
  "types": "dist/index.d.ts",
13
- "files": ["dist", "src"],
14
- "scripts": { "build": "tsc -p tsconfig.json", "typecheck": "tsc --noEmit", "prepublishOnly": "npm run build" },
15
- "peerDependencies": { "react": ">=18", "react-dom": ">=18" },
16
- "devDependencies": { "@types/react": "^18.3.0", "@types/react-dom": "^18.3.0", "react": "^18.3.0", "react-dom": "^18.3.0", "typescript": "^5.6.0" }
24
+ "files": [
25
+ "dist",
26
+ "src"
27
+ ],
28
+ "scripts": {
29
+ "build": "tsc -p tsconfig.json",
30
+ "typecheck": "tsc --noEmit",
31
+ "prepublishOnly": "npm run build"
32
+ },
33
+ "peerDependencies": {
34
+ "react": ">=18",
35
+ "react-dom": ">=18"
36
+ },
37
+ "devDependencies": {
38
+ "@types/react": "^18.3.0",
39
+ "@types/react-dom": "^18.3.0",
40
+ "react": "^18.3.0",
41
+ "react-dom": "^18.3.0",
42
+ "typescript": "^5.6.0"
43
+ }
17
44
  }
@@ -0,0 +1,212 @@
1
+ import React, { useEffect, useState, CSSProperties } from "react";
2
+ import { WHEEL_PRODUCTS, WheelProduct } from "./BrandWheel.js";
3
+
4
+ /**
5
+ * The canonical MyAndGo fleet sign-in card. Portable by construction: inline
6
+ * styles (no Tailwind), inline SVG icons (no lucide), no router / framer / toast
7
+ * coupling. The host wires the REAL auth via callbacks; the card owns the layout,
8
+ * per-product theming, the "Last used" badge, and the "Trouble signing in?" helper
9
+ * so every product's sign-in looks and behaves identically.
10
+ *
11
+ * <SignInCard
12
+ * productId="bookandgo"
13
+ * onOAuth={(p) => startOAuth(p)}
14
+ * onEmailAuth={({email,password,isSignUp}) => ...}
15
+ * onMagicLink={(email) => ...}
16
+ * />
17
+ */
18
+
19
+ const LAST_METHOD_DEFAULT = "myandgo_last_auth_method";
20
+ const PLATFORM_ACCENT = "#0097b2";
21
+ // Yellow (#FFCD00) is unreadable as an accent on white — fall back to platform cyan.
22
+ const accentFor = (p?: WheelProduct, override?: string) =>
23
+ override ?? (p && p.colour.toUpperCase() !== "#FFCD00" ? p.colour : PLATFORM_ACCENT);
24
+
25
+ export type AuthMethod = "google" | "microsoft" | "email" | "magic";
26
+
27
+ export interface SignInCardProps {
28
+ /** Theme + "Sign in to <Product>". Omit for the neutral MyAndGo card. */
29
+ productId?: string;
30
+ /** Product catalogue for theming/logo lookup (default WHEEL_PRODUCTS). */
31
+ products?: WheelProduct[];
32
+ /** Where shield logos live: `${logoBase}<id>-logo.png`. Default "/". */
33
+ logoBase?: string;
34
+ /** Override the derived accent colour. */
35
+ accent?: string;
36
+
37
+ /** Host wires real auth. All optional — a missing handler hides its control. */
38
+ onOAuth?: (provider: "google" | "microsoft") => void;
39
+ onEmailAuth?: (args: { email: string; password: string; isSignUp: boolean }) => void | Promise<void>;
40
+ onMagicLink?: (email: string) => void | Promise<void>;
41
+ onForgotPassword?: (email: string) => void;
42
+
43
+ /** Disable the submit button + show a working state. */
44
+ busy?: boolean;
45
+ /** Show the sign-in/create-account toggle (default true). */
46
+ allowSignUp?: boolean;
47
+ /** Show the "email link instead" option (default true, requires onMagicLink). */
48
+ showMagicLink?: boolean;
49
+ /** localStorage key for the "Last used" badge (default myandgo_last_auth_method). */
50
+ lastMethodKey?: string;
51
+ /** Support address surfaced in the trouble-helper. */
52
+ supportEmail?: string;
53
+ /** Optional footer (privacy/terms links etc.) rendered under the card. */
54
+ footer?: React.ReactNode;
55
+ /** Optional toast hook for lightweight confirmations (magic sent, reset sent). */
56
+ onToast?: (msg: string) => void;
57
+ }
58
+
59
+ const readLast = (key: string): AuthMethod | null => {
60
+ try { return (localStorage.getItem(key) as AuthMethod) ?? null; } catch { return null; }
61
+ };
62
+ const writeLast = (key: string, m: AuthMethod) => { try { localStorage.setItem(key, m); } catch { /* ignore */ } };
63
+
64
+ // ── inline icons (no lucide dependency) ──────────────────────────────────────
65
+ const GoogleIcon = () => (
66
+ <svg viewBox="0 0 24 24" width={18} height={18} aria-hidden><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92a5.06 5.06 0 01-2.2 3.32v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.1z" /><path fill="#34A853" 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.84A11 11 0 0012 23z" /><path fill="#FBBC05" d="M5.84 14.1a6.6 6.6 0 010-4.2V7.06H2.18a11 11 0 000 9.88l3.66-2.84z" /><path fill="#EA4335" d="M12 4.75c1.62 0 3.06.56 4.21 1.64l3.15-3.15A11 11 0 002.18 7.06L5.84 9.9C6.71 7.3 9.14 4.75 12 4.75z" /></svg>
67
+ );
68
+ const MsIcon = () => (
69
+ <svg viewBox="0 0 24 24" width={18} height={18} aria-hidden><path fill="#F25022" d="M2 2h9.5v9.5H2z" /><path fill="#7FBA00" d="M12.5 2H22v9.5h-9.5z" /><path fill="#00A4EF" d="M2 12.5h9.5V22H2z" /><path fill="#FFB900" d="M12.5 12.5H22V22h-9.5z" /></svg>
70
+ );
71
+ const stroke = { fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round" as const, strokeLinejoin: "round" as const };
72
+ const MailIcon = () => (<svg viewBox="0 0 24 24" width={16} height={16} {...stroke}><rect x="2" y="4" width="20" height="16" rx="2" /><path d="m22 7-10 6L2 7" /></svg>);
73
+ const LockIcon = () => (<svg viewBox="0 0 24 24" width={16} height={16} {...stroke}><rect x="3" y="11" width="18" height="11" rx="2" /><path d="M7 11V7a5 5 0 0 1 10 0v4" /></svg>);
74
+ const EyeIcon = () => (<svg viewBox="0 0 24 24" width={16} height={16} {...stroke}><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z" /><circle cx="12" cy="12" r="3" /></svg>);
75
+ const EyeOffIcon = () => (<svg viewBox="0 0 24 24" width={16} height={16} {...stroke}><path d="M9.9 4.24A9.1 9.1 0 0 1 12 4c6.5 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68M6.6 6.6C3.6 8.3 2 11 2 11s3.5 7 10 7a9.3 9.3 0 0 0 5.4-1.6M1 1l22 22" /><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" /></svg>);
76
+ const ChevronIcon = ({ open }: { open: boolean }) => (<svg viewBox="0 0 24 24" width={16} height={16} {...stroke} style={{ transition: "transform .2s", transform: open ? "rotate(180deg)" : "none" }}><path d="m6 9 6 6 6-6" /></svg>);
77
+
78
+ // ── little style helpers ─────────────────────────────────────────────────────
79
+ const label: CSSProperties = { display: "block", marginBottom: 4, fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: ".04em", color: "#64748b" };
80
+ const field: CSSProperties = { width: "100%", boxSizing: "border-box", borderRadius: 12, border: "1px solid #e2e8f0", padding: "10px 12px 10px 34px", fontSize: 14, outline: "none", color: "#0f172a" };
81
+ const iconWrap: CSSProperties = { position: "absolute", left: 11, top: "50%", transform: "translateY(-50%)", color: "#94a3b8", display: "flex" };
82
+
83
+ function LastUsed({ show }: { show: boolean }) {
84
+ if (!show) return null;
85
+ return <span style={{ position: "absolute", top: -8, left: "50%", transform: "translateX(-50%)", borderRadius: 999, background: PLATFORM_ACCENT, padding: "1px 8px", fontSize: 10, fontWeight: 600, color: "#fff", boxShadow: "0 1px 2px rgba(0,0,0,.15)", whiteSpace: "nowrap" }}>Last used</span>;
86
+ }
87
+
88
+ export function SignInCard({
89
+ productId, products = WHEEL_PRODUCTS, logoBase = "/", accent: accentOverride,
90
+ onOAuth, onEmailAuth, onMagicLink, onForgotPassword,
91
+ busy = false, allowSignUp = true, showMagicLink = true,
92
+ lastMethodKey = LAST_METHOD_DEFAULT, supportEmail, footer, onToast,
93
+ }: SignInCardProps) {
94
+ const product = products.find((p) => p.id === productId);
95
+ const accent = accentFor(product, accentOverride);
96
+ const logo = product ? (product.logo ?? `${logoBase}${product.id}-logo.png`) : `${logoBase}andgo-logo.png`;
97
+
98
+ const [email, setEmail] = useState("");
99
+ const [password, setPassword] = useState("");
100
+ const [showPw, setShowPw] = useState(false);
101
+ const [isSignUp, setIsSignUp] = useState(false);
102
+ const [magicSent, setMagicSent] = useState(false);
103
+ const [troubleOpen, setTroubleOpen] = useState(false);
104
+ const [last, setLast] = useState<AuthMethod | null>(null);
105
+ const [focused, setFocused] = useState<string | null>(null);
106
+
107
+ useEffect(() => { setLast(readLast(lastMethodKey)); }, [lastMethodKey]);
108
+
109
+ const mark = (m: AuthMethod) => { writeLast(lastMethodKey, m); setLast(m); };
110
+ const doOAuth = (p: "google" | "microsoft") => { mark(p); onOAuth?.(p); };
111
+ const doSubmit = (e: React.FormEvent) => { e.preventDefault(); mark("email"); onEmailAuth?.({ email, password, isSignUp }); };
112
+ const doMagic = async () => {
113
+ if (!email) { onToast?.("Enter your email first"); return; }
114
+ mark("magic"); await onMagicLink?.(email); setMagicSent(true); onToast?.("Sign-in link sent — check your inbox.");
115
+ };
116
+
117
+ const fieldStyle = (name: string): CSSProperties => ({ ...field, borderColor: focused === name ? accent : "#e2e8f0", boxShadow: focused === name ? `0 0 0 3px ${accent}22` : "none", transition: "border-color .15s, box-shadow .15s" });
118
+ const oauthBtn: CSSProperties = { position: "relative", display: "flex", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 8, border: "1px solid #e2e8f0", padding: "10px 0", fontSize: 14, fontWeight: 600, color: "#334155", background: "#fff", cursor: "pointer" };
119
+
120
+ return (
121
+ <div style={{ width: "100%", maxWidth: 384, margin: "0 auto", fontFamily: "Inter, system-ui, -apple-system, sans-serif" }}>
122
+ {/* brand */}
123
+ <div style={{ marginBottom: 28, textAlign: "center" }}>
124
+ <img src={logo} alt="" style={{ width: 48, height: 48, margin: "0 auto 12px", objectFit: "contain" }} />
125
+ <h1 style={{ fontSize: 20, fontWeight: 700, letterSpacing: "-0.01em", color: "#0f172a", margin: 0 }}>
126
+ {product ? <>Sign in to <span style={{ color: accent }}>{product.name}</span></> : <>My<span style={{ color: accent }}>AndGo</span></>}
127
+ </h1>
128
+ <p style={{ marginTop: 4, fontSize: 14, color: "#64748b" }}>{product ? "with your MyAndGo account" : "Sign in to your account"}</p>
129
+ </div>
130
+
131
+ <div style={{ borderRadius: 16, border: "1px solid #e2e8f0", background: "#fff", padding: 24, boxShadow: "0 1px 2px rgba(0,0,0,.04)" }}>
132
+ {onOAuth && (
133
+ <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
134
+ <button type="button" onClick={() => doOAuth("google")} style={oauthBtn}><LastUsed show={last === "google"} /><GoogleIcon /> Google</button>
135
+ <button type="button" onClick={() => doOAuth("microsoft")} style={oauthBtn}><LastUsed show={last === "microsoft"} /><MsIcon /> Microsoft</button>
136
+ </div>
137
+ )}
138
+
139
+ {/* divider only when there's actually an email section below to divide to —
140
+ a product that wires only onOAuth (owns no identity) shows no dangling rule */}
141
+ {onOAuth && onEmailAuth && (
142
+ <div style={{ display: "flex", alignItems: "center", gap: 12, margin: "20px 0", fontSize: 12, color: "#94a3b8" }}>
143
+ <span style={{ height: 1, flex: 1, background: "#e2e8f0" }} /> or sign in with email <span style={{ height: 1, flex: 1, background: "#e2e8f0" }} />
144
+ </div>
145
+ )}
146
+
147
+ {onEmailAuth && (
148
+ <form onSubmit={doSubmit} style={{ display: "flex", flexDirection: "column", gap: 12 }}>
149
+ <div>
150
+ <label style={label}>Email</label>
151
+ <div style={{ position: "relative" }}>
152
+ <span style={iconWrap}><MailIcon /></span>
153
+ <input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} onFocus={() => setFocused("email")} onBlur={() => setFocused(null)} placeholder="you@company.com" style={fieldStyle("email")} />
154
+ </div>
155
+ </div>
156
+ <div>
157
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }}>
158
+ <label style={{ ...label, marginBottom: 0 }}>Password</label>
159
+ {onForgotPassword && <button type="button" onClick={() => { if (!email) { onToast?.("Enter your email first"); return; } onForgotPassword(email); onToast?.("Password reset link sent if the email exists."); }} style={{ fontSize: 12, fontWeight: 500, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }}>Forgot password?</button>}
160
+ </div>
161
+ <div style={{ position: "relative" }}>
162
+ <span style={iconWrap}><LockIcon /></span>
163
+ <input type={showPw ? "text" : "password"} required value={password} onChange={(e) => setPassword(e.target.value)} onFocus={() => setFocused("pw")} onBlur={() => setFocused(null)} placeholder="Your password" style={{ ...fieldStyle("pw"), paddingRight: 36 }} />
164
+ <button type="button" onClick={() => setShowPw((s) => !s)} style={{ position: "absolute", right: 10, top: "50%", transform: "translateY(-50%)", color: "#94a3b8", background: "none", border: "none", cursor: "pointer", display: "flex" }}>{showPw ? <EyeOffIcon /> : <EyeIcon />}</button>
165
+ </div>
166
+ </div>
167
+ <button type="submit" disabled={busy} style={{ position: "relative", display: "flex", width: "100%", alignItems: "center", justifyContent: "center", gap: 8, borderRadius: 8, padding: "12px 0", fontSize: 14, fontWeight: 600, color: "#fff", background: accent, border: "none", cursor: busy ? "default" : "pointer", opacity: busy ? 0.6 : 1, boxShadow: "0 1px 2px rgba(0,0,0,.08)" }}>
168
+ <LastUsed show={last === "email"} />{busy ? "Signing in…" : isSignUp ? "Create account" : "Sign in"}
169
+ </button>
170
+ </form>
171
+ )}
172
+
173
+ {showMagicLink && onMagicLink && (
174
+ <div style={{ marginTop: 20, borderTop: "1px solid #f1f5f9", paddingTop: 16, textAlign: "center" }}>
175
+ {magicSent
176
+ ? <p style={{ fontSize: 14, color: "#475569", margin: 0 }}>Check your inbox for a sign-in link.</p>
177
+ : <button type="button" onClick={doMagic} style={{ position: "relative", fontSize: 14, fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer" }}><LastUsed show={last === "magic"} />Sign in with an email link instead</button>}
178
+ </div>
179
+ )}
180
+ </div>
181
+
182
+ {allowSignUp && onEmailAuth && (
183
+ <p style={{ marginTop: 20, textAlign: "center", fontSize: 14, color: "#64748b" }}>
184
+ {isSignUp ? "Already have an account?" : "Don't have an account?"}{" "}
185
+ <button type="button" onClick={() => setIsSignUp((s) => !s)} style={{ fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }}>{isSignUp ? "Sign in" : "Create one"}</button>
186
+ </p>
187
+ )}
188
+
189
+ {/* trouble signing in */}
190
+ <div style={{ marginTop: 16, borderRadius: 12, border: "1px solid #e2e8f0", background: "#fff", overflow: "hidden" }}>
191
+ <button type="button" onClick={() => setTroubleOpen((o) => !o)} style={{ display: "flex", width: "100%", alignItems: "center", justifyContent: "space-between", padding: "12px 16px", fontSize: 14, fontWeight: 500, color: "#334155", background: "none", border: "none", cursor: "pointer" }}>
192
+ Trouble signing in? <ChevronIcon open={troubleOpen} />
193
+ </button>
194
+ {troubleOpen && (
195
+ <div style={{ padding: "0 16px 16px", display: "flex", flexDirection: "column", gap: 8, fontSize: 14 }}>
196
+ <p style={{ color: "#64748b", margin: 0 }}>If Google or Microsoft sign-in keeps failing, your browser is likely blocking a cookie our sign-in needs. Three things that almost always help:</p>
197
+ {onMagicLink && (
198
+ <div style={{ borderRadius: 8, background: "#f8fafc", padding: 12 }}>
199
+ <button type="button" onClick={doMagic} style={{ fontWeight: 600, color: accent, background: "none", border: "none", cursor: "pointer", padding: 0 }}>Email me a one-time sign-in link</button>
200
+ <p style={{ margin: "2px 0 0", fontSize: 12, color: "#64748b" }}>Skips the cookie requirement entirely. Arrives in seconds.</p>
201
+ </div>
202
+ )}
203
+ <div style={{ borderRadius: 8, background: "#f8fafc", padding: 12 }}><p style={{ margin: 0, fontWeight: 600, color: "#334155" }}>Try a different browser</p><p style={{ margin: "2px 0 0", fontSize: 12, color: "#64748b" }}>Safari and some privacy extensions block cross-site cookies by default. Chrome, Firefox or Edge in a fresh window usually work.</p></div>
204
+ <div style={{ borderRadius: 8, background: "#f8fafc", padding: 12 }}><p style={{ margin: 0, fontWeight: 600, color: "#334155" }}>Still stuck?</p><p style={{ margin: "2px 0 0", fontSize: 12, color: "#64748b" }}>{supportEmail ? <>Email <a href={`mailto:${supportEmail}`} style={{ color: accent }}>{supportEmail}</a> and we'll sort it.</> : "Email support and we'll sort it."}</p></div>
205
+ </div>
206
+ )}
207
+ </div>
208
+
209
+ {footer && <div style={{ marginTop: 24, display: "flex", justifyContent: "center", gap: 16, fontSize: 12, color: "#94a3b8" }}>{footer}</div>}
210
+ </div>
211
+ );
212
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  // @myandgo/signin — the MyAndGo fleet sign-in kit. Portable (inline styles, no
2
- // Tailwind or router coupling); theme per product. v0.1: the brand wheel + the
3
- // post-auth loader. The sign-in card + badge + trouble-helper land in v0.2.
2
+ // Tailwind or router coupling); theme per product.
3
+ // v0.1: the brand wheel + the post-auth loader.
4
+ // v0.2: the sign-in card (OAuth + email + magic-link), the "Last used" badge,
5
+ // and the "Trouble signing in?" helper — the full drop-in sign-in surface.
4
6
  export { BrandWheel, BrandWheelLoader, WHEEL_PRODUCTS } from "./BrandWheel.js";
5
7
  export type { WheelProduct, BrandWheelProps } from "./BrandWheel.js";
8
+ export { SignInCard } from "./SignInCard.js";
9
+ export type { SignInCardProps, AuthMethod } from "./SignInCard.js";