@stackframe/stack 2.8.63 → 2.8.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/team-switcher.js +1 -1
- package/dist/components/team-switcher.js.map +1 -1
- package/dist/components-page/account-settings/payments/payments-page.js +20 -5
- package/dist/components-page/account-settings/payments/payments-page.js.map +1 -1
- package/dist/components-page/account-settings.js +65 -3
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/components-page/cli-auth-confirm.js +1 -7
- package/dist/components-page/cli-auth-confirm.js.map +1 -1
- package/dist/esm/components/team-switcher.js +1 -1
- package/dist/esm/components/team-switcher.js.map +1 -1
- package/dist/esm/components-page/account-settings/payments/payments-page.js +21 -6
- package/dist/esm/components-page/account-settings/payments/payments-page.js.map +1 -1
- package/dist/esm/components-page/account-settings.js +66 -4
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/cli-auth-confirm.js +1 -7
- package/dist/esm/components-page/cli-auth-confirm.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/index.d.mts +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/package.json +4 -4
|
@@ -60,7 +60,7 @@ function Inner(props) {
|
|
|
60
60
|
} : userFromHook;
|
|
61
61
|
const navigate = app.useNavigate();
|
|
62
62
|
const project = app.useProject();
|
|
63
|
-
const rawTeams = user?.useTeams();
|
|
63
|
+
const rawTeams = props.teams ?? user?.useTeams();
|
|
64
64
|
const selectedTeam = props.team || rawTeams?.find((team) => team.id === props.teamId);
|
|
65
65
|
const teams = (0, import_react.useMemo)(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);
|
|
66
66
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,oBAAoC;AACpC,sBAA2C;AAC3C,sBAaO;AACP,0BAAqC;AACrC,mBAAkC;AAClC,eAA2C;AAC3C,0BAA+B;AAC/B,uBAAyB;
|
|
1
|
+
{"version":3,"sources":["../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n teams?: Team[],\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = props.teams ?? user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
|
|
@@ -35,11 +35,25 @@ var import_jsx_runtime = require("react/jsx-runtime");
|
|
|
35
35
|
function PaymentsPage(props) {
|
|
36
36
|
const { t } = (0, import_translations.useTranslation)();
|
|
37
37
|
const user = (0, import_hooks.useUser)({ or: props.mockMode ? "return-null" : "redirect" });
|
|
38
|
-
const teams = user?.useTeams() ?? [];
|
|
38
|
+
const teams = props.availableTeams ?? user?.useTeams() ?? [];
|
|
39
|
+
const allowPersonal = props.allowPersonal ?? true;
|
|
39
40
|
const hasTeams = teams.length > 0;
|
|
40
41
|
const [selectedTeam, setSelectedTeam] = (0, import_react.useState)(null);
|
|
41
|
-
const
|
|
42
|
-
const
|
|
42
|
+
const effectiveSelectedTeam = selectedTeam ?? (!allowPersonal ? teams[0] ?? null : null);
|
|
43
|
+
const customer = effectiveSelectedTeam ?? (allowPersonal ? user : null);
|
|
44
|
+
const customerType = effectiveSelectedTeam ? "team" : "user";
|
|
45
|
+
(0, import_react.useEffect)(() => {
|
|
46
|
+
if (props.mockMode) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (!allowPersonal && !selectedTeam && teams.length > 0) {
|
|
50
|
+
setSelectedTeam(teams[0]);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (selectedTeam && !teams.some((team) => team.id === selectedTeam.id)) {
|
|
54
|
+
setSelectedTeam(allowPersonal ? null : teams[0] ?? null);
|
|
55
|
+
}
|
|
56
|
+
}, [allowPersonal, props.mockMode, selectedTeam, teams]);
|
|
43
57
|
if (props.mockMode) {
|
|
44
58
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_page_layout.PageLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
45
59
|
import_payments_panel.PaymentsPanel,
|
|
@@ -55,8 +69,9 @@ function PaymentsPage(props) {
|
|
|
55
69
|
hasTeams ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
56
70
|
import__.TeamSwitcher,
|
|
57
71
|
{
|
|
58
|
-
team:
|
|
59
|
-
|
|
72
|
+
team: effectiveSelectedTeam ?? void 0,
|
|
73
|
+
teams,
|
|
74
|
+
allowNull: allowPersonal,
|
|
60
75
|
nullLabel: t("Personal"),
|
|
61
76
|
onChange: async (team) => {
|
|
62
77
|
setSelectedTeam(team);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components-page/account-settings/payments/payments-page.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { useState } from \"react\";\nimport { Team, TeamSwitcher } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { PaymentsPanel } from \"./payments-panel\";\n\nexport function PaymentsPage(props: { mockMode?: boolean }) {\n const { t } = useTranslation();\n const user = useUser({ or: props.mockMode ? \"return-null\" : \"redirect\" });\n const teams = user?.useTeams() ?? [];\n const hasTeams = teams.length > 0;\n const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);\n const
|
|
1
|
+
{"version":3,"sources":["../../../../src/components-page/account-settings/payments/payments-page.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { useEffect, useState } from \"react\";\nimport { Team, TeamSwitcher } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { PaymentsPanel } from \"./payments-panel\";\n\nexport function PaymentsPage(props: { mockMode?: boolean, availableTeams?: Team[], allowPersonal?: boolean }) {\n const { t } = useTranslation();\n const user = useUser({ or: props.mockMode ? \"return-null\" : \"redirect\" });\n const teams = props.availableTeams ?? user?.useTeams() ?? [];\n const allowPersonal = props.allowPersonal ?? true;\n const hasTeams = teams.length > 0;\n const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);\n const effectiveSelectedTeam = selectedTeam ?? (!allowPersonal ? (teams[0] ?? null) : null);\n const customer = effectiveSelectedTeam ?? (allowPersonal ? user : null);\n const customerType = effectiveSelectedTeam ? \"team\" : \"user\";\n\n useEffect(() => {\n if (props.mockMode) {\n return;\n }\n if (!allowPersonal && !selectedTeam && teams.length > 0) {\n setSelectedTeam(teams[0]);\n return;\n }\n if (selectedTeam && !teams.some(team => team.id === selectedTeam.id)) {\n setSelectedTeam(allowPersonal ? null : (teams[0] ?? null));\n }\n }, [allowPersonal, props.mockMode, selectedTeam, teams]);\n\n if (props.mockMode) {\n return (\n <PageLayout>\n <PaymentsPanel\n mockMode\n />\n </PageLayout>\n );\n }\n\n if (!customer) {\n return null;\n }\n\n\n return (\n <PageLayout>\n {hasTeams ? (\n <TeamSwitcher\n team={effectiveSelectedTeam ?? undefined}\n teams={teams}\n allowNull={allowPersonal}\n nullLabel={t(\"Personal\")}\n onChange={async (team) => {\n setSelectedTeam(team);\n }}\n />\n ) : null}\n <PaymentsPanel\n customer={customer}\n customerType={customerType}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,mBAAoC;AACpC,eAAmC;AACnC,mBAAwB;AACxB,0BAA+B;AAC/B,yBAA2B;AAC3B,4BAA8B;AA6BtB;AA3BD,SAAS,aAAa,OAAiF;AAC5G,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,MAAM,WAAW,gBAAgB,WAAW,CAAC;AACxE,QAAM,QAAQ,MAAM,kBAAkB,MAAM,SAAS,KAAK,CAAC;AAC3D,QAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAM,WAAW,MAAM,SAAS;AAChC,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAsB,IAAI;AAClE,QAAM,wBAAwB,iBAAiB,CAAC,gBAAiB,MAAM,CAAC,KAAK,OAAQ;AACrF,QAAM,WAAW,0BAA0B,gBAAgB,OAAO;AAClE,QAAM,eAAe,wBAAwB,SAAS;AAEtD,8BAAU,MAAM;AACd,QAAI,MAAM,UAAU;AAClB;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB,CAAC,gBAAgB,MAAM,SAAS,GAAG;AACvD,sBAAgB,MAAM,CAAC,CAAC;AACxB;AAAA,IACF;AACA,QAAI,gBAAgB,CAAC,MAAM,KAAK,UAAQ,KAAK,OAAO,aAAa,EAAE,GAAG;AACpE,sBAAgB,gBAAgB,OAAQ,MAAM,CAAC,KAAK,IAAK;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,eAAe,MAAM,UAAU,cAAc,KAAK,CAAC;AAEvD,MAAI,MAAM,UAAU;AAClB,WACE,4CAAC,iCACC;AAAA,MAAC;AAAA;AAAA,QACC,UAAQ;AAAA;AAAA,IACV,GACF;AAAA,EAEJ;AAEA,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAGA,SACE,6CAAC,iCACE;AAAA,eACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,yBAAyB;AAAA,QAC/B;AAAA,QACA,WAAW;AAAA,QACX,WAAW,EAAE,UAAU;AAAA,QACvB,UAAU,OAAO,SAAS;AACxB,0BAAgB,IAAI;AAAA,QACtB;AAAA;AAAA,IACF,IACE;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -25,6 +25,7 @@ __export(account_settings_exports, {
|
|
|
25
25
|
AccountSettings: () => AccountSettings
|
|
26
26
|
});
|
|
27
27
|
module.exports = __toCommonJS(account_settings_exports);
|
|
28
|
+
var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
|
|
28
29
|
var import_stack_ui = require("@stackframe/stack-ui");
|
|
29
30
|
var import_lucide_react = require("lucide-react");
|
|
30
31
|
var import_react = require("react");
|
|
@@ -71,6 +72,60 @@ function AccountSettings(props) {
|
|
|
71
72
|
const project = props.mockProject || projectFromHook;
|
|
72
73
|
const teams = user?.useTeams() || [];
|
|
73
74
|
const billing = user?.useBilling() || null;
|
|
75
|
+
const teamsKey = (0, import_react.useMemo)(() => teams.map((team) => team.id).join("|"), [teams]);
|
|
76
|
+
const teamsById = (0, import_react.useMemo)(() => teams, [teamsKey]);
|
|
77
|
+
const userRef = (0, import_react.useRef)(userFromHook ?? null);
|
|
78
|
+
const userId = userFromHook?.id ?? null;
|
|
79
|
+
const [paymentsAvailability, setPaymentsAvailability] = (0, import_react.useState)(() => ({
|
|
80
|
+
userHasProducts: false,
|
|
81
|
+
teamIdsWithProducts: /* @__PURE__ */ new Set(),
|
|
82
|
+
isReady: !!props.mockUser
|
|
83
|
+
}));
|
|
84
|
+
(0, import_react.useEffect)(() => {
|
|
85
|
+
userRef.current = userFromHook ?? null;
|
|
86
|
+
}, [userFromHook]);
|
|
87
|
+
(0, import_react.useEffect)(() => {
|
|
88
|
+
if (props.mockUser || !userId) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
let cancelled = false;
|
|
92
|
+
(0, import_promises.runAsynchronouslyWithAlert)(async () => {
|
|
93
|
+
const currentUser = userRef.current;
|
|
94
|
+
if (!currentUser || currentUser.id !== userId) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const [userProducts, teamsWithProducts2] = await Promise.all([
|
|
98
|
+
currentUser.listProducts({ limit: 1 }),
|
|
99
|
+
Promise.all(teamsById.map(async (team) => {
|
|
100
|
+
const isTeamAdmin = await currentUser.hasPermission(team, "team_admin");
|
|
101
|
+
if (!isTeamAdmin) {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
const teamProducts = await team.listProducts({ limit: 1 });
|
|
105
|
+
const hasTeamProducts = teamProducts.some((product) => product.customerType === "team");
|
|
106
|
+
return hasTeamProducts ? team.id : null;
|
|
107
|
+
}))
|
|
108
|
+
]);
|
|
109
|
+
if (cancelled) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const userHasProducts = userProducts.some((product) => product.customerType === "user");
|
|
113
|
+
const teamIdsWithProducts = new Set(teamsWithProducts2.filter((id) => id !== null));
|
|
114
|
+
setPaymentsAvailability({
|
|
115
|
+
userHasProducts,
|
|
116
|
+
teamIdsWithProducts,
|
|
117
|
+
isReady: true
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
return () => {
|
|
121
|
+
cancelled = true;
|
|
122
|
+
};
|
|
123
|
+
}, [props.mockUser, teamsById, userId]);
|
|
124
|
+
const teamsWithProducts = (0, import_react.useMemo)(
|
|
125
|
+
() => teamsById.filter((team) => paymentsAvailability.teamIdsWithProducts.has(team.id)),
|
|
126
|
+
[paymentsAvailability.teamIdsWithProducts, teamsById]
|
|
127
|
+
);
|
|
128
|
+
const shouldShowPaymentsTab = props.mockUser || paymentsAvailability.isReady && (paymentsAvailability.userHasProducts || teamsWithProducts.length > 0);
|
|
74
129
|
if (!props.mockUser && !userFromHook) {
|
|
75
130
|
return null;
|
|
76
131
|
}
|
|
@@ -113,13 +168,20 @@ function AccountSettings(props) {
|
|
|
113
168
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "Key" }),
|
|
114
169
|
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_api_keys_page.ApiKeysPage, { mockApiKeys: props.mockApiKeys, mockMode: !!props.mockUser }) })
|
|
115
170
|
}] : [],
|
|
116
|
-
{
|
|
171
|
+
...shouldShowPaymentsTab ? [{
|
|
117
172
|
title: t("Payments"),
|
|
118
173
|
type: "item",
|
|
119
174
|
id: "payments",
|
|
120
175
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "CreditCard" }),
|
|
121
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaymentsPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
122
|
-
|
|
176
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PaymentsPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
177
|
+
import_payments_page.PaymentsPage,
|
|
178
|
+
{
|
|
179
|
+
mockMode: !!props.mockUser,
|
|
180
|
+
allowPersonal: paymentsAvailability.userHasProducts,
|
|
181
|
+
availableTeams: teamsWithProducts
|
|
182
|
+
}
|
|
183
|
+
) })
|
|
184
|
+
}] : [],
|
|
123
185
|
{
|
|
124
186
|
title: t("Settings"),
|
|
125
187
|
type: "item",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { Skeleton, Typography } from '@stackframe/stack-ui';\nimport { Contact, ShieldCheck, Bell, Monitor, Key, Settings, CirclePlus, CreditCard } from 'lucide-react';\nimport React, { Suspense } from \"react\";\nimport { useStackApp, useUser } from '..';\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { SidebarLayout } from '../components/elements/sidebar-layout';\nimport { TeamIcon } from '../components/team-icon';\nimport { useTranslation } from \"../lib/translations\";\nimport { ActiveSessionsPage } from \"./account-settings/active-sessions/active-sessions-page\";\nimport { ApiKeysPage } from \"./account-settings/api-keys/api-keys-page\";\nimport { EmailsAndAuthPage } from './account-settings/email-and-auth/email-and-auth-page';\nimport { NotificationsPage } from './account-settings/notifications/notifications-page';\nimport { ProfilePage } from \"./account-settings/profile-page/profile-page\";\nimport { PaymentsPage } from \"./account-settings/payments/payments-page\";\nimport { SettingsPage } from './account-settings/settings/settings-page';\nimport { TeamCreationPage } from './account-settings/teams/team-creation-page';\nimport { TeamPage } from './account-settings/teams/team-page';\n\nconst iconMap = {\n Contact,\n ShieldCheck,\n Bell,\n Monitor,\n Key,\n Settings,\n CirclePlus,\n CreditCard,\n} as const;\n\nconst Icon = ({ name }: { name: keyof typeof iconMap }) => {\n const LucideIcon = iconMap[name];\n return <LucideIcon className=\"mr-2 h-4 w-4\"/>;\n};\n\nexport function AccountSettings(props: {\n fullPage?: boolean,\n extraItems?: ({\n title: string,\n content: React.ReactNode,\n id: string,\n } & ({\n icon?: React.ReactNode,\n } | {\n iconName?: keyof typeof iconMap,\n }))[],\n mockUser?: {\n displayName?: string,\n profileImageUrl?: string,\n },\n mockApiKeys?: Array<{\n id: string,\n description: string,\n createdAt: string,\n expiresAt?: string,\n manuallyRevokedAt?: string,\n }>,\n mockProject?: {\n config: {\n allowUserApiKeys: boolean,\n clientTeamCreationEnabled: boolean,\n },\n },\n mockSessions?: Array<{\n id: string,\n isCurrentSession: boolean,\n isImpersonation?: boolean,\n createdAt: string,\n lastUsedAt?: string,\n geoInfo?: {\n ip?: string,\n cityName?: string,\n },\n }>,\n}) {\n const { t } = useTranslation();\n const userFromHook = useUser({ or: props.mockUser ? 'return-null' : 'redirect' });\n const stackApp = useStackApp();\n const projectFromHook = stackApp.useProject();\n\n // Use mock data if provided, otherwise use real data\n const user = props.mockUser ? {\n useTeams: () => [], // Mock empty teams for now\n useBilling: () => ({ hasCustomer: false }), // Mock empty billing for now\n } : userFromHook;\n\n const project = props.mockProject || projectFromHook;\n const teams = user?.useTeams() || [];\n const billing = user?.useBilling() || null;\n\n // If we're not in mock mode and don't have a user, the useUser hook will handle redirect\n if (!props.mockUser && !userFromHook) {\n return null;\n }\n\n return (\n <MaybeFullPage fullPage={!!props.fullPage}>\n <div className=\"self-stretch flex-grow w-full\">\n <SidebarLayout\n items={([\n {\n title: t('My Profile'),\n type: 'item',\n id: 'profile',\n icon: <Icon name=\"Contact\"/>,\n content: <ProfilePage mockUser={props.mockUser}/>,\n },\n {\n title: t('Emails & Auth'),\n type: 'item',\n id: 'auth',\n icon: <Icon name=\"ShieldCheck\"/>,\n content: <Suspense fallback={<EmailsAndAuthPageSkeleton/>}>\n <EmailsAndAuthPage mockMode={!!props.mockUser}/>\n </Suspense>,\n },\n {\n title: t('Notifications'),\n type: 'item',\n id: 'notifications',\n icon: <Icon name=\"Bell\"/>,\n content: <Suspense fallback={<NotificationsPageSkeleton/>}>\n <NotificationsPage/>\n </Suspense>,\n },\n {\n title: t('Active Sessions'),\n type: 'item',\n id: 'sessions',\n icon: <Icon name=\"Monitor\"/>,\n content: <Suspense fallback={<ActiveSessionsPageSkeleton/>}>\n <ActiveSessionsPage mockSessions={props.mockSessions} mockMode={!!props.mockUser}/>\n </Suspense>,\n },\n ...(project.config.allowUserApiKeys ? [{\n title: t('API Keys'),\n type: 'item',\n id: 'api-keys',\n icon: <Icon name=\"Key\" />,\n content: <Suspense fallback={<ApiKeysPageSkeleton/>}>\n <ApiKeysPage mockApiKeys={props.mockApiKeys} mockMode={!!props.mockUser} />\n </Suspense>,\n }] as const : []),\n {\n title: t('Payments'),\n type: 'item',\n id: 'payments',\n icon: <Icon name=\"CreditCard\" />,\n content: <Suspense fallback={<PaymentsPageSkeleton/>}>\n <PaymentsPage mockMode={!!props.mockUser} />\n </Suspense>,\n },\n {\n title: t('Settings'),\n type: 'item',\n id: 'settings',\n icon: <Icon name=\"Settings\"/>,\n content: <SettingsPage mockMode={!!props.mockUser}/>,\n },\n ...(props.extraItems?.map(item => ({\n title: item.title,\n type: 'item',\n id: item.id,\n icon: (() => {\n const iconName = (item as any).iconName as keyof typeof iconMap | undefined;\n if (iconName) {\n return <Icon name={iconName}/>;\n } else if ((item as any).icon) {\n return (item as any).icon;\n }\n return null;\n })(),\n content: item.content,\n } as const)) || []),\n ...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{\n title: t('Teams'),\n type: 'divider',\n }] as const : [],\n ...teams.map(team => ({\n title: <div className='flex gap-2 items-center w-full'>\n <TeamIcon team={team}/>\n <Typography className=\"max-w-[320px] md:w-[90%] truncate\">{team.displayName}</Typography>\n </div>,\n type: 'item',\n id: `team-${team.id}`,\n content: <Suspense fallback={<TeamPageSkeleton/>}>\n <TeamPage team={team}/>\n </Suspense>,\n } as const)),\n ...project.config.clientTeamCreationEnabled ? [{\n title: t('Create a team'),\n icon: <Icon name=\"CirclePlus\"/>,\n type: 'item',\n id: 'team-creation',\n content: <Suspense fallback={<TeamCreationSkeleton/>}>\n <TeamCreationPage mockMode={!!props.mockUser} />\n </Suspense>,\n }] as const : [],\n ] as const).filter((p) => p.type === 'divider' || (p as any).content )}\n title={t(\"Account Settings\")}\n />\n </div>\n </MaybeFullPage>\n );\n}\n\nfunction PageLayout(props: { children: React.ReactNode }) {\n return (\n <div className='flex flex-col gap-6'>\n {props.children}\n </div>\n );\n}\n\nfunction EmailsAndAuthPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n\nfunction ActiveSessionsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-6 w-48 mb-2\"/>\n <Skeleton className=\"h-4 w-full mb-4\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction ApiKeysPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction PaymentsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-6 w-48 mb-2\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction TeamPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction TeamCreationSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n\nfunction NotificationsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/components-page/account-settings.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Skeleton, Typography } from '@stackframe/stack-ui';\nimport { Contact, ShieldCheck, Bell, Monitor, Key, Settings, CirclePlus, CreditCard } from 'lucide-react';\nimport React, { Suspense, useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { useStackApp, useUser } from '..';\nimport { MaybeFullPage } from \"../components/elements/maybe-full-page\";\nimport { SidebarLayout } from '../components/elements/sidebar-layout';\nimport { TeamIcon } from '../components/team-icon';\nimport { useTranslation } from \"../lib/translations\";\nimport { ActiveSessionsPage } from \"./account-settings/active-sessions/active-sessions-page\";\nimport { ApiKeysPage } from \"./account-settings/api-keys/api-keys-page\";\nimport { EmailsAndAuthPage } from './account-settings/email-and-auth/email-and-auth-page';\nimport { NotificationsPage } from './account-settings/notifications/notifications-page';\nimport { ProfilePage } from \"./account-settings/profile-page/profile-page\";\nimport { PaymentsPage } from \"./account-settings/payments/payments-page\";\nimport { SettingsPage } from './account-settings/settings/settings-page';\nimport { TeamCreationPage } from './account-settings/teams/team-creation-page';\nimport { TeamPage } from './account-settings/teams/team-page';\n\nconst iconMap = {\n Contact,\n ShieldCheck,\n Bell,\n Monitor,\n Key,\n Settings,\n CirclePlus,\n CreditCard,\n} as const;\n\nconst Icon = ({ name }: { name: keyof typeof iconMap }) => {\n const LucideIcon = iconMap[name];\n return <LucideIcon className=\"mr-2 h-4 w-4\"/>;\n};\n\nexport function AccountSettings(props: {\n fullPage?: boolean,\n extraItems?: ({\n title: string,\n content: React.ReactNode,\n id: string,\n } & ({\n icon?: React.ReactNode,\n } | {\n iconName?: keyof typeof iconMap,\n }))[],\n mockUser?: {\n displayName?: string,\n profileImageUrl?: string,\n },\n mockApiKeys?: Array<{\n id: string,\n description: string,\n createdAt: string,\n expiresAt?: string,\n manuallyRevokedAt?: string,\n }>,\n mockProject?: {\n config: {\n allowUserApiKeys: boolean,\n clientTeamCreationEnabled: boolean,\n },\n },\n mockSessions?: Array<{\n id: string,\n isCurrentSession: boolean,\n isImpersonation?: boolean,\n createdAt: string,\n lastUsedAt?: string,\n geoInfo?: {\n ip?: string,\n cityName?: string,\n },\n }>,\n}) {\n const { t } = useTranslation();\n const userFromHook = useUser({ or: props.mockUser ? 'return-null' : 'redirect' });\n const stackApp = useStackApp();\n const projectFromHook = stackApp.useProject();\n\n // Use mock data if provided, otherwise use real data\n const user = props.mockUser ? {\n useTeams: () => [], // Mock empty teams for now\n useBilling: () => ({ hasCustomer: false }), // Mock empty billing for now\n } : userFromHook;\n\n const project = props.mockProject || projectFromHook;\n const teams = user?.useTeams() || [];\n const billing = user?.useBilling() || null;\n const teamsKey = useMemo(() => teams.map(team => team.id).join(\"|\"), [teams]);\n const teamsById = useMemo(() => teams, [teamsKey]);\n const userRef = useRef(userFromHook ?? null);\n const userId = userFromHook?.id ?? null;\n const [paymentsAvailability, setPaymentsAvailability] = useState<{\n userHasProducts: boolean,\n teamIdsWithProducts: Set<string>,\n isReady: boolean,\n }>(() => ({\n userHasProducts: false,\n teamIdsWithProducts: new Set<string>(),\n isReady: !!props.mockUser,\n }));\n\n useEffect(() => {\n userRef.current = userFromHook ?? null;\n }, [userFromHook]);\n\n useEffect(() => {\n if (props.mockUser || !userId) {\n return;\n }\n let cancelled = false;\n runAsynchronouslyWithAlert(async () => {\n const currentUser = userRef.current;\n if (!currentUser || currentUser.id !== userId) {\n return;\n }\n const [userProducts, teamsWithProducts] = await Promise.all([\n currentUser.listProducts({ limit: 1 }),\n Promise.all(teamsById.map(async (team) => {\n const isTeamAdmin = await currentUser.hasPermission(team, \"team_admin\");\n if (!isTeamAdmin) {\n return null;\n }\n const teamProducts = await team.listProducts({ limit: 1 });\n const hasTeamProducts = teamProducts.some((product) => product.customerType === \"team\");\n return hasTeamProducts ? team.id : null;\n })),\n ]);\n if (cancelled) {\n return;\n }\n const userHasProducts = userProducts.some((product) => product.customerType === \"user\");\n const teamIdsWithProducts = new Set<string>(teamsWithProducts.filter((id): id is string => id !== null));\n setPaymentsAvailability({\n userHasProducts,\n teamIdsWithProducts,\n isReady: true,\n });\n });\n return () => {\n cancelled = true;\n };\n }, [props.mockUser, teamsById, userId]);\n\n const teamsWithProducts = useMemo(\n () => teamsById.filter(team => paymentsAvailability.teamIdsWithProducts.has(team.id)),\n [paymentsAvailability.teamIdsWithProducts, teamsById],\n );\n const shouldShowPaymentsTab = props.mockUser\n || (paymentsAvailability.isReady\n && (paymentsAvailability.userHasProducts || teamsWithProducts.length > 0));\n\n // If we're not in mock mode and don't have a user, the useUser hook will handle redirect\n if (!props.mockUser && !userFromHook) {\n return null;\n }\n\n return (\n <MaybeFullPage fullPage={!!props.fullPage}>\n <div className=\"self-stretch flex-grow w-full\">\n <SidebarLayout\n items={([\n {\n title: t('My Profile'),\n type: 'item',\n id: 'profile',\n icon: <Icon name=\"Contact\"/>,\n content: <ProfilePage mockUser={props.mockUser}/>,\n },\n {\n title: t('Emails & Auth'),\n type: 'item',\n id: 'auth',\n icon: <Icon name=\"ShieldCheck\"/>,\n content: <Suspense fallback={<EmailsAndAuthPageSkeleton/>}>\n <EmailsAndAuthPage mockMode={!!props.mockUser}/>\n </Suspense>,\n },\n {\n title: t('Notifications'),\n type: 'item',\n id: 'notifications',\n icon: <Icon name=\"Bell\"/>,\n content: <Suspense fallback={<NotificationsPageSkeleton/>}>\n <NotificationsPage/>\n </Suspense>,\n },\n {\n title: t('Active Sessions'),\n type: 'item',\n id: 'sessions',\n icon: <Icon name=\"Monitor\"/>,\n content: <Suspense fallback={<ActiveSessionsPageSkeleton/>}>\n <ActiveSessionsPage mockSessions={props.mockSessions} mockMode={!!props.mockUser}/>\n </Suspense>,\n },\n ...(project.config.allowUserApiKeys ? [{\n title: t('API Keys'),\n type: 'item',\n id: 'api-keys',\n icon: <Icon name=\"Key\" />,\n content: <Suspense fallback={<ApiKeysPageSkeleton/>}>\n <ApiKeysPage mockApiKeys={props.mockApiKeys} mockMode={!!props.mockUser} />\n </Suspense>,\n }] as const : []),\n ...(shouldShowPaymentsTab ? [{\n title: t('Payments'),\n type: 'item',\n id: 'payments',\n icon: <Icon name=\"CreditCard\" />,\n content: <Suspense fallback={<PaymentsPageSkeleton/>}>\n <PaymentsPage\n mockMode={!!props.mockUser}\n allowPersonal={paymentsAvailability.userHasProducts}\n availableTeams={teamsWithProducts}\n />\n </Suspense>,\n }] as const : []),\n {\n title: t('Settings'),\n type: 'item',\n id: 'settings',\n icon: <Icon name=\"Settings\"/>,\n content: <SettingsPage mockMode={!!props.mockUser}/>,\n },\n ...(props.extraItems?.map(item => ({\n title: item.title,\n type: 'item',\n id: item.id,\n icon: (() => {\n const iconName = (item as any).iconName as keyof typeof iconMap | undefined;\n if (iconName) {\n return <Icon name={iconName}/>;\n } else if ((item as any).icon) {\n return (item as any).icon;\n }\n return null;\n })(),\n content: item.content,\n } as const)) || []),\n ...(teams.length > 0 || project.config.clientTeamCreationEnabled) ? [{\n title: t('Teams'),\n type: 'divider',\n }] as const : [],\n ...teams.map(team => ({\n title: <div className='flex gap-2 items-center w-full'>\n <TeamIcon team={team}/>\n <Typography className=\"max-w-[320px] md:w-[90%] truncate\">{team.displayName}</Typography>\n </div>,\n type: 'item',\n id: `team-${team.id}`,\n content: <Suspense fallback={<TeamPageSkeleton/>}>\n <TeamPage team={team}/>\n </Suspense>,\n } as const)),\n ...project.config.clientTeamCreationEnabled ? [{\n title: t('Create a team'),\n icon: <Icon name=\"CirclePlus\"/>,\n type: 'item',\n id: 'team-creation',\n content: <Suspense fallback={<TeamCreationSkeleton/>}>\n <TeamCreationPage mockMode={!!props.mockUser} />\n </Suspense>,\n }] as const : [],\n ] as const).filter((p) => p.type === 'divider' || (p as any).content )}\n title={t(\"Account Settings\")}\n />\n </div>\n </MaybeFullPage>\n );\n}\n\nfunction PageLayout(props: { children: React.ReactNode }) {\n return (\n <div className='flex flex-col gap-6'>\n {props.children}\n </div>\n );\n}\n\nfunction EmailsAndAuthPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n\nfunction ActiveSessionsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-6 w-48 mb-2\"/>\n <Skeleton className=\"h-4 w-full mb-4\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction ApiKeysPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction PaymentsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-6 w-48 mb-2\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction TeamPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-[200px] w-full mt-1 rounded-md\"/>\n </PageLayout>;\n}\n\nfunction TeamCreationSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n\nfunction NotificationsPageSkeleton() {\n return <PageLayout>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n <Skeleton className=\"h-9 w-full mt-1\"/>\n </PageLayout>;\n}\n"],"mappings":"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","names":["teamsWithProducts"]}
|
|
@@ -77,9 +77,7 @@ function CliAuthConfirmation({ fullPage = true }) {
|
|
|
77
77
|
{
|
|
78
78
|
title: t("CLI Authorization Successful"),
|
|
79
79
|
fullPage,
|
|
80
|
-
|
|
81
|
-
primaryAction: () => window.close(),
|
|
82
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: t("The CLI application has been authorized successfully. You can now close this window and return to the command line.") })
|
|
80
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: t("The CLI application has been authorized successfully. You can close this window and return to the command line.") })
|
|
83
81
|
}
|
|
84
82
|
);
|
|
85
83
|
}
|
|
@@ -91,8 +89,6 @@ function CliAuthConfirmation({ fullPage = true }) {
|
|
|
91
89
|
fullPage,
|
|
92
90
|
primaryButtonText: t("Try Again"),
|
|
93
91
|
primaryAction: () => setError(null),
|
|
94
|
-
secondaryButtonText: t("Cancel"),
|
|
95
|
-
secondaryAction: () => window.close(),
|
|
96
92
|
children: [
|
|
97
93
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "text-red-600", children: t("Failed to authorize the CLI application:") }),
|
|
98
94
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "text-red-600", children: error.message })
|
|
@@ -107,8 +103,6 @@ function CliAuthConfirmation({ fullPage = true }) {
|
|
|
107
103
|
fullPage,
|
|
108
104
|
primaryButtonText: authorizing ? t("Authorizing...") : t("Authorize"),
|
|
109
105
|
primaryAction: handleAuthorize,
|
|
110
|
-
secondaryButtonText: t("Cancel"),
|
|
111
|
-
secondaryAction: () => window.close(),
|
|
112
106
|
children: [
|
|
113
107
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: t("A command line application is requesting access to your account. Click the button below to authorize it.") }),
|
|
114
108
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.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.") })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components-page/cli-auth-confirm.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { stackAppInternalsSymbol, useStackApp } from \"..\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function CliAuthConfirmation({ fullPage = true }: { fullPage?: boolean }) {\n const { t } = useTranslation();\n const app = useStackApp();\n const [authorizing, setAuthorizing] = useState(false);\n const [success, setSuccess] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const user = app.useUser({ or: \"redirect\" });\n\n const handleAuthorize = async () => {\n if (authorizing) return;\n\n setAuthorizing(true);\n try {\n // Get login code from URL query parameters\n const urlParams = new URLSearchParams(window.location.search);\n const loginCode = urlParams.get(\"login_code\");\n\n if (!loginCode) {\n throw new Error(\"Missing login code in URL parameters\");\n }\n const refreshToken = (await user.currentSession.getTokens()).refreshToken;\n if (!refreshToken) {\n throw new Error(\"You must be logged in to authorize CLI access\");\n }\n\n // Use the internal API to send the CLI login request\n const result = await (app as any)[stackAppInternalsSymbol].sendRequest(\"/auth/cli/complete\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n login_code: loginCode,\n refresh_token: (await user.currentSession.getTokens()).refreshToken\n })\n });\n\n if (!result.ok) {\n throw new Error(`Authorization failed: ${result.status} ${await result.text()}`);\n }\n\n setSuccess(true);\n } catch (err) {\n setError(err as Error);\n } finally {\n setAuthorizing(false);\n }\n };\n\n if (success) {\n return (\n <MessageCard\n title={t(\"CLI Authorization Successful\")}\n fullPage={fullPage}\n
|
|
1
|
+
{"version":3,"sources":["../../src/components-page/cli-auth-confirm.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { stackAppInternalsSymbol, useStackApp } from \"..\";\nimport { MessageCard } from \"../components/message-cards/message-card\";\nimport { useTranslation } from \"../lib/translations\";\n\nexport function CliAuthConfirmation({ fullPage = true }: { fullPage?: boolean }) {\n const { t } = useTranslation();\n const app = useStackApp();\n const [authorizing, setAuthorizing] = useState(false);\n const [success, setSuccess] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n const user = app.useUser({ or: \"redirect\" });\n\n const handleAuthorize = async () => {\n if (authorizing) return;\n\n setAuthorizing(true);\n try {\n // Get login code from URL query parameters\n const urlParams = new URLSearchParams(window.location.search);\n const loginCode = urlParams.get(\"login_code\");\n\n if (!loginCode) {\n throw new Error(\"Missing login code in URL parameters\");\n }\n const refreshToken = (await user.currentSession.getTokens()).refreshToken;\n if (!refreshToken) {\n throw new Error(\"You must be logged in to authorize CLI access\");\n }\n\n // Use the internal API to send the CLI login request\n const result = await (app as any)[stackAppInternalsSymbol].sendRequest(\"/auth/cli/complete\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\"\n },\n body: JSON.stringify({\n login_code: loginCode,\n refresh_token: (await user.currentSession.getTokens()).refreshToken\n })\n });\n\n if (!result.ok) {\n throw new Error(`Authorization failed: ${result.status} ${await result.text()}`);\n }\n\n setSuccess(true);\n } catch (err) {\n setError(err as Error);\n } finally {\n setAuthorizing(false);\n }\n };\n\n if (success) {\n return (\n <MessageCard\n title={t(\"CLI Authorization Successful\")}\n fullPage={fullPage}\n >\n <Typography>\n {t(\"The CLI application has been authorized successfully. You can close this window and return to the command line.\")}\n </Typography>\n </MessageCard>\n );\n }\n\n if (error) {\n return (\n <MessageCard\n title={t(\"Authorization Failed\")}\n fullPage={fullPage}\n primaryButtonText={t(\"Try Again\")}\n primaryAction={() => setError(null)}\n >\n <Typography className=\"text-red-600\">\n {t(\"Failed to authorize the CLI application:\")}\n </Typography>\n <Typography className=\"text-red-600\">\n {error.message}\n </Typography>\n </MessageCard>\n );\n }\n\n return (\n <MessageCard\n title={t(\"Authorize CLI Application\")}\n fullPage={fullPage}\n primaryButtonText={authorizing ? t(\"Authorizing...\") : t(\"Authorize\")}\n primaryAction={handleAuthorize}\n >\n <Typography>\n {t(\"A command line application is requesting access to your account. Click the button below to authorize it.\")}\n </Typography>\n <Typography variant=\"destructive\">\n {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.\")}\n </Typography>\n </MessageCard>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,sBAA2B;AAC3B,mBAAyB;AACzB,eAAqD;AACrD,0BAA4B;AAC5B,0BAA+B;AA0DvB;AAxDD,SAAS,oBAAoB,EAAE,WAAW,KAAK,GAA2B;AAC/E,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,UAAM,sBAAY;AACxB,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAS,KAAK;AACpD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAuB,IAAI;AAErD,QAAM,OAAO,IAAI,QAAQ,EAAE,IAAI,WAAW,CAAC;AAE3C,QAAM,kBAAkB,YAAY;AAClC,QAAI,YAAa;AAEjB,mBAAe,IAAI;AACnB,QAAI;AAEF,YAAM,YAAY,IAAI,gBAAgB,OAAO,SAAS,MAAM;AAC5D,YAAM,YAAY,UAAU,IAAI,YAAY;AAE5C,UAAI,CAAC,WAAW;AACd,cAAM,IAAI,MAAM,sCAAsC;AAAA,MACxD;AACA,YAAM,gBAAgB,MAAM,KAAK,eAAe,UAAU,GAAG;AAC7D,UAAI,CAAC,cAAc;AACjB,cAAM,IAAI,MAAM,+CAA+C;AAAA,MACjE;AAGA,YAAM,SAAS,MAAO,IAAY,gCAAuB,EAAE,YAAY,sBAAsB;AAAA,QAC3F,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB,YAAY;AAAA,UACZ,gBAAgB,MAAM,KAAK,eAAe,UAAU,GAAG;AAAA,QACzD,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,OAAO,IAAI;AACd,cAAM,IAAI,MAAM,yBAAyB,OAAO,MAAM,IAAI,MAAM,OAAO,KAAK,CAAC,EAAE;AAAA,MACjF;AAEA,iBAAW,IAAI;AAAA,IACjB,SAAS,KAAK;AACZ,eAAS,GAAY;AAAA,IACvB,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,8BAA8B;AAAA,QACvC;AAAA,QAEA,sDAAC,8BACE,YAAE,iHAAiH,GACtH;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,MAAI,OAAO;AACT,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,sBAAsB;AAAA,QAC/B;AAAA,QACA,mBAAmB,EAAE,WAAW;AAAA,QAChC,eAAe,MAAM,SAAS,IAAI;AAAA,QAElC;AAAA,sDAAC,8BAAW,WAAU,gBACnB,YAAE,0CAA0C,GAC/C;AAAA,UACA,4CAAC,8BAAW,WAAU,gBACnB,gBAAM,SACT;AAAA;AAAA;AAAA,IACF;AAAA,EAEJ;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,OAAO,EAAE,2BAA2B;AAAA,MACpC;AAAA,MACA,mBAAmB,cAAc,EAAE,gBAAgB,IAAI,EAAE,WAAW;AAAA,MACpE,eAAe;AAAA,MAEf;AAAA,oDAAC,8BACE,YAAE,0GAA0G,GAC/G;AAAA,QACA,4CAAC,8BAAW,SAAQ,eACjB,YAAE,gPAAgP,GACrP;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
|
|
@@ -50,7 +50,7 @@ function Inner(props) {
|
|
|
50
50
|
} : userFromHook;
|
|
51
51
|
const navigate = app.useNavigate();
|
|
52
52
|
const project = app.useProject();
|
|
53
|
-
const rawTeams = user?.useTeams();
|
|
53
|
+
const rawTeams = props.teams ?? user?.useTeams();
|
|
54
54
|
const selectedTeam = props.team || rawTeams?.find((team) => team.id === props.teamId);
|
|
55
55
|
const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);
|
|
56
56
|
return /* @__PURE__ */ jsxs(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":";;;AAMA,SAAS,2BAA2B;AACpC,SAAS,kCAAkC;AAC3C;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,YAAY,gBAAgB;AACrC,SAAS,UAAU,eAAe;AAClC,SAAe,aAAa,eAAe;AAC3C,SAAS,sBAAsB;AAC/B,SAAS,gBAAgB;
|
|
1
|
+
{"version":3,"sources":["../../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n teams?: Team[],\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = props.teams ?? user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
// src/components-page/account-settings/payments/payments-page.tsx
|
|
5
|
-
import { useState } from "react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
6
|
import { TeamSwitcher } from "../../../index.js";
|
|
7
7
|
import { useUser } from "../../../lib/hooks.js";
|
|
8
8
|
import { useTranslation } from "../../../lib/translations.js";
|
|
@@ -12,11 +12,25 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
function PaymentsPage(props) {
|
|
13
13
|
const { t } = useTranslation();
|
|
14
14
|
const user = useUser({ or: props.mockMode ? "return-null" : "redirect" });
|
|
15
|
-
const teams = user?.useTeams() ?? [];
|
|
15
|
+
const teams = props.availableTeams ?? user?.useTeams() ?? [];
|
|
16
|
+
const allowPersonal = props.allowPersonal ?? true;
|
|
16
17
|
const hasTeams = teams.length > 0;
|
|
17
18
|
const [selectedTeam, setSelectedTeam] = useState(null);
|
|
18
|
-
const
|
|
19
|
-
const
|
|
19
|
+
const effectiveSelectedTeam = selectedTeam ?? (!allowPersonal ? teams[0] ?? null : null);
|
|
20
|
+
const customer = effectiveSelectedTeam ?? (allowPersonal ? user : null);
|
|
21
|
+
const customerType = effectiveSelectedTeam ? "team" : "user";
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (props.mockMode) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (!allowPersonal && !selectedTeam && teams.length > 0) {
|
|
27
|
+
setSelectedTeam(teams[0]);
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (selectedTeam && !teams.some((team) => team.id === selectedTeam.id)) {
|
|
31
|
+
setSelectedTeam(allowPersonal ? null : teams[0] ?? null);
|
|
32
|
+
}
|
|
33
|
+
}, [allowPersonal, props.mockMode, selectedTeam, teams]);
|
|
20
34
|
if (props.mockMode) {
|
|
21
35
|
return /* @__PURE__ */ jsx(PageLayout, { children: /* @__PURE__ */ jsx(
|
|
22
36
|
PaymentsPanel,
|
|
@@ -32,8 +46,9 @@ function PaymentsPage(props) {
|
|
|
32
46
|
hasTeams ? /* @__PURE__ */ jsx(
|
|
33
47
|
TeamSwitcher,
|
|
34
48
|
{
|
|
35
|
-
team:
|
|
36
|
-
|
|
49
|
+
team: effectiveSelectedTeam ?? void 0,
|
|
50
|
+
teams,
|
|
51
|
+
allowNull: allowPersonal,
|
|
37
52
|
nullLabel: t("Personal"),
|
|
38
53
|
onChange: async (team) => {
|
|
39
54
|
setSelectedTeam(team);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components-page/account-settings/payments/payments-page.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { useState } from \"react\";\nimport { Team, TeamSwitcher } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { PaymentsPanel } from \"./payments-panel\";\n\nexport function PaymentsPage(props: { mockMode?: boolean }) {\n const { t } = useTranslation();\n const user = useUser({ or: props.mockMode ? \"return-null\" : \"redirect\" });\n const teams = user?.useTeams() ?? [];\n const hasTeams = teams.length > 0;\n const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);\n const
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components-page/account-settings/payments/payments-page.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { useEffect, useState } from \"react\";\nimport { Team, TeamSwitcher } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { PaymentsPanel } from \"./payments-panel\";\n\nexport function PaymentsPage(props: { mockMode?: boolean, availableTeams?: Team[], allowPersonal?: boolean }) {\n const { t } = useTranslation();\n const user = useUser({ or: props.mockMode ? \"return-null\" : \"redirect\" });\n const teams = props.availableTeams ?? user?.useTeams() ?? [];\n const allowPersonal = props.allowPersonal ?? true;\n const hasTeams = teams.length > 0;\n const [selectedTeam, setSelectedTeam] = useState<Team | null>(null);\n const effectiveSelectedTeam = selectedTeam ?? (!allowPersonal ? (teams[0] ?? null) : null);\n const customer = effectiveSelectedTeam ?? (allowPersonal ? user : null);\n const customerType = effectiveSelectedTeam ? \"team\" : \"user\";\n\n useEffect(() => {\n if (props.mockMode) {\n return;\n }\n if (!allowPersonal && !selectedTeam && teams.length > 0) {\n setSelectedTeam(teams[0]);\n return;\n }\n if (selectedTeam && !teams.some(team => team.id === selectedTeam.id)) {\n setSelectedTeam(allowPersonal ? null : (teams[0] ?? null));\n }\n }, [allowPersonal, props.mockMode, selectedTeam, teams]);\n\n if (props.mockMode) {\n return (\n <PageLayout>\n <PaymentsPanel\n mockMode\n />\n </PageLayout>\n );\n }\n\n if (!customer) {\n return null;\n }\n\n\n return (\n <PageLayout>\n {hasTeams ? (\n <TeamSwitcher\n team={effectiveSelectedTeam ?? undefined}\n teams={teams}\n allowNull={allowPersonal}\n nullLabel={t(\"Personal\")}\n onChange={async (team) => {\n setSelectedTeam(team);\n }}\n />\n ) : null}\n <PaymentsPanel\n customer={customer}\n customerType={customerType}\n />\n </PageLayout>\n );\n}\n"],"mappings":";;;AAOA,SAAS,WAAW,gBAAgB;AACpC,SAAe,oBAAoB;AACnC,SAAS,eAAe;AACxB,SAAS,sBAAsB;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,qBAAqB;AA6BtB,cAaJ,YAbI;AA3BD,SAAS,aAAa,OAAiF;AAC5G,QAAM,EAAE,EAAE,IAAI,eAAe;AAC7B,QAAM,OAAO,QAAQ,EAAE,IAAI,MAAM,WAAW,gBAAgB,WAAW,CAAC;AACxE,QAAM,QAAQ,MAAM,kBAAkB,MAAM,SAAS,KAAK,CAAC;AAC3D,QAAM,gBAAgB,MAAM,iBAAiB;AAC7C,QAAM,WAAW,MAAM,SAAS;AAChC,QAAM,CAAC,cAAc,eAAe,IAAI,SAAsB,IAAI;AAClE,QAAM,wBAAwB,iBAAiB,CAAC,gBAAiB,MAAM,CAAC,KAAK,OAAQ;AACrF,QAAM,WAAW,0BAA0B,gBAAgB,OAAO;AAClE,QAAM,eAAe,wBAAwB,SAAS;AAEtD,YAAU,MAAM;AACd,QAAI,MAAM,UAAU;AAClB;AAAA,IACF;AACA,QAAI,CAAC,iBAAiB,CAAC,gBAAgB,MAAM,SAAS,GAAG;AACvD,sBAAgB,MAAM,CAAC,CAAC;AACxB;AAAA,IACF;AACA,QAAI,gBAAgB,CAAC,MAAM,KAAK,UAAQ,KAAK,OAAO,aAAa,EAAE,GAAG;AACpE,sBAAgB,gBAAgB,OAAQ,MAAM,CAAC,KAAK,IAAK;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,eAAe,MAAM,UAAU,cAAc,KAAK,CAAC;AAEvD,MAAI,MAAM,UAAU;AAClB,WACE,oBAAC,cACC;AAAA,MAAC;AAAA;AAAA,QACC,UAAQ;AAAA;AAAA,IACV,GACF;AAAA,EAEJ;AAEA,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAGA,SACE,qBAAC,cACE;AAAA,eACC;AAAA,MAAC;AAAA;AAAA,QACC,MAAM,yBAAyB;AAAA,QAC/B;AAAA,QACA,WAAW;AAAA,QACX,WAAW,EAAE,UAAU;AAAA,QACvB,UAAU,OAAO,SAAS;AACxB,0BAAgB,IAAI;AAAA,QACtB;AAAA;AAAA,IACF,IACE;AAAA,IACJ;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;","names":[]}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
"use client";
|
|
3
3
|
|
|
4
4
|
// src/components-page/account-settings.tsx
|
|
5
|
+
import { runAsynchronouslyWithAlert } from "@stackframe/stack-shared/dist/utils/promises";
|
|
5
6
|
import { Skeleton, Typography } from "@stackframe/stack-ui";
|
|
6
7
|
import { Contact, ShieldCheck, Bell, Monitor, Key, Settings, CirclePlus, CreditCard } from "lucide-react";
|
|
7
|
-
import { Suspense } from "react";
|
|
8
|
+
import { Suspense, useEffect, useMemo, useRef, useState } from "react";
|
|
8
9
|
import { useStackApp, useUser } from "../index.js";
|
|
9
10
|
import { MaybeFullPage } from "../components/elements/maybe-full-page.js";
|
|
10
11
|
import { SidebarLayout } from "../components/elements/sidebar-layout.js";
|
|
@@ -48,6 +49,60 @@ function AccountSettings(props) {
|
|
|
48
49
|
const project = props.mockProject || projectFromHook;
|
|
49
50
|
const teams = user?.useTeams() || [];
|
|
50
51
|
const billing = user?.useBilling() || null;
|
|
52
|
+
const teamsKey = useMemo(() => teams.map((team) => team.id).join("|"), [teams]);
|
|
53
|
+
const teamsById = useMemo(() => teams, [teamsKey]);
|
|
54
|
+
const userRef = useRef(userFromHook ?? null);
|
|
55
|
+
const userId = userFromHook?.id ?? null;
|
|
56
|
+
const [paymentsAvailability, setPaymentsAvailability] = useState(() => ({
|
|
57
|
+
userHasProducts: false,
|
|
58
|
+
teamIdsWithProducts: /* @__PURE__ */ new Set(),
|
|
59
|
+
isReady: !!props.mockUser
|
|
60
|
+
}));
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
userRef.current = userFromHook ?? null;
|
|
63
|
+
}, [userFromHook]);
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
if (props.mockUser || !userId) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let cancelled = false;
|
|
69
|
+
runAsynchronouslyWithAlert(async () => {
|
|
70
|
+
const currentUser = userRef.current;
|
|
71
|
+
if (!currentUser || currentUser.id !== userId) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const [userProducts, teamsWithProducts2] = await Promise.all([
|
|
75
|
+
currentUser.listProducts({ limit: 1 }),
|
|
76
|
+
Promise.all(teamsById.map(async (team) => {
|
|
77
|
+
const isTeamAdmin = await currentUser.hasPermission(team, "team_admin");
|
|
78
|
+
if (!isTeamAdmin) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const teamProducts = await team.listProducts({ limit: 1 });
|
|
82
|
+
const hasTeamProducts = teamProducts.some((product) => product.customerType === "team");
|
|
83
|
+
return hasTeamProducts ? team.id : null;
|
|
84
|
+
}))
|
|
85
|
+
]);
|
|
86
|
+
if (cancelled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const userHasProducts = userProducts.some((product) => product.customerType === "user");
|
|
90
|
+
const teamIdsWithProducts = new Set(teamsWithProducts2.filter((id) => id !== null));
|
|
91
|
+
setPaymentsAvailability({
|
|
92
|
+
userHasProducts,
|
|
93
|
+
teamIdsWithProducts,
|
|
94
|
+
isReady: true
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
return () => {
|
|
98
|
+
cancelled = true;
|
|
99
|
+
};
|
|
100
|
+
}, [props.mockUser, teamsById, userId]);
|
|
101
|
+
const teamsWithProducts = useMemo(
|
|
102
|
+
() => teamsById.filter((team) => paymentsAvailability.teamIdsWithProducts.has(team.id)),
|
|
103
|
+
[paymentsAvailability.teamIdsWithProducts, teamsById]
|
|
104
|
+
);
|
|
105
|
+
const shouldShowPaymentsTab = props.mockUser || paymentsAvailability.isReady && (paymentsAvailability.userHasProducts || teamsWithProducts.length > 0);
|
|
51
106
|
if (!props.mockUser && !userFromHook) {
|
|
52
107
|
return null;
|
|
53
108
|
}
|
|
@@ -90,13 +145,20 @@ function AccountSettings(props) {
|
|
|
90
145
|
icon: /* @__PURE__ */ jsx(Icon, { name: "Key" }),
|
|
91
146
|
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ jsx(ApiKeysPage, { mockApiKeys: props.mockApiKeys, mockMode: !!props.mockUser }) })
|
|
92
147
|
}] : [],
|
|
93
|
-
{
|
|
148
|
+
...shouldShowPaymentsTab ? [{
|
|
94
149
|
title: t("Payments"),
|
|
95
150
|
type: "item",
|
|
96
151
|
id: "payments",
|
|
97
152
|
icon: /* @__PURE__ */ jsx(Icon, { name: "CreditCard" }),
|
|
98
|
-
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(PaymentsPageSkeleton, {}), children: /* @__PURE__ */ jsx(
|
|
99
|
-
|
|
153
|
+
content: /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(PaymentsPageSkeleton, {}), children: /* @__PURE__ */ jsx(
|
|
154
|
+
PaymentsPage,
|
|
155
|
+
{
|
|
156
|
+
mockMode: !!props.mockUser,
|
|
157
|
+
allowPersonal: paymentsAvailability.userHasProducts,
|
|
158
|
+
availableTeams: teamsWithProducts
|
|
159
|
+
}
|
|
160
|
+
) })
|
|
161
|
+
}] : [],
|
|
100
162
|
{
|
|
101
163
|
title: t("Settings"),
|
|
102
164
|
type: "item",
|