@eifi1/ui-kit 0.28.0 → 0.29.0
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/README.md +22 -4
- package/dist/auth/accept-invitation.d.ts +90 -0
- package/dist/auth/accept-invitation.js +117 -0
- package/dist/auth/accept-invitation.js.map +1 -0
- package/dist/auth/auth-errors.d.ts +60 -0
- package/dist/auth/auth-errors.js +32 -0
- package/dist/auth/auth-errors.js.map +1 -0
- package/dist/auth/complete-name-dialog.d.ts +71 -0
- package/dist/auth/complete-name-dialog.js +168 -0
- package/dist/auth/complete-name-dialog.js.map +1 -0
- package/dist/auth/email-tag.d.ts +60 -0
- package/dist/auth/email-tag.js +23 -0
- package/dist/auth/email-tag.js.map +1 -0
- package/dist/auth/forgot-password-form.d.ts +79 -0
- package/dist/auth/forgot-password-form.js +120 -0
- package/dist/auth/forgot-password-form.js.map +1 -0
- package/dist/auth/form-rules.d.ts +27 -0
- package/dist/auth/form-rules.js +23 -0
- package/dist/auth/form-rules.js.map +1 -0
- package/dist/auth/new-password-fields.d.ts +38 -0
- package/dist/auth/new-password-fields.js +54 -0
- package/dist/auth/new-password-fields.js.map +1 -0
- package/dist/auth/not-found-page.d.ts +57 -0
- package/dist/auth/not-found-page.js +40 -0
- package/dist/auth/not-found-page.js.map +1 -0
- package/dist/auth/register-form.d.ts +86 -0
- package/dist/auth/register-form.js +268 -0
- package/dist/auth/register-form.js.map +1 -0
- package/dist/auth/reset-password-form.d.ts +124 -0
- package/dist/auth/reset-password-form.js +164 -0
- package/dist/auth/reset-password-form.js.map +1 -0
- package/dist/auth/sign-in-form.d.ts +243 -0
- package/dist/auth/sign-in-form.js +345 -0
- package/dist/auth/sign-in-form.js.map +1 -0
- package/dist/auth/status-parts.d.ts +78 -0
- package/dist/auth/status-parts.js +83 -0
- package/dist/auth/status-parts.js.map +1 -0
- package/dist/auth/verify-email.d.ts +121 -0
- package/dist/auth/verify-email.js +207 -0
- package/dist/auth/verify-email.js.map +1 -0
- package/dist/chart.d.ts +17 -2
- package/dist/components/amount-input.d.ts +18 -2
- package/dist/components/button-group.d.ts +18 -2
- package/dist/components/calculator.d.ts +18 -2
- package/dist/components/confirm-dialog.d.ts +18 -2
- package/dist/components/copy-button.d.ts +18 -2
- package/dist/components/danger-confirm.d.ts +18 -2
- package/dist/components/facing-pair.d.ts +18 -2
- package/dist/components/file-button.d.ts +18 -2
- package/dist/components/file-dropzone.d.ts +18 -2
- package/dist/components/form-actions.d.ts +18 -2
- package/dist/components/iban-input.d.ts +18 -2
- package/dist/components/language-select.d.ts +18 -9
- package/dist/components/legal-page.d.ts +26 -4
- package/dist/components/legal-page.js +24 -5
- package/dist/components/legal-page.js.map +1 -1
- package/dist/components/legal.d.ts +18 -2
- package/dist/components/legal.js +49 -5
- package/dist/components/legal.js.map +1 -1
- package/dist/components/money-field.d.ts +18 -2
- package/dist/components/number-field.d.ts +18 -2
- package/dist/components/number-input.d.ts +18 -2
- package/dist/components/numpad-sheet.d.ts +18 -2
- package/dist/components/phone-input.d.ts +18 -2
- package/dist/components/series-chart.d.ts +18 -2
- package/dist/components/settings-fields.d.ts +15 -2
- package/dist/components/share-card.d.ts +18 -2
- package/dist/components/text-link.d.ts +18 -2
- package/dist/components/time-input.d.ts +18 -2
- package/dist/components/ui.d.ts +17 -1
- package/dist/components/user-avatar.d.ts +16 -2
- package/dist/components/user-avatar.js +7 -3
- package/dist/components/user-avatar.js.map +1 -1
- package/dist/feedback/feedback-attachment.d.ts +18 -2
- package/dist/feedback/feedback-context.d.ts +18 -2
- package/dist/feedback/feedback-crash.d.ts +18 -2
- package/dist/feedback/feedback-dialog.d.ts +18 -2
- package/dist/feedback/feedback-inbox.d.ts +18 -2
- package/dist/feedback/feedback-labels.d.ts +18 -2
- package/dist/feedback/feedback-menu.d.ts +18 -2
- package/dist/feedback/feedback-record.d.ts +18 -2
- package/dist/feedback/feedback-row-detail.d.ts +18 -2
- package/dist/feedback/feedback-status-undo.d.ts +18 -2
- package/dist/feedback/feedback-submit.d.ts +18 -2
- package/dist/feedback/feedback-swipe.d.ts +18 -2
- package/dist/feedback/feedback-table.d.ts +18 -2
- package/dist/feedback/feedback-thread.d.ts +18 -2
- package/dist/feedback.d.ts +18 -2
- package/dist/hooks/use-file-drop.d.ts +18 -2
- package/dist/i18n/defaults.d.ts +18 -2
- package/dist/i18n/defaults.js +18 -0
- package/dist/i18n/defaults.js.map +1 -1
- package/dist/i18n/german.d.ts +18 -2
- package/dist/i18n/german.js +145 -0
- package/dist/i18n/german.js.map +1 -1
- package/dist/i18n/kit-labels.d.ts +18 -2
- package/dist/i18n/kit-labels.js.map +1 -1
- package/dist/i18n/languages.d.ts +13 -201
- package/dist/i18n/locales/de-CH.d.ts +18 -2
- package/dist/i18n/locales/en.d.ts +18 -2
- package/dist/i18n/locales/es.d.ts +18 -2
- package/dist/i18n/locales/es.js +147 -3
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +18 -2
- package/dist/i18n/locales/fr.js +147 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hu.d.ts +18 -2
- package/dist/i18n/locales/hu.js +149 -3
- package/dist/i18n/locales/hu.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +18 -2
- package/dist/i18n/locales/it.js +146 -1
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +18 -2
- package/dist/i18n/locales/zh.js +146 -1
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/i18n/review.d.ts +18 -2
- package/dist/i18n/review.js +17 -7
- package/dist/i18n/review.js.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.js +49 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/person-name.d.ts +69 -0
- package/dist/lib/person-name.js +65 -0
- package/dist/lib/person-name.js.map +1 -0
- package/dist/{kit-labels-SBN0biPl.d.ts → register-form-V1OeNlMr.d.ts} +423 -6
- package/dist/rhf/fields.d.ts +18 -2
- package/dist/rhf/form.d.ts +18 -2
- package/dist/rhf.d.ts +18 -2
- package/dist/shell/app-shell.d.ts +18 -2
- package/dist/shell/company-switcher.d.ts +59 -0
- package/dist/shell/company-switcher.js +85 -0
- package/dist/shell/company-switcher.js.map +1 -0
- package/dist/shell/top-bar-brand.d.ts +18 -2
- package/dist/shell/topbar-action-menu.d.ts +1 -0
- package/dist/shell.d.ts +12 -2
- package/dist/wizard/stepper-nav.d.ts +18 -2
- package/dist/wizard.d.ts +18 -2
- package/package.json +1 -1
- package/src/auth/accept-invitation.tsx +227 -0
- package/src/auth/auth-errors.ts +105 -0
- package/src/auth/complete-name-dialog.tsx +253 -0
- package/src/auth/email-tag.ts +75 -0
- package/src/auth/forgot-password-form.tsx +213 -0
- package/src/auth/form-rules.ts +40 -0
- package/src/auth/new-password-fields.tsx +85 -0
- package/src/auth/not-found-page.tsx +98 -0
- package/src/auth/register-form.tsx +459 -0
- package/src/auth/reset-password-form.tsx +337 -0
- package/src/auth/sign-in-form.tsx +687 -0
- package/src/auth/status-parts.tsx +138 -0
- package/src/auth/verify-email.tsx +358 -0
- package/src/components/legal-page.tsx +35 -0
- package/src/components/legal.tsx +93 -7
- package/src/components/user-avatar.tsx +22 -4
- package/src/i18n/defaults.ts +18 -0
- package/src/i18n/german.ts +153 -0
- package/src/i18n/kit-labels.tsx +27 -0
- package/src/i18n/locales/es.ts +156 -3
- package/src/i18n/locales/fr.ts +157 -0
- package/src/i18n/locales/hu.ts +159 -3
- package/src/i18n/locales/it.ts +154 -1
- package/src/i18n/locales/zh.ts +149 -1
- package/src/i18n/review.ts +15 -6
- package/src/index.ts +63 -1
- package/src/lib/person-name.ts +157 -0
- package/src/shell/company-switcher.tsx +140 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { ReactNode, ComponentPropsWithoutRef } from 'react';
|
|
3
|
+
import { AuthHeadingLevel } from './status-parts.js';
|
|
4
|
+
|
|
5
|
+
/** Every string the verification page and banner render — the `verifyEmail` namespace.
|
|
6
|
+
* keksdose's words (`verify_email.*`, and its server's "has expired"). */
|
|
7
|
+
interface VerifyEmailLabels {
|
|
8
|
+
/** The page's heading. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** While the link is redeemed. */
|
|
11
|
+
verifying: string;
|
|
12
|
+
verified: string;
|
|
13
|
+
/** The way on from the page — into the app, or to sign-in. */
|
|
14
|
+
continue: string;
|
|
15
|
+
invalid: string;
|
|
16
|
+
expired: string;
|
|
17
|
+
/** The page was opened without a `token`. */
|
|
18
|
+
noToken: string;
|
|
19
|
+
/** Under `invalid`: why a link stops working. */
|
|
20
|
+
invalidHint: string;
|
|
21
|
+
/** Under a dead link, signed out: where a new one comes from. */
|
|
22
|
+
requestInApp: string;
|
|
23
|
+
/** Under a dead link, signed in, beside "Send again". */
|
|
24
|
+
requestHere: string;
|
|
25
|
+
/** The resend button, on the page and in the banner. */
|
|
26
|
+
resend: string;
|
|
27
|
+
/** The resend button while a just-sent mail cools down, given the seconds left. */
|
|
28
|
+
resendIn: (seconds: number) => string;
|
|
29
|
+
/** Said (and shown) after a resend went out. */
|
|
30
|
+
resent: string;
|
|
31
|
+
/** Said (and shown) after a resend failed. */
|
|
32
|
+
resendError: string;
|
|
33
|
+
/** The banner's message. */
|
|
34
|
+
banner: string;
|
|
35
|
+
/** The banner's ×: it defers, it does not close anything. */
|
|
36
|
+
dismiss: string;
|
|
37
|
+
}
|
|
38
|
+
declare const DEFAULT_VERIFY_EMAIL_LABELS: VerifyEmailLabels;
|
|
39
|
+
/** What became of a confirmation link that did not verify. */
|
|
40
|
+
type VerifyEmailFailure = "invalid" | "expired";
|
|
41
|
+
interface VerifyEmailStatusProps extends Omit<ComponentPropsWithoutRef<"div">, "children" | "title"> {
|
|
42
|
+
/** The link's token — the app's `?token=`. Empty: the link was cut short, and the
|
|
43
|
+
* page says so without asking anyone. */
|
|
44
|
+
token: string;
|
|
45
|
+
/**
|
|
46
|
+
* Redeem it — `POST /auth/verify-email {token}`, once per token, on mount. A POST and
|
|
47
|
+
* not the link's own GET (§10.10): a mail scanner that opens every link must not
|
|
48
|
+
* confirm an address nobody read. Resolve when it verified (an address that already
|
|
49
|
+
* was counts: keksdose's redeem is idempotent); reject with the client's error.
|
|
50
|
+
*/
|
|
51
|
+
onVerify: (token: string) => Promise<unknown>;
|
|
52
|
+
/** Which dead end a rejection is. Default: `invalid` for everything — §5.2's servers
|
|
53
|
+
* answer one `token_invalid`; an app whose server tells expiry apart maps it here. */
|
|
54
|
+
classifyError?: (error: unknown) => VerifyEmailFailure;
|
|
55
|
+
/** Someone is signed in on this device: "Continue" goes into the app, and a dead link
|
|
56
|
+
* offers "Send again" (with `onResend`). */
|
|
57
|
+
signedIn?: boolean;
|
|
58
|
+
/** Send a new confirmation mail (`POST /auth/verify-email/resend`) — offered on a dead
|
|
59
|
+
* link while signed in. */
|
|
60
|
+
onResend?: () => Promise<unknown> | void;
|
|
61
|
+
/** Seconds "Send again" waits after a mail went out. Default 60; `0` never waits. */
|
|
62
|
+
cooldown?: number;
|
|
63
|
+
/** Where "Continue" goes. Default `/` signed in, `/login` signed out. */
|
|
64
|
+
continueHref?: string;
|
|
65
|
+
/** App content under "confirmed" — keksdose's "Others can now share a budget with you
|
|
66
|
+
* directly." */
|
|
67
|
+
verifiedContent?: ReactNode;
|
|
68
|
+
/** The heading. Default `labels.title`; `null` draws none, for a page that puts it in
|
|
69
|
+
* `AuthLayout`'s `title` instead. */
|
|
70
|
+
title?: ReactNode;
|
|
71
|
+
/** The heading's level. Default `h2`, under the page's `h1`. */
|
|
72
|
+
headingAs?: AuthHeadingLevel;
|
|
73
|
+
labels?: Partial<VerifyEmailLabels>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Redeem an email-confirmation link (`/verify-email?token=…`, docs/auth-harmonization.md
|
|
77
|
+
* §7) — keksdose's `VerifyEmailPage` (features/auth/verify-email-page.tsx), with
|
|
78
|
+
* kastlan's outcome blocks and its separate "expired".
|
|
79
|
+
*
|
|
80
|
+
* Opened from an inbox, often in a browser with no session, so the token is the only
|
|
81
|
+
* credential and the page renders signed-out. It redeems once per token on mount
|
|
82
|
+
* (StrictMode's double effect included — the token is single-use) and shows one of:
|
|
83
|
+
* confirmed; not valid; expired; or the link had no token at all. Verification gates
|
|
84
|
+
* nothing (§2.8), so every outcome ends on "Continue". Signed in, a dead link also
|
|
85
|
+
* offers "Send again", which waits `cooldown` seconds after a mail went out.
|
|
86
|
+
*
|
|
87
|
+
* The outcome replaces the spinner, which says nothing to a screen reader on its own,
|
|
88
|
+
* so it is also announced.
|
|
89
|
+
*/
|
|
90
|
+
declare function VerifyEmailStatus({ token, onVerify, classifyError, signedIn, onResend, cooldown, continueHref, verifiedContent, title, headingAs: Heading, labels: labelsProp, className, ...rest }: VerifyEmailStatusProps): react.JSX.Element;
|
|
91
|
+
interface EmailVerificationBannerProps {
|
|
92
|
+
/** Send a new confirmation mail (`POST /auth/verify-email/resend`). Resolve when it
|
|
93
|
+
* went out; reject and the banner says it could not. */
|
|
94
|
+
onResend: () => Promise<unknown> | void;
|
|
95
|
+
/** Seconds "Send again" waits after a mail went out. Default 60; `0` never waits. */
|
|
96
|
+
cooldown?: number;
|
|
97
|
+
/** The ×, "Not now". The banner does not hide itself: the app stops rendering it —
|
|
98
|
+
* keksdose for the rest of the session. Left out, no ×. */
|
|
99
|
+
onDismiss?: () => void;
|
|
100
|
+
/** `strip` (default): keksdose's band across the top of the shell. `box`: kastlan's
|
|
101
|
+
* callout inside the page. */
|
|
102
|
+
variant?: "strip" | "box";
|
|
103
|
+
/** The app's sentence after the kit's — keksdose's "Until you do, nobody can share a
|
|
104
|
+
* budget with you directly." */
|
|
105
|
+
children?: ReactNode;
|
|
106
|
+
className?: string;
|
|
107
|
+
labels?: Partial<VerifyEmailLabels>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The nudge for a signed-in account whose address is not confirmed — keksdose's
|
|
111
|
+
* `VerifyEmailBanner` (features/auth/verify-email-banner.tsx), kastlan's copy of it.
|
|
112
|
+
*
|
|
113
|
+
* A prompt, not a wall: verification gates nothing (§2.8). The app renders it while
|
|
114
|
+
* `/auth/me` says `email_verified: false`, and stops when the user dismisses it or the
|
|
115
|
+
* page above confirms the address. "Send again" waits `cooldown` seconds after a mail
|
|
116
|
+
* went out (the button says how long), and its outcome is written into the banner and
|
|
117
|
+
* read out — no toast needed.
|
|
118
|
+
*/
|
|
119
|
+
declare function EmailVerificationBanner({ onResend, cooldown, onDismiss, variant, children, className, labels: labelsProp, }: EmailVerificationBannerProps): react.JSX.Element;
|
|
120
|
+
|
|
121
|
+
export { DEFAULT_VERIFY_EMAIL_LABELS, EmailVerificationBanner, type EmailVerificationBannerProps, type VerifyEmailFailure, type VerifyEmailLabels, VerifyEmailStatus, type VerifyEmailStatusProps };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { CheckCircle2, MailWarning, TimerOff, XCircle } from "lucide-react";
|
|
5
|
+
import { cn } from "../lib/cn.js";
|
|
6
|
+
import { useKitLabels } from "../i18n/kit-labels.js";
|
|
7
|
+
import { useAnnounce } from "../hooks/use-announce.js";
|
|
8
|
+
import { AlertBanner } from "../components/alert-banner.js";
|
|
9
|
+
import { LoadingState } from "../components/loading-state.js";
|
|
10
|
+
import { Button, EmptyState } from "../components/ui.js";
|
|
11
|
+
import { OUTCOME_CLASS, STATUS_HEADING_CLASS, isThenable, useOncePerKey } from "./status-parts.js";
|
|
12
|
+
const DEFAULT_VERIFY_EMAIL_LABELS = {
|
|
13
|
+
title: "Confirm your email address",
|
|
14
|
+
verifying: "Confirming\u2026",
|
|
15
|
+
verified: "Your email address is confirmed.",
|
|
16
|
+
continue: "Continue",
|
|
17
|
+
invalid: "This confirmation link is not valid",
|
|
18
|
+
expired: "This confirmation link has expired",
|
|
19
|
+
noToken: "This link is missing its confirmation token.",
|
|
20
|
+
invalidHint: "The link may have expired or already been used.",
|
|
21
|
+
requestInApp: "You can request a new one from inside the app.",
|
|
22
|
+
requestHere: "You can request a new one here.",
|
|
23
|
+
resend: "Send again",
|
|
24
|
+
resendIn: (seconds) => `Send again in ${seconds} s`,
|
|
25
|
+
resent: "Confirmation email sent",
|
|
26
|
+
resendError: "Could not send the confirmation email",
|
|
27
|
+
banner: "Please confirm your email address.",
|
|
28
|
+
dismiss: "Not now"
|
|
29
|
+
};
|
|
30
|
+
const DEFAULT_COOLDOWN = 60;
|
|
31
|
+
function useResend(onResend, cooldown) {
|
|
32
|
+
const [phase, setPhase] = useState("idle");
|
|
33
|
+
const [remaining, setRemaining] = useState(0);
|
|
34
|
+
const counting = remaining > 0;
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (!counting) return;
|
|
37
|
+
const id = setInterval(() => setRemaining((r) => Math.max(0, r - 1)), 1e3);
|
|
38
|
+
return () => clearInterval(id);
|
|
39
|
+
}, [counting]);
|
|
40
|
+
const sent = () => {
|
|
41
|
+
setPhase("sent");
|
|
42
|
+
setRemaining(Math.max(0, Math.round(cooldown)));
|
|
43
|
+
};
|
|
44
|
+
const send = () => {
|
|
45
|
+
if (!onResend || phase === "sending" || counting) return;
|
|
46
|
+
let result;
|
|
47
|
+
try {
|
|
48
|
+
result = onResend();
|
|
49
|
+
} catch {
|
|
50
|
+
setPhase("failed");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!isThenable(result)) {
|
|
54
|
+
sent();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
setPhase("sending");
|
|
58
|
+
result.then(sent, () => setPhase("failed"));
|
|
59
|
+
};
|
|
60
|
+
return { phase, remaining, send };
|
|
61
|
+
}
|
|
62
|
+
function ResendButton({
|
|
63
|
+
state,
|
|
64
|
+
labels,
|
|
65
|
+
variant
|
|
66
|
+
}) {
|
|
67
|
+
return /* @__PURE__ */ jsx(
|
|
68
|
+
Button,
|
|
69
|
+
{
|
|
70
|
+
type: "button",
|
|
71
|
+
variant,
|
|
72
|
+
size: variant === "secondary" ? "sm" : void 0,
|
|
73
|
+
onClick: state.send,
|
|
74
|
+
pending: state.phase === "sending",
|
|
75
|
+
disabledReason: state.remaining > 0 ? labels.resent : void 0,
|
|
76
|
+
children: state.remaining > 0 ? labels.resendIn(state.remaining) : labels.resend
|
|
77
|
+
}
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
function ResendStatus({ phase, labels, className }) {
|
|
81
|
+
return /* @__PURE__ */ jsx(
|
|
82
|
+
"span",
|
|
83
|
+
{
|
|
84
|
+
role: "status",
|
|
85
|
+
className: cn(phase === "failed" ? "text-[var(--danger)]" : "text-[var(--success)]", className),
|
|
86
|
+
children: phase === "sent" ? labels.resent : phase === "failed" ? labels.resendError : ""
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
function VerifyEmailStatus({
|
|
91
|
+
token,
|
|
92
|
+
onVerify,
|
|
93
|
+
classifyError,
|
|
94
|
+
signedIn = false,
|
|
95
|
+
onResend,
|
|
96
|
+
cooldown = DEFAULT_COOLDOWN,
|
|
97
|
+
continueHref,
|
|
98
|
+
verifiedContent,
|
|
99
|
+
title,
|
|
100
|
+
headingAs: Heading = "h2",
|
|
101
|
+
labels: labelsProp,
|
|
102
|
+
className,
|
|
103
|
+
...rest
|
|
104
|
+
}) {
|
|
105
|
+
const labels = useKitLabels("verifyEmail", DEFAULT_VERIFY_EMAIL_LABELS, labelsProp);
|
|
106
|
+
const [settled, setSettled] = useState(null);
|
|
107
|
+
const outcome = token === "" ? "invalid" : settled?.token === token ? settled.outcome : "verifying";
|
|
108
|
+
const { announce, regionProps } = useAnnounce();
|
|
109
|
+
const resend = useResend(onResend, cooldown);
|
|
110
|
+
useOncePerKey(token === "" ? null : token, onVerify, (key, result) => {
|
|
111
|
+
const next = result.ok ? "verified" : classifyError?.(result.error) ?? "invalid";
|
|
112
|
+
setSettled({ token: key, outcome: next });
|
|
113
|
+
announce(labels[next]);
|
|
114
|
+
});
|
|
115
|
+
const href = continueHref ?? (signedIn ? "/" : "/login");
|
|
116
|
+
const continueButton = /* @__PURE__ */ jsx(Button, { href, replace: true, variant: outcome === "verified" ? "brand" : "secondary", children: labels.continue });
|
|
117
|
+
const canResend = signedIn && onResend !== void 0;
|
|
118
|
+
let body;
|
|
119
|
+
if (outcome === "verifying") {
|
|
120
|
+
body = /* @__PURE__ */ jsx(LoadingState, { size: "sm", label: labels.verifying });
|
|
121
|
+
} else if (outcome === "verified") {
|
|
122
|
+
body = /* @__PURE__ */ jsx(
|
|
123
|
+
EmptyState,
|
|
124
|
+
{
|
|
125
|
+
tone: "success",
|
|
126
|
+
icon: /* @__PURE__ */ jsx(CheckCircle2, {}),
|
|
127
|
+
title: labels.verified,
|
|
128
|
+
hint: verifiedContent,
|
|
129
|
+
action: continueButton,
|
|
130
|
+
className: OUTCOME_CLASS
|
|
131
|
+
}
|
|
132
|
+
);
|
|
133
|
+
} else {
|
|
134
|
+
const hint = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
135
|
+
outcome === "invalid" && token !== "" ? `${labels.invalidHint} ` : "",
|
|
136
|
+
canResend ? labels.requestHere : labels.requestInApp
|
|
137
|
+
] });
|
|
138
|
+
body = /* @__PURE__ */ jsx(
|
|
139
|
+
EmptyState,
|
|
140
|
+
{
|
|
141
|
+
tone: "danger",
|
|
142
|
+
icon: outcome === "expired" ? /* @__PURE__ */ jsx(TimerOff, {}) : /* @__PURE__ */ jsx(XCircle, {}),
|
|
143
|
+
title: token === "" ? labels.noToken : labels[outcome],
|
|
144
|
+
hint,
|
|
145
|
+
action: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
146
|
+
canResend && /* @__PURE__ */ jsx(ResendButton, { state: resend, labels, variant: "brand" }),
|
|
147
|
+
continueButton
|
|
148
|
+
] }),
|
|
149
|
+
className: OUTCOME_CLASS
|
|
150
|
+
}
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
return /* @__PURE__ */ jsxs(
|
|
154
|
+
"div",
|
|
155
|
+
{
|
|
156
|
+
...rest,
|
|
157
|
+
"data-state": outcome,
|
|
158
|
+
"aria-busy": outcome === "verifying" || void 0,
|
|
159
|
+
className: cn("space-y-3", className),
|
|
160
|
+
children: [
|
|
161
|
+
title === null ? null : /* @__PURE__ */ jsx(Heading, { className: STATUS_HEADING_CLASS, children: title ?? labels.title }),
|
|
162
|
+
body,
|
|
163
|
+
canResend && /* @__PURE__ */ jsx(ResendStatus, { phase: resend.phase, labels, className: "block text-center text-xs" }),
|
|
164
|
+
/* @__PURE__ */ jsx("div", { ...regionProps })
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
function EmailVerificationBanner({
|
|
170
|
+
onResend,
|
|
171
|
+
cooldown = DEFAULT_COOLDOWN,
|
|
172
|
+
onDismiss,
|
|
173
|
+
variant = "strip",
|
|
174
|
+
children,
|
|
175
|
+
className,
|
|
176
|
+
labels: labelsProp
|
|
177
|
+
}) {
|
|
178
|
+
const labels = useKitLabels("verifyEmail", DEFAULT_VERIFY_EMAIL_LABELS, labelsProp);
|
|
179
|
+
const resend = useResend(onResend, cooldown);
|
|
180
|
+
return /* @__PURE__ */ jsxs(
|
|
181
|
+
AlertBanner,
|
|
182
|
+
{
|
|
183
|
+
tone: "warning",
|
|
184
|
+
variant,
|
|
185
|
+
icon: /* @__PURE__ */ jsx(MailWarning, {}),
|
|
186
|
+
action: /* @__PURE__ */ jsx(ResendButton, { state: resend, labels, variant: "secondary" }),
|
|
187
|
+
onDismiss,
|
|
188
|
+
dismissLabel: onDismiss ? labels.dismiss : void 0,
|
|
189
|
+
className,
|
|
190
|
+
children: [
|
|
191
|
+
labels.banner,
|
|
192
|
+
children != null && children !== false && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
193
|
+
" ",
|
|
194
|
+
children
|
|
195
|
+
] }),
|
|
196
|
+
" ",
|
|
197
|
+
/* @__PURE__ */ jsx(ResendStatus, { phase: resend.phase, labels, className: "font-medium" })
|
|
198
|
+
]
|
|
199
|
+
}
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
export {
|
|
203
|
+
DEFAULT_VERIFY_EMAIL_LABELS,
|
|
204
|
+
EmailVerificationBanner,
|
|
205
|
+
VerifyEmailStatus
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=verify-email.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/verify-email.tsx"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ComponentPropsWithoutRef, ReactNode } from \"react\";\nimport { CheckCircle2, MailWarning, TimerOff, XCircle } from \"lucide-react\";\nimport { cn } from \"../lib/cn\";\nimport { useKitLabels } from \"../i18n/kit-labels\";\nimport { useAnnounce } from \"../hooks/use-announce\";\nimport { AlertBanner } from \"../components/alert-banner\";\nimport { LoadingState } from \"../components/loading-state\";\nimport { Button, EmptyState } from \"../components/ui\";\nimport { OUTCOME_CLASS, STATUS_HEADING_CLASS, isThenable, useOncePerKey } from \"./status-parts\";\nimport type { AuthHeadingLevel } from \"./status-parts\";\n\n/* ── Labels ──────────────────────────────────────────────────────────────── */\n\n/** Every string the verification page and banner render — the `verifyEmail` namespace.\n * keksdose's words (`verify_email.*`, and its server's \"has expired\"). */\nexport interface VerifyEmailLabels {\n /** The page's heading. */\n title: string;\n /** While the link is redeemed. */\n verifying: string;\n verified: string;\n /** The way on from the page — into the app, or to sign-in. */\n continue: string;\n invalid: string;\n expired: string;\n /** The page was opened without a `token`. */\n noToken: string;\n /** Under `invalid`: why a link stops working. */\n invalidHint: string;\n /** Under a dead link, signed out: where a new one comes from. */\n requestInApp: string;\n /** Under a dead link, signed in, beside \"Send again\". */\n requestHere: string;\n /** The resend button, on the page and in the banner. */\n resend: string;\n /** The resend button while a just-sent mail cools down, given the seconds left. */\n resendIn: (seconds: number) => string;\n /** Said (and shown) after a resend went out. */\n resent: string;\n /** Said (and shown) after a resend failed. */\n resendError: string;\n /** The banner's message. */\n banner: string;\n /** The banner's ×: it defers, it does not close anything. */\n dismiss: string;\n}\n\nexport const DEFAULT_VERIFY_EMAIL_LABELS: VerifyEmailLabels = {\n title: \"Confirm your email address\",\n verifying: \"Confirming…\",\n verified: \"Your email address is confirmed.\",\n continue: \"Continue\",\n invalid: \"This confirmation link is not valid\",\n expired: \"This confirmation link has expired\",\n noToken: \"This link is missing its confirmation token.\",\n invalidHint: \"The link may have expired or already been used.\",\n requestInApp: \"You can request a new one from inside the app.\",\n requestHere: \"You can request a new one here.\",\n resend: \"Send again\",\n resendIn: (seconds) => `Send again in ${seconds} s`,\n resent: \"Confirmation email sent\",\n resendError: \"Could not send the confirmation email\",\n banner: \"Please confirm your email address.\",\n dismiss: \"Not now\",\n};\n\n/** A minute: long enough that an impatient second click does not mail twice, short\n * enough that a mail lost to a spam filter can be re-sent without leaving the page. The\n * server's own limit is the real one (kastlan: 3 an hour per address). */\nconst DEFAULT_COOLDOWN = 60;\n\n/* ── Resend ──────────────────────────────────────────────────────────────── */\n\ntype ResendPhase = \"idle\" | \"sending\" | \"sent\" | \"failed\";\n\n/**\n * \"Send again\" with its own memory: busy while the request runs, then — after a mail\n * went out — held for `cooldown` seconds with the countdown on the button, so a second\n * click cannot mail twice and a throttled server is not asked to refuse. A failure frees\n * it at once. The outcome is a polite live region that is always rendered (a region that\n * mounts with its message is often not read), and visible.\n */\nfunction useResend(onResend: (() => Promise<unknown> | void) | undefined, cooldown: number) {\n const [phase, setPhase] = useState<ResendPhase>(\"idle\");\n const [remaining, setRemaining] = useState(0);\n const counting = remaining > 0;\n useEffect(() => {\n if (!counting) return;\n const id = setInterval(() => setRemaining((r) => Math.max(0, r - 1)), 1000);\n return () => clearInterval(id);\n }, [counting]);\n\n const sent = () => {\n setPhase(\"sent\");\n setRemaining(Math.max(0, Math.round(cooldown)));\n };\n const send = () => {\n if (!onResend || phase === \"sending\" || counting) return;\n let result: unknown;\n try {\n result = onResend();\n } catch {\n setPhase(\"failed\");\n return;\n }\n if (!isThenable(result)) {\n sent();\n return;\n }\n setPhase(\"sending\");\n result.then(sent, () => setPhase(\"failed\"));\n };\n return { phase, remaining, send };\n}\n\nfunction ResendButton({\n state,\n labels,\n variant,\n}: {\n state: ReturnType<typeof useResend>;\n labels: VerifyEmailLabels;\n variant: \"secondary\" | \"brand\";\n}) {\n return (\n <Button\n type=\"button\"\n variant={variant}\n size={variant === \"secondary\" ? \"sm\" : undefined}\n onClick={state.send}\n pending={state.phase === \"sending\"}\n // `disabledReason`, not `disabled`: the button just pressed has the focus, and a\n // native `disabled` would drop it to <body>. The reason is what just happened.\n disabledReason={state.remaining > 0 ? labels.resent : undefined}\n >\n {state.remaining > 0 ? labels.resendIn(state.remaining) : labels.resend}\n </Button>\n );\n}\n\nfunction ResendStatus({ phase, labels, className }: { phase: ResendPhase; labels: VerifyEmailLabels; className?: string }) {\n return (\n <span\n role=\"status\"\n className={cn(phase === \"failed\" ? \"text-[var(--danger)]\" : \"text-[var(--success)]\", className)}\n >\n {phase === \"sent\" ? labels.resent : phase === \"failed\" ? labels.resendError : \"\"}\n </span>\n );\n}\n\n/* ── The page ────────────────────────────────────────────────────────────── */\n\n/** What became of a confirmation link that did not verify. */\nexport type VerifyEmailFailure = \"invalid\" | \"expired\";\n\nexport interface VerifyEmailStatusProps extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\" | \"title\"> {\n /** The link's token — the app's `?token=`. Empty: the link was cut short, and the\n * page says so without asking anyone. */\n token: string;\n /**\n * Redeem it — `POST /auth/verify-email {token}`, once per token, on mount. A POST and\n * not the link's own GET (§10.10): a mail scanner that opens every link must not\n * confirm an address nobody read. Resolve when it verified (an address that already\n * was counts: keksdose's redeem is idempotent); reject with the client's error.\n */\n onVerify: (token: string) => Promise<unknown>;\n /** Which dead end a rejection is. Default: `invalid` for everything — §5.2's servers\n * answer one `token_invalid`; an app whose server tells expiry apart maps it here. */\n classifyError?: (error: unknown) => VerifyEmailFailure;\n /** Someone is signed in on this device: \"Continue\" goes into the app, and a dead link\n * offers \"Send again\" (with `onResend`). */\n signedIn?: boolean;\n /** Send a new confirmation mail (`POST /auth/verify-email/resend`) — offered on a dead\n * link while signed in. */\n onResend?: () => Promise<unknown> | void;\n /** Seconds \"Send again\" waits after a mail went out. Default 60; `0` never waits. */\n cooldown?: number;\n /** Where \"Continue\" goes. Default `/` signed in, `/login` signed out. */\n continueHref?: string;\n /** App content under \"confirmed\" — keksdose's \"Others can now share a budget with you\n * directly.\" */\n verifiedContent?: ReactNode;\n /** The heading. Default `labels.title`; `null` draws none, for a page that puts it in\n * `AuthLayout`'s `title` instead. */\n title?: ReactNode;\n /** The heading's level. Default `h2`, under the page's `h1`. */\n headingAs?: AuthHeadingLevel;\n labels?: Partial<VerifyEmailLabels>;\n}\n\ntype VerifyOutcome = \"verified\" | VerifyEmailFailure;\n\n/**\n * Redeem an email-confirmation link (`/verify-email?token=…`, docs/auth-harmonization.md\n * §7) — keksdose's `VerifyEmailPage` (features/auth/verify-email-page.tsx), with\n * kastlan's outcome blocks and its separate \"expired\".\n *\n * Opened from an inbox, often in a browser with no session, so the token is the only\n * credential and the page renders signed-out. It redeems once per token on mount\n * (StrictMode's double effect included — the token is single-use) and shows one of:\n * confirmed; not valid; expired; or the link had no token at all. Verification gates\n * nothing (§2.8), so every outcome ends on \"Continue\". Signed in, a dead link also\n * offers \"Send again\", which waits `cooldown` seconds after a mail went out.\n *\n * The outcome replaces the spinner, which says nothing to a screen reader on its own,\n * so it is also announced.\n */\nexport function VerifyEmailStatus({\n token,\n onVerify,\n classifyError,\n signedIn = false,\n onResend,\n cooldown = DEFAULT_COOLDOWN,\n continueHref,\n verifiedContent,\n title,\n headingAs: Heading = \"h2\",\n labels: labelsProp,\n className,\n ...rest\n}: VerifyEmailStatusProps) {\n const labels = useKitLabels(\"verifyEmail\", DEFAULT_VERIFY_EMAIL_LABELS, labelsProp);\n const [settled, setSettled] = useState<{ token: string; outcome: VerifyOutcome } | null>(null);\n const outcome: VerifyOutcome | \"verifying\" =\n token === \"\" ? \"invalid\" : settled?.token === token ? settled.outcome : \"verifying\";\n const { announce, regionProps } = useAnnounce();\n const resend = useResend(onResend, cooldown);\n\n useOncePerKey(token === \"\" ? null : token, onVerify, (key, result) => {\n const next: VerifyOutcome = result.ok ? \"verified\" : (classifyError?.(result.error) ?? \"invalid\");\n setSettled({ token: key, outcome: next });\n announce(labels[next]);\n });\n\n const href = continueHref ?? (signedIn ? \"/\" : \"/login\");\n // `replace`: the token is spent, so Back must not land on this page again (keksdose F1).\n const continueButton = (\n <Button href={href} replace variant={outcome === \"verified\" ? \"brand\" : \"secondary\"}>\n {labels.continue}\n </Button>\n );\n const canResend = signedIn && onResend !== undefined;\n\n let body: ReactNode;\n if (outcome === \"verifying\") {\n body = <LoadingState size=\"sm\" label={labels.verifying} />;\n } else if (outcome === \"verified\") {\n body = (\n <EmptyState\n tone=\"success\"\n icon={<CheckCircle2 />}\n title={labels.verified}\n hint={verifiedContent}\n action={continueButton}\n className={OUTCOME_CLASS}\n />\n );\n } else {\n const hint = (\n <>\n {outcome === \"invalid\" && token !== \"\" ? `${labels.invalidHint} ` : \"\"}\n {canResend ? labels.requestHere : labels.requestInApp}\n </>\n );\n body = (\n <EmptyState\n tone=\"danger\"\n icon={outcome === \"expired\" ? <TimerOff /> : <XCircle />}\n title={token === \"\" ? labels.noToken : labels[outcome]}\n hint={hint}\n action={\n <>\n {canResend && <ResendButton state={resend} labels={labels} variant=\"brand\" />}\n {continueButton}\n </>\n }\n className={OUTCOME_CLASS}\n />\n );\n }\n\n return (\n <div\n {...rest}\n data-state={outcome}\n aria-busy={outcome === \"verifying\" || undefined}\n className={cn(\"space-y-3\", className)}\n >\n {title === null ? null : <Heading className={STATUS_HEADING_CLASS}>{title ?? labels.title}</Heading>}\n {body}\n {canResend && <ResendStatus phase={resend.phase} labels={labels} className=\"block text-center text-xs\" />}\n {/* Rendered from the start, so the outcome is read when it arrives. */}\n <div {...regionProps} />\n </div>\n );\n}\n\n/* ── The banner ──────────────────────────────────────────────────────────── */\n\nexport interface EmailVerificationBannerProps {\n /** Send a new confirmation mail (`POST /auth/verify-email/resend`). Resolve when it\n * went out; reject and the banner says it could not. */\n onResend: () => Promise<unknown> | void;\n /** Seconds \"Send again\" waits after a mail went out. Default 60; `0` never waits. */\n cooldown?: number;\n /** The ×, \"Not now\". The banner does not hide itself: the app stops rendering it —\n * keksdose for the rest of the session. Left out, no ×. */\n onDismiss?: () => void;\n /** `strip` (default): keksdose's band across the top of the shell. `box`: kastlan's\n * callout inside the page. */\n variant?: \"strip\" | \"box\";\n /** The app's sentence after the kit's — keksdose's \"Until you do, nobody can share a\n * budget with you directly.\" */\n children?: ReactNode;\n className?: string;\n labels?: Partial<VerifyEmailLabels>;\n}\n\n/**\n * The nudge for a signed-in account whose address is not confirmed — keksdose's\n * `VerifyEmailBanner` (features/auth/verify-email-banner.tsx), kastlan's copy of it.\n *\n * A prompt, not a wall: verification gates nothing (§2.8). The app renders it while\n * `/auth/me` says `email_verified: false`, and stops when the user dismisses it or the\n * page above confirms the address. \"Send again\" waits `cooldown` seconds after a mail\n * went out (the button says how long), and its outcome is written into the banner and\n * read out — no toast needed.\n */\nexport function EmailVerificationBanner({\n onResend,\n cooldown = DEFAULT_COOLDOWN,\n onDismiss,\n variant = \"strip\",\n children,\n className,\n labels: labelsProp,\n}: EmailVerificationBannerProps) {\n const labels = useKitLabels(\"verifyEmail\", DEFAULT_VERIFY_EMAIL_LABELS, labelsProp);\n const resend = useResend(onResend, cooldown);\n return (\n <AlertBanner\n tone=\"warning\"\n variant={variant}\n icon={<MailWarning />}\n action={<ResendButton state={resend} labels={labels} variant=\"secondary\" />}\n onDismiss={onDismiss}\n dismissLabel={onDismiss ? labels.dismiss : undefined}\n className={className}\n >\n {labels.banner}\n {children != null && children !== false && <> {children}</>}{\" \"}\n <ResendStatus phase={resend.phase} labels={labels} className=\"font-medium\" />\n </AlertBanner>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/chart.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export { TimeTickUnit } from './components/series-chart-ticks.js';
|
|
|
13
13
|
import 'recharts';
|
|
14
14
|
import 'react';
|
|
15
15
|
import './lib/direction.js';
|
|
16
|
-
import './
|
|
16
|
+
import './register-form-V1OeNlMr.js';
|
|
17
17
|
import './data-table-labels-DUELeXpa.js';
|
|
18
18
|
import './components/data-table-sort.js';
|
|
19
19
|
import './components/swipeable-row.js';
|
|
@@ -26,6 +26,7 @@ import './components/field-sync.js';
|
|
|
26
26
|
import './components/tooltip.js';
|
|
27
27
|
import './hooks/use-anchored-rect.js';
|
|
28
28
|
import './lib/clipping.js';
|
|
29
|
+
import './components/field-parts.js';
|
|
29
30
|
import './wizard/types.js';
|
|
30
31
|
import './tour/tour.js';
|
|
31
32
|
import './search/command-palette.js';
|
|
@@ -45,6 +46,7 @@ import './components/modal.js';
|
|
|
45
46
|
import './hooks/use-search-param-state.js';
|
|
46
47
|
import './components/measured-grid.js';
|
|
47
48
|
import './components/user-avatar.js';
|
|
49
|
+
import './lib/person-name.js';
|
|
48
50
|
import './components/status-dot.js';
|
|
49
51
|
import './components/progress-bar.js';
|
|
50
52
|
import './components/signed-amount.js';
|
|
@@ -64,6 +66,15 @@ import './components/authed-image.js';
|
|
|
64
66
|
import './components/image-grid.js';
|
|
65
67
|
import './components/lightbox.js';
|
|
66
68
|
import './components/write-lock.js';
|
|
69
|
+
import './auth/sign-in-form.js';
|
|
70
|
+
import './auth/complete-name-dialog.js';
|
|
71
|
+
import './auth/forgot-password-form.js';
|
|
72
|
+
import './auth/status-parts.js';
|
|
73
|
+
import './auth/reset-password-form.js';
|
|
74
|
+
import './auth/verify-email.js';
|
|
75
|
+
import './auth/not-found-page.js';
|
|
76
|
+
import './auth/accept-invitation.js';
|
|
77
|
+
import './shell/company-switcher.js';
|
|
67
78
|
import './components/account-chips.js';
|
|
68
79
|
import './lib/format.js';
|
|
69
80
|
import './components/reauth-dialog.js';
|
|
@@ -79,4 +90,8 @@ import './components/sign-chip.js';
|
|
|
79
90
|
import './components/column-mapper.js';
|
|
80
91
|
import './lib/column-mapping.js';
|
|
81
92
|
import './lib/table-text.js';
|
|
82
|
-
import './
|
|
93
|
+
import './shell/topbar-controls.js';
|
|
94
|
+
import './theme/palette-presets.js';
|
|
95
|
+
import './theme/theme-store.js';
|
|
96
|
+
import 'zustand/middleware';
|
|
97
|
+
import 'zustand';
|
|
@@ -2,7 +2,7 @@ import * as react from 'react';
|
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { CalculatorButtonLabels } from './calculator.js';
|
|
4
4
|
import { NumberPadSheetLabels } from './numpad-sheet.js';
|
|
5
|
-
import '../
|
|
5
|
+
import '../register-form-V1OeNlMr.js';
|
|
6
6
|
import '../data-table-labels-DUELeXpa.js';
|
|
7
7
|
import './data-table-sort.js';
|
|
8
8
|
import './swipeable-row.js';
|
|
@@ -15,6 +15,7 @@ import './field-sync.js';
|
|
|
15
15
|
import './tooltip.js';
|
|
16
16
|
import '../hooks/use-anchored-rect.js';
|
|
17
17
|
import '../lib/clipping.js';
|
|
18
|
+
import './field-parts.js';
|
|
18
19
|
import '../wizard/types.js';
|
|
19
20
|
import '../tour/tour.js';
|
|
20
21
|
import '../search/command-palette.js';
|
|
@@ -36,6 +37,7 @@ import './modal.js';
|
|
|
36
37
|
import '../hooks/use-search-param-state.js';
|
|
37
38
|
import './measured-grid.js';
|
|
38
39
|
import './user-avatar.js';
|
|
40
|
+
import '../lib/person-name.js';
|
|
39
41
|
import './status-dot.js';
|
|
40
42
|
import './progress-bar.js';
|
|
41
43
|
import './signed-amount.js';
|
|
@@ -55,6 +57,15 @@ import './authed-image.js';
|
|
|
55
57
|
import './image-grid.js';
|
|
56
58
|
import './lightbox.js';
|
|
57
59
|
import './write-lock.js';
|
|
60
|
+
import '../auth/sign-in-form.js';
|
|
61
|
+
import '../auth/complete-name-dialog.js';
|
|
62
|
+
import '../auth/forgot-password-form.js';
|
|
63
|
+
import '../auth/status-parts.js';
|
|
64
|
+
import '../auth/reset-password-form.js';
|
|
65
|
+
import '../auth/verify-email.js';
|
|
66
|
+
import '../auth/not-found-page.js';
|
|
67
|
+
import '../auth/accept-invitation.js';
|
|
68
|
+
import '../shell/company-switcher.js';
|
|
58
69
|
import './account-chips.js';
|
|
59
70
|
import '../lib/format.js';
|
|
60
71
|
import './reauth-dialog.js';
|
|
@@ -70,7 +81,12 @@ import './sign-chip.js';
|
|
|
70
81
|
import './column-mapper.js';
|
|
71
82
|
import '../lib/column-mapping.js';
|
|
72
83
|
import '../lib/table-text.js';
|
|
73
|
-
import '
|
|
84
|
+
import '../shell/topbar-controls.js';
|
|
85
|
+
import '../theme/palette-presets.js';
|
|
86
|
+
import '../theme/chart-palette.js';
|
|
87
|
+
import '../theme/theme-store.js';
|
|
88
|
+
import 'zustand/middleware';
|
|
89
|
+
import 'zustand';
|
|
74
90
|
|
|
75
91
|
interface AmountInputProps {
|
|
76
92
|
value: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ComponentPropsWithoutRef, ReactNode, Ref, MouseEvent, ReactElement } from 'react';
|
|
3
|
-
import { bA as ButtonVariant, by as ButtonSize } from '../
|
|
3
|
+
import { bA as ButtonVariant, by as ButtonSize } from '../register-form-V1OeNlMr.js';
|
|
4
4
|
import '../data-table-labels-DUELeXpa.js';
|
|
5
5
|
import './data-table-sort.js';
|
|
6
6
|
import './swipeable-row.js';
|
|
@@ -13,6 +13,7 @@ import './field-sync.js';
|
|
|
13
13
|
import './tooltip.js';
|
|
14
14
|
import '../hooks/use-anchored-rect.js';
|
|
15
15
|
import '../lib/clipping.js';
|
|
16
|
+
import './field-parts.js';
|
|
16
17
|
import '../wizard/types.js';
|
|
17
18
|
import '../tour/tour.js';
|
|
18
19
|
import '../search/command-palette.js';
|
|
@@ -34,6 +35,7 @@ import './modal.js';
|
|
|
34
35
|
import '../hooks/use-search-param-state.js';
|
|
35
36
|
import './measured-grid.js';
|
|
36
37
|
import './user-avatar.js';
|
|
38
|
+
import '../lib/person-name.js';
|
|
37
39
|
import './status-dot.js';
|
|
38
40
|
import './progress-bar.js';
|
|
39
41
|
import './signed-amount.js';
|
|
@@ -53,6 +55,15 @@ import './authed-image.js';
|
|
|
53
55
|
import './image-grid.js';
|
|
54
56
|
import './lightbox.js';
|
|
55
57
|
import './write-lock.js';
|
|
58
|
+
import '../auth/sign-in-form.js';
|
|
59
|
+
import '../auth/complete-name-dialog.js';
|
|
60
|
+
import '../auth/forgot-password-form.js';
|
|
61
|
+
import '../auth/status-parts.js';
|
|
62
|
+
import '../auth/reset-password-form.js';
|
|
63
|
+
import '../auth/verify-email.js';
|
|
64
|
+
import '../auth/not-found-page.js';
|
|
65
|
+
import '../auth/accept-invitation.js';
|
|
66
|
+
import '../shell/company-switcher.js';
|
|
56
67
|
import './account-chips.js';
|
|
57
68
|
import '../lib/format.js';
|
|
58
69
|
import './reauth-dialog.js';
|
|
@@ -68,7 +79,12 @@ import './sign-chip.js';
|
|
|
68
79
|
import './column-mapper.js';
|
|
69
80
|
import '../lib/column-mapping.js';
|
|
70
81
|
import '../lib/table-text.js';
|
|
71
|
-
import '
|
|
82
|
+
import '../shell/topbar-controls.js';
|
|
83
|
+
import '../theme/palette-presets.js';
|
|
84
|
+
import '../theme/chart-palette.js';
|
|
85
|
+
import '../theme/theme-store.js';
|
|
86
|
+
import 'zustand/middleware';
|
|
87
|
+
import 'zustand';
|
|
72
88
|
|
|
73
89
|
interface ButtonGroupProps extends Omit<ComponentPropsWithoutRef<"div">, "role"> {
|
|
74
90
|
/** `horizontal` (default): side by side, in reading order. `vertical`: stacked —
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
|
-
import { bB as CalculatorLabels } from '../
|
|
2
|
+
import { bB as CalculatorLabels } from '../register-form-V1OeNlMr.js';
|
|
3
3
|
import '../data-table-labels-DUELeXpa.js';
|
|
4
4
|
import './data-table-sort.js';
|
|
5
5
|
import './swipeable-row.js';
|
|
@@ -12,6 +12,7 @@ import './field-sync.js';
|
|
|
12
12
|
import './tooltip.js';
|
|
13
13
|
import '../hooks/use-anchored-rect.js';
|
|
14
14
|
import '../lib/clipping.js';
|
|
15
|
+
import './field-parts.js';
|
|
15
16
|
import '../wizard/types.js';
|
|
16
17
|
import '../tour/tour.js';
|
|
17
18
|
import '../search/command-palette.js';
|
|
@@ -33,6 +34,7 @@ import './modal.js';
|
|
|
33
34
|
import '../hooks/use-search-param-state.js';
|
|
34
35
|
import './measured-grid.js';
|
|
35
36
|
import './user-avatar.js';
|
|
37
|
+
import '../lib/person-name.js';
|
|
36
38
|
import './status-dot.js';
|
|
37
39
|
import './progress-bar.js';
|
|
38
40
|
import './signed-amount.js';
|
|
@@ -52,6 +54,15 @@ import './authed-image.js';
|
|
|
52
54
|
import './image-grid.js';
|
|
53
55
|
import './lightbox.js';
|
|
54
56
|
import './write-lock.js';
|
|
57
|
+
import '../auth/sign-in-form.js';
|
|
58
|
+
import '../auth/complete-name-dialog.js';
|
|
59
|
+
import '../auth/forgot-password-form.js';
|
|
60
|
+
import '../auth/status-parts.js';
|
|
61
|
+
import '../auth/reset-password-form.js';
|
|
62
|
+
import '../auth/verify-email.js';
|
|
63
|
+
import '../auth/not-found-page.js';
|
|
64
|
+
import '../auth/accept-invitation.js';
|
|
65
|
+
import '../shell/company-switcher.js';
|
|
55
66
|
import './account-chips.js';
|
|
56
67
|
import '../lib/format.js';
|
|
57
68
|
import './reauth-dialog.js';
|
|
@@ -67,7 +78,12 @@ import './sign-chip.js';
|
|
|
67
78
|
import './column-mapper.js';
|
|
68
79
|
import '../lib/column-mapping.js';
|
|
69
80
|
import '../lib/table-text.js';
|
|
70
|
-
import '
|
|
81
|
+
import '../shell/topbar-controls.js';
|
|
82
|
+
import '../theme/palette-presets.js';
|
|
83
|
+
import '../theme/chart-palette.js';
|
|
84
|
+
import '../theme/theme-store.js';
|
|
85
|
+
import 'zustand/middleware';
|
|
86
|
+
import 'zustand';
|
|
71
87
|
|
|
72
88
|
/** Screen-reader names for the calculator panel's own controls — any part of the
|
|
73
89
|
* kit's `calculator` namespace. Once three optional keys of its own; widened, not
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import 'react';
|
|
2
|
-
export { bX as ConfirmDialogLabels, bY as ConfirmFn, bZ as ConfirmOptions, b_ as ConfirmProvider, b$ as ConfirmProviderProps, c0 as ConfirmTone, cd as DEFAULT_CONFIRM_DIALOG_LABELS,
|
|
2
|
+
export { bX as ConfirmDialogLabels, bY as ConfirmFn, bZ as ConfirmOptions, b_ as ConfirmProvider, b$ as ConfirmProviderProps, c0 as ConfirmTone, cd as DEFAULT_CONFIRM_DIALOG_LABELS, eF as useConfirm } from '../register-form-V1OeNlMr.js';
|
|
3
3
|
import '../data-table-labels-DUELeXpa.js';
|
|
4
4
|
import './data-table-sort.js';
|
|
5
5
|
import './swipeable-row.js';
|
|
@@ -12,6 +12,7 @@ import './field-sync.js';
|
|
|
12
12
|
import './tooltip.js';
|
|
13
13
|
import '../hooks/use-anchored-rect.js';
|
|
14
14
|
import '../lib/clipping.js';
|
|
15
|
+
import './field-parts.js';
|
|
15
16
|
import '../wizard/types.js';
|
|
16
17
|
import '../tour/tour.js';
|
|
17
18
|
import '../search/command-palette.js';
|
|
@@ -33,6 +34,7 @@ import './modal.js';
|
|
|
33
34
|
import '../hooks/use-search-param-state.js';
|
|
34
35
|
import './measured-grid.js';
|
|
35
36
|
import './user-avatar.js';
|
|
37
|
+
import '../lib/person-name.js';
|
|
36
38
|
import './status-dot.js';
|
|
37
39
|
import './progress-bar.js';
|
|
38
40
|
import './signed-amount.js';
|
|
@@ -52,6 +54,15 @@ import './authed-image.js';
|
|
|
52
54
|
import './image-grid.js';
|
|
53
55
|
import './lightbox.js';
|
|
54
56
|
import './write-lock.js';
|
|
57
|
+
import '../auth/sign-in-form.js';
|
|
58
|
+
import '../auth/complete-name-dialog.js';
|
|
59
|
+
import '../auth/forgot-password-form.js';
|
|
60
|
+
import '../auth/status-parts.js';
|
|
61
|
+
import '../auth/reset-password-form.js';
|
|
62
|
+
import '../auth/verify-email.js';
|
|
63
|
+
import '../auth/not-found-page.js';
|
|
64
|
+
import '../auth/accept-invitation.js';
|
|
65
|
+
import '../shell/company-switcher.js';
|
|
55
66
|
import './account-chips.js';
|
|
56
67
|
import '../lib/format.js';
|
|
57
68
|
import './reauth-dialog.js';
|
|
@@ -67,4 +78,9 @@ import './sign-chip.js';
|
|
|
67
78
|
import './column-mapper.js';
|
|
68
79
|
import '../lib/column-mapping.js';
|
|
69
80
|
import '../lib/table-text.js';
|
|
70
|
-
import '
|
|
81
|
+
import '../shell/topbar-controls.js';
|
|
82
|
+
import '../theme/palette-presets.js';
|
|
83
|
+
import '../theme/chart-palette.js';
|
|
84
|
+
import '../theme/theme-store.js';
|
|
85
|
+
import 'zustand/middleware';
|
|
86
|
+
import 'zustand';
|