mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-04-30 12:40:10 +00:00
Initial commit of eigent-main
This commit is contained in:
commit
723df5a03e
1144 changed files with 103478 additions and 0 deletions
151
package/@stackframe/react/dist/esm/components-page/account-settings.js
vendored
Normal file
151
package/@stackframe/react/dist/esm/components-page/account-settings.js
vendored
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/account-settings.tsx
|
||||
import { Skeleton, Typography } from "@stackframe/stack-ui";
|
||||
import { icons } from "lucide-react";
|
||||
import { Suspense } from "react";
|
||||
import { useStackApp, useUser } from "..";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { SidebarLayout } from "../components/elements/sidebar-layout";
|
||||
import { TeamIcon } from "../components/team-icon";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { ActiveSessionsPage } from "./account-settings/active-sessions/active-sessions-page";
|
||||
import { ApiKeysPage } from "./account-settings/api-keys/api-keys-page";
|
||||
import { EmailsAndAuthPage } from "./account-settings/email-and-auth/email-and-auth-page";
|
||||
import { ProfilePage } from "./account-settings/profile-page/profile-page";
|
||||
import { SettingsPage } from "./account-settings/settings/settings-page";
|
||||
import { TeamCreationPage } from "./account-settings/teams/team-creation-page";
|
||||
import { TeamPage } from "./account-settings/teams/team-page";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
var Icon = ({ name }) => {
|
||||
const LucideIcon = icons[name];
|
||||
return /* @__PURE__ */ jsx(LucideIcon, { className: "mr-2 h-4 w-4" });
|
||||
};
|
||||
function AccountSettings(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const teams = user.useTeams();
|
||||
const stackApp = useStackApp();
|
||||
const project = stackApp.useProject();
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("div", { className: "self-stretch flex-grow w-full", children: /* @__PURE__ */ jsx(
|
||||
SidebarLayout,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
title: t("My Profile"),
|
||||
type: "item",
|
||||
id: "profile",
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "Contact" }),
|
||||
content: /* @__PURE__ */ jsx(ProfilePage, {})
|
||||
},
|
||||
{
|
||||
title: t("Emails & Auth"),
|
||||
type: "item",
|
||||
id: "auth",
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "ShieldCheck" }),
|
||||
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(EmailsAndAuthPageSkeleton, {}), children: /* @__PURE__ */ jsx(EmailsAndAuthPage, {}) })
|
||||
},
|
||||
{
|
||||
title: t("Active Sessions"),
|
||||
type: "item",
|
||||
id: "sessions",
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "Monitor" }),
|
||||
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ActiveSessionsPageSkeleton, {}), children: /* @__PURE__ */ jsx(ActiveSessionsPage, {}) })
|
||||
},
|
||||
...project.config.allowUserApiKeys ? [{
|
||||
title: t("API Keys"),
|
||||
type: "item",
|
||||
id: "api-keys",
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "Key" }),
|
||||
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ jsx(ApiKeysPage, {}) })
|
||||
}] : [],
|
||||
{
|
||||
title: t("Settings"),
|
||||
type: "item",
|
||||
id: "settings",
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "Settings" }),
|
||||
content: /* @__PURE__ */ jsx(SettingsPage, {})
|
||||
},
|
||||
...props.extraItems?.map((item) => ({
|
||||
title: item.title,
|
||||
type: "item",
|
||||
id: item.id,
|
||||
icon: (() => {
|
||||
const iconName = item.iconName;
|
||||
if (iconName) {
|
||||
return /* @__PURE__ */ jsx(Icon, { name: iconName });
|
||||
} else if (item.icon) {
|
||||
return item.icon;
|
||||
}
|
||||
return null;
|
||||
})(),
|
||||
content: item.content
|
||||
})) || [],
|
||||
...teams.length > 0 || project.config.clientTeamCreationEnabled ? [{
|
||||
title: t("Teams"),
|
||||
type: "divider"
|
||||
}] : [],
|
||||
...teams.map((team) => ({
|
||||
title: /* @__PURE__ */ jsxs("div", { className: "flex gap-2 items-center w-full", children: [
|
||||
/* @__PURE__ */ jsx(TeamIcon, { team }),
|
||||
/* @__PURE__ */ jsx(Typography, { className: "max-w-[320px] md:w-[90%] truncate", children: team.displayName })
|
||||
] }),
|
||||
type: "item",
|
||||
id: `team-${team.id}`,
|
||||
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(TeamPageSkeleton, {}), children: /* @__PURE__ */ jsx(TeamPage, { team }) })
|
||||
})),
|
||||
...project.config.clientTeamCreationEnabled ? [{
|
||||
title: t("Create a team"),
|
||||
icon: /* @__PURE__ */ jsx(Icon, { name: "CirclePlus" }),
|
||||
type: "item",
|
||||
id: "team-creation",
|
||||
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(TeamCreationSkeleton, {}), children: /* @__PURE__ */ jsx(TeamCreationPage, {}) })
|
||||
}] : []
|
||||
].filter((p) => p.type === "divider" || p.content),
|
||||
title: t("Account Settings")
|
||||
}
|
||||
) }) });
|
||||
}
|
||||
function PageLayout(props) {
|
||||
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6", children: props.children });
|
||||
}
|
||||
function EmailsAndAuthPageSkeleton() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" })
|
||||
] });
|
||||
}
|
||||
function ActiveSessionsPageSkeleton() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-6 w-48 mb-2" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full mb-4" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
||||
] });
|
||||
}
|
||||
function ApiKeysPageSkeleton() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
||||
] });
|
||||
}
|
||||
function TeamPageSkeleton() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
||||
] });
|
||||
}
|
||||
function TeamCreationSkeleton() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
AccountSettings
|
||||
};
|
||||
//# sourceMappingURL=account-settings.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/account-settings.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/account-settings.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,126 @@
|
|||
// src/components-page/account-settings/active-sessions/active-sessions-page.tsx
|
||||
import { fromNow } from "@stackframe/stack-shared/dist/utils/dates";
|
||||
import { captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { ActionCell, Badge, Button, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function ActiveSessionsPage() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "throw" });
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [isRevokingAll, setIsRevokingAll] = useState(false);
|
||||
const [sessions, setSessions] = useState([]);
|
||||
const [showConfirmRevokeAll, setShowConfirmRevokeAll] = useState(false);
|
||||
useEffect(() => {
|
||||
runAsynchronously(async () => {
|
||||
setIsLoading(true);
|
||||
const sessionsData = await user.getActiveSessions();
|
||||
const enhancedSessions = sessionsData;
|
||||
setSessions(enhancedSessions);
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, [user]);
|
||||
const handleRevokeSession = async (sessionId) => {
|
||||
try {
|
||||
await user.revokeSession(sessionId);
|
||||
setSessions((prev) => prev.filter((session) => session.id !== sessionId));
|
||||
} catch (error) {
|
||||
captureError("Failed to revoke session", { sessionId, error });
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
const handleRevokeAllSessions = async () => {
|
||||
setIsRevokingAll(true);
|
||||
try {
|
||||
const deletionPromises = sessions.filter((session) => !session.isCurrentSession).map((session) => user.revokeSession(session.id));
|
||||
await Promise.all(deletionPromises);
|
||||
setSessions((prevSessions) => prevSessions.filter((session) => session.isCurrentSession));
|
||||
} catch (error) {
|
||||
captureError("Failed to revoke all sessions", { error, sessionIds: sessions.map((session) => session.id) });
|
||||
throw error;
|
||||
} finally {
|
||||
setIsRevokingAll(false);
|
||||
setShowConfirmRevokeAll(false);
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsx(PageLayout, { children: /* @__PURE__ */ jsxs("div", { children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center mb-2", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: t("Active Sessions") }),
|
||||
sessions.filter((s) => !s.isCurrentSession).length > 0 && !isLoading && (showConfirmRevokeAll ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
size: "sm",
|
||||
loading: isRevokingAll,
|
||||
onClick: handleRevokeAllSessions,
|
||||
children: t("Confirm")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
size: "sm",
|
||||
disabled: isRevokingAll,
|
||||
onClick: () => setShowConfirmRevokeAll(false),
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] }) : /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "outline",
|
||||
size: "sm",
|
||||
onClick: () => setShowConfirmRevokeAll(true),
|
||||
children: t("Revoke All Other Sessions")
|
||||
}
|
||||
))
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", className: "mb-4", children: t("These are devices where you're currently logged in. You can revoke access to end a session.") }),
|
||||
isLoading ? /* @__PURE__ */ jsx(Skeleton, { className: "h-[300px] w-full rounded-md" }) : /* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsxs(Table, { children: [
|
||||
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Session") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[150px]", children: t("IP Address") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[150px]", children: t("Location") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[150px]", children: t("Last used") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[80px]" })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsx(TableBody, { children: sessions.length === 0 ? /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: 5, className: "text-center py-6", children: /* @__PURE__ */ jsx(Typography, { variant: "secondary", children: t("No active sessions found") }) }) }) : sessions.map((session) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { children: session.isCurrentSession ? t("Current Session") : t("Other Session") }),
|
||||
session.isImpersonation && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "w-fit mt-1", children: t("Impersonation") }),
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", children: t("Signed in {time}", { time: new Date(session.createdAt).toLocaleDateString() }) })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: session.geoInfo?.ip || t("-") }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: session.geoInfo?.cityName || t("Unknown") }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { children: session.lastUsedAt ? fromNow(new Date(session.lastUsedAt)) : t("Never") }),
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", title: session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleString() : "", children: session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleDateString() : "" })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { align: "right", children: /* @__PURE__ */ jsx(
|
||||
ActionCell,
|
||||
{
|
||||
items: [
|
||||
{
|
||||
item: t("Revoke"),
|
||||
onClick: () => handleRevokeSession(session.id),
|
||||
danger: true,
|
||||
disabled: session.isCurrentSession,
|
||||
disabledTooltip: session.isCurrentSession ? t("You cannot revoke your current session") : void 0
|
||||
}
|
||||
]
|
||||
}
|
||||
) })
|
||||
] }, session.id)) })
|
||||
] }) })
|
||||
] }) });
|
||||
}
|
||||
export {
|
||||
ActiveSessionsPage
|
||||
};
|
||||
//# sourceMappingURL=active-sessions-page.js.map
|
||||
File diff suppressed because one or more lines are too long
45
package/@stackframe/react/dist/esm/components-page/account-settings/api-keys/api-keys-page.js
vendored
Normal file
45
package/@stackframe/react/dist/esm/components-page/account-settings/api-keys/api-keys-page.js
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// src/components-page/account-settings/api-keys/api-keys-page.tsx
|
||||
import { Button } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { CreateApiKeyDialog, ShowApiKeyDialog } from "../../../components/api-key-dialogs";
|
||||
import { ApiKeyTable } from "../../../components/api-key-table";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function ApiKeysPage() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const apiKeys = user.useApiKeys();
|
||||
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);
|
||||
const [returnedApiKey, setReturnedApiKey] = useState(null);
|
||||
const CreateDialog = CreateApiKeyDialog;
|
||||
const ShowDialog = ShowApiKeyDialog;
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") }),
|
||||
/* @__PURE__ */ jsx(ApiKeyTable, { apiKeys }),
|
||||
/* @__PURE__ */ jsx(
|
||||
CreateDialog,
|
||||
{
|
||||
open: isNewApiKeyDialogOpen,
|
||||
onOpenChange: setIsNewApiKeyDialogOpen,
|
||||
onKeyCreated: setReturnedApiKey,
|
||||
createApiKey: async (data) => {
|
||||
const apiKey = await user.createApiKey(data);
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
ShowDialog,
|
||||
{
|
||||
apiKey: returnedApiKey,
|
||||
onClose: () => setReturnedApiKey(null)
|
||||
}
|
||||
)
|
||||
] });
|
||||
}
|
||||
export {
|
||||
ApiKeysPage
|
||||
};
|
||||
//# sourceMappingURL=api-keys-page.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/api-keys/api-keys-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ApiKey, ApiKeyCreationOptions } from \"../../../lib/stack-app/api-keys\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\n\nexport function ApiKeysPage() {\n const { t } = useTranslation();\n\n const user = useUser({ or: 'redirect' });\n const apiKeys = user.useApiKeys();\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<ApiKey<\"user\", true> | null>(null);\n\n const CreateDialog = CreateApiKeyDialog<\"user\">;\n const ShowDialog = ShowApiKeyDialog<\"user\">;\n\n return (\n <PageLayout>\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n <ApiKeyTable apiKeys={apiKeys} />\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={async (data: ApiKeyCreationOptions<\"user\">) => {\n const apiKey = await user.createApiKey(data);\n return apiKey;\n }}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </PageLayout>\n );\n}\n"],"mappings":";AAIA,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,mBAAmB;AAC5B,SAAS,eAAe;AAExB,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAgBvB,SACE,KADF;AAbG,SAAS,cAAc;AAC5B,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,UAAU,KAAK,WAAW;AAEhC,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,SAAS,KAAK;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwC,IAAI;AAExF,QAAM,eAAe;AACrB,QAAM,aAAa;AAEnB,SACE,qBAAC,cACC;AAAA,wBAAC,UAAO,SAAS,MAAM,yBAAyB,IAAI,GACjD,YAAE,gBAAgB,GACrB;AAAA,IACA,oBAAC,eAAY,SAAkB;AAAA,IAC/B;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,cAAc;AAAA,QACd,cAAc,OAAO,SAAwC;AAC3D,gBAAM,SAAS,MAAM,KAAK,aAAa,IAAI;AAC3C,iBAAO;AAAA,QACT;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,MAAM,kBAAkB,IAAI;AAAA;AAAA,IACvC;AAAA,KACF;AAEJ;","names":[]}
|
||||
50
package/@stackframe/react/dist/esm/components-page/account-settings/editable-text.js
vendored
Normal file
50
package/@stackframe/react/dist/esm/components-page/account-settings/editable-text.js
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// src/components-page/account-settings/editable-text.tsx
|
||||
import { Button, Input, Typography } from "@stackframe/stack-ui";
|
||||
import { Edit } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "../../lib/translations";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function EditableText(props) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [editingValue, setEditingValue] = useState(props.value);
|
||||
const { t } = useTranslation();
|
||||
return /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: editing ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
value: editingValue,
|
||||
onChange: (e) => setEditingValue(e.target.value)
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
size: "sm",
|
||||
onClick: async () => {
|
||||
await props.onSave?.(editingValue);
|
||||
setEditing(false);
|
||||
},
|
||||
children: t("Save")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
size: "sm",
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
setEditingValue(props.value);
|
||||
setEditing(false);
|
||||
},
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(Typography, { children: props.value }),
|
||||
/* @__PURE__ */ jsx(Button, { onClick: () => setEditing(true), size: "icon", variant: "ghost", children: /* @__PURE__ */ jsx(Edit, { className: "w-4 h-4" }) })
|
||||
] }) });
|
||||
}
|
||||
export {
|
||||
EditableText
|
||||
};
|
||||
//# sourceMappingURL=editable-text.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/account-settings/editable-text.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/account-settings/editable-text.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components-page/account-settings/editable-text.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport { Edit } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useTranslation } from \"../../lib/translations\";\n\n\nexport function EditableText(props: { value: string, onSave?: (value: string) => void | Promise<void> }) {\n const [editing, setEditing] = useState(false);\n const [editingValue, setEditingValue] = useState(props.value);\n const { t } = useTranslation();\n\n return (\n <div className='flex items-center gap-2'>\n {editing ? (\n <>\n <Input\n value={editingValue}\n onChange={(e) => setEditingValue(e.target.value)}\n />\n <Button\n size='sm'\n onClick={async () => {\n await props.onSave?.(editingValue);\n setEditing(false);\n }}\n >\n {t(\"Save\")}\n </Button>\n <Button\n size='sm'\n variant='secondary'\n onClick={() => {\n setEditingValue(props.value);\n setEditing(false);\n }}>\n {t(\"Cancel\")}\n </Button>\n </>\n ) : (\n <>\n <Typography>{props.value}</Typography>\n <Button onClick={() => setEditing(true)} size='icon' variant='ghost'>\n <Edit className=\"w-4 h-4\" />\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";AAIA,SAAS,QAAQ,OAAO,kBAAkB;AAC1C,SAAS,YAAY;AACrB,SAAS,gBAAgB;AACzB,SAAS,sBAAsB;AAWvB,mBACE,KADF;AARD,SAAS,aAAa,OAA4E;AACvG,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,MAAM,KAAK;AAC5D,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,SACE,oBAAC,SAAI,WAAU,2BACZ,oBACC,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA;AAAA,IACjD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,YAAY;AACnB,gBAAM,MAAM,SAAS,YAAY;AACjC,qBAAW,KAAK;AAAA,QAClB;AAAA,QAEC,YAAE,MAAM;AAAA;AAAA,IACX;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,SAAS,MAAM;AACb,0BAAgB,MAAM,KAAK;AAC3B,qBAAW,KAAK;AAAA,QAClB;AAAA,QACC,YAAE,QAAQ;AAAA;AAAA,IACb;AAAA,KACF,IAEA,iCACE;AAAA,wBAAC,cAAY,gBAAM,OAAM;AAAA,IACzB,oBAAC,UAAO,SAAS,MAAM,WAAW,IAAI,GAAG,MAAK,QAAO,SAAQ,SAC3D,8BAAC,QAAK,WAAU,WAAU,GAC5B;AAAA,KACF,GAEJ;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { EmailsSection } from "./emails-section";
|
||||
import { MfaSection } from "./mfa-section";
|
||||
import { OtpSection } from "./otp-section";
|
||||
import { PasskeySection } from "./passkey-section";
|
||||
import { PasswordSection } from "./password-section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function EmailsAndAuthPage() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(EmailsSection, {}),
|
||||
/* @__PURE__ */ jsx(PasswordSection, {}),
|
||||
/* @__PURE__ */ jsx(PasskeySection, {}),
|
||||
/* @__PURE__ */ jsx(OtpSection, {}),
|
||||
/* @__PURE__ */ jsx(MfaSection, {})
|
||||
] });
|
||||
}
|
||||
export {
|
||||
EmailsAndAuthPage
|
||||
};
|
||||
//# sourceMappingURL=email-and-auth-page.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { EmailsSection } from \"./emails-section\";\nimport { MfaSection } from \"./mfa-section\";\nimport { OtpSection } from \"./otp-section\";\nimport { PasskeySection } from \"./passkey-section\";\nimport { PasswordSection } from \"./password-section\";\n\nexport function EmailsAndAuthPage() {\n return (\n <PageLayout>\n <EmailsSection/>\n <PasswordSection />\n <PasskeySection />\n <OtpSection />\n <MfaSection />\n </PageLayout>\n );\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAI5B,SACE,KADF;AAFG,SAAS,oBAAoB;AAClC,SACE,qBAAC,cACC;AAAA,wBAAC,iBAAa;AAAA,IACd,oBAAC,mBAAgB;AAAA,IACjB,oBAAC,kBAAe;AAAA,IAChB,oBAAC,cAAW;AAAA,IACZ,oBAAC,cAAW;AAAA,KACd;AAEJ;","names":[]}
|
||||
163
package/@stackframe/react/dist/esm/components-page/account-settings/email-and-auth/emails-section.js
vendored
Normal file
163
package/@stackframe/react/dist/esm/components-page/account-settings/email-and-auth/emails-section.js
vendored
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
// src/components-page/account-settings/email-and-auth/emails-section.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { KnownErrors } from "@stackframe/stack-shared/dist/known-errors";
|
||||
import { strictEmailSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { ActionCell, Badge, Button, Input, Table, TableBody, TableCell, TableRow, Typography } from "@stackframe/stack-ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { FormWarningText } from "../../../components/elements/form-warning";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function EmailsSection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const contactChannels = user.useContactChannels();
|
||||
const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);
|
||||
const [addingEmailLoading, setAddingEmailLoading] = useState(false);
|
||||
const [addedEmail, setAddedEmail] = useState(null);
|
||||
const isLastEmail = contactChannels.filter((x) => x.usedForAuth && x.type === "email").length === 1;
|
||||
useEffect(() => {
|
||||
if (addedEmail) {
|
||||
runAsynchronously(async () => {
|
||||
const cc = contactChannels.find((x) => x.value === addedEmail);
|
||||
if (cc && !cc.isVerified) {
|
||||
await cc.sendVerificationEmail();
|
||||
}
|
||||
setAddedEmail(null);
|
||||
});
|
||||
}
|
||||
}, [contactChannels, addedEmail]);
|
||||
const emailSchema = yupObject({
|
||||
email: strictEmailSchema(t("Please enter a valid email address")).notOneOf(contactChannels.map((x) => x.value), t("Email already exists")).defined().nonEmpty(t("Email is required"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, reset } = useForm({
|
||||
resolver: yupResolver(emailSchema)
|
||||
});
|
||||
const onSubmit = async (data) => {
|
||||
setAddingEmailLoading(true);
|
||||
try {
|
||||
await user.createContactChannel({ type: "email", value: data.email, usedForAuth: false });
|
||||
setAddedEmail(data.email);
|
||||
} finally {
|
||||
setAddingEmailLoading(false);
|
||||
}
|
||||
setAddingEmail(false);
|
||||
reset();
|
||||
};
|
||||
return /* @__PURE__ */ jsxs("div", { children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row justify-between mb-4 gap-4", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: t("Emails") }),
|
||||
addingEmail ? /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: (e) => {
|
||||
e.preventDefault();
|
||||
runAsynchronously(handleSubmit(onSubmit));
|
||||
},
|
||||
className: "flex flex-col",
|
||||
children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
...register("email"),
|
||||
placeholder: t("Enter email")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", loading: addingEmailLoading, children: t("Add") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
setAddingEmail(false);
|
||||
reset();
|
||||
},
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] }),
|
||||
errors.email && /* @__PURE__ */ jsx(FormWarningText, { text: errors.email.message })
|
||||
]
|
||||
}
|
||||
) : /* @__PURE__ */ jsx("div", { className: "flex md:justify-end", children: /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: () => setAddingEmail(true), children: t("Add an email") }) })
|
||||
] }),
|
||||
contactChannels.length > 0 ? /* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsx(Table, { children: /* @__PURE__ */ jsx(TableBody, { children: contactChannels.filter((x) => x.type === "email").sort((a, b) => {
|
||||
if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
|
||||
if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;
|
||||
return 0;
|
||||
}).map((x) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col md:flex-row gap-2 md:gap-4", children: [
|
||||
x.value,
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
x.isPrimary ? /* @__PURE__ */ jsx(Badge, { children: t("Primary") }) : null,
|
||||
!x.isVerified ? /* @__PURE__ */ jsx(Badge, { variant: "destructive", children: t("Unverified") }) : null,
|
||||
x.usedForAuth ? /* @__PURE__ */ jsx(Badge, { variant: "outline", children: t("Used for sign-in") }) : null
|
||||
] })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { className: "flex justify-end", children: /* @__PURE__ */ jsx(ActionCell, { items: [
|
||||
...!x.isVerified ? [{
|
||||
item: t("Send verification email"),
|
||||
onClick: async () => {
|
||||
await x.sendVerificationEmail();
|
||||
}
|
||||
}] : [],
|
||||
...!x.isPrimary && x.isVerified ? [{
|
||||
item: t("Set as primary"),
|
||||
onClick: async () => {
|
||||
await x.update({ isPrimary: true });
|
||||
}
|
||||
}] : !x.isPrimary ? [{
|
||||
item: t("Set as primary"),
|
||||
onClick: async () => {
|
||||
},
|
||||
disabled: true,
|
||||
disabledTooltip: t("Please verify your email first")
|
||||
}] : [],
|
||||
...!x.usedForAuth && x.isVerified ? [{
|
||||
item: t("Use for sign-in"),
|
||||
onClick: async () => {
|
||||
try {
|
||||
await x.update({ usedForAuth: true });
|
||||
} catch (e) {
|
||||
if (KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse.isInstance(e)) {
|
||||
alert(t("This email is already used for sign-in by another user."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}] : [],
|
||||
...x.usedForAuth && !isLastEmail ? [{
|
||||
item: t("Stop using for sign-in"),
|
||||
onClick: async () => {
|
||||
await x.update({ usedForAuth: false });
|
||||
}
|
||||
}] : x.usedForAuth ? [{
|
||||
item: t("Stop using for sign-in"),
|
||||
onClick: async () => {
|
||||
},
|
||||
disabled: true,
|
||||
disabledTooltip: t("You can not remove your last sign-in email")
|
||||
}] : [],
|
||||
...!isLastEmail || !x.usedForAuth ? [{
|
||||
item: t("Remove"),
|
||||
onClick: async () => {
|
||||
await x.delete();
|
||||
},
|
||||
danger: true
|
||||
}] : [{
|
||||
item: t("Remove"),
|
||||
onClick: async () => {
|
||||
},
|
||||
disabled: true,
|
||||
disabledTooltip: t("You can not remove your last sign-in email")
|
||||
}]
|
||||
] }) })
|
||||
] }, x.id)) }) }) }) : null
|
||||
] });
|
||||
}
|
||||
export {
|
||||
EmailsSection
|
||||
};
|
||||
//# sourceMappingURL=emails-section.js.map
|
||||
File diff suppressed because one or more lines are too long
111
package/@stackframe/react/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js
vendored
Normal file
111
package/@stackframe/react/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js
vendored
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// src/components-page/account-settings/email-and-auth/mfa-section.tsx
|
||||
import { createTOTPKeyURI, verifyTOTP } from "@oslojs/otp";
|
||||
import { useAsyncCallback } from "@stackframe/stack-shared/dist/hooks/use-async-callback";
|
||||
import { generateRandomValues } from "@stackframe/stack-shared/dist/utils/crypto";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Typography } from "@stackframe/stack-ui";
|
||||
import * as QRCode from "qrcode";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useStackApp, useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function MfaSection() {
|
||||
const { t } = useTranslation();
|
||||
const project = useStackApp().useProject();
|
||||
const user = useUser({ or: "throw" });
|
||||
const [generatedSecret, setGeneratedSecret] = useState(null);
|
||||
const [qrCodeUrl, setQrCodeUrl] = useState(null);
|
||||
const [mfaCode, setMfaCode] = useState("");
|
||||
const [isMaybeWrong, setIsMaybeWrong] = useState(false);
|
||||
const isEnabled = user.isMultiFactorRequired;
|
||||
const [handleSubmit, isLoading] = useAsyncCallback(async () => {
|
||||
await user.update({
|
||||
totpMultiFactorSecret: generatedSecret
|
||||
});
|
||||
setGeneratedSecret(null);
|
||||
setQrCodeUrl(null);
|
||||
setMfaCode("");
|
||||
}, [generatedSecret, user]);
|
||||
useEffect(() => {
|
||||
setIsMaybeWrong(false);
|
||||
runAsynchronouslyWithAlert(async () => {
|
||||
if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) {
|
||||
await handleSubmit();
|
||||
}
|
||||
setIsMaybeWrong(true);
|
||||
});
|
||||
}, [mfaCode, generatedSecret, handleSubmit]);
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Multi-factor authentication"),
|
||||
description: isEnabled ? t("Multi-factor authentication is currently enabled.") : t("Multi-factor authentication is currently disabled."),
|
||||
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
||||
!isEnabled && generatedSecret && /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(Typography, { children: t("Scan this QR code with your authenticator app:") }),
|
||||
/* @__PURE__ */ jsx("img", { width: 200, height: 200, src: qrCodeUrl ?? throwErr("TOTP QR code failed to generate"), alt: t("TOTP multi-factor authentication QR code") }),
|
||||
/* @__PURE__ */ jsx(Typography, { children: t("Then, enter your six-digit MFA code:") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
value: mfaCode,
|
||||
onChange: (e) => {
|
||||
setIsMaybeWrong(false);
|
||||
setMfaCode(e.target.value);
|
||||
},
|
||||
placeholder: "123456",
|
||||
maxLength: 6,
|
||||
disabled: isLoading
|
||||
}
|
||||
),
|
||||
isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Incorrect code. Please try again.") }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex", children: /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
setGeneratedSecret(null);
|
||||
setQrCodeUrl(null);
|
||||
setMfaCode("");
|
||||
},
|
||||
children: t("Cancel")
|
||||
}
|
||||
) })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: isEnabled ? /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: async () => {
|
||||
await user.update({
|
||||
totpMultiFactorSecret: null
|
||||
});
|
||||
},
|
||||
children: t("Disable MFA")
|
||||
}
|
||||
) : !generatedSecret && /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: async () => {
|
||||
const secret = generateRandomValues(new Uint8Array(20));
|
||||
setQrCodeUrl(await generateTotpQrCode(project, user, secret));
|
||||
setGeneratedSecret(secret);
|
||||
},
|
||||
children: t("Enable MFA")
|
||||
}
|
||||
) })
|
||||
] })
|
||||
}
|
||||
);
|
||||
}
|
||||
async function generateTotpQrCode(project, user, secret) {
|
||||
const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);
|
||||
return await QRCode.toDataURL(uri);
|
||||
}
|
||||
export {
|
||||
MfaSection
|
||||
};
|
||||
//# sourceMappingURL=mfa-section.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,64 @@
|
|||
// src/components-page/account-settings/email-and-auth/otp-section.tsx
|
||||
import { Button, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useStackApp, useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function OtpSection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "throw" });
|
||||
const project = useStackApp().useProject();
|
||||
const contactChannels = user.useContactChannels();
|
||||
const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;
|
||||
const [disabling, setDisabling] = useState(false);
|
||||
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
||||
if (!project.config.magicLinkEnabled) {
|
||||
return null;
|
||||
}
|
||||
const handleDisableOTP = async () => {
|
||||
await user.update({ otpAuthEnabled: false });
|
||||
setDisabling(false);
|
||||
};
|
||||
return /* @__PURE__ */ jsx(Section, { title: t("OTP sign-in"), description: user.otpAuthEnabled ? t("OTP/magic link sign-in is currently enabled.") : t("Enable sign-in via magic link or OTP sent to your sign-in emails."), children: /* @__PURE__ */ jsx("div", { className: "flex md:justify-end", children: hasValidEmail ? user.otpAuthEnabled ? !isLastAuth ? !disabling ? /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setDisabling(true),
|
||||
children: t("Disable OTP")
|
||||
}
|
||||
) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.") }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
onClick: handleDisableOTP,
|
||||
children: t("Disable")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setDisabling(false),
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] })
|
||||
] }) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method") }) : /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: async () => {
|
||||
await user.update({ otpAuthEnabled: true });
|
||||
},
|
||||
children: t("Enable OTP")
|
||||
}
|
||||
) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("To enable OTP sign-in, please add a verified sign-in email.") }) }) });
|
||||
}
|
||||
export {
|
||||
OtpSection
|
||||
};
|
||||
//# sourceMappingURL=otp-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/email-and-auth/otp-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function OtpSection() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n const project = useStackApp().useProject();\n const contactChannels = user.useContactChannels();\n const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;\n const [disabling, setDisabling] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.magicLinkEnabled) {\n return null;\n }\n\n const handleDisableOTP = async () => {\n await user.update({ otpAuthEnabled: false });\n setDisabling(false);\n };\n\n return (\n <Section title={t(\"OTP sign-in\")} description={user.otpAuthEnabled ? t(\"OTP/magic link sign-in is currently enabled.\") : t(\"Enable sign-in via magic link or OTP sent to your sign-in emails.\")}>\n <div className='flex md:justify-end'>\n {hasValidEmail ? (\n user.otpAuthEnabled ? (\n !isLastAuth ? (\n !disabling ? (\n <Button\n variant='secondary'\n onClick={() => setDisabling(true)}\n >\n {t(\"Disable OTP\")}\n </Button>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDisableOTP}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDisabling(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )\n ) : (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({ otpAuthEnabled: true });\n }}\n >\n {t(\"Enable OTP\")}\n </Button>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To enable OTP sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":";AAIA,SAAS,QAAQ,kBAAkB;AACnC,SAAS,gBAAgB;AACzB,SAAS,aAAa,eAAe;AACrC,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AA6BR,cAWE,YAXF;AA3BT,SAAS,aAAa;AAC3B,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,QAAQ,CAAC;AACpC,QAAM,UAAU,YAAY,EAAE,WAAW;AACzC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,aAAa,KAAK,kBAAkB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AACzG,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,KAAK;AAGhD,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAC3C,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,oBAAC,WAAQ,OAAO,EAAE,aAAa,GAAG,aAAa,KAAK,iBAAiB,EAAE,8CAA8C,IAAI,EAAE,mEAAmE,GAC5L,8BAAC,SAAI,WAAU,uBACZ,0BACC,KAAK,iBACH,CAAC,aACC,CAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM,aAAa,IAAI;AAAA,MAE/B,YAAE,aAAa;AAAA;AAAA,EAClB,IAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,wBAAC,cAAW,SAAQ,eACjB,YAAE,yGAAyG,GAC9G;AAAA,IACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UAER,YAAE,SAAS;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,aAAa,KAAK;AAAA,UAEhC,YAAE,QAAQ;AAAA;AAAA,MACb;AAAA,OACF;AAAA,KACF,IAGF,oBAAC,cAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,0FAA0F,GAAE,IAG9I;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,MAC5C;AAAA,MAEC,YAAE,YAAY;AAAA;AAAA,EACjB,IAGF,oBAAC,cAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,6DAA6D,GAAE,GAEnH,GACF;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// src/components-page/account-settings/email-and-auth/passkey-section.tsx
|
||||
import { Button, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useStackApp } from "../../..";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function PasskeySection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "throw" });
|
||||
const stackApp = useStackApp();
|
||||
const project = stackApp.useProject();
|
||||
const contactChannels = user.useContactChannels();
|
||||
const hasPasskey = user.passkeyAuthEnabled;
|
||||
const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;
|
||||
const [showConfirmationModal, setShowConfirmationModal] = useState(false);
|
||||
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
||||
if (!project.config.passkeyEnabled) {
|
||||
return null;
|
||||
}
|
||||
const handleDeletePasskey = async () => {
|
||||
await user.update({ passkeyAuthEnabled: false });
|
||||
setShowConfirmationModal(false);
|
||||
};
|
||||
const handleAddNewPasskey = async () => {
|
||||
await user.registerPasskey();
|
||||
};
|
||||
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(Section, { title: t("Passkey"), description: hasPasskey ? t("Passkey registered") : t("Register a passkey"), children: /* @__PURE__ */ jsxs("div", { className: "flex md:justify-end gap-2", children: [
|
||||
!hasValidEmail && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("To enable Passkey sign-in, please add a verified sign-in email.") }),
|
||||
hasValidEmail && hasPasskey && isLastAuth && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method") }),
|
||||
!hasPasskey && hasValidEmail && /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(Button, { onClick: handleAddNewPasskey, variant: "secondary", children: t("Add new passkey") }) }),
|
||||
hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setShowConfirmationModal(true),
|
||||
children: t("Delete Passkey")
|
||||
}
|
||||
),
|
||||
hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.") }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
onClick: handleDeletePasskey,
|
||||
children: t("Disable")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setShowConfirmationModal(false),
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] })
|
||||
] })
|
||||
] }) }) });
|
||||
}
|
||||
export {
|
||||
PasskeySection
|
||||
};
|
||||
//# sourceMappingURL=passkey-section.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,146 @@
|
|||
// src/components-page/account-settings/email-and-auth/password-section.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
|
||||
import { passwordSchema as schemaFieldsPasswordSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Label, PasswordInput, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { useStackApp } from "../../..";
|
||||
import { FormWarningText } from "../../../components/elements/form-warning";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function PasswordSection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "throw" });
|
||||
const contactChannels = user.useContactChannels();
|
||||
const [changingPassword, setChangingPassword] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const project = useStackApp().useProject();
|
||||
const passwordSchema = yupObject({
|
||||
oldPassword: user.hasPassword ? schemaFieldsPasswordSchema.defined().nonEmpty(t("Please enter your old password")) : yupString(),
|
||||
newPassword: schemaFieldsPasswordSchema.defined().nonEmpty(t("Please enter your password")).test({
|
||||
name: "is-valid-password",
|
||||
test: (value, ctx) => {
|
||||
const error = getPasswordError(value);
|
||||
if (error) {
|
||||
return ctx.createError({ message: error.message });
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}),
|
||||
newPasswordRepeat: yupString().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).defined().nonEmpty(t("Please repeat your password"))
|
||||
});
|
||||
const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({
|
||||
resolver: yupResolver(passwordSchema)
|
||||
});
|
||||
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.usedForAuth).length > 0;
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { oldPassword, newPassword } = data;
|
||||
const error = user.hasPassword ? await user.updatePassword({ oldPassword, newPassword }) : await user.setPassword({ password: newPassword });
|
||||
if (error) {
|
||||
setError("oldPassword", { type: "manual", message: t("Incorrect password") });
|
||||
} else {
|
||||
reset();
|
||||
setChangingPassword(false);
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const registerPassword = register("newPassword");
|
||||
const registerPasswordRepeat = register("newPasswordRepeat");
|
||||
if (!project.config.credentialEnabled) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Password"),
|
||||
description: user.hasPassword ? t("Update your password") : t("Set a password for your account"),
|
||||
children: /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: !changingPassword ? hasValidEmail ? /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setChangingPassword(true),
|
||||
children: user.hasPassword ? t("Update password") : t("Set password")
|
||||
}
|
||||
) : /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: t("To set a password, please add a sign-in email.") }) : /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
user.hasPassword && /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "old-password",
|
||||
type: "password",
|
||||
autoComplete: "current-password",
|
||||
...register("oldPassword")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.oldPassword?.message?.toString() })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("New password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "new-password",
|
||||
autoComplete: "new-password",
|
||||
...registerPassword,
|
||||
onChange: (e) => {
|
||||
clearErrors("newPassword");
|
||||
clearErrors("newPasswordRepeat");
|
||||
runAsynchronously(registerPassword.onChange(e));
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPassword?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat new password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "repeat-password",
|
||||
autoComplete: "new-password",
|
||||
...registerPasswordRepeat,
|
||||
onChange: (e) => {
|
||||
clearErrors("newPassword");
|
||||
clearErrors("newPasswordRepeat");
|
||||
runAsynchronously(registerPasswordRepeat.onChange(e));
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex gap-4", children: [
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: user.hasPassword ? t("Update Password") : t("Set Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
setChangingPassword(false);
|
||||
reset();
|
||||
},
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] })
|
||||
]
|
||||
}
|
||||
) })
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
PasswordSection
|
||||
};
|
||||
//# sourceMappingURL=password-section.js.map
|
||||
File diff suppressed because one or more lines are too long
9
package/@stackframe/react/dist/esm/components-page/account-settings/page-layout.js
vendored
Normal file
9
package/@stackframe/react/dist/esm/components-page/account-settings/page-layout.js
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// src/components-page/account-settings/page-layout.tsx
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function PageLayout(props) {
|
||||
return /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-6", children: props.children });
|
||||
}
|
||||
export {
|
||||
PageLayout
|
||||
};
|
||||
//# sourceMappingURL=page-layout.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/account-settings/page-layout.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/account-settings/page-layout.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components-page/account-settings/page-layout.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nexport function PageLayout(props: { children: React.ReactNode }) {\n return (\n <div className='flex flex-col gap-6'>\n {props.children}\n </div>\n );\n}\n"],"mappings":";AAMI;AAFG,SAAS,WAAW,OAAsC;AAC/D,SACE,oBAAC,SAAI,WAAU,uBACZ,gBAAM,UACT;AAEJ;","names":[]}
|
||||
50
package/@stackframe/react/dist/esm/components-page/account-settings/profile-page/profile-page.js
vendored
Normal file
50
package/@stackframe/react/dist/esm/components-page/account-settings/profile-page/profile-page.js
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
// src/components-page/account-settings/profile-page/profile-page.tsx
|
||||
import { ProfileImageEditor } from "../../../components/profile-image-editor";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { EditableText } from "../editable-text";
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { Section } from "../section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function ProfilePage() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("User name"),
|
||||
description: t("This is a display name and is not used for authentication"),
|
||||
children: /* @__PURE__ */ jsx(
|
||||
EditableText,
|
||||
{
|
||||
value: user.displayName || "",
|
||||
onSave: async (newDisplayName) => {
|
||||
await user.update({ displayName: newDisplayName });
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Profile image"),
|
||||
description: t("Upload your own image as your avatar"),
|
||||
children: /* @__PURE__ */ jsx(
|
||||
ProfileImageEditor,
|
||||
{
|
||||
user,
|
||||
onProfileImageUrlChange: async (profileImageUrl) => {
|
||||
await user.update({ profileImageUrl });
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
] });
|
||||
}
|
||||
export {
|
||||
ProfilePage
|
||||
};
|
||||
//# sourceMappingURL=profile-page.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/profile-page/profile-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { ProfileImageEditor } from \"../../../components/profile-image-editor\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { EditableText } from \"../editable-text\";\nimport { PageLayout } from \"../page-layout\";\nimport { Section } from \"../section\";\n\nexport function ProfilePage() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n\n return (\n <PageLayout>\n <Section\n title={t(\"User name\")}\n description={t(\"This is a display name and is not used for authentication\")}\n >\n <EditableText\n value={user.displayName || ''}\n onSave={async (newDisplayName) => {\n await user.update({ displayName: newDisplayName });\n }}/>\n </Section>\n\n <Section\n title={t(\"Profile image\")}\n description={t(\"Upload your own image as your avatar\")}\n >\n <ProfileImageEditor\n user={user}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await user.update({ profileImageUrl });\n }}\n />\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":";AAIA,SAAS,0BAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AAOpB,SAKI,KALJ;AALG,SAAS,cAAc;AAC5B,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AAEvC,SACE,qBAAC,cACC;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,WAAW;AAAA,QACpB,aAAa,EAAE,2DAA2D;AAAA,QAE1E;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,KAAK,eAAe;AAAA,YAC3B,QAAQ,OAAO,mBAAmB;AAChC,oBAAM,KAAK,OAAO,EAAE,aAAa,eAAe,CAAC;AAAA,YACnD;AAAA;AAAA,QAAE;AAAA;AAAA,IACN;AAAA,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,eAAe;AAAA,QACxB,aAAa,EAAE,sCAAsC;AAAA,QAErD;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,yBAAyB,OAAO,oBAAoB;AAClD,oBAAM,KAAK,OAAO,EAAE,gBAAgB,CAAC;AAAA,YACvC;AAAA;AAAA,QACF;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
||||
19
package/@stackframe/react/dist/esm/components-page/account-settings/section.js
vendored
Normal file
19
package/@stackframe/react/dist/esm/components-page/account-settings/section.js
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// src/components-page/account-settings/section.tsx
|
||||
import { Separator, Typography } from "@stackframe/stack-ui";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function Section(props) {
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(Separator, {}),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex flex-col sm:flex-row gap-2", children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "sm:flex-1 flex flex-col justify-center", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { className: "font-medium", children: props.title }),
|
||||
props.description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "footnote", children: props.description })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "sm:flex-1 sm:items-end flex flex-col gap-2 ", children: props.children })
|
||||
] })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
Section
|
||||
};
|
||||
//# sourceMappingURL=section.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/account-settings/section.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/account-settings/section.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components-page/account-settings/section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Separator, Typography } from \"@stackframe/stack-ui\";\n\nexport function Section(props: { title: string, description?: string, children: React.ReactNode }) {\n return (\n <>\n <Separator />\n <div className='flex flex-col sm:flex-row gap-2'>\n <div className='sm:flex-1 flex flex-col justify-center'>\n <Typography className='font-medium'>\n {props.title}\n </Typography>\n {props.description && <Typography variant='secondary' type='footnote'>\n {props.description}\n </Typography>}\n </div>\n <div className='sm:flex-1 sm:items-end flex flex-col gap-2 '>\n {props.children}\n </div>\n </div>\n </>\n );\n}\n"],"mappings":";AAIA,SAAS,WAAW,kBAAkB;AAIlC,mBACE,KAEE,YAHJ;AAFG,SAAS,QAAQ,OAA2E;AACjG,SACE,iCACE;AAAA,wBAAC,aAAU;AAAA,IACX,qBAAC,SAAI,WAAU,mCACb;AAAA,2BAAC,SAAI,WAAU,0CACb;AAAA,4BAAC,cAAW,WAAU,eACnB,gBAAM,OACT;AAAA,QACC,MAAM,eAAe,oBAAC,cAAW,SAAQ,aAAY,MAAK,YACxD,gBAAM,aACT;AAAA,SACF;AAAA,MACA,oBAAC,SAAI,WAAU,+CACZ,gBAAM,UACT;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// src/components-page/account-settings/settings/delete-account-section.tsx
|
||||
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useStackApp, useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function DeleteAccountSection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const app = useStackApp();
|
||||
const project = app.useProject();
|
||||
const [deleting, setDeleting] = useState(false);
|
||||
if (!project.config.clientUserDeletionEnabled) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Delete Account"),
|
||||
description: t("Permanently remove your account and all associated data"),
|
||||
children: /* @__PURE__ */ jsx("div", { className: "stack-scope flex flex-col items-stretch", children: /* @__PURE__ */ jsx(Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ jsxs(AccordionItem, { value: "item-1", children: [
|
||||
/* @__PURE__ */ jsx(AccordionTrigger, { children: t("Danger zone") }),
|
||||
/* @__PURE__ */ jsx(AccordionContent, { children: !deleting ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
onClick: () => setDeleting(true),
|
||||
children: t("Delete account")
|
||||
}
|
||||
) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to delete your account? This action is IRREVERSIBLE and will delete ALL associated data.") }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
onClick: async () => {
|
||||
await user.delete();
|
||||
await app.redirectToHome();
|
||||
},
|
||||
children: t("Delete Account")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setDeleting(false),
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] })
|
||||
] }) })
|
||||
] }) }) })
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
DeleteAccountSection
|
||||
};
|
||||
//# sourceMappingURL=delete-account-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/settings/delete-account-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function DeleteAccountSection() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const app = useStackApp();\n const project = app.useProject();\n const [deleting, setDeleting] = useState(false);\n if (!project.config.clientUserDeletionEnabled) {\n return null;\n }\n\n return (\n <Section\n title={t(\"Delete Account\")}\n description={t(\"Permanently remove your account and all associated data\")}\n >\n <div className='stack-scope flex flex-col items-stretch'>\n <Accordion type=\"single\" collapsible className=\"w-full\">\n <AccordionItem value=\"item-1\">\n <AccordionTrigger>{t(\"Danger zone\")}</AccordionTrigger>\n <AccordionContent>\n {!deleting ? (\n <div>\n <Button\n variant='destructive'\n onClick={() => setDeleting(true)}\n >\n {t(\"Delete account\")}\n </Button>\n </div>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to delete your account? This action is IRREVERSIBLE and will delete ALL associated data.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={async () => {\n await user.delete();\n await app.redirectToHome();\n }}\n >\n {t(\"Delete Account\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDeleting(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </AccordionContent>\n </AccordionItem>\n </Accordion>\n </div>\n </Section>\n );\n}\n"],"mappings":";AAIA,SAAS,WAAW,kBAAkB,eAAe,kBAAkB,QAAQ,kBAAkB;AACjG,SAAS,gBAAgB;AACzB,SAAS,aAAa,eAAe;AACrC,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AAoBZ,cAgBM,YAhBN;AAlBL,SAAS,uBAAuB;AACrC,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,MAAM,YAAY;AACxB,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAC9C,MAAI,CAAC,QAAQ,OAAO,2BAA2B;AAC7C,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,gBAAgB;AAAA,MACzB,aAAa,EAAE,yDAAyD;AAAA,MAExE,8BAAC,SAAI,WAAU,2CACb,8BAAC,aAAU,MAAK,UAAS,aAAW,MAAC,WAAU,UAC7C,+BAAC,iBAAc,OAAM,UACnB;AAAA,4BAAC,oBAAkB,YAAE,aAAa,GAAE;AAAA,QACpC,oBAAC,oBACE,WAAC,WACA,oBAAC,SACC;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,MAAM,YAAY,IAAI;AAAA,YAE9B,YAAE,gBAAgB;AAAA;AAAA,QACrB,GACF,IAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,8BAAC,cAAW,SAAQ,eACjB,YAAE,gHAAgH,GACrH;AAAA,UACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,SAAS,YAAY;AACnB,wBAAM,KAAK,OAAO;AAClB,wBAAM,IAAI,eAAe;AAAA,gBAC3B;AAAA,gBAEC,YAAE,gBAAgB;AAAA;AAAA,YACrB;AAAA,YACA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAQ;AAAA,gBACR,SAAS,MAAM,YAAY,KAAK;AAAA,gBAE/B,YAAE,QAAQ;AAAA;AAAA,YACb;AAAA,aACF;AAAA,WACF,GAEJ;AAAA,SACF,GACF,GACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
15
package/@stackframe/react/dist/esm/components-page/account-settings/settings/settings-page.js
vendored
Normal file
15
package/@stackframe/react/dist/esm/components-page/account-settings/settings/settings-page.js
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
// src/components-page/account-settings/settings/settings-page.tsx
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { DeleteAccountSection } from "./delete-account-section";
|
||||
import { SignOutSection } from "./sign-out-section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function SettingsPage() {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(DeleteAccountSection, {}),
|
||||
/* @__PURE__ */ jsx(SignOutSection, {})
|
||||
] });
|
||||
}
|
||||
export {
|
||||
SettingsPage
|
||||
};
|
||||
//# sourceMappingURL=settings-page.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/settings/settings-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { DeleteAccountSection } from \"./delete-account-section\";\nimport { SignOutSection } from \"./sign-out-section\";\n\n\nexport function SettingsPage() {\n return (\n <PageLayout>\n <DeleteAccountSection />\n <SignOutSection />\n </PageLayout>\n );\n}\n"],"mappings":";AAIA,SAAS,kBAAkB;AAC3B,SAAS,4BAA4B;AACrC,SAAS,sBAAsB;AAK3B,SACE,KADF;AAFG,SAAS,eAAe;AAC7B,SACE,qBAAC,cACC;AAAA,wBAAC,wBAAqB;AAAA,IACtB,oBAAC,kBAAe;AAAA,KAClB;AAEJ;","names":[]}
|
||||
29
package/@stackframe/react/dist/esm/components-page/account-settings/settings/sign-out-section.js
vendored
Normal file
29
package/@stackframe/react/dist/esm/components-page/account-settings/settings/sign-out-section.js
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
// src/components-page/account-settings/settings/sign-out-section.tsx
|
||||
import { Button } from "@stackframe/stack-ui";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function SignOutSection() {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "throw" });
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Sign out"),
|
||||
description: t("End your current session"),
|
||||
children: /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => user.signOut(),
|
||||
children: t("Sign out")
|
||||
}
|
||||
) })
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
SignOutSection
|
||||
};
|
||||
//# sourceMappingURL=sign-out-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/settings/sign-out-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function SignOutSection() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n\n return (\n <Section\n title={t(\"Sign out\")}\n description={t(\"End your current session\")}\n >\n <div>\n <Button\n variant='secondary'\n onClick={() => user.signOut()}\n >\n {t(\"Sign out\")}\n </Button>\n </div>\n </Section>\n );\n}\n"],"mappings":";AAIA,SAAS,cAAc;AACvB,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AAYhB;AAVD,SAAS,iBAAiB;AAC/B,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,QAAQ,CAAC;AAEpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,UAAU;AAAA,MACnB,aAAa,EAAE,0BAA0B;AAAA,MAEzC,8BAAC,SACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,KAAK,QAAQ;AAAA,UAE3B,YAAE,UAAU;AAAA;AAAA,MACf,GACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
54
package/@stackframe/react/dist/esm/components-page/account-settings/teams/leave-team-section.js
vendored
Normal file
54
package/@stackframe/react/dist/esm/components-page/account-settings/teams/leave-team-section.js
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// src/components-page/account-settings/teams/leave-team-section.tsx
|
||||
import { Button, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function LeaveTeamSection(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const [leaving, setLeaving] = useState(false);
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Leave Team"),
|
||||
description: t("leave this team and remove your team profile"),
|
||||
children: !leaving ? /* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setLeaving(true),
|
||||
children: t("Leave team")
|
||||
}
|
||||
) }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("Are you sure you want to leave the team?") }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "destructive",
|
||||
onClick: async () => {
|
||||
await user.leaveTeam(props.team);
|
||||
window.location.reload();
|
||||
},
|
||||
children: t("Leave")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "secondary",
|
||||
onClick: () => setLeaving(false),
|
||||
children: t("Cancel")
|
||||
}
|
||||
)
|
||||
] })
|
||||
] })
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
LeaveTeamSection
|
||||
};
|
||||
//# sourceMappingURL=leave-team-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/leave-team-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { Team } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function LeaveTeamSection(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const [leaving, setLeaving] = useState(false);\n\n return (\n <Section\n title={t(\"Leave Team\")}\n description={t(\"leave this team and remove your team profile\")}\n >\n {!leaving ? (\n <div>\n <Button\n variant='secondary'\n onClick={() => setLeaving(true)}\n >\n {t(\"Leave team\")}\n </Button>\n </div>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to leave the team?\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={async () => {\n await user.leaveTeam(props.team);\n window.location.reload();\n }}\n >\n {t(\"Leave\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setLeaving(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </Section>\n );\n}\n"],"mappings":";AAIA,SAAS,QAAQ,kBAAkB;AACnC,SAAS,gBAAgB;AAEzB,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AAcd,cAYA,YAZA;AAZH,SAAS,iBAAiB,OAAuB;AACtD,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,YAAY;AAAA,MACrB,aAAa,EAAE,8CAA8C;AAAA,MAE5D,WAAC,UACA,oBAAC,SACC;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,WAAW,IAAI;AAAA,UAE7B,YAAE,YAAY;AAAA;AAAA,MACjB,GACF,IAEA,qBAAC,SAAI,WAAU,uBACb;AAAA,4BAAC,cAAW,SAAQ,eACjB,YAAE,0CAA0C,GAC/C;AAAA,QACA,qBAAC,SAAI,WAAU,cACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,YAAY;AACnB,sBAAM,KAAK,UAAU,MAAM,IAAI;AAC/B,uBAAO,SAAS,OAAO;AAAA,cACzB;AAAA,cAEC,YAAE,OAAO;AAAA;AAAA,UACZ;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,SAAS,MAAM,WAAW,KAAK;AAAA,cAE9B,YAAE,QAAQ;AAAA;AAAA,UACb;AAAA,WACF;AAAA,SACF;AAAA;AAAA,EAEJ;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// src/components-page/account-settings/teams/team-api-keys-section.tsx
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { Button } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { CreateApiKeyDialog, ShowApiKeyDialog } from "../../../components/api-key-dialogs";
|
||||
import { ApiKeyTable } from "../../../components/api-key-table";
|
||||
import { useStackApp, useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamApiKeysSection(props) {
|
||||
const user = useUser({ or: "redirect" });
|
||||
const team = user.useTeam(props.team.id);
|
||||
const stackApp = useStackApp();
|
||||
const project = stackApp.useProject();
|
||||
if (!team) {
|
||||
throw new StackAssertionError("Team not found");
|
||||
}
|
||||
const teamApiKeysEnabled = project.config.allowTeamApiKeys;
|
||||
const manageApiKeysPermission = user.usePermission(props.team, "$manage_api_keys");
|
||||
if (!manageApiKeysPermission || !teamApiKeysEnabled) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(TeamApiKeysSectionInner, { team: props.team });
|
||||
}
|
||||
function TeamApiKeysSectionInner(props) {
|
||||
const { t } = useTranslation();
|
||||
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);
|
||||
const [returnedApiKey, setReturnedApiKey] = useState(null);
|
||||
const apiKeys = props.team.useApiKeys();
|
||||
const CreateDialog = CreateApiKeyDialog;
|
||||
const ShowDialog = ShowApiKeyDialog;
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("API Keys"),
|
||||
description: t("API keys grant programmatic access to your team."),
|
||||
children: /* @__PURE__ */ jsx(Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") })
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(ApiKeyTable, { apiKeys }),
|
||||
/* @__PURE__ */ jsx(
|
||||
CreateDialog,
|
||||
{
|
||||
open: isNewApiKeyDialogOpen,
|
||||
onOpenChange: setIsNewApiKeyDialogOpen,
|
||||
onKeyCreated: setReturnedApiKey,
|
||||
createApiKey: async (data) => {
|
||||
const apiKey = await props.team.createApiKey(data);
|
||||
return apiKey;
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(
|
||||
ShowDialog,
|
||||
{
|
||||
apiKey: returnedApiKey,
|
||||
onClose: () => setReturnedApiKey(null)
|
||||
}
|
||||
)
|
||||
] });
|
||||
}
|
||||
export {
|
||||
TeamApiKeysSection
|
||||
};
|
||||
//# sourceMappingURL=team-api-keys-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-api-keys-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { TeamApiKeyFirstView } from \"../../../lib/stack-app/api-keys\";\nimport { Team } from \"../../../lib/stack-app/teams\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\n\nexport function TeamApiKeysSection(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const team = user.useTeam(props.team.id);\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n\n if (!team) {\n throw new StackAssertionError(\"Team not found\");\n }\n\n const teamApiKeysEnabled = project.config.allowTeamApiKeys;\n const manageApiKeysPermission = user.usePermission(props.team, '$manage_api_keys');\n if (!manageApiKeysPermission || !teamApiKeysEnabled) {\n return null;\n }\n\n return <TeamApiKeysSectionInner team={props.team} />;\n}\n\nfunction TeamApiKeysSectionInner(props: { team: Team }) {\n const { t } = useTranslation();\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<TeamApiKeyFirstView | null>(null);\n\n const apiKeys = props.team.useApiKeys();\n\n const CreateDialog = CreateApiKeyDialog<\"team\">;\n const ShowDialog = ShowApiKeyDialog<\"team\">;\n\n return (\n <>\n <Section\n title={t(\"API Keys\")}\n description={t(\"API keys grant programmatic access to your team.\")}\n >\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n </Section>\n <ApiKeyTable apiKeys={apiKeys} />\n\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={async (data) => {\n const apiKey = await props.team.createApiKey(data);\n return apiKey;\n }}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </>\n );\n}\n"],"mappings":";AAIA,SAAS,2BAA2B;AACpC,SAAS,cAAc;AACvB,SAAS,gBAAgB;AACzB,SAAS,oBAAoB,wBAAwB;AACrD,SAAS,mBAAmB;AAC5B,SAAS,aAAa,eAAe;AAGrC,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AAmBf,SAeL,UAfK,KAeL,YAfK;AAhBF,SAAS,mBAAmB,OAAuB;AACxD,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,OAAO,KAAK,QAAQ,MAAM,KAAK,EAAE;AACvC,QAAM,WAAW,YAAY;AAC7B,QAAM,UAAU,SAAS,WAAW;AAEpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,oBAAoB,gBAAgB;AAAA,EAChD;AAEA,QAAM,qBAAqB,QAAQ,OAAO;AAC1C,QAAM,0BAA0B,KAAK,cAAc,MAAM,MAAM,kBAAkB;AACjF,MAAI,CAAC,2BAA2B,CAAC,oBAAoB;AACnD,WAAO;AAAA,EACT;AAEA,SAAO,oBAAC,2BAAwB,MAAM,MAAM,MAAM;AACpD;AAEA,SAAS,wBAAwB,OAAuB;AACtD,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,QAAM,CAAC,uBAAuB,wBAAwB,IAAI,SAAS,KAAK;AACxE,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAqC,IAAI;AAErF,QAAM,UAAU,MAAM,KAAK,WAAW;AAEtC,QAAM,eAAe;AACrB,QAAM,aAAa;AAEnB,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,UAAU;AAAA,QACnB,aAAa,EAAE,kDAAkD;AAAA,QAEjE,8BAAC,UAAO,SAAS,MAAM,yBAAyB,IAAI,GACjD,YAAE,gBAAgB,GACrB;AAAA;AAAA,IACF;AAAA,IACA,oBAAC,eAAY,SAAkB;AAAA,IAE/B;AAAA,MAAC;AAAA;AAAA,QACC,MAAM;AAAA,QACN,cAAc;AAAA,QACd,cAAc;AAAA,QACd,cAAc,OAAO,SAAS;AAC5B,gBAAM,SAAS,MAAM,MAAM,KAAK,aAAa,IAAI;AACjD,iBAAO;AAAA,QACT;AAAA;AAAA,IACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ;AAAA,QACR,SAAS,MAAM,kBAAkB,IAAI;AAAA;AAAA,IACvC;AAAA,KACF;AAEJ;","names":[]}
|
||||
67
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-creation-page.js
vendored
Normal file
67
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-creation-page.js
vendored
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// src/components-page/account-settings/teams/team-creation-page.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { FormWarningText } from "../../../components/elements/form-warning";
|
||||
import { MessageCard } from "../../../components/message-cards/message-card";
|
||||
import { useStackApp, useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { Section } from "../section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamCreationPage() {
|
||||
const { t } = useTranslation();
|
||||
const teamCreationSchema = yupObject({
|
||||
displayName: yupString().defined().nonEmpty(t("Please enter a team name"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
resolver: yupResolver(teamCreationSchema)
|
||||
});
|
||||
const app = useStackApp();
|
||||
const project = app.useProject();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const navigate = app.useNavigate();
|
||||
const [loading, setLoading] = useState(false);
|
||||
if (!project.config.clientTeamCreationEnabled) {
|
||||
return /* @__PURE__ */ jsx(MessageCard, { title: t("Team creation is not enabled") });
|
||||
}
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
let team;
|
||||
try {
|
||||
team = await user.createTeam({ displayName: data.displayName });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
navigate(`#team-${team.id}`);
|
||||
};
|
||||
return /* @__PURE__ */ jsx(PageLayout, { children: /* @__PURE__ */ jsx(Section, { title: t("Create a Team"), description: t("Enter a display name for your new team"), children: /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
className: "flex gap-2 flex-col sm:flex-row",
|
||||
children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "displayName",
|
||||
type: "text",
|
||||
...register("displayName")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.displayName?.message?.toString() })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Create") })
|
||||
]
|
||||
}
|
||||
) }) });
|
||||
}
|
||||
export {
|
||||
TeamCreationPage
|
||||
};
|
||||
//# sourceMappingURL=team-creation-page.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-creation-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { MessageCard } from \"../../../components/message-cards/message-card\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { Section } from \"../section\";\n\nexport function TeamCreationPage() {\n const { t } = useTranslation();\n\n const teamCreationSchema = yupObject({\n displayName: yupString().defined().nonEmpty(t(\"Please enter a team name\")),\n });\n\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(teamCreationSchema)\n });\n const app = useStackApp();\n const project = app.useProject();\n const user = useUser({ or: 'redirect' });\n const navigate = app.useNavigate();\n const [loading, setLoading] = useState(false);\n\n if (!project.config.clientTeamCreationEnabled) {\n return <MessageCard title={t(\"Team creation is not enabled\")} />;\n }\n\n const onSubmit = async (data: yup.InferType<typeof teamCreationSchema>) => {\n setLoading(true);\n\n let team;\n try {\n team = await user.createTeam({ displayName: data.displayName });\n } finally {\n setLoading(false);\n }\n\n navigate(`#team-${team.id}`);\n };\n\n return (\n <PageLayout>\n <Section title={t(\"Create a Team\")} description={t(\"Enter a display name for your new team\")}>\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n className='flex gap-2 flex-col sm:flex-row'\n >\n <div className='flex flex-col flex-1'>\n <Input\n id=\"displayName\"\n type=\"text\"\n {...register(\"displayName\")}\n />\n <FormWarningText text={errors.displayName?.message?.toString()} />\n </div>\n <Button type=\"submit\" loading={loading}>{t(\"Create\")}</Button>\n </form>\n </Section>\n </PageLayout>\n );\n}\n"],"mappings":";AAIA,SAAS,mBAAmB;AAC5B,SAAS,WAAW,iBAAiB;AACrC,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,aAAa;AAC9B,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAExB,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,aAAa,eAAe;AACrC,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AAmBb,cAwBD,YAxBC;AAjBJ,SAAS,mBAAmB;AACjC,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,QAAM,qBAAqB,UAAU;AAAA,IACnC,aAAa,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,0BAA0B,CAAC;AAAA,EAC3E,CAAC;AAED,QAAM,EAAE,UAAU,cAAc,WAAW,EAAE,OAAO,EAAE,IAAI,QAAQ;AAAA,IAChE,UAAU,YAAY,kBAAkB;AAAA,EAC1C,CAAC;AACD,QAAM,MAAM,YAAY;AACxB,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,WAAW,IAAI,YAAY;AACjC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,MAAI,CAAC,QAAQ,OAAO,2BAA2B;AAC7C,WAAO,oBAAC,eAAY,OAAO,EAAE,8BAA8B,GAAG;AAAA,EAChE;AAEA,QAAM,WAAW,OAAO,SAAmD;AACzE,eAAW,IAAI;AAEf,QAAI;AACJ,QAAI;AACF,aAAO,MAAM,KAAK,WAAW,EAAE,aAAa,KAAK,YAAY,CAAC;AAAA,IAChE,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAEA,aAAS,SAAS,KAAK,EAAE,EAAE;AAAA,EAC7B;AAEA,SACE,oBAAC,cACC,8BAAC,WAAQ,OAAO,EAAE,eAAe,GAAG,aAAa,EAAE,wCAAwC,GACzF;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,OAAK,2BAA2B,aAAa,QAAQ,EAAE,CAAC,CAAC;AAAA,MACnE,YAAU;AAAA,MACV,WAAU;AAAA,MAEV;AAAA,6BAAC,SAAI,WAAU,wBACb;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACH,MAAK;AAAA,cACJ,GAAG,SAAS,aAAa;AAAA;AAAA,UAC5B;AAAA,UACA,oBAAC,mBAAgB,MAAM,OAAO,aAAa,SAAS,SAAS,GAAG;AAAA,WAClE;AAAA,QACA,oBAAC,UAAO,MAAK,UAAS,SAAmB,YAAE,QAAQ,GAAE;AAAA;AAAA;AAAA,EACvD,GACF,GACF;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// src/components-page/account-settings/teams/team-display-name-section.tsx
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { EditableText } from "../editable-text";
|
||||
import { Section } from "../section";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function TeamDisplayNameSection(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
||||
if (!updateTeamPermission) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Team display name"),
|
||||
description: t("Change the display name of your team"),
|
||||
children: /* @__PURE__ */ jsx(
|
||||
EditableText,
|
||||
{
|
||||
value: props.team.displayName,
|
||||
onSave: async (newDisplayName) => await props.team.update({ displayName: newDisplayName })
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
TeamDisplayNameSection
|
||||
};
|
||||
//# sourceMappingURL=team-display-name-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-display-name-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Team } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { EditableText } from \"../editable-text\";\nimport { Section } from \"../section\";\n\nexport function TeamDisplayNameSection(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const updateTeamPermission = user.usePermission(props.team, '$update_team');\n\n if (!updateTeamPermission) {\n return null;\n }\n\n return (\n <Section\n title={t(\"Team display name\")}\n description={t(\"Change the display name of your team\")}\n >\n <EditableText\n value={props.team.displayName}\n onSave={async (newDisplayName) => await props.team.update({ displayName: newDisplayName })}\n />\n </Section>\n );\n}\n"],"mappings":";AAKA,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AAgBlB;AAdC,SAAS,uBAAuB,OAAuB;AAC5D,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,uBAAuB,KAAK,cAAc,MAAM,MAAM,cAAc;AAE1E,MAAI,CAAC,sBAAsB;AACzB,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,mBAAmB;AAAA,MAC5B,aAAa,EAAE,sCAAsC;AAAA,MAErD;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,MAAM,KAAK;AAAA,UAClB,QAAQ,OAAO,mBAAmB,MAAM,MAAM,KAAK,OAAO,EAAE,aAAa,eAAe,CAAC;AAAA;AAAA,MAC3F;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
// src/components-page/account-settings/teams/team-member-invitation-section.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { strictEmailSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
||||
import { Trash } from "lucide-react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { FormWarningText } from "../../../components/elements/form-warning";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamMemberInvitationSection(props) {
|
||||
const user = useUser({ or: "redirect" });
|
||||
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
||||
if (!inviteMemberPermission) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(MemberInvitationSectionInner, { team: props.team });
|
||||
}
|
||||
function MemberInvitationsSectionInvitationsList(props) {
|
||||
const user = useUser({ or: "redirect" });
|
||||
const { t } = useTranslation();
|
||||
const invitationsToShow = props.team.useInvitations();
|
||||
const removeMemberPermission = user.usePermission(props.team, "$remove_members");
|
||||
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs(Table, { className: "mt-6", children: [
|
||||
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Outstanding invitations") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[60px]", children: t("Expires") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[36px] max-w-[36px]" })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsxs(TableBody, { children: [
|
||||
invitationsToShow.map((invitation, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { children: invitation.recipientEmail }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Typography, { variant: "secondary", children: invitation.expiresAt.toLocaleString() }) }),
|
||||
/* @__PURE__ */ jsx(TableCell, { align: "right", className: "max-w-[36px]", children: removeMemberPermission && /* @__PURE__ */ jsx(Button, { onClick: async () => await invitation.revoke(), size: "icon", variant: "ghost", children: /* @__PURE__ */ jsx(Trash, { className: "w-4 h-4" }) }) })
|
||||
] }, invitation.id)),
|
||||
invitationsToShow.length === 0 && /* @__PURE__ */ jsx(TableRow, { children: /* @__PURE__ */ jsx(TableCell, { colSpan: 3, children: /* @__PURE__ */ jsx(Typography, { variant: "secondary", children: t("No outstanding invitations") }) }) })
|
||||
] })
|
||||
] }) });
|
||||
}
|
||||
function MemberInvitationSectionInner(props) {
|
||||
const user = useUser({ or: "redirect" });
|
||||
const { t } = useTranslation();
|
||||
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
||||
const invitationSchema = yupObject({
|
||||
email: strictEmailSchema(t("Please enter a valid email address")).defined().nonEmpty(t("Please enter an email address"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, watch } = useForm({
|
||||
resolver: yupResolver(invitationSchema)
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [invitedEmail, setInvitedEmail] = useState(null);
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await props.team.inviteUser({ email: data.email });
|
||||
setInvitedEmail(data.email);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
setInvitedEmail(null);
|
||||
}, [watch("email")]);
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Invite member"),
|
||||
description: t("Invite a user to your team through email"),
|
||||
children: /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
className: "w-full",
|
||||
children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4 sm:flex-row w-full", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
placeholder: t("Email"),
|
||||
...register("email")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Invite User") })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
||||
invitedEmail && /* @__PURE__ */ jsxs(Typography, { type: "label", variant: "secondary", children: [
|
||||
"Invited ",
|
||||
invitedEmail
|
||||
] })
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
),
|
||||
readMemberPermission && /* @__PURE__ */ jsx(MemberInvitationsSectionInvitationsList, { team: props.team })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
TeamMemberInvitationSection
|
||||
};
|
||||
//# sourceMappingURL=team-member-invitation-section.js.map
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,39 @@
|
|||
// src/components-page/account-settings/teams/team-member-list-section.tsx
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from "@stackframe/stack-ui";
|
||||
import { UserAvatar } from "../../../components/elements/user-avatar";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamMemberListSection(props) {
|
||||
const user = useUser({ or: "redirect" });
|
||||
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
||||
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
||||
if (!readMemberPermission && !inviteMemberPermission) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(MemberListSectionInner, { team: props.team });
|
||||
}
|
||||
function MemberListSectionInner(props) {
|
||||
const { t } = useTranslation();
|
||||
const users = props.team.useUsers();
|
||||
return /* @__PURE__ */ jsxs("div", { children: [
|
||||
/* @__PURE__ */ jsx(Typography, { className: "font-medium mb-2", children: t("Members") }),
|
||||
/* @__PURE__ */ jsx("div", { className: "border rounded-md", children: /* @__PURE__ */ jsxs(Table, { children: [
|
||||
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[100px]", children: t("User") }),
|
||||
/* @__PURE__ */ jsx(TableHead, { className: "w-[200px]", children: t("Name") })
|
||||
] }) }),
|
||||
/* @__PURE__ */ jsx(TableBody, { children: users.map(({ id, teamProfile }, i) => /* @__PURE__ */ jsxs(TableRow, { children: [
|
||||
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(UserAvatar, { user: teamProfile }) }),
|
||||
/* @__PURE__ */ jsxs(TableCell, { children: [
|
||||
teamProfile.displayName && /* @__PURE__ */ jsx(Typography, { children: teamProfile.displayName }),
|
||||
!teamProfile.displayName && /* @__PURE__ */ jsx(Typography, { className: "text-muted-foreground italic", children: t("No display name set") })
|
||||
] })
|
||||
] }, id)) })
|
||||
] }) })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
TeamMemberListSection
|
||||
};
|
||||
//# sourceMappingURL=team-member-list-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-member-list-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { Team } from \"../../..\";\nimport { UserAvatar } from \"../../../components/elements/user-avatar\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\n\nexport function TeamMemberListSection(props: { team: Team }) {\n const user = useUser({ or: 'redirect' });\n const readMemberPermission = user.usePermission(props.team, '$read_members');\n const inviteMemberPermission = user.usePermission(props.team, '$invite_members');\n\n if (!readMemberPermission && !inviteMemberPermission) {\n return null;\n }\n\n return <MemberListSectionInner team={props.team} />;\n}\n\nfunction MemberListSectionInner(props: { team: Team }) {\n const { t } = useTranslation();\n const users = props.team.useUsers();\n\n return (\n <div>\n <Typography className='font-medium mb-2'>{t(\"Members\")}</Typography>\n <div className='border rounded-md'>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[100px]\">{t(\"User\")}</TableHead>\n <TableHead className=\"w-[200px]\">{t(\"Name\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {users.map(({ id, teamProfile }, i) => (\n <TableRow key={id}>\n <TableCell>\n <UserAvatar user={teamProfile} />\n </TableCell>\n <TableCell>\n {teamProfile.displayName && (\n <Typography>{teamProfile.displayName}</Typography>\n )}\n {!teamProfile.displayName && (\n <Typography className=\"text-muted-foreground italic\">{t(\"No display name set\")}</Typography>\n )}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n </div>\n );\n}\n"],"mappings":";AAIA,SAAS,OAAO,WAAW,WAAW,WAAW,aAAa,UAAU,kBAAkB;AAE1F,SAAS,kBAAkB;AAC3B,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAWtB,cAaG,YAbH;AATF,SAAS,sBAAsB,OAAuB;AAC3D,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,uBAAuB,KAAK,cAAc,MAAM,MAAM,eAAe;AAC3E,QAAM,yBAAyB,KAAK,cAAc,MAAM,MAAM,iBAAiB;AAE/E,MAAI,CAAC,wBAAwB,CAAC,wBAAwB;AACpD,WAAO;AAAA,EACT;AAEA,SAAO,oBAAC,0BAAuB,MAAM,MAAM,MAAM;AACnD;AAEA,SAAS,uBAAuB,OAAuB;AACrD,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,QAAQ,MAAM,KAAK,SAAS;AAElC,SACE,qBAAC,SACC;AAAA,wBAAC,cAAW,WAAU,oBAAoB,YAAE,SAAS,GAAE;AAAA,IACvD,oBAAC,SAAI,WAAU,qBACb,+BAAC,SACC;AAAA,0BAAC,eACC,+BAAC,YACC;AAAA,4BAAC,aAAU,WAAU,aAAa,YAAE,MAAM,GAAE;AAAA,QAC5C,oBAAC,aAAU,WAAU,aAAa,YAAE,MAAM,GAAE;AAAA,SAC9C,GACF;AAAA,MACA,oBAAC,aACE,gBAAM,IAAI,CAAC,EAAE,IAAI,YAAY,GAAG,MAC/B,qBAAC,YACC;AAAA,4BAAC,aACC,8BAAC,cAAW,MAAM,aAAa,GACjC;AAAA,QACA,qBAAC,aACE;AAAA,sBAAY,eACX,oBAAC,cAAY,sBAAY,aAAY;AAAA,UAEtC,CAAC,YAAY,eACZ,oBAAC,cAAW,WAAU,gCAAgC,YAAE,qBAAqB,GAAE;AAAA,WAEnF;AAAA,WAXa,EAYf,CACD,GACH;AAAA,OACF,GACF;AAAA,KACF;AAEJ;","names":[]}
|
||||
25
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-page.js
vendored
Normal file
25
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-page.js
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// src/components-page/account-settings/teams/team-page.tsx
|
||||
import { PageLayout } from "../page-layout";
|
||||
import { LeaveTeamSection } from "./leave-team-section";
|
||||
import { TeamApiKeysSection } from "./team-api-keys-section";
|
||||
import { TeamDisplayNameSection } from "./team-display-name-section";
|
||||
import { TeamMemberInvitationSection } from "./team-member-invitation-section";
|
||||
import { TeamMemberListSection } from "./team-member-list-section";
|
||||
import { TeamProfileImageSection } from "./team-profile-image-section";
|
||||
import { TeamUserProfileSection } from "./team-profile-user-section";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamPage(props) {
|
||||
return /* @__PURE__ */ jsxs(PageLayout, { children: [
|
||||
/* @__PURE__ */ jsx(TeamUserProfileSection, { team: props.team }, `user-profile-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(TeamProfileImageSection, { team: props.team }, `profile-image-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(TeamDisplayNameSection, { team: props.team }, `display-name-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(TeamMemberListSection, { team: props.team }, `member-list-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(TeamMemberInvitationSection, { team: props.team }, `member-invitation-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(TeamApiKeysSection, { team: props.team }, `api-keys-${props.team.id}`),
|
||||
/* @__PURE__ */ jsx(LeaveTeamSection, { team: props.team }, `leave-team-${props.team.id}`)
|
||||
] });
|
||||
}
|
||||
export {
|
||||
TeamPage
|
||||
};
|
||||
//# sourceMappingURL=team-page.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-page.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/account-settings/teams/team-page.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Team } from \"../../..\";\nimport { PageLayout } from \"../page-layout\";\nimport { LeaveTeamSection } from \"./leave-team-section\";\nimport { TeamApiKeysSection } from \"./team-api-keys-section\";\nimport { TeamDisplayNameSection } from \"./team-display-name-section\";\nimport { TeamMemberInvitationSection } from \"./team-member-invitation-section\";\nimport { TeamMemberListSection } from \"./team-member-list-section\";\nimport { TeamProfileImageSection } from \"./team-profile-image-section\";\nimport { TeamUserProfileSection } from \"./team-profile-user-section\";\n\n\nexport function TeamPage(props: { team: Team }) {\n return (\n <PageLayout>\n <TeamUserProfileSection key={`user-profile-${props.team.id}`} team={props.team} />\n <TeamProfileImageSection key={`profile-image-${props.team.id}`} team={props.team} />\n <TeamDisplayNameSection key={`display-name-${props.team.id}`} team={props.team} />\n <TeamMemberListSection key={`member-list-${props.team.id}`} team={props.team} />\n <TeamMemberInvitationSection key={`member-invitation-${props.team.id}`} team={props.team} />\n <TeamApiKeysSection key={`api-keys-${props.team.id}`} team={props.team} />\n <LeaveTeamSection key={`leave-team-${props.team.id}`} team={props.team} />\n </PageLayout>\n );\n}\n"],"mappings":";AAKA,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,8BAA8B;AACvC,SAAS,mCAAmC;AAC5C,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AAKnC,SACE,KADF;AAFG,SAAS,SAAS,OAAuB;AAC9C,SACE,qBAAC,cACC;AAAA,wBAAC,0BAA6D,MAAM,MAAM,QAA7C,gBAAgB,MAAM,KAAK,EAAE,EAAsB;AAAA,IAChF,oBAAC,2BAA+D,MAAM,MAAM,QAA9C,iBAAiB,MAAM,KAAK,EAAE,EAAsB;AAAA,IAClF,oBAAC,0BAA6D,MAAM,MAAM,QAA7C,gBAAgB,MAAM,KAAK,EAAE,EAAsB;AAAA,IAChF,oBAAC,yBAA2D,MAAM,MAAM,QAA5C,eAAe,MAAM,KAAK,EAAE,EAAsB;AAAA,IAC9E,oBAAC,+BAAuE,MAAM,MAAM,QAAlD,qBAAqB,MAAM,KAAK,EAAE,EAAsB;AAAA,IAC1F,oBAAC,sBAAqD,MAAM,MAAM,QAAzC,YAAY,MAAM,KAAK,EAAE,EAAsB;AAAA,IACxE,oBAAC,oBAAqD,MAAM,MAAM,QAA3C,cAAc,MAAM,KAAK,EAAE,EAAsB;AAAA,KAC1E;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
// src/components-page/account-settings/teams/team-profile-image-section.tsx
|
||||
import { ProfileImageEditor } from "../../../components/profile-image-editor";
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { Section } from "../section";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function TeamProfileImageSection(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
||||
if (!updateTeamPermission) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Team profile image"),
|
||||
description: t("Upload an image for your team"),
|
||||
children: /* @__PURE__ */ jsx(
|
||||
ProfileImageEditor,
|
||||
{
|
||||
user: props.team,
|
||||
onProfileImageUrlChange: async (profileImageUrl) => {
|
||||
await props.team.update({ profileImageUrl });
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
TeamProfileImageSection
|
||||
};
|
||||
//# sourceMappingURL=team-profile-image-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-profile-image-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Team } from \"../../..\";\nimport { ProfileImageEditor } from \"../../../components/profile-image-editor\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function TeamProfileImageSection(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const updateTeamPermission = user.usePermission(props.team, '$update_team');\n\n if (!updateTeamPermission) {\n return null;\n }\n\n return (\n <Section\n title={t(\"Team profile image\")}\n description={t(\"Upload an image for your team\")}\n >\n <ProfileImageEditor\n user={props.team}\n onProfileImageUrlChange={async (profileImageUrl) => {\n await props.team.update({ profileImageUrl });\n }}\n />\n </Section>\n );\n}\n"],"mappings":";AAKA,SAAS,0BAA0B;AACnC,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,eAAe;AAgBlB;AAdC,SAAS,wBAAwB,OAAuB;AAC7D,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,uBAAuB,KAAK,cAAc,MAAM,MAAM,cAAc;AAE1E,MAAI,CAAC,sBAAsB;AACzB,WAAO;AAAA,EACT;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,oBAAoB;AAAA,MAC7B,aAAa,EAAE,+BAA+B;AAAA,MAE9C;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,MAAM;AAAA,UACZ,yBAAyB,OAAO,oBAAoB;AAClD,kBAAM,MAAM,KAAK,OAAO,EAAE,gBAAgB,CAAC;AAAA,UAC7C;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// src/components-page/account-settings/teams/team-profile-user-section.tsx
|
||||
import { useUser } from "../../../lib/hooks";
|
||||
import { useTranslation } from "../../../lib/translations";
|
||||
import { EditableText } from "../editable-text";
|
||||
import { Section } from "../section";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function TeamUserProfileSection(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const profile = user.useTeamProfile(props.team);
|
||||
return /* @__PURE__ */ jsx(
|
||||
Section,
|
||||
{
|
||||
title: t("Team user name"),
|
||||
description: t("Overwrite your user display name in this team"),
|
||||
children: /* @__PURE__ */ jsx(
|
||||
EditableText,
|
||||
{
|
||||
value: profile.displayName || "",
|
||||
onSave: async (newDisplayName) => {
|
||||
await profile.update({ displayName: newDisplayName });
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
TeamUserProfileSection
|
||||
};
|
||||
//# sourceMappingURL=team-profile-user-section.js.map
|
||||
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../../src/components-page/account-settings/teams/team-profile-user-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Team } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { EditableText } from \"../editable-text\";\nimport { Section } from \"../section\";\n\nexport function TeamUserProfileSection(props: { team: Team }) {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const profile = user.useTeamProfile(props.team);\n\n return (\n <Section\n title={t(\"Team user name\")}\n description={t(\"Overwrite your user display name in this team\")}\n >\n <EditableText\n value={profile.displayName || ''}\n onSave={async (newDisplayName) => {\n await profile.update({ displayName: newDisplayName });\n }}\n />\n </Section>\n );\n}\n"],"mappings":";AAKA,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AAYlB;AAVC,SAAS,uBAAuB,OAAuB;AAC5D,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,UAAU,KAAK,eAAe,MAAM,IAAI;AAE9C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,gBAAgB;AAAA,MACzB,aAAa,EAAE,+CAA+C;AAAA,MAE9D;AAAA,QAAC;AAAA;AAAA,UACC,OAAO,QAAQ,eAAe;AAAA,UAC9B,QAAQ,OAAO,mBAAmB;AAChC,kBAAM,QAAQ,OAAO,EAAE,aAAa,eAAe,CAAC;AAAA,UACtD;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
99
package/@stackframe/react/dist/esm/components-page/auth-page.js
vendored
Normal file
99
package/@stackframe/react/dist/esm/components-page/auth-page.js
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/auth-page.tsx
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Skeleton, Tabs, TabsContent, TabsList, TabsTrigger, Typography, cn } from "@stackframe/stack-ui";
|
||||
import { Suspense, useEffect } from "react";
|
||||
import { useStackApp, useUser } from "..";
|
||||
import { CredentialSignIn } from "../components/credential-sign-in";
|
||||
import { CredentialSignUp } from "../components/credential-sign-up";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { SeparatorWithText } from "../components/elements/separator-with-text";
|
||||
import { StyledLink } from "../components/link";
|
||||
import { MagicLinkSignIn } from "../components/magic-link-sign-in";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { OAuthButtonGroup } from "../components/oauth-button-group";
|
||||
import { PasskeyButton } from "../components/passkey-button";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function AuthPage(props) {
|
||||
return /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Fallback, { ...props }), children: /* @__PURE__ */ jsx(Inner, { ...props }) });
|
||||
}
|
||||
function Fallback(props) {
|
||||
const { t } = useTranslation();
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("div", { className: "stack-scope flex flex-col items-stretch", style: { maxWidth: "380px", flexBasis: "380px", padding: props.fullPage ? "1rem" : 0 }, children: /* @__PURE__ */ jsxs("div", { className: "text-center mb-6 flex flex-col", children: [
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-2/3 self-center" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-16 mt-8" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24 mt-2" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-1" }),
|
||||
/* @__PURE__ */ jsx(Skeleton, { className: "h-9 w-full mt-6" })
|
||||
] }) }) });
|
||||
}
|
||||
function Inner(props) {
|
||||
const stackApp = useStackApp();
|
||||
const user = useUser();
|
||||
const projectFromHook = stackApp.useProject();
|
||||
const project = props.mockProject || projectFromHook;
|
||||
const { t } = useTranslation();
|
||||
useEffect(() => {
|
||||
if (props.automaticRedirect && user && !props.mockProject) {
|
||||
runAsynchronously(
|
||||
props.type === "sign-in" ? stackApp.redirectToAfterSignIn({ replace: true }) : stackApp.redirectToAfterSignUp({ replace: true })
|
||||
);
|
||||
}
|
||||
}, [user, props.mockProject, stackApp, props.automaticRedirect]);
|
||||
if (user && !props.mockProject && !props.automaticRedirect) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedIn", fullPage: props.fullPage });
|
||||
}
|
||||
if (props.type === "sign-up" && !project.config.signUpEnabled) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signUpDisabled", fullPage: props.fullPage });
|
||||
}
|
||||
const hasOAuthProviders = project.config.oauthProviders.length > 0;
|
||||
const hasPasskey = project.config.passkeyEnabled === true && props.type === "sign-in";
|
||||
const enableSeparator = (project.config.credentialEnabled || project.config.magicLinkEnabled) && (hasOAuthProviders || hasPasskey);
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsxs("div", { className: "stack-scope flex flex-col items-stretch", style: { maxWidth: "380px", flexBasis: "380px", padding: props.fullPage ? "1rem" : 0 }, children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "text-center mb-6", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { type: "h2", children: props.type === "sign-in" ? t("Sign in to your account") : t("Create a new account") }),
|
||||
props.type === "sign-in" ? project.config.signUpEnabled && /* @__PURE__ */ jsxs(Typography, { children: [
|
||||
t("Don't have an account?"),
|
||||
" ",
|
||||
/* @__PURE__ */ jsx(StyledLink, { href: stackApp.urls.signUp, onClick: (e) => {
|
||||
runAsynchronously(stackApp.redirectToSignUp());
|
||||
e.preventDefault();
|
||||
}, children: t("Sign up") })
|
||||
] }) : /* @__PURE__ */ jsxs(Typography, { children: [
|
||||
t("Already have an account?"),
|
||||
" ",
|
||||
/* @__PURE__ */ jsx(StyledLink, { href: stackApp.urls.signIn, onClick: (e) => {
|
||||
runAsynchronously(stackApp.redirectToSignIn());
|
||||
e.preventDefault();
|
||||
}, children: t("Sign in") })
|
||||
] })
|
||||
] }),
|
||||
(hasOAuthProviders || hasPasskey) && /* @__PURE__ */ jsxs("div", { className: "gap-4 flex flex-col items-stretch stack-scope", children: [
|
||||
hasOAuthProviders && /* @__PURE__ */ jsx(OAuthButtonGroup, { type: props.type, mockProject: props.mockProject }),
|
||||
hasPasskey && /* @__PURE__ */ jsx(PasskeyButton, { type: props.type })
|
||||
] }),
|
||||
enableSeparator && /* @__PURE__ */ jsx(SeparatorWithText, { text: t("Or continue with") }),
|
||||
project.config.credentialEnabled && project.config.magicLinkEnabled ? /* @__PURE__ */ jsxs(Tabs, { defaultValue: props.firstTab || "magic-link", children: [
|
||||
/* @__PURE__ */ jsxs(TabsList, { className: cn("w-full mb-2", {
|
||||
"flex-row-reverse": props.firstTab === "password"
|
||||
}), children: [
|
||||
/* @__PURE__ */ jsx(TabsTrigger, { value: "magic-link", className: "flex-1", children: t("Email") }),
|
||||
/* @__PURE__ */ jsx(TabsTrigger, { value: "password", className: "flex-1", children: t("Email & Password") })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(TabsContent, { value: "magic-link", children: /* @__PURE__ */ jsx(MagicLinkSignIn, {}) }),
|
||||
/* @__PURE__ */ jsx(TabsContent, { value: "password", children: props.type === "sign-up" ? /* @__PURE__ */ jsx(CredentialSignUp, { noPasswordRepeat: props.noPasswordRepeat }) : /* @__PURE__ */ jsx(CredentialSignIn, {}) })
|
||||
] }) : project.config.credentialEnabled ? props.type === "sign-up" ? /* @__PURE__ */ jsx(CredentialSignUp, { noPasswordRepeat: props.noPasswordRepeat }) : /* @__PURE__ */ jsx(CredentialSignIn, {}) : project.config.magicLinkEnabled ? /* @__PURE__ */ jsx(MagicLinkSignIn, {}) : !(hasOAuthProviders || hasPasskey) ? /* @__PURE__ */ jsx(Typography, { variant: "destructive", className: "text-center", children: t("No authentication method enabled.") }) : null,
|
||||
props.extraInfo && /* @__PURE__ */ jsx("div", { className: cn("flex flex-col items-center text-center text-sm text-gray-500", {
|
||||
"mt-2": project.config.credentialEnabled || project.config.magicLinkEnabled,
|
||||
"mt-6": !(project.config.credentialEnabled || project.config.magicLinkEnabled)
|
||||
}), children: /* @__PURE__ */ jsx("div", { children: props.extraInfo }) })
|
||||
] }) });
|
||||
}
|
||||
export {
|
||||
AuthPage
|
||||
};
|
||||
//# sourceMappingURL=auth-page.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/auth-page.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/auth-page.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
99
package/@stackframe/react/dist/esm/components-page/cli-auth-confirm.js
vendored
Normal file
99
package/@stackframe/react/dist/esm/components-page/cli-auth-confirm.js
vendored
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/cli-auth-confirm.tsx
|
||||
import { Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { stackAppInternalsSymbol, useStackApp } from "..";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function CliAuthConfirmation({ fullPage = true }) {
|
||||
const { t } = useTranslation();
|
||||
const app = useStackApp();
|
||||
const [authorizing, setAuthorizing] = useState(false);
|
||||
const [success, setSuccess] = useState(false);
|
||||
const [error, setError] = useState(null);
|
||||
const user = app.useUser({ or: "redirect" });
|
||||
const handleAuthorize = async () => {
|
||||
if (authorizing) return;
|
||||
setAuthorizing(true);
|
||||
try {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const loginCode = urlParams.get("login_code");
|
||||
if (!loginCode) {
|
||||
throw new Error("Missing login code in URL parameters");
|
||||
}
|
||||
const refreshToken = (await user.currentSession.getTokens()).refreshToken;
|
||||
if (!refreshToken) {
|
||||
throw new Error("You must be logged in to authorize CLI access");
|
||||
}
|
||||
const result = await app[stackAppInternalsSymbol].sendRequest("/auth/cli/complete", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
login_code: loginCode,
|
||||
refresh_token: (await user.currentSession.getTokens()).refreshToken
|
||||
})
|
||||
});
|
||||
if (!result.ok) {
|
||||
throw new Error(`Authorization failed: ${result.status} ${await result.text()}`);
|
||||
}
|
||||
setSuccess(true);
|
||||
} catch (err) {
|
||||
setError(err);
|
||||
} finally {
|
||||
setAuthorizing(false);
|
||||
}
|
||||
};
|
||||
if (success) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("CLI Authorization Successful"),
|
||||
fullPage,
|
||||
primaryButtonText: t("Close"),
|
||||
primaryAction: () => window.close(),
|
||||
children: /* @__PURE__ */ jsx(Typography, { children: t("The CLI application has been authorized successfully. You can now close this window and return to the command line.") })
|
||||
}
|
||||
);
|
||||
}
|
||||
if (error) {
|
||||
return /* @__PURE__ */ jsxs(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Authorization Failed"),
|
||||
fullPage,
|
||||
primaryButtonText: t("Try Again"),
|
||||
primaryAction: () => setError(null),
|
||||
secondaryButtonText: t("Cancel"),
|
||||
secondaryAction: () => window.close(),
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Typography, { className: "text-red-600", children: t("Failed to authorize the CLI application:") }),
|
||||
/* @__PURE__ */ jsx(Typography, { className: "text-red-600", children: error.message })
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
return /* @__PURE__ */ jsxs(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Authorize CLI Application"),
|
||||
fullPage,
|
||||
primaryButtonText: authorizing ? t("Authorizing...") : t("Authorize"),
|
||||
primaryAction: handleAuthorize,
|
||||
secondaryButtonText: t("Cancel"),
|
||||
secondaryAction: () => window.close(),
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Typography, { children: t("A command line application is requesting access to your account. Click the button below to authorize it.") }),
|
||||
/* @__PURE__ */ jsx(Typography, { variant: "destructive", children: t("WARNING: Make sure you trust the command line application, as it will gain access to your account. If you did not initiate this request, you can close this page and ignore it. We will never send you this link via email or any other means.") })
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
CliAuthConfirmation
|
||||
};
|
||||
//# sourceMappingURL=cli-auth-confirm.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/cli-auth-confirm.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/cli-auth-confirm.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
66
package/@stackframe/react/dist/esm/components-page/email-verification.js
vendored
Normal file
66
package/@stackframe/react/dist/esm/components-page/email-verification.js
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/email-verification.tsx
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import React from "react";
|
||||
import { useStackApp, useUser } from "..";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function EmailVerification(props) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const user = useUser();
|
||||
const [result, setResult] = React.useState(null);
|
||||
const invalidJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Invalid Verification Link"), fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("p", { children: t("Please check if you have the correct link. If you continue to have issues, please contact support.") }) });
|
||||
const expiredJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Expired Verification Link"), fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("p", { children: t("Your email verification link has expired. Please request a new verification link from your account settings.") }) });
|
||||
if (!props.searchParams?.code) {
|
||||
return invalidJsx;
|
||||
}
|
||||
if (!result) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Do you want to verify your email?"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Verify"),
|
||||
primaryAction: async () => {
|
||||
const result2 = await stackApp.verifyEmail(props.searchParams?.code || throwErr("No verification code provided"));
|
||||
setResult(result2);
|
||||
},
|
||||
secondaryButtonText: t("Cancel"),
|
||||
secondaryAction: async () => {
|
||||
await stackApp.redirectToHome();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (result.status === "error") {
|
||||
if (KnownErrors.VerificationCodeNotFound.isInstance(result.error)) {
|
||||
return invalidJsx;
|
||||
} else if (KnownErrors.VerificationCodeExpired.isInstance(result.error)) {
|
||||
return expiredJsx;
|
||||
} else if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(result.error)) {
|
||||
} else {
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("You email has been verified!"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Go home"),
|
||||
primaryAction: async () => {
|
||||
await stackApp.redirectToHome();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
export {
|
||||
EmailVerification
|
||||
};
|
||||
//# sourceMappingURL=email-verification.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/email-verification.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/email-verification.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/email-verification.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { KnownErrors } from \"@stackframe/stack-shared\";\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport React from \"react\";\nimport { useStackApp, useUser } from \"..\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function EmailVerification(props: {\n searchParams?: Record<string, string>,\n fullPage?: boolean,\n}) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const user = useUser();\n const [result, setResult] = React.useState<Awaited<ReturnType<typeof stackApp.verifyEmail>> | null>(null);\n\n const invalidJsx = (\n <MessageCard title={t(\"Invalid Verification Link\")} fullPage={!!props.fullPage}>\n <p>{t(\"Please check if you have the correct link. If you continue to have issues, please contact support.\")}</p>\n </MessageCard>\n );\n\n const expiredJsx = (\n <MessageCard title={t(\"Expired Verification Link\")} fullPage={!!props.fullPage}>\n <p>{t(\"Your email verification link has expired. Please request a new verification link from your account settings.\")}</p>\n </MessageCard>\n );\n\n if (!props.searchParams?.code) {\n return invalidJsx;\n }\n\n if (!result) {\n return <MessageCard\n title={t(\"Do you want to verify your email?\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Verify\")}\n primaryAction={async () => {\n const result = await stackApp.verifyEmail(props.searchParams?.code || throwErr(\"No verification code provided\"));\n setResult(result);\n }}\n secondaryButtonText={t(\"Cancel\")}\n secondaryAction={async () => {\n await stackApp.redirectToHome();\n }}\n />;\n } else {\n if (result.status === 'error') {\n if (KnownErrors.VerificationCodeNotFound.isInstance(result.error)) {\n return invalidJsx;\n } else if (KnownErrors.VerificationCodeExpired.isInstance(result.error)) {\n return expiredJsx;\n } else if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(result.error)) {\n // everything fine, continue\n } else {\n throw result.error;\n }\n }\n\n return <MessageCard\n title={t(\"You email has been verified!\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Go home\")}\n primaryAction={async () => {\n await stackApp.redirectToHome();\n }}\n />;\n }\n}\n"],"mappings":";;;AAOA,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB;AACzB,OAAO,WAAW;AAClB,SAAS,aAAa,eAAe;AACrC,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAazB;AAXC,SAAS,kBAAkB,OAG/B;AACD,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,WAAW,YAAY;AAC7B,QAAM,OAAO,QAAQ;AACrB,QAAM,CAAC,QAAQ,SAAS,IAAI,MAAM,SAAkE,IAAI;AAExG,QAAM,aACJ,oBAAC,eAAY,OAAO,EAAE,2BAA2B,GAAG,UAAU,CAAC,CAAC,MAAM,UACpE,8BAAC,OAAG,YAAE,oGAAoG,GAAE,GAC9G;AAGF,QAAM,aACJ,oBAAC,eAAY,OAAO,EAAE,2BAA2B,GAAG,UAAU,CAAC,CAAC,MAAM,UACpE,8BAAC,OAAG,YAAE,8GAA8G,GAAE,GACxH;AAGF,MAAI,CAAC,MAAM,cAAc,MAAM;AAC7B,WAAO;AAAA,EACT;AAEA,MAAI,CAAC,QAAQ;AACX,WAAO;AAAA,MAAC;AAAA;AAAA,QACN,OAAO,EAAE,mCAAmC;AAAA,QAC5C,UAAU,CAAC,CAAC,MAAM;AAAA,QAClB,mBAAmB,EAAE,QAAQ;AAAA,QAC7B,eAAe,YAAY;AACzB,gBAAMA,UAAS,MAAM,SAAS,YAAY,MAAM,cAAc,QAAQ,SAAS,+BAA+B,CAAC;AAC/G,oBAAUA,OAAM;AAAA,QAClB;AAAA,QACA,qBAAqB,EAAE,QAAQ;AAAA,QAC/B,iBAAiB,YAAY;AAC3B,gBAAM,SAAS,eAAe;AAAA,QAChC;AAAA;AAAA,IACF;AAAA,EACF,OAAO;AACL,QAAI,OAAO,WAAW,SAAS;AAC7B,UAAI,YAAY,yBAAyB,WAAW,OAAO,KAAK,GAAG;AACjE,eAAO;AAAA,MACT,WAAW,YAAY,wBAAwB,WAAW,OAAO,KAAK,GAAG;AACvE,eAAO;AAAA,MACT,WAAW,YAAY,4BAA4B,WAAW,OAAO,KAAK,GAAG;AAAA,MAE7E,OAAO;AACL,cAAM,OAAO;AAAA,MACf;AAAA,IACF;AAEA,WAAO;AAAA,MAAC;AAAA;AAAA,QACN,OAAO,EAAE,8BAA8B;AAAA,QACvC,UAAU,CAAC,CAAC,MAAM;AAAA,QAClB,mBAAmB,EAAE,SAAS;AAAA,QAC9B,eAAe,YAAY;AACzB,gBAAM,SAAS,eAAe;AAAA,QAChC;AAAA;AAAA,IACF;AAAA,EACF;AACF;","names":["result"]}
|
||||
73
package/@stackframe/react/dist/esm/components-page/error-page.js
vendored
Normal file
73
package/@stackframe/react/dist/esm/components-page/error-page.js
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/error-page.tsx
|
||||
import { KnownError, KnownErrors } from "@stackframe/stack-shared";
|
||||
import { Typography } from "@stackframe/stack-ui";
|
||||
import { useStackApp } from "..";
|
||||
import { KnownErrorMessageCard } from "../components/message-cards/known-error-message-card";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function ErrorPage(props) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const errorCode = props.searchParams.errorCode;
|
||||
const message = props.searchParams.message;
|
||||
const details = props.searchParams.details;
|
||||
const unknownErrorCard = /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "unknownError", fullPage: !!props.fullPage });
|
||||
if (!errorCode || !message) {
|
||||
return unknownErrorCard;
|
||||
}
|
||||
let error;
|
||||
try {
|
||||
const detailJson = details ? JSON.parse(details) : {};
|
||||
error = KnownError.fromJson({ code: errorCode, message, details: detailJson });
|
||||
} catch (e) {
|
||||
return unknownErrorCard;
|
||||
}
|
||||
if (KnownErrors.OAuthConnectionAlreadyConnectedToAnotherUser.isInstance(error)) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Failed to connect account"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Go Home"),
|
||||
primaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsx(Typography, { children: t("This account is already connected to another user. Please connect a different account.") })
|
||||
}
|
||||
);
|
||||
}
|
||||
if (KnownErrors.UserAlreadyConnectedToAnotherOAuthConnection.isInstance(error)) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Failed to connect account"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Go Home"),
|
||||
primaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsx(Typography, { children: t("The user is already connected to another OAuth account. Did you maybe selected the wrong account on the OAuth provider page?") })
|
||||
}
|
||||
);
|
||||
}
|
||||
if (KnownErrors.OAuthProviderAccessDenied.isInstance(error)) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("OAuth provider access denied"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Sign in again"),
|
||||
primaryAction: () => stackApp.redirectToSignIn(),
|
||||
secondaryButtonText: t("Go Home"),
|
||||
secondaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsx(Typography, { children: t("The sign-in operation has been cancelled. Please try again. [access_denied]") })
|
||||
}
|
||||
);
|
||||
}
|
||||
return /* @__PURE__ */ jsx(KnownErrorMessageCard, { error, fullPage: !!props.fullPage });
|
||||
}
|
||||
export {
|
||||
ErrorPage
|
||||
};
|
||||
//# sourceMappingURL=error-page.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/error-page.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/error-page.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/error-page.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { KnownError, KnownErrors } from \"@stackframe/stack-shared\";\nimport { Typography } from \"@stackframe/stack-ui\";\nimport { useStackApp } from \"..\";\nimport { KnownErrorMessageCard } from \"../components/message-cards/known-error-message-card\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\n\nexport function ErrorPage(props: { fullPage?: boolean, searchParams: Record<string, string> }) {\n const { t } = useTranslation();\n const stackApp = useStackApp();\n const errorCode = props.searchParams.errorCode;\n const message = props.searchParams.message;\n const details = props.searchParams.details;\n\n const unknownErrorCard = <PredefinedMessageCard type='unknownError' fullPage={!!props.fullPage} />;\n\n if (!errorCode || !message) {\n return unknownErrorCard;\n }\n\n let error;\n try {\n const detailJson = details ? JSON.parse(details) : {};\n error = KnownError.fromJson({ code: errorCode, message, details: detailJson });\n } catch (e) {\n return unknownErrorCard;\n }\n\n if (KnownErrors.OAuthConnectionAlreadyConnectedToAnotherUser.isInstance(error)) {\n // TODO: add \"Connect a different account\" button\n return (\n <MessageCard\n title={t(\"Failed to connect account\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Go Home\")}\n primaryAction={() => stackApp.redirectToHome()}\n >\n <Typography>\n {t(\"This account is already connected to another user. Please connect a different account.\")}\n </Typography>\n </MessageCard>\n );\n }\n\n if (KnownErrors.UserAlreadyConnectedToAnotherOAuthConnection.isInstance(error)) {\n // TODO: add \"Connect again\" button\n return (\n <MessageCard\n title={t(\"Failed to connect account\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Go Home\")}\n primaryAction={() => stackApp.redirectToHome()}\n >\n <Typography>\n {t(\"The user is already connected to another OAuth account. Did you maybe selected the wrong account on the OAuth provider page?\")}\n </Typography>\n </MessageCard>\n );\n }\n\n if (KnownErrors.OAuthProviderAccessDenied.isInstance(error)) {\n return (\n <MessageCard\n title={t(\"OAuth provider access denied\")}\n fullPage={!!props.fullPage}\n primaryButtonText={t(\"Sign in again\")}\n primaryAction={() => stackApp.redirectToSignIn()}\n secondaryButtonText={t(\"Go Home\")}\n secondaryAction={() => stackApp.redirectToHome()}\n >\n <Typography>\n {t(\"The sign-in operation has been cancelled. Please try again. [access_denied]\")}\n </Typography>\n </MessageCard>\n );\n }\n\n return <KnownErrorMessageCard error={error} fullPage={!!props.fullPage} />;\n}\n"],"mappings":";;;AAOA,SAAS,YAAY,mBAAmB;AACxC,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,6BAA6B;AACtC,SAAS,mBAAmB;AAC5B,SAAS,6BAA6B;AACtC,SAAS,sBAAsB;AAUJ;AAPpB,SAAS,UAAU,OAAqE;AAC7F,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,WAAW,YAAY;AAC7B,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,UAAU,MAAM,aAAa;AACnC,QAAM,UAAU,MAAM,aAAa;AAEnC,QAAM,mBAAmB,oBAAC,yBAAsB,MAAK,gBAAe,UAAU,CAAC,CAAC,MAAM,UAAU;AAEhG,MAAI,CAAC,aAAa,CAAC,SAAS;AAC1B,WAAO;AAAA,EACT;AAEA,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,UAAU,KAAK,MAAM,OAAO,IAAI,CAAC;AACpD,YAAQ,WAAW,SAAS,EAAE,MAAM,WAAW,SAAS,SAAS,WAAW,CAAC;AAAA,EAC/E,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AAEA,MAAI,YAAY,6CAA6C,WAAW,KAAK,GAAG;AAE9E,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,2BAA2B;AAAA,QACpC,UAAU,CAAC,CAAC,MAAM;AAAA,QAClB,mBAAmB,EAAE,SAAS;AAAA,QAC9B,eAAe,MAAM,SAAS,eAAe;AAAA,QAE7C,8BAAC,cACE,YAAE,wFAAwF,GAC7F;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,YAAY,6CAA6C,WAAW,KAAK,GAAG;AAE9E,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,2BAA2B;AAAA,QACpC,UAAU,CAAC,CAAC,MAAM;AAAA,QAClB,mBAAmB,EAAE,SAAS;AAAA,QAC9B,eAAe,MAAM,SAAS,eAAe;AAAA,QAE7C,8BAAC,cACE,YAAE,8HAA8H,GACnI;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,YAAY,0BAA0B,WAAW,KAAK,GAAG;AAC3D,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,8BAA8B;AAAA,QACvC,UAAU,CAAC,CAAC,MAAM;AAAA,QAClB,mBAAmB,EAAE,eAAe;AAAA,QACpC,eAAe,MAAM,SAAS,iBAAiB;AAAA,QAC/C,qBAAqB,EAAE,SAAS;AAAA,QAChC,iBAAiB,MAAM,SAAS,eAAe;AAAA,QAE/C,8BAAC,cACE,YAAE,6EAA6E,GAClF;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SAAO,oBAAC,yBAAsB,OAAc,UAAU,CAAC,CAAC,MAAM,UAAU;AAC1E;","names":[]}
|
||||
92
package/@stackframe/react/dist/esm/components-page/forgot-password.js
vendored
Normal file
92
package/@stackframe/react/dist/esm/components-page/forgot-password.js
vendored
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/forgot-password.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { strictEmailSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Label, Typography, cn } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useStackApp, useUser } from "..";
|
||||
import { FormWarningText } from "../components/elements/form-warning";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { StyledLink } from "../components/link";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function ForgotPasswordForm({ onSent }) {
|
||||
const { t } = useTranslation();
|
||||
const schema = yupObject({
|
||||
email: strictEmailSchema(t("Please enter a valid email")).defined().nonEmpty(t("Please enter your email"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
const stackApp = useStackApp();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { email } = data;
|
||||
await stackApp.sendForgotPasswordEmail(email);
|
||||
onSent?.();
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
className: "flex flex-col items-stretch stack-scope",
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: t("Your Email") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "email",
|
||||
type: "email",
|
||||
autoComplete: "email",
|
||||
...register("email"),
|
||||
onChange: () => clearErrors("email")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Send Email") })
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
function ForgotPassword(props) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const user = useUser();
|
||||
const [sent, setSent] = useState(false);
|
||||
if (user) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedIn", fullPage: !!props.fullPage });
|
||||
}
|
||||
if (sent) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "emailSent", fullPage: !!props.fullPage });
|
||||
}
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsxs("div", { className: cn(
|
||||
"stack-scope max-w-[380px] flex-basis-[380px]",
|
||||
props.fullPage ? "p-4" : "p-0"
|
||||
), children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { type: "h2", children: t("Reset Your Password") }),
|
||||
/* @__PURE__ */ jsxs(Typography, { children: [
|
||||
t("Don't need to reset?"),
|
||||
" ",
|
||||
/* @__PURE__ */ jsx(StyledLink, { href: stackApp.urls["signIn"], children: t("Sign in") })
|
||||
] })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(ForgotPasswordForm, { onSent: () => setSent(true) }) })
|
||||
] }) });
|
||||
}
|
||||
export {
|
||||
ForgotPassword,
|
||||
ForgotPasswordForm
|
||||
};
|
||||
//# sourceMappingURL=forgot-password.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/forgot-password.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/forgot-password.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
76
package/@stackframe/react/dist/esm/components-page/magic-link-callback.js
vendored
Normal file
76
package/@stackframe/react/dist/esm/components-page/magic-link-callback.js
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/magic-link-callback.tsx
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
|
||||
import { throwErr } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import React from "react";
|
||||
import { useStackApp, useUser } from "..";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
var cacheSignInWithMagicLink = cacheFunction(async (stackApp, code) => {
|
||||
return await stackApp.signInWithMagicLink(code);
|
||||
});
|
||||
function MagicLinkCallback(props) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const user = useUser();
|
||||
const [result, setResult] = React.useState(null);
|
||||
if (user) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedIn", fullPage: !!props.fullPage });
|
||||
}
|
||||
const invalidJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Invalid Magic Link"), fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("p", { children: t("Please check if you have the correct link. If you continue to have issues, please contact support.") }) });
|
||||
const expiredJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Expired Magic Link"), fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("p", { children: t("Your magic link has expired. Please request a new magic link if you need to sign-in.") }) });
|
||||
const alreadyUsedJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Magic Link Already Used"), fullPage: !!props.fullPage, children: /* @__PURE__ */ jsx("p", { children: t("The magic link has already been used. The link can only be used once. Please request a new magic link if you need to sign-in again.") }) });
|
||||
if (!props.searchParams?.code) {
|
||||
return invalidJsx;
|
||||
}
|
||||
if (!result) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Do you want to sign in?"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Sign in"),
|
||||
primaryAction: async () => {
|
||||
const result2 = await stackApp.signInWithMagicLink(props.searchParams?.code || throwErr("No magic link provided"));
|
||||
setResult(result2);
|
||||
},
|
||||
secondaryButtonText: t("Cancel"),
|
||||
secondaryAction: async () => {
|
||||
await stackApp.redirectToHome();
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (result.status === "error") {
|
||||
if (KnownErrors.VerificationCodeNotFound.isInstance(result.error)) {
|
||||
return invalidJsx;
|
||||
} else if (KnownErrors.VerificationCodeExpired.isInstance(result.error)) {
|
||||
return expiredJsx;
|
||||
} else if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(result.error)) {
|
||||
return alreadyUsedJsx;
|
||||
} else {
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Signed in successfully!"),
|
||||
fullPage: !!props.fullPage,
|
||||
primaryButtonText: t("Go home"),
|
||||
primaryAction: async () => {
|
||||
await stackApp.redirectToHome();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
export {
|
||||
MagicLinkCallback
|
||||
};
|
||||
//# sourceMappingURL=magic-link-callback.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/magic-link-callback.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/magic-link-callback.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
69
package/@stackframe/react/dist/esm/components-page/oauth-callback.js
vendored
Normal file
69
package/@stackframe/react/dist/esm/components-page/oauth-callback.js
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/oauth-callback.tsx
|
||||
import { captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Spinner, cn } from "@stackframe/stack-ui";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useStackApp } from "..";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { StyledLink } from "../components/link";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function OAuthCallback({ fullPage }) {
|
||||
const { t } = useTranslation();
|
||||
const app = useStackApp();
|
||||
const called = useRef(false);
|
||||
const [error, setError] = useState(null);
|
||||
const [showRedirectLink, setShowRedirectLink] = useState(false);
|
||||
useEffect(() => runAsynchronously(async () => {
|
||||
if (called.current) return;
|
||||
called.current = true;
|
||||
let hasRedirected = false;
|
||||
try {
|
||||
hasRedirected = await app.callOAuthCallback();
|
||||
} catch (e) {
|
||||
captureError("<OAuthCallback />", e);
|
||||
setError(e);
|
||||
}
|
||||
if (!hasRedirected && (!error || process.env.NODE_ENV === "production")) {
|
||||
await app.redirectToSignIn({ noRedirectBack: true });
|
||||
}
|
||||
}), []);
|
||||
useEffect(() => {
|
||||
setTimeout(() => setShowRedirectLink(true), 3e3);
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsx(
|
||||
MaybeFullPage,
|
||||
{
|
||||
fullPage: fullPage ?? false,
|
||||
containerClassName: "flex items-center justify-center",
|
||||
children: /* @__PURE__ */ jsxs(
|
||||
"div",
|
||||
{
|
||||
className: cn(
|
||||
"text-center justify-center items-center stack-scope flex flex-col gap-4 max-w-[380px]",
|
||||
fullPage ? "p-4" : "p-0"
|
||||
),
|
||||
children: [
|
||||
/* @__PURE__ */ jsx("div", { className: "flex flex-col justify-center items-center gap-4", children: /* @__PURE__ */ jsx(Spinner, { size: 20 }) }),
|
||||
showRedirectLink ? /* @__PURE__ */ jsxs("p", { children: [
|
||||
t("If you are not redirected automatically, "),
|
||||
/* @__PURE__ */ jsx(StyledLink, { className: "whitespace-nowrap", href: app.urls.home, children: t("click here") })
|
||||
] }) : null,
|
||||
error ? /* @__PURE__ */ jsxs("div", { children: [
|
||||
/* @__PURE__ */ jsx("p", { children: t("Something went wrong while processing the OAuth callback:") }),
|
||||
/* @__PURE__ */ jsx("pre", { children: JSON.stringify(error, null, 2) }),
|
||||
/* @__PURE__ */ jsx("p", { children: t("This is most likely an error in Stack. Please report it.") })
|
||||
] }) : null
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
OAuthCallback
|
||||
};
|
||||
//# sourceMappingURL=oauth-callback.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/oauth-callback.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/oauth-callback.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/oauth-callback.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { captureError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Spinner, cn } from \"@stackframe/stack-ui\";\nimport { useEffect, useRef, useState } from \"react\";\nimport { useStackApp } from \"..\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { StyledLink } from \"../components/link\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function OAuthCallback({ fullPage }: { fullPage?: boolean }) {\n const { t } = useTranslation();\n const app = useStackApp();\n const called = useRef(false);\n const [error, setError] = useState<unknown>(null);\n const [showRedirectLink, setShowRedirectLink] = useState(false);\n\n useEffect(() => runAsynchronously(async () => {\n if (called.current) return;\n called.current = true;\n let hasRedirected = false;\n try {\n hasRedirected = await app.callOAuthCallback();\n } catch (e) {\n captureError(\"<OAuthCallback />\", e);\n setError(e);\n }\n if (!hasRedirected && (!error || process.env.NODE_ENV === 'production')) {\n await app.redirectToSignIn({ noRedirectBack: true });\n }\n }), []);\n\n useEffect(() => {\n setTimeout(() => setShowRedirectLink(true), 3000);\n }, []);\n\n return (\n <MaybeFullPage\n fullPage={fullPage ?? false}\n containerClassName=\"flex items-center justify-center\"\n >\n <div\n className={cn(\n \"text-center justify-center items-center stack-scope flex flex-col gap-4 max-w-[380px]\",\n fullPage ? \"p-4\" : \"p-0\"\n )}\n >\n <div className=\"flex flex-col justify-center items-center gap-4\">\n <Spinner size={20} />\n </div>\n {showRedirectLink ? <p>{t('If you are not redirected automatically, ')}<StyledLink className=\"whitespace-nowrap\" href={app.urls.home}>{t(\"click here\")}</StyledLink></p> : null}\n {error ? <div>\n <p>{t(\"Something went wrong while processing the OAuth callback:\")}</p>\n <pre>{JSON.stringify(error, null, 2)}</pre>\n <p>{t(\"This is most likely an error in Stack. Please report it.\")}</p>\n </div> : null}\n </div>\n </MaybeFullPage>\n );\n}\n"],"mappings":";;;AAOA,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAClC,SAAS,SAAS,UAAU;AAC5B,SAAS,WAAW,QAAQ,gBAAgB;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAC9B,SAAS,kBAAkB;AAC3B,SAAS,sBAAsB;AAwCrB,cAEkB,YAFlB;AAtCH,SAAS,cAAc,EAAE,SAAS,GAA2B;AAClE,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,MAAM,YAAY;AACxB,QAAM,SAAS,OAAO,KAAK;AAC3B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkB,IAAI;AAChD,QAAM,CAAC,kBAAkB,mBAAmB,IAAI,SAAS,KAAK;AAE9D,YAAU,MAAM,kBAAkB,YAAY;AAC5C,QAAI,OAAO,QAAS;AACpB,WAAO,UAAU;AACjB,QAAI,gBAAgB;AACpB,QAAI;AACF,sBAAgB,MAAM,IAAI,kBAAkB;AAAA,IAC9C,SAAS,GAAG;AACV,mBAAa,qBAAqB,CAAC;AACnC,eAAS,CAAC;AAAA,IACZ;AACA,QAAI,CAAC,kBAAkB,CAAC,SAAS,QAAQ,IAAI,aAAa,eAAe;AACvE,YAAM,IAAI,iBAAiB,EAAE,gBAAgB,KAAK,CAAC;AAAA,IACrD;AAAA,EACF,CAAC,GAAG,CAAC,CAAC;AAEN,YAAU,MAAM;AACd,eAAW,MAAM,oBAAoB,IAAI,GAAG,GAAI;AAAA,EAClD,GAAG,CAAC,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,YAAY;AAAA,MACtB,oBAAmB;AAAA,MAEnB;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,WAAW,QAAQ;AAAA,UACrB;AAAA,UAEA;AAAA,gCAAC,SAAI,WAAU,mDACb,8BAAC,WAAQ,MAAM,IAAI,GACrB;AAAA,YACC,mBAAmB,qBAAC,OAAG;AAAA,gBAAE,2CAA2C;AAAA,cAAE,oBAAC,cAAW,WAAU,qBAAoB,MAAM,IAAI,KAAK,MAAO,YAAE,YAAY,GAAE;AAAA,eAAa,IAAO;AAAA,YAC1K,QAAQ,qBAAC,SACR;AAAA,kCAAC,OAAG,YAAE,2DAA2D,GAAE;AAAA,cACnE,oBAAC,SAAK,eAAK,UAAU,OAAO,MAAM,CAAC,GAAE;AAAA,cACrC,oBAAC,OAAG,YAAE,0DAA0D,GAAE;AAAA,eACpE,IAAS;AAAA;AAAA;AAAA,MACX;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
145
package/@stackframe/react/dist/esm/components-page/password-reset.js
vendored
Normal file
145
package/@stackframe/react/dist/esm/components-page/password-reset.js
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/password-reset.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
|
||||
import { passwordSchema, yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Label, PasswordInput, Typography, cn } from "@stackframe/stack-ui";
|
||||
import React, { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { useStackApp } from "..";
|
||||
import { FormWarningText } from "../components/elements/form-warning";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function PasswordResetForm(props) {
|
||||
const { t } = useTranslation();
|
||||
const schema = yupObject({
|
||||
password: passwordSchema.defined(t("Please enter your password")).nonEmpty(t("Please enter your password")).test({
|
||||
name: "is-valid-password",
|
||||
test: (value, ctx) => {
|
||||
const error = getPasswordError(value);
|
||||
if (error) {
|
||||
return ctx.createError({ message: error.message });
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}),
|
||||
passwordRepeat: yupString().nullable().oneOf([yup.ref("password"), null], t("Passwords do not match")).defined().nonEmpty(t("Please repeat your password"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, clearErrors } = useForm({
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
const stackApp = useStackApp();
|
||||
const [finished, setFinished] = useState(false);
|
||||
const [resetError, setResetError] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { password } = data;
|
||||
const result = await stackApp.resetPassword({ password, code: props.code });
|
||||
if (result.status === "error") {
|
||||
setResetError(true);
|
||||
return;
|
||||
}
|
||||
setFinished(true);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
if (finished) {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "passwordReset", fullPage: !!props.fullPage });
|
||||
}
|
||||
if (resetError) {
|
||||
return /* @__PURE__ */ jsx(MessageCard, { title: t("Failed to reset password"), fullPage: !!props.fullPage, children: t("Failed to reset password. Please request a new password reset link") });
|
||||
}
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsxs("div", { className: cn(
|
||||
"flex flex-col items-stretch max-w-[380px] flex-basis-[380px]",
|
||||
props.fullPage ? "p-4" : "p-0"
|
||||
), children: [
|
||||
/* @__PURE__ */ jsx("div", { className: "text-center mb-6", children: /* @__PURE__ */ jsx(Typography, { type: "h2", children: t("Reset Your Password") }) }),
|
||||
/* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
className: "flex flex-col items-stretch",
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "password", className: "mb-1", children: t("New Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "password",
|
||||
autoComplete: "new-password",
|
||||
...register("password"),
|
||||
onChange: () => {
|
||||
clearErrors("password");
|
||||
clearErrors("passwordRepeat");
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.password?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat New Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "repeat-password",
|
||||
autoComplete: "new-password",
|
||||
...register("passwordRepeat"),
|
||||
onChange: () => {
|
||||
clearErrors("password");
|
||||
clearErrors("passwordRepeat");
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.passwordRepeat?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Reset Password") })
|
||||
]
|
||||
}
|
||||
)
|
||||
] }) });
|
||||
}
|
||||
var cachedVerifyPasswordResetCode = cacheFunction(async (stackApp, code) => {
|
||||
return await stackApp.verifyPasswordResetCode(code);
|
||||
});
|
||||
function PasswordReset({
|
||||
searchParams,
|
||||
fullPage = false
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const invalidJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Invalid Password Reset Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("Please double check if you have the correct password reset link.") }) });
|
||||
const expiredJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Expired Password Reset Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("Your password reset link has expired. Please request a new password reset link from the login page.") }) });
|
||||
const usedJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Used Password Reset Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("This password reset link has already been used. If you need to reset your password again, please request a new password reset link from the login page.") }) });
|
||||
const code = searchParams.code;
|
||||
if (!code) {
|
||||
return invalidJsx;
|
||||
}
|
||||
const result = React.use(cachedVerifyPasswordResetCode(stackApp, code));
|
||||
if (result.status === "error") {
|
||||
if (KnownErrors.VerificationCodeNotFound.isInstance(result.error)) {
|
||||
return invalidJsx;
|
||||
} else if (KnownErrors.VerificationCodeExpired.isInstance(result.error)) {
|
||||
return expiredJsx;
|
||||
} else if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(result.error)) {
|
||||
return usedJsx;
|
||||
} else {
|
||||
throw result.error;
|
||||
}
|
||||
}
|
||||
return /* @__PURE__ */ jsx(PasswordResetForm, { code, fullPage });
|
||||
}
|
||||
export {
|
||||
PasswordReset,
|
||||
PasswordResetForm as default
|
||||
};
|
||||
//# sourceMappingURL=password-reset.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/password-reset.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/password-reset.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
4
package/@stackframe/react/dist/esm/components-page/section.js
vendored
Normal file
4
package/@stackframe/react/dist/esm/components-page/section.js
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
// src/components-page/section.tsx
|
||||
import { Separator, Typography } from "@stackframe/stack-ui";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
//# sourceMappingURL=section.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/section.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/section.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Separator, Typography } from \"@stackframe/stack-ui\";\n\n\nfunction Section(props: { title: string, description?: string, children: React.ReactNode }) {\n return (\n <>\n <Separator />\n <div className='flex flex-col sm:flex-row gap-2'>\n <div className='sm:flex-1 flex flex-col justify-center'>\n <Typography className='font-medium'>\n {props.title}\n </Typography>\n {props.description && <Typography variant='secondary' type='footnote'>\n {props.description}\n </Typography>}\n </div>\n <div className='sm:flex-1 sm:items-end flex flex-col gap-2 '>\n {props.children}\n </div>\n </div>\n </>\n );\n}\n"],"mappings":";AAIA,SAAS,WAAW,kBAAkB;AAKlC,mBACE,KAEE,YAHJ;","names":[]}
|
||||
19
package/@stackframe/react/dist/esm/components-page/sign-in.js
vendored
Normal file
19
package/@stackframe/react/dist/esm/components-page/sign-in.js
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// src/components-page/sign-in.tsx
|
||||
import { AuthPage } from "./auth-page";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function SignIn(props) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
AuthPage,
|
||||
{
|
||||
fullPage: !!props.fullPage,
|
||||
type: "sign-in",
|
||||
automaticRedirect: !!props.automaticRedirect,
|
||||
extraInfo: props.extraInfo,
|
||||
firstTab: props.firstTab
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
SignIn
|
||||
};
|
||||
//# sourceMappingURL=sign-in.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/sign-in.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/sign-in.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/sign-in.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { AuthPage } from \"./auth-page\";\n\nexport function SignIn(props: {\n fullPage?: boolean,\n automaticRedirect?: boolean,\n extraInfo?: React.ReactNode,\n firstTab?: 'magic-link' | 'password',\n}) {\n return (\n <AuthPage\n fullPage={!!props.fullPage}\n type=\"sign-in\"\n automaticRedirect={!!props.automaticRedirect}\n extraInfo={props.extraInfo}\n firstTab={props.firstTab}\n />\n );\n}\n"],"mappings":";AAIA,SAAS,gBAAgB;AASrB;AAPG,SAAS,OAAO,OAKpB;AACD,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,CAAC,CAAC,MAAM;AAAA,MAClB,MAAK;AAAA,MACL,mBAAmB,CAAC,CAAC,MAAM;AAAA,MAC3B,WAAW,MAAM;AAAA,MACjB,UAAU,MAAM;AAAA;AAAA,EAClB;AAEJ;","names":[]}
|
||||
23
package/@stackframe/react/dist/esm/components-page/sign-out.js
vendored
Normal file
23
package/@stackframe/react/dist/esm/components-page/sign-out.js
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/sign-out.tsx
|
||||
import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
|
||||
import React from "react";
|
||||
import { useUser } from "..";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
var cacheSignOut = cacheFunction(async (user) => {
|
||||
return await user.signOut();
|
||||
});
|
||||
function SignOut(props) {
|
||||
const user = useUser();
|
||||
if (user) {
|
||||
React.use(cacheSignOut(user));
|
||||
}
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "signedOut", fullPage: props.fullPage });
|
||||
}
|
||||
export {
|
||||
SignOut
|
||||
};
|
||||
//# sourceMappingURL=sign-out.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/sign-out.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/sign-out.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/sign-out.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { cacheFunction } from \"@stackframe/stack-shared/dist/utils/caches\";\nimport React from \"react\";\nimport { CurrentUser, useUser } from \"..\";\nimport { PredefinedMessageCard } from \"../components/message-cards/predefined-message-card\";\n\nconst cacheSignOut = cacheFunction(async (user: CurrentUser) => {\n return await user.signOut();\n});\n\nexport function SignOut(props: { fullPage?: boolean }) {\n const user = useUser();\n\n if (user) {\n React.use(cacheSignOut(user));\n }\n\n return <PredefinedMessageCard type='signedOut' fullPage={props.fullPage} />;\n}\n"],"mappings":";;;AAOA,SAAS,qBAAqB;AAC9B,OAAO,WAAW;AAClB,SAAsB,eAAe;AACrC,SAAS,6BAA6B;AAa7B;AAXT,IAAM,eAAe,cAAc,OAAO,SAAsB;AAC9D,SAAO,MAAM,KAAK,QAAQ;AAC5B,CAAC;AAEM,SAAS,QAAQ,OAA+B;AACrD,QAAM,OAAO,QAAQ;AAErB,MAAI,MAAM;AACR,UAAM,IAAI,aAAa,IAAI,CAAC;AAAA,EAC9B;AAEA,SAAO,oBAAC,yBAAsB,MAAK,aAAY,UAAU,MAAM,UAAU;AAC3E;","names":[]}
|
||||
23
package/@stackframe/react/dist/esm/components-page/sign-up.js
vendored
Normal file
23
package/@stackframe/react/dist/esm/components-page/sign-up.js
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/sign-up.tsx
|
||||
import { AuthPage } from "./auth-page";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function SignUp(props) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
AuthPage,
|
||||
{
|
||||
fullPage: !!props.fullPage,
|
||||
type: "sign-up",
|
||||
automaticRedirect: !!props.automaticRedirect,
|
||||
noPasswordRepeat: props.noPasswordRepeat,
|
||||
extraInfo: props.extraInfo,
|
||||
firstTab: props.firstTab
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
SignUp
|
||||
};
|
||||
//# sourceMappingURL=sign-up.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/sign-up.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/sign-up.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/sign-up.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { AuthPage } from './auth-page';\n\nexport function SignUp(props: {\n fullPage?: boolean,\n automaticRedirect?: boolean,\n noPasswordRepeat?: boolean,\n extraInfo?: React.ReactNode,\n firstTab?: 'magic-link' | 'password',\n}) {\n return <AuthPage\n fullPage={!!props.fullPage}\n type='sign-up'\n automaticRedirect={!!props.automaticRedirect}\n noPasswordRepeat={props.noPasswordRepeat}\n extraInfo={props.extraInfo}\n firstTab={props.firstTab}\n />;\n}\n"],"mappings":";;;AAMA,SAAS,gBAAgB;AAShB;AAPF,SAAS,OAAO,OAMpB;AACD,SAAO;AAAA,IAAC;AAAA;AAAA,MACN,UAAU,CAAC,CAAC,MAAM;AAAA,MAClB,MAAK;AAAA,MACL,mBAAmB,CAAC,CAAC,MAAM;AAAA,MAC3B,kBAAkB,MAAM;AAAA,MACxB,WAAW,MAAM;AAAA,MACjB,UAAU,MAAM;AAAA;AAAA,EAClB;AACF;","names":[]}
|
||||
234
package/@stackframe/react/dist/esm/components-page/stack-handler.js
vendored
Normal file
234
package/@stackframe/react/dist/esm/components-page/stack-handler.js
vendored
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
// src/components-page/stack-handler.tsx
|
||||
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { filterUndefined } from "@stackframe/stack-shared/dist/utils/objects";
|
||||
import { getRelativePart } from "@stackframe/stack-shared/dist/utils/urls";
|
||||
import { useMemo } from "react";
|
||||
import { SignIn, SignUp } from "..";
|
||||
import { IframePreventer } from "../components/iframe-preventer";
|
||||
import { MessageCard } from "../components/message-cards/message-card";
|
||||
import { AccountSettings } from "./account-settings";
|
||||
import { CliAuthConfirmation } from "./cli-auth-confirm";
|
||||
import { EmailVerification } from "./email-verification";
|
||||
import { ErrorPage } from "./error-page";
|
||||
import { ForgotPassword } from "./forgot-password";
|
||||
import { MagicLinkCallback } from "./magic-link-callback";
|
||||
import { OAuthCallback } from "./oauth-callback";
|
||||
import { PasswordReset } from "./password-reset";
|
||||
import { SignOut } from "./sign-out";
|
||||
import { TeamInvitation } from "./team-invitation";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
var availablePaths = {
|
||||
signIn: "sign-in",
|
||||
signUp: "sign-up",
|
||||
emailVerification: "email-verification",
|
||||
passwordReset: "password-reset",
|
||||
forgotPassword: "forgot-password",
|
||||
signOut: "sign-out",
|
||||
oauthCallback: "oauth-callback",
|
||||
magicLinkCallback: "magic-link-callback",
|
||||
teamInvitation: "team-invitation",
|
||||
accountSettings: "account-settings",
|
||||
cliAuthConfirm: "cli-auth-confirm",
|
||||
error: "error"
|
||||
};
|
||||
var pathAliases = {
|
||||
// also includes the uppercase and non-dashed versions
|
||||
...Object.fromEntries(Object.entries(availablePaths).map(([key, value]) => [value, value])),
|
||||
"log-in": availablePaths.signIn,
|
||||
"register": availablePaths.signUp
|
||||
};
|
||||
function renderComponent(props) {
|
||||
const { path, searchParams, fullPage, componentProps, redirectIfNotHandler, onNotFound, app } = props;
|
||||
switch (path) {
|
||||
case availablePaths.signIn: {
|
||||
redirectIfNotHandler?.("signIn");
|
||||
return /* @__PURE__ */ jsx(
|
||||
SignIn,
|
||||
{
|
||||
fullPage,
|
||||
automaticRedirect: true,
|
||||
...filterUndefinedINU(componentProps?.SignIn)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.signUp: {
|
||||
redirectIfNotHandler?.("signUp");
|
||||
return /* @__PURE__ */ jsx(
|
||||
SignUp,
|
||||
{
|
||||
fullPage,
|
||||
automaticRedirect: true,
|
||||
...filterUndefinedINU(componentProps?.SignUp)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.emailVerification: {
|
||||
redirectIfNotHandler?.("emailVerification");
|
||||
return /* @__PURE__ */ jsx(
|
||||
EmailVerification,
|
||||
{
|
||||
searchParams,
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.EmailVerification)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.passwordReset: {
|
||||
redirectIfNotHandler?.("passwordReset");
|
||||
return /* @__PURE__ */ jsx(
|
||||
PasswordReset,
|
||||
{
|
||||
searchParams,
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.PasswordReset)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.forgotPassword: {
|
||||
redirectIfNotHandler?.("forgotPassword");
|
||||
return /* @__PURE__ */ jsx(
|
||||
ForgotPassword,
|
||||
{
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.ForgotPassword)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.signOut: {
|
||||
redirectIfNotHandler?.("signOut");
|
||||
return /* @__PURE__ */ jsx(
|
||||
SignOut,
|
||||
{
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.SignOut)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.oauthCallback: {
|
||||
redirectIfNotHandler?.("oauthCallback");
|
||||
return /* @__PURE__ */ jsx(
|
||||
OAuthCallback,
|
||||
{
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.OAuthCallback)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.magicLinkCallback: {
|
||||
redirectIfNotHandler?.("magicLinkCallback");
|
||||
return /* @__PURE__ */ jsx(
|
||||
MagicLinkCallback,
|
||||
{
|
||||
searchParams,
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.MagicLinkCallback)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.teamInvitation: {
|
||||
redirectIfNotHandler?.("teamInvitation");
|
||||
return /* @__PURE__ */ jsx(
|
||||
TeamInvitation,
|
||||
{
|
||||
searchParams,
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.TeamInvitation)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.accountSettings: {
|
||||
return /* @__PURE__ */ jsx(
|
||||
AccountSettings,
|
||||
{
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.AccountSettings)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.error: {
|
||||
return /* @__PURE__ */ jsx(
|
||||
ErrorPage,
|
||||
{
|
||||
searchParams,
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.ErrorPage)
|
||||
}
|
||||
);
|
||||
}
|
||||
case availablePaths.cliAuthConfirm: {
|
||||
return /* @__PURE__ */ jsx(
|
||||
CliAuthConfirmation,
|
||||
{
|
||||
fullPage,
|
||||
...filterUndefinedINU(componentProps?.CliAuthConfirmation)
|
||||
}
|
||||
);
|
||||
}
|
||||
default: {
|
||||
if (Object.values(availablePaths).includes(path)) {
|
||||
throw new StackAssertionError(`Path alias ${path} not included in switch statement, but in availablePaths?`, { availablePaths });
|
||||
}
|
||||
for (const [key, value] of Object.entries(pathAliases)) {
|
||||
if (path === key.toLowerCase().replaceAll("-", "")) {
|
||||
const redirectUrl = `${app.urls.handler}/${value}?${new URLSearchParams(searchParams).toString()}`;
|
||||
return { redirect: redirectUrl };
|
||||
}
|
||||
}
|
||||
return onNotFound();
|
||||
}
|
||||
}
|
||||
}
|
||||
function ReactStackHandler(props) {
|
||||
const { path, searchParams } = useMemo(() => {
|
||||
const search = window.location.search;
|
||||
const handlerPath = new URL(props.app.urls.handler, window.location.origin).pathname;
|
||||
const relativePath = props.location.startsWith(handlerPath) ? props.location.slice(handlerPath.length).replace(/^\/+/, "") : props.location.replace(/^\/+/, "");
|
||||
return {
|
||||
path: relativePath,
|
||||
searchParams: Object.fromEntries(new URLSearchParams(search).entries())
|
||||
};
|
||||
}, [props.location, props.app.urls.handler]);
|
||||
const redirectIfNotHandler = (name) => {
|
||||
const url = props.app.urls[name];
|
||||
const handlerUrl = props.app.urls.handler;
|
||||
if (url !== handlerUrl && url.startsWith(handlerUrl + "/")) {
|
||||
return;
|
||||
}
|
||||
const urlObj = new URL(url, window.location.origin);
|
||||
for (const [key, value] of Object.entries(searchParams)) {
|
||||
urlObj.searchParams.set(key, value);
|
||||
}
|
||||
window.location.href = getRelativePart(urlObj);
|
||||
};
|
||||
const result = renderComponent({
|
||||
path,
|
||||
searchParams,
|
||||
fullPage: props.fullPage,
|
||||
componentProps: props.componentProps,
|
||||
redirectIfNotHandler,
|
||||
onNotFound: () => /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: "Page does not exist",
|
||||
fullPage: props.fullPage,
|
||||
primaryButtonText: "Go to Home",
|
||||
primaryAction: () => props.app.redirectToHome(),
|
||||
children: "The page you are looking for could not be found. Please check the URL and try again."
|
||||
}
|
||||
),
|
||||
app: props.app
|
||||
});
|
||||
if (result && "redirect" in result) {
|
||||
window.location.href = result.redirect;
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx(IframePreventer, { children: result });
|
||||
}
|
||||
var stack_handler_default = ReactStackHandler;
|
||||
function filterUndefinedINU(value) {
|
||||
return value === void 0 ? value : filterUndefined(value);
|
||||
}
|
||||
export {
|
||||
stack_handler_default as default
|
||||
};
|
||||
//# sourceMappingURL=stack-handler.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/stack-handler.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/stack-handler.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
68
package/@stackframe/react/dist/esm/components-page/team-creation.js
vendored
Normal file
68
package/@stackframe/react/dist/esm/components-page/team-creation.js
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/team-creation.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Label, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { MessageCard, useStackApp, useUser } from "..";
|
||||
import { FormWarningText } from "../components/elements/form-warning";
|
||||
import { MaybeFullPage } from "../components/elements/maybe-full-page";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function TeamCreation(props) {
|
||||
const { t } = useTranslation();
|
||||
const schema = yupObject({
|
||||
displayName: yupString().defined().nonEmpty(t("Please enter a team name"))
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
const app = useStackApp();
|
||||
const project = app.useProject();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const navigate = app.useNavigate();
|
||||
if (!project.config.clientTeamCreationEnabled) {
|
||||
return /* @__PURE__ */ jsx(MessageCard, { title: t("Team creation is not enabled") });
|
||||
}
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const team = await user.createTeam({ displayName: data.displayName });
|
||||
navigate(`${app.urls.handler}/team-settings/${team.id}`);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsx(MaybeFullPage, { fullPage: !!props.fullPage, children: /* @__PURE__ */ jsxs("div", { className: "stack-scope flex flex-col items-stretch", style: { maxWidth: "380px", flexBasis: "380px", padding: props.fullPage ? "1rem" : 0 }, children: [
|
||||
/* @__PURE__ */ jsx("div", { className: "text-center mb-6", children: /* @__PURE__ */ jsx(Typography, { type: "h2", children: t("Create a Team") }) }),
|
||||
/* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
className: "flex flex-col items-stretch stack-scope",
|
||||
onSubmit: (e) => runAsynchronously(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "display-name", className: "mb-1", children: t("Display name") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "display-name",
|
||||
...register("displayName")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.displayName?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Create") })
|
||||
]
|
||||
}
|
||||
)
|
||||
] }) });
|
||||
}
|
||||
export {
|
||||
TeamCreation
|
||||
};
|
||||
//# sourceMappingURL=team-creation.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/team-creation.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/team-creation.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components-page/team-creation.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { MessageCard, useStackApp, useUser } from \"..\";\nimport { FormWarningText } from \"../components/elements/form-warning\";\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function TeamCreation(props: { fullPage?: boolean }) {\n const { t } = useTranslation();\n\n const schema = yupObject({\n displayName: yupString().defined().nonEmpty(t('Please enter a team name')),\n });\n\n const { register, handleSubmit, formState: { errors } } = useForm({\n resolver: yupResolver(schema)\n });\n const app = useStackApp();\n const project = app.useProject();\n const user = useUser({ or: 'redirect' });\n const [loading, setLoading] = useState(false);\n const navigate = app.useNavigate();\n\n if (!project.config.clientTeamCreationEnabled) {\n return <MessageCard title={t('Team creation is not enabled')} />;\n }\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n\n try {\n const team = await user.createTeam({ displayName: data.displayName });\n navigate(`${app.urls.handler}/team-settings/${team.id}`);\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <MaybeFullPage fullPage={!!props.fullPage}>\n <div className='stack-scope flex flex-col items-stretch' style={{ maxWidth: '380px', flexBasis: '380px', padding: props.fullPage ? '1rem' : 0 }}>\n <div className=\"text-center mb-6\">\n <Typography type='h2'>\n {t('Create a Team')}\n </Typography>\n </div>\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronously(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"display-name\" className=\"mb-1\">{t('Display name')}</Label>\n <Input\n id=\"display-name\"\n {...register('displayName')}\n />\n <FormWarningText text={errors.displayName?.message?.toString()} />\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n {t('Create')}\n </Button>\n </form>\n </div>\n </MaybeFullPage>\n );\n}\n"],"mappings":";;;AAOA,SAAS,mBAAmB;AAC5B,SAAS,WAAW,iBAAiB;AACrC,SAAS,yBAAyB;AAClC,SAAS,QAAQ,OAAO,OAAO,kBAAkB;AACjD,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAExB,SAAS,aAAa,aAAa,eAAe;AAClD,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,sBAAsB;AAmBpB,cAsBH,YAtBG;AAjBJ,SAAS,aAAa,OAA+B;AAC1D,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,QAAM,SAAS,UAAU;AAAA,IACvB,aAAa,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,0BAA0B,CAAC;AAAA,EAC3E,CAAC;AAED,QAAM,EAAE,UAAU,cAAc,WAAW,EAAE,OAAO,EAAE,IAAI,QAAQ;AAAA,IAChE,UAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,MAAM,YAAY;AACxB,QAAM,UAAU,IAAI,WAAW;AAC/B,QAAM,OAAO,QAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAC5C,QAAM,WAAW,IAAI,YAAY;AAEjC,MAAI,CAAC,QAAQ,OAAO,2BAA2B;AAC7C,WAAO,oBAAC,eAAY,OAAO,EAAE,8BAA8B,GAAG;AAAA,EAChE;AAEA,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AAEf,QAAI;AACF,YAAM,OAAO,MAAM,KAAK,WAAW,EAAE,aAAa,KAAK,YAAY,CAAC;AACpE,eAAS,GAAG,IAAI,KAAK,OAAO,kBAAkB,KAAK,EAAE,EAAE;AAAA,IACzD,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE,oBAAC,iBAAc,UAAU,CAAC,CAAC,MAAM,UAC/B,+BAAC,SAAI,WAAU,2CAA0C,OAAO,EAAE,UAAU,SAAS,WAAW,SAAS,SAAS,MAAM,WAAW,SAAS,EAAE,GAC5I;AAAA,wBAAC,SAAI,WAAU,oBACb,8BAAC,cAAW,MAAK,MACd,YAAE,eAAe,GACpB,GACF;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,WAAU;AAAA,QACV,UAAU,OAAK,kBAAkB,aAAa,QAAQ,EAAE,CAAC,CAAC;AAAA,QAC1D,YAAU;AAAA,QAEV;AAAA,8BAAC,SAAM,SAAQ,gBAAe,WAAU,QAAQ,YAAE,cAAc,GAAE;AAAA,UAClE;AAAA,YAAC;AAAA;AAAA,cACC,IAAG;AAAA,cACF,GAAG,SAAS,aAAa;AAAA;AAAA,UAC5B;AAAA,UACA,oBAAC,mBAAgB,MAAM,OAAO,aAAa,SAAS,SAAS,GAAG;AAAA,UAEhE,oBAAC,UAAO,MAAK,UAAS,WAAU,QAAO,SACpC,YAAE,QAAQ,GACb;AAAA;AAAA;AAAA,IACF;AAAA,KACF,GACF;AAEJ;","names":[]}
|
||||
110
package/@stackframe/react/dist/esm/components-page/team-invitation.js
vendored
Normal file
110
package/@stackframe/react/dist/esm/components-page/team-invitation.js
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components-page/team-invitation.tsx
|
||||
import { KnownErrors } from "@stackframe/stack-shared";
|
||||
import { cacheFunction } from "@stackframe/stack-shared/dist/utils/caches";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Typography } from "@stackframe/stack-ui";
|
||||
import React from "react";
|
||||
import { MessageCard, useStackApp, useUser } from "..";
|
||||
import { PredefinedMessageCard } from "../components/message-cards/predefined-message-card";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
var cachedVerifyInvitation = cacheFunction(async (stackApp, code) => {
|
||||
return await stackApp.verifyTeamInvitationCode(code);
|
||||
});
|
||||
var cachedGetInvitationDetails = cacheFunction(async (stackApp, code) => {
|
||||
return await stackApp.getTeamInvitationDetails(code);
|
||||
});
|
||||
function TeamInvitationInner(props) {
|
||||
const { t } = useTranslation();
|
||||
const stackApp = useStackApp();
|
||||
const [success, setSuccess] = React.useState(false);
|
||||
const [errorMessage, setErrorMessage] = React.useState(null);
|
||||
const details = React.use(cachedGetInvitationDetails(stackApp, props.searchParams.code || ""));
|
||||
if (errorMessage || details.status === "error") {
|
||||
return /* @__PURE__ */ jsx(PredefinedMessageCard, { type: "unknownError", fullPage: props.fullPage });
|
||||
}
|
||||
if (success) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Team invitation"),
|
||||
fullPage: props.fullPage,
|
||||
primaryButtonText: "Go home",
|
||||
primaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsxs(Typography, { children: [
|
||||
"You have successfully joined ",
|
||||
details.data.teamDisplayName
|
||||
] })
|
||||
}
|
||||
);
|
||||
}
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Team invitation"),
|
||||
fullPage: props.fullPage,
|
||||
primaryButtonText: t("Join"),
|
||||
primaryAction: () => runAsynchronouslyWithAlert(async () => {
|
||||
const result = await stackApp.acceptTeamInvitation(props.searchParams.code || "");
|
||||
if (result.status === "error") {
|
||||
setErrorMessage(result.error.message);
|
||||
} else {
|
||||
setSuccess(true);
|
||||
}
|
||||
}),
|
||||
secondaryButtonText: t("Ignore"),
|
||||
secondaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsxs(Typography, { children: [
|
||||
"You are invited to join ",
|
||||
details.data.teamDisplayName
|
||||
] })
|
||||
}
|
||||
);
|
||||
}
|
||||
function TeamInvitation({ fullPage = false, searchParams }) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser();
|
||||
const stackApp = useStackApp();
|
||||
const invalidJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Invalid Team Invitation Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("Please double check if you have the correct team invitation link.") }) });
|
||||
const expiredJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Expired Team Invitation Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("Your team invitation link has expired. Please request a new team invitation link ") }) });
|
||||
const usedJsx = /* @__PURE__ */ jsx(MessageCard, { title: t("Used Team Invitation Link"), fullPage, children: /* @__PURE__ */ jsx(Typography, { children: t("This team invitation link has already been used.") }) });
|
||||
const code = searchParams.code;
|
||||
if (!code) {
|
||||
return invalidJsx;
|
||||
}
|
||||
if (!user) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
MessageCard,
|
||||
{
|
||||
title: t("Team invitation"),
|
||||
fullPage,
|
||||
primaryButtonText: t("Sign in"),
|
||||
primaryAction: () => stackApp.redirectToSignIn(),
|
||||
secondaryButtonText: t("Cancel"),
|
||||
secondaryAction: () => stackApp.redirectToHome(),
|
||||
children: /* @__PURE__ */ jsx(Typography, { children: t("Sign in or create an account to join the team.") })
|
||||
}
|
||||
);
|
||||
}
|
||||
const verificationResult = React.use(cachedVerifyInvitation(stackApp, searchParams.code || ""));
|
||||
if (verificationResult.status === "error") {
|
||||
const error = verificationResult.error;
|
||||
if (KnownErrors.VerificationCodeNotFound.isInstance(error)) {
|
||||
return invalidJsx;
|
||||
} else if (KnownErrors.VerificationCodeExpired.isInstance(error)) {
|
||||
return expiredJsx;
|
||||
} else if (KnownErrors.VerificationCodeAlreadyUsed.isInstance(error)) {
|
||||
return usedJsx;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return /* @__PURE__ */ jsx(TeamInvitationInner, { fullPage, searchParams });
|
||||
}
|
||||
export {
|
||||
TeamInvitation
|
||||
};
|
||||
//# sourceMappingURL=team-invitation.js.map
|
||||
1
package/@stackframe/react/dist/esm/components-page/team-invitation.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components-page/team-invitation.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
157
package/@stackframe/react/dist/esm/components/api-key-dialogs.js
vendored
Normal file
157
package/@stackframe/react/dist/esm/components/api-key-dialogs.js
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/api-key-dialogs.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { yupObject, yupString } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { captureError } from "@stackframe/stack-shared/dist/utils/errors";
|
||||
import { runAsynchronously } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { ActionDialog, Button, CopyField, Input, Label, Typography } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useUser } from "..";
|
||||
import { FormWarningText } from "../components/elements/form-warning";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
var neverInMs = 1e3 * 60 * 60 * 24 * 365 * 200;
|
||||
var expiresInOptions = {
|
||||
[1e3 * 60 * 60 * 24 * 1]: "1 day",
|
||||
[1e3 * 60 * 60 * 24 * 7]: "7 days",
|
||||
[1e3 * 60 * 60 * 24 * 30]: "30 days",
|
||||
[1e3 * 60 * 60 * 24 * 90]: "90 days",
|
||||
[1e3 * 60 * 60 * 24 * 365]: "1 year",
|
||||
[neverInMs]: "Never"
|
||||
};
|
||||
function CreateApiKeyDialog(props) {
|
||||
const { t } = useTranslation();
|
||||
const user = useUser({ or: "redirect" });
|
||||
const [loading, setLoading] = useState(false);
|
||||
const apiKeySchema = yupObject({
|
||||
description: yupString().defined().nonEmpty(t("Description is required")),
|
||||
expiresIn: yupString().defined()
|
||||
});
|
||||
const { register, handleSubmit, formState: { errors }, reset } = useForm({
|
||||
resolver: yupResolver(apiKeySchema),
|
||||
defaultValues: {
|
||||
description: "",
|
||||
expiresIn: Object.keys(expiresInOptions)[2]
|
||||
// Default to 30 days
|
||||
}
|
||||
});
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const expiresAt = new Date(Date.now() + parseInt(data.expiresIn));
|
||||
const apiKey = await props.createApiKey({
|
||||
description: data.description,
|
||||
expiresAt
|
||||
});
|
||||
if (props.onKeyCreated) {
|
||||
props.onKeyCreated(apiKey);
|
||||
}
|
||||
reset();
|
||||
props.onOpenChange(false);
|
||||
} catch (error) {
|
||||
captureError("Failed to create API key", { error });
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsx(
|
||||
ActionDialog,
|
||||
{
|
||||
open: props.open,
|
||||
onOpenChange: props.onOpenChange,
|
||||
title: t("Create API Key"),
|
||||
description: t("API keys grant programmatic access to your account."),
|
||||
children: /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
onSubmit: (e) => {
|
||||
e.preventDefault();
|
||||
runAsynchronously(handleSubmit(onSubmit));
|
||||
},
|
||||
className: "space-y-4",
|
||||
children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "description", children: t("Description") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "description",
|
||||
placeholder: t("e.g. Development, Production, CI/CD"),
|
||||
...register("description")
|
||||
}
|
||||
),
|
||||
errors.description && /* @__PURE__ */ jsx(FormWarningText, { text: errors.description.message })
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "expiresIn", children: t("Expires In") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
"select",
|
||||
{
|
||||
id: "expiresIn",
|
||||
className: "w-full p-2 border border-input rounded-md bg-background",
|
||||
...register("expiresIn"),
|
||||
children: Object.entries(expiresInOptions).map(([value, label]) => /* @__PURE__ */ jsx("option", { value, children: t(label) }, value))
|
||||
}
|
||||
),
|
||||
errors.expiresIn && /* @__PURE__ */ jsx(FormWarningText, { text: errors.expiresIn.message })
|
||||
] }),
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 pt-4", children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
type: "button",
|
||||
variant: "secondary",
|
||||
onClick: () => {
|
||||
reset();
|
||||
props.onOpenChange(false);
|
||||
},
|
||||
children: t("Cancel")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", loading, children: t("Create") })
|
||||
] })
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
function ShowApiKeyDialog(props) {
|
||||
const { t } = useTranslation();
|
||||
return /* @__PURE__ */ jsx(
|
||||
ActionDialog,
|
||||
{
|
||||
open: !!props.apiKey,
|
||||
title: t("API Key"),
|
||||
okButton: { label: t("Close") },
|
||||
onClose: props.onClose,
|
||||
preventClose: true,
|
||||
confirmText: t("I understand that I will not be able to view this key again."),
|
||||
children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-4", children: [
|
||||
/* @__PURE__ */ jsxs(Typography, { children: [
|
||||
t("Here is your API key."),
|
||||
" ",
|
||||
/* @__PURE__ */ jsx("span", { className: "font-bold", children: t("Copy it to a safe place. You will not be able to view it again.") })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(
|
||||
CopyField,
|
||||
{
|
||||
monospace: true,
|
||||
value: props.apiKey?.value ?? "",
|
||||
label: t("Secret API Key")
|
||||
}
|
||||
)
|
||||
] })
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
CreateApiKeyDialog,
|
||||
ShowApiKeyDialog,
|
||||
expiresInOptions,
|
||||
neverInMs
|
||||
};
|
||||
//# sourceMappingURL=api-key-dialogs.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/api-key-dialogs.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/api-key-dialogs.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
125
package/@stackframe/react/dist/esm/components/api-key-table.js
vendored
Normal file
125
package/@stackframe/react/dist/esm/components/api-key-table.js
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/api-key-table.tsx
|
||||
import { ActionCell, ActionDialog, BadgeCell, DataTable, DataTableColumnHeader, DataTableFacetedFilter, DateCell, SearchToolbarItem, TextCell, standardFilterFn } from "@stackframe/stack-ui";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function toolbarRender(table) {
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(SearchToolbarItem, { table, placeholder: "Search table" }),
|
||||
/* @__PURE__ */ jsx(
|
||||
DataTableFacetedFilter,
|
||||
{
|
||||
column: table.getColumn("status"),
|
||||
title: "Status",
|
||||
options: ["valid", "expired", "revoked"].map((provider) => ({
|
||||
value: provider,
|
||||
label: provider
|
||||
}))
|
||||
}
|
||||
)
|
||||
] });
|
||||
}
|
||||
function RevokeDialog(props) {
|
||||
return /* @__PURE__ */ jsx(
|
||||
ActionDialog,
|
||||
{
|
||||
open: props.open,
|
||||
onOpenChange: props.onOpenChange,
|
||||
title: "Revoke API Key",
|
||||
danger: true,
|
||||
cancelButton: true,
|
||||
okButton: { label: "Revoke Key", onClick: async () => {
|
||||
await props.apiKey.revoke();
|
||||
} },
|
||||
confirmText: "I understand this will unlink all the apps using this API key",
|
||||
children: `Are you sure you want to revoke API key *****${props.apiKey.value.lastFour}?`
|
||||
}
|
||||
);
|
||||
}
|
||||
function Actions({ row }) {
|
||||
const [isRevokeModalOpen, setIsRevokeModalOpen] = useState(false);
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(RevokeDialog, { apiKey: row.original, open: isRevokeModalOpen, onOpenChange: setIsRevokeModalOpen }),
|
||||
/* @__PURE__ */ jsx(
|
||||
ActionCell,
|
||||
{
|
||||
invisible: row.original.status !== "valid",
|
||||
items: [{
|
||||
item: "Revoke",
|
||||
danger: true,
|
||||
onClick: () => setIsRevokeModalOpen(true)
|
||||
}]
|
||||
}
|
||||
)
|
||||
] });
|
||||
}
|
||||
var columns = [
|
||||
{
|
||||
accessorKey: "description",
|
||||
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Description" }),
|
||||
cell: ({ row }) => /* @__PURE__ */ jsx(TextCell, { size: 100, children: row.original.description })
|
||||
},
|
||||
{
|
||||
accessorKey: "status",
|
||||
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Status" }),
|
||||
cell: ({ row }) => /* @__PURE__ */ jsx(BadgeCell, { badges: [row.original.status] }),
|
||||
filterFn: standardFilterFn
|
||||
},
|
||||
{
|
||||
id: "value",
|
||||
accessorFn: (row) => row.value.lastFour,
|
||||
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Client Key" }),
|
||||
cell: ({ row }) => /* @__PURE__ */ jsxs(TextCell, { children: [
|
||||
"*******",
|
||||
row.original.value.lastFour
|
||||
] }),
|
||||
enableSorting: false
|
||||
},
|
||||
{
|
||||
accessorKey: "expiresAt",
|
||||
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Expires At" }),
|
||||
cell: ({ row }) => {
|
||||
if (row.original.status === "revoked") return /* @__PURE__ */ jsx(TextCell, { children: "-" });
|
||||
return row.original.expiresAt ? /* @__PURE__ */ jsx(DateCell, { date: row.original.expiresAt, ignoreAfterYears: 50 }) : /* @__PURE__ */ jsx(TextCell, { children: "Never" });
|
||||
}
|
||||
},
|
||||
{
|
||||
accessorKey: "createdAt",
|
||||
header: ({ column }) => /* @__PURE__ */ jsx(DataTableColumnHeader, { column, columnTitle: "Created At" }),
|
||||
cell: ({ row }) => /* @__PURE__ */ jsx(DateCell, { date: row.original.createdAt, ignoreAfterYears: 50 })
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => /* @__PURE__ */ jsx(Actions, { row })
|
||||
}
|
||||
];
|
||||
function ApiKeyTable(props) {
|
||||
const extendedApiKeys = useMemo(() => {
|
||||
const keys = props.apiKeys.map((apiKey) => ({
|
||||
...apiKey,
|
||||
status: { "valid": "valid", "manually-revoked": "revoked", "expired": "expired" }[apiKey.whyInvalid() || "valid"]
|
||||
}));
|
||||
return keys.sort((a, b) => {
|
||||
if (a.status === b.status) {
|
||||
return a.createdAt < b.createdAt ? 1 : -1;
|
||||
}
|
||||
return a.status === "valid" ? -1 : 1;
|
||||
});
|
||||
}, [props.apiKeys]);
|
||||
return /* @__PURE__ */ jsx(
|
||||
DataTable,
|
||||
{
|
||||
data: extendedApiKeys,
|
||||
columns,
|
||||
toolbarRender,
|
||||
defaultColumnFilters: [],
|
||||
defaultSorting: []
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
ApiKeyTable
|
||||
};
|
||||
//# sourceMappingURL=api-key-table.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/api-key-table.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/api-key-table.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
79
package/@stackframe/react/dist/esm/components/credential-sign-in.js
vendored
Normal file
79
package/@stackframe/react/dist/esm/components/credential-sign-in.js
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/credential-sign-in.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { passwordSchema, strictEmailSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Label, PasswordInput } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useStackApp } from "..";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { FormWarningText } from "./elements/form-warning";
|
||||
import { StyledLink } from "./link";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function CredentialSignIn() {
|
||||
const { t } = useTranslation();
|
||||
const schema = yupObject({
|
||||
email: strictEmailSchema(t("Please enter a valid email")).defined().nonEmpty(t("Please enter your email")),
|
||||
password: passwordSchema.defined().nonEmpty(t("Please enter your password"))
|
||||
});
|
||||
const { register, handleSubmit, setError, formState: { errors } } = useForm({
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
const app = useStackApp();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { email, password } = data;
|
||||
const result = await app.signInWithCredential({
|
||||
email,
|
||||
password
|
||||
});
|
||||
if (result.status === "error") {
|
||||
setError("email", { type: "manual", message: result.error.message });
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
return /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
className: "flex flex-col items-stretch stack-scope",
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: t("Email") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Input,
|
||||
{
|
||||
id: "email",
|
||||
type: "email",
|
||||
autoComplete: "email",
|
||||
...register("email")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "password", className: "mt-4 mb-1", children: t("Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "password",
|
||||
autoComplete: "current-password",
|
||||
...register("password")
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.password?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(StyledLink, { href: app.urls.forgotPassword, className: "mt-1 text-sm", children: t("Forgot password?") }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Sign In") })
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
CredentialSignIn
|
||||
};
|
||||
//# sourceMappingURL=credential-sign-in.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/credential-sign-in.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/credential-sign-in.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../src/components/credential-sign-in.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { passwordSchema, strictEmailSchema, yupObject } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, PasswordInput } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { FormWarningText } from \"./elements/form-warning\";\nimport { StyledLink } from \"./link\";\n\nexport function CredentialSignIn() {\n const { t } = useTranslation();\n\n const schema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email')).defined().nonEmpty(t('Please enter your email')),\n password: passwordSchema.defined().nonEmpty(t('Please enter your password'))\n });\n\n const { register, handleSubmit, setError, formState: { errors } } = useForm({\n resolver: yupResolver(schema)\n });\n const app = useStackApp();\n const [loading, setLoading] = useState(false);\n\n const onSubmit = async (data: yup.InferType<typeof schema>) => {\n setLoading(true);\n\n try {\n const { email, password } = data;\n const result = await app.signInWithCredential({\n email,\n password,\n });\n if (result.status === 'error') {\n setError('email', { type: 'manual', message: result.error.message });\n }\n } finally {\n setLoading(false);\n }\n };\n\n return (\n <form\n className=\"flex flex-col items-stretch stack-scope\"\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n <Label htmlFor=\"email\" className=\"mb-1\">{t('Email')}</Label>\n <Input\n id=\"email\"\n type=\"email\"\n autoComplete=\"email\"\n {...register('email')}\n />\n <FormWarningText text={errors.email?.message?.toString()} />\n\n <Label htmlFor=\"password\" className=\"mt-4 mb-1\">{t('Password')}</Label>\n <PasswordInput\n id=\"password\"\n autoComplete=\"current-password\"\n {...register('password')}\n />\n <FormWarningText text={errors.password?.message?.toString()} />\n\n <StyledLink href={app.urls.forgotPassword} className=\"mt-1 text-sm\">\n {t('Forgot password?')}\n </StyledLink>\n\n <Button type=\"submit\" className=\"mt-6\" loading={loading}>\n {t('Sign In')}\n </Button>\n </form>\n );\n}\n"],"mappings":";;;AAOA,SAAS,mBAAmB;AAC5B,SAAS,gBAAgB,mBAAmB,iBAAiB;AAC7D,SAAS,kCAAkC;AAC3C,SAAS,QAAQ,OAAO,OAAO,qBAAqB;AACpD,SAAS,gBAAgB;AACzB,SAAS,eAAe;AAExB,SAAS,mBAAmB;AAC5B,SAAS,sBAAsB;AAC/B,SAAS,uBAAuB;AAChC,SAAS,kBAAkB;AAkCvB,SAKE,KALF;AAhCG,SAAS,mBAAmB;AACjC,QAAM,EAAE,EAAE,IAAI,eAAe;AAE7B,QAAM,SAAS,UAAU;AAAA,IACvB,OAAO,kBAAkB,EAAE,4BAA4B,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,yBAAyB,CAAC;AAAA,IACzG,UAAU,eAAe,QAAQ,EAAE,SAAS,EAAE,4BAA4B,CAAC;AAAA,EAC7E,CAAC;AAED,QAAM,EAAE,UAAU,cAAc,UAAU,WAAW,EAAE,OAAO,EAAE,IAAI,QAAQ;AAAA,IAC1E,UAAU,YAAY,MAAM;AAAA,EAC9B,CAAC;AACD,QAAM,MAAM,YAAY;AACxB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,KAAK;AAE5C,QAAM,WAAW,OAAO,SAAuC;AAC7D,eAAW,IAAI;AAEf,QAAI;AACF,YAAM,EAAE,OAAO,SAAS,IAAI;AAC5B,YAAM,SAAS,MAAM,IAAI,qBAAqB;AAAA,QAC5C;AAAA,QACA;AAAA,MACF,CAAC;AACD,UAAI,OAAO,WAAW,SAAS;AAC7B,iBAAS,SAAS,EAAE,MAAM,UAAU,SAAS,OAAO,MAAM,QAAQ,CAAC;AAAA,MACrE;AAAA,IACF,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAU;AAAA,MACV,UAAU,OAAK,2BAA2B,aAAa,QAAQ,EAAE,CAAC,CAAC;AAAA,MACnE,YAAU;AAAA,MAEV;AAAA,4BAAC,SAAM,SAAQ,SAAQ,WAAU,QAAQ,YAAE,OAAO,GAAE;AAAA,QACpD;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACL,cAAa;AAAA,YACZ,GAAG,SAAS,OAAO;AAAA;AAAA,QACtB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,OAAO,SAAS,SAAS,GAAG;AAAA,QAE1D,oBAAC,SAAM,SAAQ,YAAW,WAAU,aAAa,YAAE,UAAU,GAAE;AAAA,QAC/D;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,cAAa;AAAA,YACZ,GAAG,SAAS,UAAU;AAAA;AAAA,QACzB;AAAA,QACA,oBAAC,mBAAgB,MAAM,OAAO,UAAU,SAAS,SAAS,GAAG;AAAA,QAE7D,oBAAC,cAAW,MAAM,IAAI,KAAK,gBAAgB,WAAU,gBAClD,YAAE,kBAAkB,GACvB;AAAA,QAEA,oBAAC,UAAO,MAAK,UAAS,WAAU,QAAO,SACpC,YAAE,SAAS,GACd;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
||||
104
package/@stackframe/react/dist/esm/components/credential-sign-up.js
vendored
Normal file
104
package/@stackframe/react/dist/esm/components/credential-sign-up.js
vendored
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/credential-sign-up.tsx
|
||||
import { yupResolver } from "@hookform/resolvers/yup";
|
||||
import { getPasswordError } from "@stackframe/stack-shared/dist/helpers/password";
|
||||
import { passwordSchema, strictEmailSchema, yupObject } from "@stackframe/stack-shared/dist/schema-fields";
|
||||
import { runAsynchronously, runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
||||
import { Button, Input, Label, PasswordInput } from "@stackframe/stack-ui";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import * as yup from "yup";
|
||||
import { useStackApp } from "..";
|
||||
import { useTranslation } from "../lib/translations";
|
||||
import { FormWarningText } from "./elements/form-warning";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function CredentialSignUp(props) {
|
||||
const { t } = useTranslation();
|
||||
const schema = yupObject({
|
||||
email: strictEmailSchema(t("Please enter a valid email")).defined().nonEmpty(t("Please enter your email")),
|
||||
password: passwordSchema.defined().nonEmpty(t("Please enter your password")).test({
|
||||
name: "is-valid-password",
|
||||
test: (value, ctx) => {
|
||||
const error = getPasswordError(value);
|
||||
if (error) {
|
||||
return ctx.createError({ message: error.message });
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}),
|
||||
...!props.noPasswordRepeat && {
|
||||
passwordRepeat: passwordSchema.nullable().oneOf([yup.ref("password"), "", null], t("Passwords do not match")).nonEmpty(t("Please repeat your password"))
|
||||
}
|
||||
});
|
||||
const { register, handleSubmit, setError, formState: { errors }, clearErrors } = useForm({
|
||||
resolver: yupResolver(schema)
|
||||
});
|
||||
const app = useStackApp();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const onSubmit = async (data) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { email, password } = data;
|
||||
const result = await app.signUpWithCredential({ email, password });
|
||||
if (result.status === "error") {
|
||||
setError("email", { type: "manual", message: result.error.message });
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
const registerPassword = register("password");
|
||||
const registerPasswordRepeat = register("passwordRepeat");
|
||||
return /* @__PURE__ */ jsxs(
|
||||
"form",
|
||||
{
|
||||
className: "flex flex-col items-stretch stack-scope",
|
||||
onSubmit: (e) => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e)),
|
||||
noValidate: true,
|
||||
children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "email", className: "mb-1", children: t("Email") }),
|
||||
/* @__PURE__ */ jsx(Input, { id: "email", type: "email", autoComplete: "email", ...register("email") }),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.email?.message?.toString() }),
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "password", className: "mt-4 mb-1", children: t("Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "password",
|
||||
autoComplete: "new-password",
|
||||
...registerPassword,
|
||||
onChange: (e) => {
|
||||
clearErrors("password");
|
||||
clearErrors("passwordRepeat");
|
||||
runAsynchronously(registerPassword.onChange(e));
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.password?.message?.toString() }),
|
||||
!props.noPasswordRepeat && /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat Password") }),
|
||||
/* @__PURE__ */ jsx(
|
||||
PasswordInput,
|
||||
{
|
||||
id: "repeat-password",
|
||||
...registerPasswordRepeat,
|
||||
onChange: (e) => {
|
||||
clearErrors("password");
|
||||
clearErrors("passwordRepeat");
|
||||
runAsynchronously(registerPasswordRepeat.onChange(e));
|
||||
}
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(FormWarningText, { text: errors.passwordRepeat?.message?.toString() })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx(Button, { type: "submit", className: "mt-6", loading, children: t("Sign Up") })
|
||||
]
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
CredentialSignUp
|
||||
};
|
||||
//# sourceMappingURL=credential-sign-up.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/credential-sign-up.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/credential-sign-up.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
15
package/@stackframe/react/dist/esm/components/elements/form-warning.js
vendored
Normal file
15
package/@stackframe/react/dist/esm/components/elements/form-warning.js
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/elements/form-warning.tsx
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function FormWarningText({ text }) {
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
return /* @__PURE__ */ jsx("div", { className: "text-red-500 text-sm mt-1", children: text });
|
||||
}
|
||||
export {
|
||||
FormWarningText
|
||||
};
|
||||
//# sourceMappingURL=form-warning.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/form-warning.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/form-warning.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components/elements/form-warning.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nexport function FormWarningText({ text }: { text?: string }) {\n if (!text) {\n return null;\n }\n return (\n <div className=\"text-red-500 text-sm mt-1\">\n {text}\n </div>\n );\n}\n"],"mappings":";;;AAYI;AALG,SAAS,gBAAgB,EAAE,KAAK,GAAsB;AAC3D,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,SACE,oBAAC,SAAI,WAAU,6BACZ,gBACH;AAEJ;","names":[]}
|
||||
51
package/@stackframe/react/dist/esm/components/elements/maybe-full-page.js
vendored
Normal file
51
package/@stackframe/react/dist/esm/components/elements/maybe-full-page.js
vendored
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/elements/maybe-full-page.tsx
|
||||
import { useId } from "react";
|
||||
import { SsrScript } from "./ssr-layout-effect";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function MaybeFullPage({
|
||||
children,
|
||||
fullPage
|
||||
}) {
|
||||
const uniqueId = useId();
|
||||
const id = `stack-full-page-container-${uniqueId}`;
|
||||
const scriptString = `(([id]) => {
|
||||
const el = document.getElementById(id);
|
||||
if (!el) {
|
||||
// component is not full page
|
||||
return;
|
||||
}
|
||||
const offset = el.getBoundingClientRect().top + document.documentElement.scrollTop;
|
||||
el.style.minHeight = \`calc(100vh - \${offset}px)\`;
|
||||
})(${JSON.stringify([id])})`;
|
||||
if (fullPage) {
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx(
|
||||
"div",
|
||||
{
|
||||
suppressHydrationWarning: true,
|
||||
id,
|
||||
style: {
|
||||
minHeight: "100vh",
|
||||
alignSelf: "stretch",
|
||||
flexGrow: 1,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center"
|
||||
},
|
||||
className: "stack-scope",
|
||||
children
|
||||
}
|
||||
),
|
||||
/* @__PURE__ */ jsx(SsrScript, { script: scriptString })
|
||||
] });
|
||||
} else {
|
||||
return /* @__PURE__ */ jsx(Fragment, { children });
|
||||
}
|
||||
}
|
||||
export {
|
||||
MaybeFullPage
|
||||
};
|
||||
//# sourceMappingURL=maybe-full-page.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/maybe-full-page.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/maybe-full-page.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components/elements/maybe-full-page.tsx"],"sourcesContent":["\"use client\";\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport React, { useId } from \"react\";\nimport { SsrScript } from \"./ssr-layout-effect\";\n\nexport function MaybeFullPage({\n children,\n fullPage,\n}: {\n children: React.ReactNode,\n fullPage: boolean,\n size?: number,\n containerClassName?: string,\n}) {\n const uniqueId = useId();\n const id = `stack-full-page-container-${uniqueId}`;\n\n const scriptString = `(([id]) => {\n const el = document.getElementById(id);\n if (!el) {\n // component is not full page\n return;\n }\n const offset = el.getBoundingClientRect().top + document.documentElement.scrollTop;\n el.style.minHeight = \\`calc(100vh - \\${offset}px)\\`;\n })(${JSON.stringify([id])})`;\n\n if (fullPage) {\n return (\n <>\n <div\n suppressHydrationWarning\n id={id}\n style={{\n minHeight: '100vh',\n alignSelf: 'stretch',\n flexGrow: 1,\n display: 'flex',\n justifyContent: 'center',\n alignItems: 'center',\n }}\n className=\"stack-scope\"\n >\n {children}\n </div>\n <SsrScript script={scriptString} />\n </>\n );\n } else {\n return <>\n {children}\n </>;\n }\n\n}\n"],"mappings":";;;AAOA,SAAgB,aAAa;AAC7B,SAAS,iBAAiB;AA0BpB,mBACE,KADF;AAxBC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AACF,GAKG;AACD,QAAM,WAAW,MAAM;AACvB,QAAM,KAAK,6BAA6B,QAAQ;AAEhD,QAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAQhB,KAAK,UAAU,CAAC,EAAE,CAAC,CAAC;AAEzB,MAAI,UAAU;AACZ,WACE,iCACE;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,0BAAwB;AAAA,UACxB;AAAA,UACA,OAAO;AAAA,YACL,WAAW;AAAA,YACX,WAAW;AAAA,YACX,UAAU;AAAA,YACV,SAAS;AAAA,YACT,gBAAgB;AAAA,YAChB,YAAY;AAAA,UACd;AAAA,UACA,WAAU;AAAA,UAET;AAAA;AAAA,MACH;AAAA,MACA,oBAAC,aAAU,QAAQ,cAAc;AAAA,OACnC;AAAA,EAEJ,OAAO;AACL,WAAO,gCACJ,UACH;AAAA,EACF;AAEF;","names":[]}
|
||||
17
package/@stackframe/react/dist/esm/components/elements/separator-with-text.js
vendored
Normal file
17
package/@stackframe/react/dist/esm/components/elements/separator-with-text.js
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/elements/separator-with-text.tsx
|
||||
import { Separator } from "@stackframe/stack-ui";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
function SeparatorWithText({ text }) {
|
||||
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center my-6 stack-scope", children: [
|
||||
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Separator, {}) }),
|
||||
/* @__PURE__ */ jsx("div", { className: "mx-2 text-sm text-zinc-500", children: text }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(Separator, {}) })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
SeparatorWithText
|
||||
};
|
||||
//# sourceMappingURL=separator-with-text.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/separator-with-text.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/separator-with-text.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components/elements/separator-with-text.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { Separator } from \"@stackframe/stack-ui\";\n\nexport function SeparatorWithText({ text }: { text: string }) {\n return (\n <div className=\"flex items-center justify-center my-6 stack-scope\">\n <div className=\"flex-1\">\n <Separator />\n </div>\n <div className=\"mx-2 text-sm text-zinc-500\">{text}</div>\n <div className=\"flex-1\">\n <Separator />\n </div>\n </div>\n );\n}\n"],"mappings":";;;AAOA,SAAS,iBAAiB;AAItB,SAEI,KAFJ;AAFG,SAAS,kBAAkB,EAAE,KAAK,GAAqB;AAC5D,SACE,qBAAC,SAAI,WAAU,qDACb;AAAA,wBAAC,SAAI,WAAU,UACb,8BAAC,aAAU,GACb;AAAA,IACA,oBAAC,SAAI,WAAU,8BAA8B,gBAAK;AAAA,IAClD,oBAAC,SAAI,WAAU,UACb,8BAAC,aAAU,GACb;AAAA,KACF;AAEJ;","names":[]}
|
||||
95
package/@stackframe/react/dist/esm/components/elements/sidebar-layout.js
vendored
Normal file
95
package/@stackframe/react/dist/esm/components/elements/sidebar-layout.js
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/elements/sidebar-layout.tsx
|
||||
import { useHash } from "@stackframe/stack-shared/dist/hooks/use-hash";
|
||||
import { Button, Typography, cn } from "@stackframe/stack-ui";
|
||||
import { XIcon } from "lucide-react";
|
||||
import { useStackApp } from "../..";
|
||||
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
||||
function SidebarLayout(props) {
|
||||
const hash = useHash();
|
||||
const selectedIndex = props.items.findIndex((item) => item.id && item.id === hash);
|
||||
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
||||
/* @__PURE__ */ jsx("div", { className: cn("hidden sm:flex stack-scope h-full", props.className), children: /* @__PURE__ */ jsx(DesktopLayout, { items: props.items, title: props.title, selectedIndex }) }),
|
||||
/* @__PURE__ */ jsx("div", { className: cn("sm:hidden stack-scope h-full", props.className), children: /* @__PURE__ */ jsx(MobileLayout, { items: props.items, title: props.title, selectedIndex }) })
|
||||
] });
|
||||
}
|
||||
function Items(props) {
|
||||
const app = useStackApp();
|
||||
const navigate = app.useNavigate();
|
||||
const activeItemIndex = props.selectedIndex === -1 ? 0 : props.selectedIndex;
|
||||
return props.items.map((item, index) => item.type === "item" ? /* @__PURE__ */ jsxs(
|
||||
Button,
|
||||
{
|
||||
variant: "ghost",
|
||||
size: "sm",
|
||||
className: cn(
|
||||
activeItemIndex === index && "sm:bg-muted",
|
||||
"justify-start text-md text-zinc-800 dark:text-zinc-300 px-2 text-left"
|
||||
),
|
||||
onClick: () => {
|
||||
if (item.id) {
|
||||
navigate("#" + item.id);
|
||||
}
|
||||
},
|
||||
children: [
|
||||
item.icon,
|
||||
item.title
|
||||
]
|
||||
},
|
||||
index
|
||||
) : /* @__PURE__ */ jsx(Typography, { children: item.title }, index));
|
||||
}
|
||||
function DesktopLayout(props) {
|
||||
const selectedItem = props.items[props.selectedIndex === -1 ? 0 : props.selectedIndex];
|
||||
return /* @__PURE__ */ jsxs("div", { className: "stack-scope flex w-full h-full max-w-full relative", children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex max-w-[200px] min-w-[200px] border-r flex-col items-stretch gap-2 p-2 overflow-y-auto", children: [
|
||||
props.title && /* @__PURE__ */ jsx("div", { className: "mb-2 ml-2", children: /* @__PURE__ */ jsx(Typography, { type: "h2", className: "text-lg font-semibold text-zinc-800 dark:text-zinc-300", children: props.title }) }),
|
||||
/* @__PURE__ */ jsx(Items, { items: props.items, selectedIndex: props.selectedIndex })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex-1 w-0 flex justify-center gap-4 py-2 px-4", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col max-w-[800px] w-[800px]", children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "mt-4 mb-6", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { type: "h4", className: "font-semibold", children: selectedItem.title }),
|
||||
selectedItem.description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: selectedItem.description })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex-1", children: selectedItem.content })
|
||||
] }) })
|
||||
] });
|
||||
}
|
||||
function MobileLayout(props) {
|
||||
const selectedItem = props.items[props.selectedIndex];
|
||||
const app = useStackApp();
|
||||
const navigate = app.useNavigate();
|
||||
if (props.selectedIndex === -1) {
|
||||
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2 p-2", children: [
|
||||
props.title && /* @__PURE__ */ jsx("div", { className: "mb-2 ml-2", children: /* @__PURE__ */ jsx(Typography, { type: "h2", className: "text-lg font-semibold text-zinc-800 dark:text-zinc-300", children: props.title }) }),
|
||||
/* @__PURE__ */ jsx(Items, { items: props.items, selectedIndex: props.selectedIndex })
|
||||
] });
|
||||
} else {
|
||||
return /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col gap-4 py-2 px-4", children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex flex-col", children: [
|
||||
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
||||
/* @__PURE__ */ jsx(Typography, { type: "h4", className: "font-semibold", children: selectedItem.title }),
|
||||
/* @__PURE__ */ jsx(
|
||||
Button,
|
||||
{
|
||||
variant: "ghost",
|
||||
size: "icon",
|
||||
onClick: () => {
|
||||
navigate("#");
|
||||
},
|
||||
children: /* @__PURE__ */ jsx(XIcon, { className: "h-5 w-5" })
|
||||
}
|
||||
)
|
||||
] }),
|
||||
selectedItem.description && /* @__PURE__ */ jsx(Typography, { variant: "secondary", type: "label", children: selectedItem.description })
|
||||
] }),
|
||||
/* @__PURE__ */ jsx("div", { className: "flex-1", children: selectedItem.content })
|
||||
] });
|
||||
}
|
||||
}
|
||||
export {
|
||||
SidebarLayout
|
||||
};
|
||||
//# sourceMappingURL=sidebar-layout.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/sidebar-layout.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/sidebar-layout.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
23
package/@stackframe/react/dist/esm/components/elements/ssr-layout-effect.js
vendored
Normal file
23
package/@stackframe/react/dist/esm/components/elements/ssr-layout-effect.js
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
"use client";
|
||||
"use client";
|
||||
|
||||
// src/components/elements/ssr-layout-effect.tsx
|
||||
import { useLayoutEffect } from "react";
|
||||
import { jsx } from "react/jsx-runtime";
|
||||
function SsrScript(props) {
|
||||
useLayoutEffect(() => {
|
||||
(0, eval)(props.script);
|
||||
}, []);
|
||||
return /* @__PURE__ */ jsx(
|
||||
"script",
|
||||
{
|
||||
suppressHydrationWarning: true,
|
||||
nonce: props.nonce,
|
||||
dangerouslySetInnerHTML: { __html: props.script }
|
||||
}
|
||||
);
|
||||
}
|
||||
export {
|
||||
SsrScript
|
||||
};
|
||||
//# sourceMappingURL=ssr-layout-effect.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/ssr-layout-effect.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/ssr-layout-effect.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components/elements/ssr-layout-effect.tsx"],"sourcesContent":["\"use client\";\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { useLayoutEffect } from \"react\";\n\nexport function SsrScript(props: { script: string, nonce?: string }) {\n useLayoutEffect(() => {\n // TODO fix workaround: React has a bug where it doesn't run the script on the first CSR render if SSR has been skipped due to suspense\n // As a workaround, we run the script in the <script> tag again after the first render\n // Note that we do an indirect eval as described here: https://esbuild.github.io/content-types/#direct-eval\n (0, eval)(props.script);\n }, []);\n\n return (\n <script\n suppressHydrationWarning // the transpiler is setup differently for client/server targets, so if `script` was generated with Function.toString they will differ\n nonce={props.nonce}\n dangerouslySetInnerHTML={{ __html: props.script }}\n />\n );\n}\n"],"mappings":";;;AAMA,SAAS,uBAAuB;AAW5B;AATG,SAAS,UAAU,OAA2C;AACnE,kBAAgB,MAAM;AAIpB,KAAC,GAAG,MAAM,MAAM,MAAM;AAAA,EACxB,GAAG,CAAC,CAAC;AAEL,SACE;AAAA,IAAC;AAAA;AAAA,MACC,0BAAwB;AAAA,MACxB,OAAO,MAAM;AAAA,MACb,yBAAyB,EAAE,QAAQ,MAAM,OAAO;AAAA;AAAA,EAClD;AAEJ;","names":[]}
|
||||
16
package/@stackframe/react/dist/esm/components/elements/user-avatar.js
vendored
Normal file
16
package/@stackframe/react/dist/esm/components/elements/user-avatar.js
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// src/components/elements/user-avatar.tsx
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@stackframe/stack-ui";
|
||||
import { UserRound } from "lucide-react";
|
||||
import { jsx, jsxs } from "react/jsx-runtime";
|
||||
var defaultSize = 34;
|
||||
function UserAvatar(props) {
|
||||
const user = props.user;
|
||||
return /* @__PURE__ */ jsxs(Avatar, { style: { height: props.size || defaultSize, width: props.size || defaultSize }, className: props.border ? "border" : "", children: [
|
||||
/* @__PURE__ */ jsx(AvatarImage, { src: user?.profileImageUrl || "" }),
|
||||
/* @__PURE__ */ jsx(AvatarFallback, { children: user ? /* @__PURE__ */ jsx("div", { className: "font-medium", style: { fontSize: (props.size || defaultSize) * 0.4 }, children: (user.displayName || user.primaryEmail)?.slice(0, 2).toUpperCase() }) : /* @__PURE__ */ jsx(UserRound, { className: "text-zinc-500", size: (props.size || defaultSize) * 0.6 }) })
|
||||
] });
|
||||
}
|
||||
export {
|
||||
UserAvatar
|
||||
};
|
||||
//# sourceMappingURL=user-avatar.js.map
|
||||
1
package/@stackframe/react/dist/esm/components/elements/user-avatar.js.map
vendored
Normal file
1
package/@stackframe/react/dist/esm/components/elements/user-avatar.js.map
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["../../../../src/components/elements/user-avatar.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Avatar, AvatarFallback, AvatarImage } from \"@stackframe/stack-ui\";\nimport { UserRound } from \"lucide-react\";\n\nconst defaultSize = 34;\n\nexport function UserAvatar(props: {\n size?: number,\n user?: {\n profileImageUrl?: string | null,\n displayName?: string | null,\n primaryEmail?: string | null,\n } | null,\n border?: boolean,\n}) {\n const user = props.user;\n return (\n <Avatar style={{ height: props.size || defaultSize, width: props.size || defaultSize }} className={props.border ? 'border' : ''}>\n <AvatarImage src={user?.profileImageUrl || ''} />\n <AvatarFallback>\n {user ?\n <div className='font-medium' style={{ fontSize: (props.size || defaultSize) * 0.4 }}>\n {(user.displayName || user.primaryEmail)?.slice(0, 2).toUpperCase()}\n </div> :\n <UserRound className=\"text-zinc-500\" size={(props.size || defaultSize) * 0.6} />}\n </AvatarFallback>\n </Avatar>\n );\n}\n"],"mappings":";AAIA,SAAS,QAAQ,gBAAgB,mBAAmB;AACpD,SAAS,iBAAiB;AAetB,SACE,KADF;AAbJ,IAAM,cAAc;AAEb,SAAS,WAAW,OAQxB;AACD,QAAM,OAAO,MAAM;AACnB,SACE,qBAAC,UAAO,OAAO,EAAE,QAAQ,MAAM,QAAQ,aAAa,OAAO,MAAM,QAAQ,YAAY,GAAG,WAAW,MAAM,SAAS,WAAW,IAC3H;AAAA,wBAAC,eAAY,KAAK,MAAM,mBAAmB,IAAI;AAAA,IAC/C,oBAAC,kBACE,iBACC,oBAAC,SAAI,WAAU,eAAc,OAAO,EAAE,WAAW,MAAM,QAAQ,eAAe,IAAI,GAC9E,gBAAK,eAAe,KAAK,eAAe,MAAM,GAAG,CAAC,EAAE,YAAY,GACpE,IACA,oBAAC,aAAU,WAAU,iBAAgB,OAAO,MAAM,QAAQ,eAAe,KAAK,GAClF;AAAA,KACF;AAEJ;","names":[]}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue