@replicated/portal-components 0.0.2 → 0.0.3
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/components/metadata/registry.json +83 -2
- package/components/metadata/registry.md +27 -2
- package/dist/actions/index.d.mts +566 -3
- package/dist/actions/index.d.ts +566 -3
- package/dist/actions/index.js +1853 -12
- package/dist/actions/index.js.map +1 -1
- package/dist/airgap-instances.d.mts +26 -0
- package/dist/airgap-instances.d.ts +26 -0
- package/dist/airgap-instances.js +354 -0
- package/dist/airgap-instances.js.map +1 -0
- package/dist/error-page.d.mts +14 -0
- package/dist/error-page.d.ts +14 -0
- package/dist/error-page.js +153 -0
- package/dist/error-page.js.map +1 -0
- package/dist/error.d.mts +15 -0
- package/dist/error.d.ts +15 -0
- package/dist/error.js +144 -0
- package/dist/error.js.map +1 -0
- package/dist/esm/actions/index.js +1816 -13
- package/dist/esm/actions/index.js.map +1 -1
- package/dist/esm/airgap-instances.js +352 -0
- package/dist/esm/airgap-instances.js.map +1 -0
- package/dist/esm/error-page.js +151 -0
- package/dist/esm/error-page.js.map +1 -0
- package/dist/esm/error.js +142 -0
- package/dist/esm/error.js.map +1 -0
- package/dist/esm/helm-install-wizard.js +1007 -0
- package/dist/esm/helm-install-wizard.js.map +1 -0
- package/dist/esm/index.js +2232 -155
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-actions.js +746 -0
- package/dist/esm/install-actions.js.map +1 -0
- package/dist/esm/install-card.js +115 -0
- package/dist/esm/install-card.js.map +1 -0
- package/dist/esm/install-targets.js +48 -0
- package/dist/esm/install-targets.js.map +1 -0
- package/dist/esm/instance-card.js +197 -0
- package/dist/esm/instance-card.js.map +1 -0
- package/dist/esm/join-team.js +218 -0
- package/dist/esm/join-team.js.map +1 -0
- package/dist/esm/license-card.js +131 -0
- package/dist/esm/license-card.js.map +1 -0
- package/dist/esm/license-details.js +667 -0
- package/dist/esm/license-details.js.map +1 -0
- package/dist/esm/linux-install-wizard.js +1083 -0
- package/dist/esm/linux-install-wizard.js.map +1 -0
- package/dist/esm/login.js +261 -0
- package/dist/esm/login.js.map +1 -0
- package/dist/esm/online-instance-list.js +287 -0
- package/dist/esm/online-instance-list.js.map +1 -0
- package/dist/esm/pending-installations.js +235 -0
- package/dist/esm/pending-installations.js.map +1 -0
- package/dist/esm/release-history-panel.js +100 -0
- package/dist/esm/release-history-panel.js.map +1 -0
- package/dist/esm/release-notes-card.js +23 -0
- package/dist/esm/release-notes-card.js.map +1 -0
- package/dist/esm/security-card.js +700 -0
- package/dist/esm/security-card.js.map +1 -0
- package/dist/esm/support-bundle-collection-card.js +170 -0
- package/dist/esm/support-bundle-collection-card.js.map +1 -0
- package/dist/esm/support-bundles-card.js +306 -0
- package/dist/esm/support-bundles-card.js.map +1 -0
- package/dist/esm/support-card.js +305 -0
- package/dist/esm/support-card.js.map +1 -0
- package/dist/esm/team-selection.js +117 -0
- package/dist/esm/team-selection.js.map +1 -0
- package/dist/esm/team-settings-card.js +78 -0
- package/dist/esm/team-settings-card.js.map +1 -0
- package/dist/esm/team-settings.js +136 -0
- package/dist/esm/team-settings.js.map +1 -0
- package/dist/esm/top-nav-user-menu.js +173 -0
- package/dist/esm/top-nav-user-menu.js.map +1 -0
- package/dist/esm/top-nav.js +398 -0
- package/dist/esm/top-nav.js.map +1 -0
- package/dist/esm/update-layout.js +405 -0
- package/dist/esm/update-layout.js.map +1 -0
- package/dist/esm/updates-card.js +85 -0
- package/dist/esm/updates-card.js.map +1 -0
- package/dist/esm/upload-support-bundle-modal.js +143 -0
- package/dist/esm/upload-support-bundle-modal.js.map +1 -0
- package/dist/esm/user-settings-card.js +21 -0
- package/dist/esm/user-settings-card.js.map +1 -0
- package/dist/esm/user-settings.js +368 -0
- package/dist/esm/user-settings.js.map +1 -0
- package/dist/esm/utils/index.js +170 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/helm-install-wizard.d.mts +38 -0
- package/dist/helm-install-wizard.d.ts +38 -0
- package/dist/helm-install-wizard.js +1011 -0
- package/dist/helm-install-wizard.js.map +1 -0
- package/dist/index.d.mts +11 -27
- package/dist/index.d.ts +11 -27
- package/dist/index.js +2258 -154
- package/dist/index.js.map +1 -1
- package/dist/install-B19AaKF_.d.mts +233 -0
- package/dist/install-Bi1qJ8Bu.d.ts +233 -0
- package/dist/install-actions.d.mts +141 -0
- package/dist/install-actions.d.ts +141 -0
- package/dist/install-actions.js +765 -0
- package/dist/install-actions.js.map +1 -0
- package/dist/install-card.d.mts +15 -0
- package/dist/install-card.d.ts +15 -0
- package/dist/install-card.js +117 -0
- package/dist/install-card.js.map +1 -0
- package/dist/install-targets.d.mts +19 -0
- package/dist/install-targets.d.ts +19 -0
- package/dist/install-targets.js +50 -0
- package/dist/install-targets.js.map +1 -0
- package/dist/instance-card.d.mts +22 -0
- package/dist/instance-card.d.ts +22 -0
- package/dist/instance-card.js +199 -0
- package/dist/instance-card.js.map +1 -0
- package/dist/join-team.d.mts +30 -0
- package/dist/join-team.d.ts +30 -0
- package/dist/join-team.js +220 -0
- package/dist/join-team.js.map +1 -0
- package/dist/license-card.d.mts +15 -0
- package/dist/license-card.d.ts +15 -0
- package/dist/license-card.js +133 -0
- package/dist/license-card.js.map +1 -0
- package/dist/license-details.d.mts +10 -0
- package/dist/license-details.d.ts +10 -0
- package/dist/license-details.js +669 -0
- package/dist/license-details.js.map +1 -0
- package/dist/linux-install-wizard.d.mts +66 -0
- package/dist/linux-install-wizard.d.ts +66 -0
- package/dist/linux-install-wizard.js +1093 -0
- package/dist/linux-install-wizard.js.map +1 -0
- package/dist/login.d.mts +37 -0
- package/dist/login.d.ts +37 -0
- package/dist/login.js +263 -0
- package/dist/login.js.map +1 -0
- package/dist/online-instance-list.d.mts +22 -0
- package/dist/online-instance-list.d.ts +22 -0
- package/dist/online-instance-list.js +289 -0
- package/dist/online-instance-list.js.map +1 -0
- package/dist/pending-installations.d.mts +15 -0
- package/dist/pending-installations.d.ts +15 -0
- package/dist/pending-installations.js +237 -0
- package/dist/pending-installations.js.map +1 -0
- package/dist/release-history-panel.d.mts +22 -0
- package/dist/release-history-panel.d.ts +22 -0
- package/dist/release-history-panel.js +102 -0
- package/dist/release-history-panel.js.map +1 -0
- package/dist/release-notes-card.d.mts +13 -0
- package/dist/release-notes-card.d.ts +13 -0
- package/dist/release-notes-card.js +25 -0
- package/dist/release-notes-card.js.map +1 -0
- package/dist/security-card.d.mts +73 -0
- package/dist/security-card.d.ts +73 -0
- package/dist/security-card.js +702 -0
- package/dist/security-card.js.map +1 -0
- package/dist/styles.css +1877 -194
- package/dist/support-bundle-collection-card.d.mts +20 -0
- package/dist/support-bundle-collection-card.d.ts +20 -0
- package/dist/support-bundle-collection-card.js +172 -0
- package/dist/support-bundle-collection-card.js.map +1 -0
- package/dist/support-bundles-card.d.mts +19 -0
- package/dist/support-bundles-card.d.ts +19 -0
- package/dist/support-bundles-card.js +308 -0
- package/dist/support-bundles-card.js.map +1 -0
- package/dist/support-card.d.mts +8 -0
- package/dist/support-card.d.ts +8 -0
- package/dist/support-card.js +307 -0
- package/dist/support-card.js.map +1 -0
- package/dist/team-selection.d.mts +23 -0
- package/dist/team-selection.d.ts +23 -0
- package/dist/team-selection.js +119 -0
- package/dist/team-selection.js.map +1 -0
- package/dist/team-settings-card-Dq1d9b5c.d.mts +14 -0
- package/dist/team-settings-card-Dq1d9b5c.d.ts +14 -0
- package/dist/team-settings-card.d.mts +2 -0
- package/dist/team-settings-card.d.ts +2 -0
- package/dist/team-settings-card.js +80 -0
- package/dist/team-settings-card.js.map +1 -0
- package/dist/team-settings.d.mts +25 -0
- package/dist/team-settings.d.ts +25 -0
- package/dist/team-settings.js +138 -0
- package/dist/team-settings.js.map +1 -0
- package/dist/top-nav-0mb1K_H0.d.mts +32 -0
- package/dist/top-nav-0mb1K_H0.d.ts +32 -0
- package/dist/top-nav-user-menu.d.mts +18 -0
- package/dist/top-nav-user-menu.d.ts +18 -0
- package/dist/top-nav-user-menu.js +175 -0
- package/dist/top-nav-user-menu.js.map +1 -0
- package/dist/top-nav.d.mts +3 -0
- package/dist/top-nav.d.ts +3 -0
- package/dist/top-nav.js +400 -0
- package/dist/top-nav.js.map +1 -0
- package/dist/update-layout.d.mts +12 -0
- package/dist/update-layout.d.ts +12 -0
- package/dist/update-layout.js +407 -0
- package/dist/update-layout.js.map +1 -0
- package/dist/updates-card-BbubBrVR.d.mts +18 -0
- package/dist/updates-card-BbubBrVR.d.ts +18 -0
- package/dist/updates-card.d.mts +2 -0
- package/dist/updates-card.d.ts +2 -0
- package/dist/updates-card.js +87 -0
- package/dist/updates-card.js.map +1 -0
- package/dist/upload-support-bundle-modal.d.mts +19 -0
- package/dist/upload-support-bundle-modal.d.ts +19 -0
- package/dist/upload-support-bundle-modal.js +145 -0
- package/dist/upload-support-bundle-modal.js.map +1 -0
- package/dist/user-settings-card.d.mts +8 -0
- package/dist/user-settings-card.d.ts +8 -0
- package/dist/user-settings-card.js +23 -0
- package/dist/user-settings-card.js.map +1 -0
- package/dist/user-settings.d.mts +47 -0
- package/dist/user-settings.d.ts +47 -0
- package/dist/user-settings.js +370 -0
- package/dist/user-settings.js.map +1 -0
- package/dist/utils/index.d.mts +70 -0
- package/dist/utils/index.d.ts +70 -0
- package/dist/utils/index.js +177 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +163 -3
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useMemo } from 'react';
|
|
3
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Enterprise Portal Components
|
|
7
|
+
* This file is generated by tsup. Do not edit manually.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
var NOTIFICATION_DESCRIPTIONS = {
|
|
11
|
+
"new-version": "New version available"
|
|
12
|
+
};
|
|
13
|
+
var EmailNotificationsSection = ({
|
|
14
|
+
teamName,
|
|
15
|
+
notifications,
|
|
16
|
+
isLoading,
|
|
17
|
+
onToggle
|
|
18
|
+
}) => {
|
|
19
|
+
const allNotificationTypes = Object.keys(NOTIFICATION_DESCRIPTIONS);
|
|
20
|
+
const mergedNotifications = allNotificationTypes.map((type) => {
|
|
21
|
+
const existing = notifications.find((n) => n.type === type);
|
|
22
|
+
return {
|
|
23
|
+
type,
|
|
24
|
+
enabled: existing ? existing.enabled : type === "new-version"
|
|
25
|
+
// Default new-version to true
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
if (isLoading) {
|
|
29
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
|
|
30
|
+
/* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-900", children: [
|
|
31
|
+
"Email Notifications for ",
|
|
32
|
+
teamName
|
|
33
|
+
] }),
|
|
34
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Choose which email notifications you want to receive for this team." }),
|
|
35
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 flex justify-center py-8", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Loading notifications..." }) })
|
|
36
|
+
] });
|
|
37
|
+
}
|
|
38
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
|
|
39
|
+
/* @__PURE__ */ jsxs("h2", { className: "text-lg font-semibold text-gray-900", children: [
|
|
40
|
+
"Email Notifications for ",
|
|
41
|
+
teamName
|
|
42
|
+
] }),
|
|
43
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Choose which email notifications you want to receive for this team." }),
|
|
44
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 overflow-hidden rounded-lg border border-gray-200", children: [
|
|
45
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between bg-gray-50 px-4 py-2", children: [
|
|
46
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase text-gray-600", children: "Description" }),
|
|
47
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium uppercase text-gray-600", children: "Enabled" })
|
|
48
|
+
] }),
|
|
49
|
+
mergedNotifications.map((notification) => /* @__PURE__ */ jsxs(
|
|
50
|
+
"div",
|
|
51
|
+
{
|
|
52
|
+
className: "flex items-center justify-between border-t border-gray-200 px-4 py-3",
|
|
53
|
+
children: [
|
|
54
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-gray-900", children: NOTIFICATION_DESCRIPTIONS[notification.type] }),
|
|
55
|
+
/* @__PURE__ */ jsx(
|
|
56
|
+
"input",
|
|
57
|
+
{
|
|
58
|
+
type: "checkbox",
|
|
59
|
+
checked: notification.enabled,
|
|
60
|
+
onChange: () => onToggle?.(notification.type, !notification.enabled),
|
|
61
|
+
className: "portal-checkbox"
|
|
62
|
+
}
|
|
63
|
+
)
|
|
64
|
+
]
|
|
65
|
+
},
|
|
66
|
+
notification.type
|
|
67
|
+
))
|
|
68
|
+
] }),
|
|
69
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-3 text-xs text-gray-500", children: [
|
|
70
|
+
"These notification preferences are specific to the ",
|
|
71
|
+
teamName,
|
|
72
|
+
" team. Switch teams to manage notifications for other teams."
|
|
73
|
+
] })
|
|
74
|
+
] });
|
|
75
|
+
};
|
|
76
|
+
var TeamsSection = ({
|
|
77
|
+
teams,
|
|
78
|
+
currentCustomer,
|
|
79
|
+
isLoading,
|
|
80
|
+
onTeamSwitch,
|
|
81
|
+
primaryColor
|
|
82
|
+
}) => {
|
|
83
|
+
const groupedTeams = useMemo(() => {
|
|
84
|
+
const groups = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const team of teams) {
|
|
86
|
+
const existing = groups.get(team.appId);
|
|
87
|
+
if (existing) {
|
|
88
|
+
existing.teams.push(team);
|
|
89
|
+
} else {
|
|
90
|
+
groups.set(team.appId, {
|
|
91
|
+
appName: team.appName,
|
|
92
|
+
teams: [team]
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return groups;
|
|
97
|
+
}, [teams]);
|
|
98
|
+
if (isLoading) {
|
|
99
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
|
|
100
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Teams" }),
|
|
101
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Teams associate your email address with any additional licenses your account has access to." }),
|
|
102
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-500", children: "Loading teams..." }) })
|
|
103
|
+
] });
|
|
104
|
+
}
|
|
105
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
|
|
106
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Teams" }),
|
|
107
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Teams associate your email address with any additional licenses your account has access to." }),
|
|
108
|
+
/* @__PURE__ */ jsx("div", { className: "mt-6 space-y-6", children: Array.from(groupedTeams.values()).map((appGroup) => /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
109
|
+
/* @__PURE__ */ jsx("h3", { className: "text-base font-medium text-gray-900", children: appGroup.appName }),
|
|
110
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: appGroup.teams.map((team) => {
|
|
111
|
+
const isCurrentTeam = currentCustomer?.id === team.id;
|
|
112
|
+
return /* @__PURE__ */ jsxs(
|
|
113
|
+
"button",
|
|
114
|
+
{
|
|
115
|
+
type: "button",
|
|
116
|
+
onClick: () => !isCurrentTeam && onTeamSwitch?.(team),
|
|
117
|
+
disabled: isCurrentTeam,
|
|
118
|
+
className: `flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${isCurrentTeam ? "cursor-default border-gray-300 bg-gray-50" : "cursor-pointer border-gray-200 hover:border-indigo-500"}`,
|
|
119
|
+
style: !isCurrentTeam && primaryColor ? { "--hover-border": primaryColor } : void 0,
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
122
|
+
/* @__PURE__ */ jsx(
|
|
123
|
+
"svg",
|
|
124
|
+
{
|
|
125
|
+
className: "h-4 w-4",
|
|
126
|
+
style: { color: primaryColor || "#6366f1" },
|
|
127
|
+
fill: "none",
|
|
128
|
+
viewBox: "0 0 24 24",
|
|
129
|
+
stroke: "currentColor",
|
|
130
|
+
strokeWidth: 2,
|
|
131
|
+
children: /* @__PURE__ */ jsx(
|
|
132
|
+
"path",
|
|
133
|
+
{
|
|
134
|
+
strokeLinecap: "round",
|
|
135
|
+
strokeLinejoin: "round",
|
|
136
|
+
d: "M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z"
|
|
137
|
+
}
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
),
|
|
141
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
142
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900", children: team.name }),
|
|
143
|
+
isCurrentTeam && /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500", children: "Current Team" })
|
|
144
|
+
] })
|
|
145
|
+
] }),
|
|
146
|
+
!isCurrentTeam && /* @__PURE__ */ jsx(
|
|
147
|
+
"svg",
|
|
148
|
+
{
|
|
149
|
+
className: "h-4 w-4",
|
|
150
|
+
style: { color: primaryColor || "#6366f1" },
|
|
151
|
+
fill: "none",
|
|
152
|
+
viewBox: "0 0 24 24",
|
|
153
|
+
stroke: "currentColor",
|
|
154
|
+
strokeWidth: 2,
|
|
155
|
+
children: /* @__PURE__ */ jsx(
|
|
156
|
+
"path",
|
|
157
|
+
{
|
|
158
|
+
strokeLinecap: "round",
|
|
159
|
+
strokeLinejoin: "round",
|
|
160
|
+
d: "M9 5l7 7-7 7"
|
|
161
|
+
}
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
team.id
|
|
168
|
+
);
|
|
169
|
+
}) })
|
|
170
|
+
] }, appGroup.appName)) })
|
|
171
|
+
] });
|
|
172
|
+
};
|
|
173
|
+
var ProfileSection = ({
|
|
174
|
+
user,
|
|
175
|
+
isLoading,
|
|
176
|
+
isUpdating,
|
|
177
|
+
error,
|
|
178
|
+
updateError,
|
|
179
|
+
onSave,
|
|
180
|
+
primaryColor
|
|
181
|
+
}) => {
|
|
182
|
+
const [isEditing, setIsEditing] = useState(false);
|
|
183
|
+
const [firstName, setFirstName] = useState(user?.firstName || "");
|
|
184
|
+
const [lastName, setLastName] = useState(user?.lastName || "");
|
|
185
|
+
const [inputError, setInputError] = useState(null);
|
|
186
|
+
const resetForm = () => {
|
|
187
|
+
setFirstName(user?.firstName || "");
|
|
188
|
+
setLastName(user?.lastName || "");
|
|
189
|
+
setInputError(null);
|
|
190
|
+
};
|
|
191
|
+
const handleSave = async () => {
|
|
192
|
+
if (!firstName && !lastName) {
|
|
193
|
+
setInputError("Please enter a first name or last name");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (firstName === user?.firstName && lastName === user?.lastName) {
|
|
197
|
+
setIsEditing(false);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
await onSave?.({ firstName, lastName });
|
|
202
|
+
setIsEditing(false);
|
|
203
|
+
} catch (err) {
|
|
204
|
+
console.error("Error updating user information:", err);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
const handleCancel = () => {
|
|
208
|
+
setIsEditing(false);
|
|
209
|
+
resetForm();
|
|
210
|
+
};
|
|
211
|
+
const handleEdit = () => {
|
|
212
|
+
resetForm();
|
|
213
|
+
setIsEditing(true);
|
|
214
|
+
};
|
|
215
|
+
const buttonStyle = primaryColor ? { backgroundColor: primaryColor, borderColor: primaryColor } : void 0;
|
|
216
|
+
return /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-6 shadow-sm", children: [
|
|
217
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
218
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
219
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Profile Information" }),
|
|
220
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Your account information." }),
|
|
221
|
+
(error || updateError) && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600", children: error?.message || updateError?.message || "Failed to update profile" }),
|
|
222
|
+
inputError && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-red-600", children: inputError })
|
|
223
|
+
] }),
|
|
224
|
+
isEditing ? /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
225
|
+
/* @__PURE__ */ jsx(
|
|
226
|
+
"button",
|
|
227
|
+
{
|
|
228
|
+
type: "button",
|
|
229
|
+
onClick: handleCancel,
|
|
230
|
+
disabled: isUpdating,
|
|
231
|
+
className: "inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 disabled:opacity-50",
|
|
232
|
+
children: "Cancel"
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
/* @__PURE__ */ jsx(
|
|
236
|
+
"button",
|
|
237
|
+
{
|
|
238
|
+
type: "button",
|
|
239
|
+
onClick: handleSave,
|
|
240
|
+
disabled: isUpdating,
|
|
241
|
+
style: buttonStyle,
|
|
242
|
+
className: "inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600 disabled:opacity-50",
|
|
243
|
+
children: isUpdating ? "Saving..." : "Save"
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
] }) : /* @__PURE__ */ jsx(
|
|
247
|
+
"button",
|
|
248
|
+
{
|
|
249
|
+
type: "button",
|
|
250
|
+
onClick: handleEdit,
|
|
251
|
+
style: buttonStyle,
|
|
252
|
+
className: "inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600",
|
|
253
|
+
children: "Edit"
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
|
+
] }),
|
|
257
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-6 flex gap-8", children: [
|
|
258
|
+
isEditing ? /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
|
|
259
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
260
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-600", children: "First Name" }),
|
|
261
|
+
/* @__PURE__ */ jsx(
|
|
262
|
+
"input",
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
value: firstName,
|
|
266
|
+
onChange: (e) => {
|
|
267
|
+
setInputError(null);
|
|
268
|
+
setFirstName(e.target.value);
|
|
269
|
+
},
|
|
270
|
+
disabled: isUpdating,
|
|
271
|
+
placeholder: "First Name",
|
|
272
|
+
className: "portal-input mt-1 block w-48"
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
] }),
|
|
276
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
277
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Last Name" }),
|
|
278
|
+
/* @__PURE__ */ jsx(
|
|
279
|
+
"input",
|
|
280
|
+
{
|
|
281
|
+
type: "text",
|
|
282
|
+
value: lastName,
|
|
283
|
+
onChange: (e) => {
|
|
284
|
+
setInputError(null);
|
|
285
|
+
setLastName(e.target.value);
|
|
286
|
+
},
|
|
287
|
+
disabled: isUpdating,
|
|
288
|
+
placeholder: "Last Name",
|
|
289
|
+
className: "portal-input mt-1 block w-48"
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
] })
|
|
293
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
294
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Full Name (optional)" }),
|
|
295
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-900", children: user?.firstName || user?.lastName ? `${user.firstName || ""} ${user.lastName || ""}`.trim() : "\u2014" })
|
|
296
|
+
] }),
|
|
297
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
298
|
+
/* @__PURE__ */ jsx("label", { className: "block text-sm font-medium text-gray-600", children: "Email" }),
|
|
299
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-gray-500", children: isLoading ? "Loading..." : user?.emailAddress || "" })
|
|
300
|
+
] })
|
|
301
|
+
] })
|
|
302
|
+
] });
|
|
303
|
+
};
|
|
304
|
+
var UserSettings = ({
|
|
305
|
+
user,
|
|
306
|
+
isUserLoading = false,
|
|
307
|
+
userError,
|
|
308
|
+
isUpdating = false,
|
|
309
|
+
updateError,
|
|
310
|
+
onUpdateUser,
|
|
311
|
+
teams = [],
|
|
312
|
+
currentCustomer,
|
|
313
|
+
isTeamsLoading = false,
|
|
314
|
+
onTeamSwitch,
|
|
315
|
+
notifications = [],
|
|
316
|
+
isNotificationsLoading = false,
|
|
317
|
+
onToggleNotification,
|
|
318
|
+
primaryColor
|
|
319
|
+
}) => {
|
|
320
|
+
if (userError) {
|
|
321
|
+
return /* @__PURE__ */ jsx("div", { className: "p-8", children: /* @__PURE__ */ jsxs("div", { className: "text-red-600", children: [
|
|
322
|
+
"Failed to load user settings. ",
|
|
323
|
+
userError?.message,
|
|
324
|
+
" Please try again later."
|
|
325
|
+
] }) });
|
|
326
|
+
}
|
|
327
|
+
return /* @__PURE__ */ jsxs("div", { className: "w-full space-y-6", children: [
|
|
328
|
+
/* @__PURE__ */ jsx("header", { children: /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold text-gray-900", children: "User Settings" }) }),
|
|
329
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
330
|
+
/* @__PURE__ */ jsx(
|
|
331
|
+
ProfileSection,
|
|
332
|
+
{
|
|
333
|
+
user,
|
|
334
|
+
isLoading: isUserLoading,
|
|
335
|
+
isUpdating,
|
|
336
|
+
error: userError,
|
|
337
|
+
updateError,
|
|
338
|
+
onSave: onUpdateUser,
|
|
339
|
+
primaryColor
|
|
340
|
+
}
|
|
341
|
+
),
|
|
342
|
+
/* @__PURE__ */ jsx(
|
|
343
|
+
TeamsSection,
|
|
344
|
+
{
|
|
345
|
+
teams,
|
|
346
|
+
currentCustomer,
|
|
347
|
+
isLoading: isTeamsLoading,
|
|
348
|
+
onTeamSwitch,
|
|
349
|
+
primaryColor
|
|
350
|
+
}
|
|
351
|
+
),
|
|
352
|
+
currentCustomer && /* @__PURE__ */ jsx(
|
|
353
|
+
EmailNotificationsSection,
|
|
354
|
+
{
|
|
355
|
+
teamName: currentCustomer.name,
|
|
356
|
+
notifications,
|
|
357
|
+
isLoading: isNotificationsLoading,
|
|
358
|
+
onToggle: onToggleNotification
|
|
359
|
+
}
|
|
360
|
+
)
|
|
361
|
+
] })
|
|
362
|
+
] });
|
|
363
|
+
};
|
|
364
|
+
UserSettings.displayName = "UserSettings";
|
|
365
|
+
|
|
366
|
+
export { UserSettings };
|
|
367
|
+
//# sourceMappingURL=user-settings.js.map
|
|
368
|
+
//# sourceMappingURL=user-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/components/user-settings.tsx"],"names":[],"mappings":"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","file":"user-settings.js","sourcesContent":["\"use client\";\n\nimport { useState, useMemo } from \"react\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface UserSettingsUser {\n emailAddress: string;\n firstName?: string;\n lastName?: string;\n}\n\nexport interface UserSettingsTeam {\n id: string;\n name: string;\n appId: string;\n appName: string;\n appSlug: string;\n}\n\nexport interface UserSettingsCustomer {\n id: string;\n name: string;\n}\n\nexport interface UserSettingsNotification {\n type: string;\n enabled: boolean;\n}\n\nexport interface UserSettingsProps {\n // User profile\n user?: UserSettingsUser;\n isUserLoading?: boolean;\n userError?: Error | null;\n \n // Profile editing\n isUpdating?: boolean;\n updateError?: Error | null;\n onUpdateUser?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n \n // Teams\n teams?: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isTeamsLoading?: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n \n // Notifications\n notifications?: UserSettingsNotification[];\n isNotificationsLoading?: boolean;\n onToggleNotification?: (notificationType: string, enabled: boolean) => Promise<void>;\n \n // Optional branding\n primaryColor?: string;\n}\n\n// =============================================================================\n// Notification descriptions\n// =============================================================================\n\nconst NOTIFICATION_DESCRIPTIONS: Record<string, string> = {\n \"new-version\": \"New version available\",\n};\n\n// =============================================================================\n// Email Notifications Section\n// =============================================================================\n\ninterface EmailNotificationsSectionProps {\n teamName: string;\n notifications: UserSettingsNotification[];\n isLoading: boolean;\n onToggle?: (type: string, enabled: boolean) => Promise<void>;\n}\n\nconst EmailNotificationsSection = ({\n teamName,\n notifications,\n isLoading,\n onToggle,\n}: EmailNotificationsSectionProps) => {\n // Merge API data with all notification types to ensure all options are always shown\n const allNotificationTypes = Object.keys(NOTIFICATION_DESCRIPTIONS);\n const mergedNotifications = allNotificationTypes.map((type) => {\n const existing = notifications.find((n) => n.type === type);\n return {\n type,\n enabled: existing ? existing.enabled : type === \"new-version\", // Default new-version to true\n };\n });\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n <div className=\"mt-6 flex justify-center py-8\">\n <p className=\"text-sm text-gray-500\">Loading notifications...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Email Notifications for {teamName}\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Choose which email notifications you want to receive for this team.\n </p>\n\n <div className=\"mt-6 overflow-hidden rounded-lg border border-gray-200\">\n <div className=\"flex items-center justify-between bg-gray-50 px-4 py-2\">\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Description\n </span>\n <span className=\"text-xs font-medium uppercase text-gray-600\">\n Enabled\n </span>\n </div>\n {mergedNotifications.map((notification) => (\n <div\n key={notification.type}\n className=\"flex items-center justify-between border-t border-gray-200 px-4 py-3\"\n >\n <span className=\"text-sm text-gray-900\">\n {NOTIFICATION_DESCRIPTIONS[notification.type]}\n </span>\n <input\n type=\"checkbox\"\n checked={notification.enabled}\n onChange={() => onToggle?.(notification.type, !notification.enabled)}\n className=\"portal-checkbox\"\n />\n </div>\n ))}\n </div>\n <p className=\"mt-3 text-xs text-gray-500\">\n These notification preferences are specific to the {teamName} team.\n Switch teams to manage notifications for other teams.\n </p>\n </div>\n );\n};\n\n// =============================================================================\n// Teams Section\n// =============================================================================\n\ninterface TeamsSectionProps {\n teams: UserSettingsTeam[];\n currentCustomer?: UserSettingsCustomer;\n isLoading: boolean;\n onTeamSwitch?: (team: UserSettingsTeam) => Promise<void>;\n primaryColor?: string;\n}\n\nconst TeamsSection = ({\n teams,\n currentCustomer,\n isLoading,\n onTeamSwitch,\n primaryColor,\n}: TeamsSectionProps) => {\n // Group teams by app\n const groupedTeams = useMemo(() => {\n const groups = new Map<string, { appName: string; teams: UserSettingsTeam[] }>();\n \n for (const team of teams) {\n const existing = groups.get(team.appId);\n if (existing) {\n existing.teams.push(team);\n } else {\n groups.set(team.appId, {\n appName: team.appName,\n teams: [team],\n });\n }\n }\n \n return groups;\n }, [teams]);\n\n if (isLoading) {\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n <div className=\"mt-6\">\n <p className=\"text-sm text-gray-500\">Loading teams...</p>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <h2 className=\"text-lg font-semibold text-gray-900\">Teams</h2>\n <p className=\"mt-1 text-sm text-gray-500\">\n Teams associate your email address with any additional licenses your\n account has access to.\n </p>\n\n <div className=\"mt-6 space-y-6\">\n {Array.from(groupedTeams.values()).map((appGroup) => (\n <div key={appGroup.appName} className=\"space-y-3\">\n <h3 className=\"text-base font-medium text-gray-900\">\n {appGroup.appName}\n </h3>\n <div className=\"space-y-2\">\n {appGroup.teams.map((team) => {\n const isCurrentTeam = currentCustomer?.id === team.id;\n return (\n <button\n key={team.id}\n type=\"button\"\n onClick={() => !isCurrentTeam && onTeamSwitch?.(team)}\n disabled={isCurrentTeam}\n className={`flex w-full items-center justify-between rounded-lg border p-4 text-left transition ${\n isCurrentTeam\n ? \"cursor-default border-gray-300 bg-gray-50\"\n : \"cursor-pointer border-gray-200 hover:border-indigo-500\"\n }`}\n style={\n !isCurrentTeam && primaryColor\n ? { \"--hover-border\": primaryColor } as React.CSSProperties\n : undefined\n }\n >\n <div className=\"flex items-center gap-3\">\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || \"#6366f1\" }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z\"\n />\n </svg>\n <div>\n <p className=\"text-sm font-medium text-gray-900\">\n {team.name}\n </p>\n {isCurrentTeam && (\n <p className=\"text-xs text-gray-500\">Current Team</p>\n )}\n </div>\n </div>\n {!isCurrentTeam && (\n <svg\n className=\"h-4 w-4\"\n style={{ color: primaryColor || \"#6366f1\" }}\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n strokeWidth={2}\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n )}\n </button>\n );\n })}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Profile Section\n// =============================================================================\n\ninterface ProfileSectionProps {\n user?: UserSettingsUser;\n isLoading: boolean;\n isUpdating: boolean;\n error?: Error | null;\n updateError?: Error | null;\n onSave?: (data: { firstName?: string; lastName?: string }) => Promise<void>;\n primaryColor?: string;\n}\n\nconst ProfileSection = ({\n user,\n isLoading,\n isUpdating,\n error,\n updateError,\n onSave,\n primaryColor,\n}: ProfileSectionProps) => {\n const [isEditing, setIsEditing] = useState(false);\n const [firstName, setFirstName] = useState(user?.firstName || \"\");\n const [lastName, setLastName] = useState(user?.lastName || \"\");\n const [inputError, setInputError] = useState<string | null>(null);\n\n // Reset form when user changes\n const resetForm = () => {\n setFirstName(user?.firstName || \"\");\n setLastName(user?.lastName || \"\");\n setInputError(null);\n };\n\n const handleSave = async () => {\n if (!firstName && !lastName) {\n setInputError(\"Please enter a first name or last name\");\n return;\n }\n\n if (firstName === user?.firstName && lastName === user?.lastName) {\n setIsEditing(false);\n return;\n }\n\n try {\n await onSave?.({ firstName, lastName });\n setIsEditing(false);\n } catch (err) {\n console.error(\"Error updating user information:\", err);\n }\n };\n\n const handleCancel = () => {\n setIsEditing(false);\n resetForm();\n };\n\n const handleEdit = () => {\n resetForm();\n setIsEditing(true);\n };\n\n const buttonStyle = primaryColor\n ? { backgroundColor: primaryColor, borderColor: primaryColor }\n : undefined;\n\n return (\n <div className=\"rounded-2xl border border-gray-200 bg-white p-6 shadow-sm\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <h2 className=\"text-lg font-semibold text-gray-900\">\n Profile Information\n </h2>\n <p className=\"mt-1 text-sm text-gray-500\">Your account information.</p>\n {(error || updateError) && (\n <p className=\"mt-2 text-sm text-red-600\">\n {error?.message || updateError?.message || \"Failed to update profile\"}\n </p>\n )}\n {inputError && (\n <p className=\"mt-2 text-sm text-red-600\">{inputError}</p>\n )}\n </div>\n {isEditing ? (\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n onClick={handleCancel}\n disabled={isUpdating}\n className=\"inline-flex items-center rounded-md border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 shadow-sm transition hover:bg-gray-50 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={handleSave}\n disabled={isUpdating}\n style={buttonStyle}\n className=\"inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600 disabled:opacity-50\"\n >\n {isUpdating ? \"Saving...\" : \"Save\"}\n </button>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={handleEdit}\n style={buttonStyle}\n className=\"inline-flex items-center rounded-md bg-indigo-500 px-3 py-1.5 text-sm font-medium text-white shadow-sm transition hover:bg-indigo-600\"\n >\n Edit\n </button>\n )}\n </div>\n\n <div className=\"mt-6 flex gap-8\">\n {isEditing ? (\n <div className=\"flex gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n First Name\n </label>\n <input\n type=\"text\"\n value={firstName}\n onChange={(e) => {\n setInputError(null);\n setFirstName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"First Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-gray-600\">\n Last Name\n </label>\n <input\n type=\"text\"\n value={lastName}\n onChange={(e) => {\n setInputError(null);\n setLastName(e.target.value);\n }}\n disabled={isUpdating}\n placeholder=\"Last Name\"\n className=\"portal-input mt-1 block w-48\"\n />\n </div>\n </div>\n ) : (\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">\n Full Name (optional)\n </label>\n <p className=\"mt-1 text-sm text-gray-900\">\n {user?.firstName || user?.lastName\n ? `${user.firstName || \"\"} ${user.lastName || \"\"}`.trim()\n : \"—\"}\n </p>\n </div>\n )}\n <div className=\"flex-1\">\n <label className=\"block text-sm font-medium text-gray-600\">Email</label>\n <p className=\"mt-1 text-sm text-gray-500\">\n {isLoading ? \"Loading...\" : user?.emailAddress || \"\"}\n </p>\n </div>\n </div>\n </div>\n );\n};\n\n// =============================================================================\n// Main Component\n// =============================================================================\n\nexport const UserSettings = ({\n user,\n isUserLoading = false,\n userError,\n isUpdating = false,\n updateError,\n onUpdateUser,\n teams = [],\n currentCustomer,\n isTeamsLoading = false,\n onTeamSwitch,\n notifications = [],\n isNotificationsLoading = false,\n onToggleNotification,\n primaryColor,\n}: UserSettingsProps) => {\n if (userError) {\n return (\n <div className=\"p-8\">\n <div className=\"text-red-600\">\n Failed to load user settings. {userError?.message} Please try again later.\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"w-full space-y-6\">\n <header>\n <h1 className=\"text-2xl font-semibold text-gray-900\">User Settings</h1>\n </header>\n\n <div className=\"space-y-6\">\n <ProfileSection\n user={user}\n isLoading={isUserLoading}\n isUpdating={isUpdating}\n error={userError}\n updateError={updateError}\n onSave={onUpdateUser}\n primaryColor={primaryColor}\n />\n\n <TeamsSection\n teams={teams}\n currentCustomer={currentCustomer}\n isLoading={isTeamsLoading}\n onTeamSwitch={onTeamSwitch}\n primaryColor={primaryColor}\n />\n\n {currentCustomer && (\n <EmailNotificationsSection\n teamName={currentCustomer.name}\n notifications={notifications}\n isLoading={isNotificationsLoading}\n onToggle={onToggleNotification}\n />\n )}\n </div>\n </div>\n );\n};\n\nUserSettings.displayName = \"UserSettings\";\n"]}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import { cache } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Enterprise Portal Components
|
|
6
|
+
* This file is generated by tsup. Do not edit manually.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// src/utils/api-client.ts
|
|
10
|
+
var UnauthorizedError = class extends Error {
|
|
11
|
+
constructor(message = "Unauthorized") {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = "UnauthorizedError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function isRedirectError(error) {
|
|
17
|
+
return typeof error === "object" && error !== null && "digest" in error && typeof error.digest === "string" && error.digest.startsWith("NEXT_REDIRECT");
|
|
18
|
+
}
|
|
19
|
+
async function authenticatedFetch(url, options = {}) {
|
|
20
|
+
const { token, ...fetchOptions } = options;
|
|
21
|
+
const headers = new Headers(fetchOptions.headers);
|
|
22
|
+
if (token) {
|
|
23
|
+
headers.set("authorization", `Bearer ${token}`);
|
|
24
|
+
}
|
|
25
|
+
const response = await fetch(url, {
|
|
26
|
+
...fetchOptions,
|
|
27
|
+
headers
|
|
28
|
+
});
|
|
29
|
+
if (response.status === 401) {
|
|
30
|
+
await handle401();
|
|
31
|
+
}
|
|
32
|
+
if (response.status === 502 || response.status === 503 || response.status === 504) {
|
|
33
|
+
await handleServerError(response.status);
|
|
34
|
+
}
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
37
|
+
async function handle401() {
|
|
38
|
+
const { redirect } = await import('next/navigation');
|
|
39
|
+
return redirect("/?expired=1");
|
|
40
|
+
}
|
|
41
|
+
async function handleServerError(statusCode) {
|
|
42
|
+
const { redirect } = await import('next/navigation');
|
|
43
|
+
let sourceUrl;
|
|
44
|
+
try {
|
|
45
|
+
const { headers } = await import('next/headers');
|
|
46
|
+
const headersList = await headers();
|
|
47
|
+
const referer = headersList.get("referer");
|
|
48
|
+
const host = headersList.get("host");
|
|
49
|
+
const pathname = headersList.get("x-invoke-path") || headersList.get("x-forwarded-path");
|
|
50
|
+
if (referer) {
|
|
51
|
+
sourceUrl = referer;
|
|
52
|
+
} else if (host && pathname) {
|
|
53
|
+
const protocol = headersList.get("x-forwarded-proto") || "https";
|
|
54
|
+
sourceUrl = `${protocol}://${host}${pathname}`;
|
|
55
|
+
}
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.debug("[portal-components] Could not determine source URL", error);
|
|
58
|
+
}
|
|
59
|
+
const params = new URLSearchParams({ code: String(statusCode) });
|
|
60
|
+
if (sourceUrl) {
|
|
61
|
+
params.set("source", sourceUrl);
|
|
62
|
+
}
|
|
63
|
+
return redirect(`/error?${params.toString()}`);
|
|
64
|
+
}
|
|
65
|
+
var normalizeColor = (color) => {
|
|
66
|
+
if (!color || typeof color !== "string") {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
const trimmed = color.trim();
|
|
70
|
+
if (/^#?[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/.test(trimmed)) {
|
|
71
|
+
return trimmed.startsWith("#") ? trimmed : `#${trimmed}`;
|
|
72
|
+
}
|
|
73
|
+
return trimmed;
|
|
74
|
+
};
|
|
75
|
+
var decodeBranding = ({ brandingData }) => {
|
|
76
|
+
if (!brandingData || typeof brandingData !== "string") {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const decoded = Buffer.from(brandingData, "base64").toString("utf-8");
|
|
81
|
+
const parsed = JSON.parse(decoded);
|
|
82
|
+
const logo = typeof parsed.logo === "string" ? parsed.logo : void 0;
|
|
83
|
+
const titleRaw = typeof parsed.title === "string" ? parsed.title.trim() : "";
|
|
84
|
+
const title = titleRaw ? titleRaw : void 0;
|
|
85
|
+
const customColor1 = normalizeColor(parsed.customColor1);
|
|
86
|
+
const customColor2 = normalizeColor(parsed.customColor2);
|
|
87
|
+
return {
|
|
88
|
+
logo,
|
|
89
|
+
title,
|
|
90
|
+
customColor1,
|
|
91
|
+
customColor2
|
|
92
|
+
};
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.debug("[portal-components] unable to parse branding JSON", error);
|
|
95
|
+
return {};
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var getApiOrigin = () => {
|
|
99
|
+
return (process.env.REPLICATED_APP_ORIGIN || "https://replicated.app").replace(/\/+$/, "");
|
|
100
|
+
};
|
|
101
|
+
var fetchCustomBrandingImpl = async () => {
|
|
102
|
+
const appSlug = process.env.PORTAL_APP_SLUG;
|
|
103
|
+
if (!appSlug) {
|
|
104
|
+
throw new Error("PORTAL_APP_SLUG is not configured");
|
|
105
|
+
}
|
|
106
|
+
const url = `${getApiOrigin()}/v3/custom-branding?app_slug=${encodeURIComponent(
|
|
107
|
+
appSlug
|
|
108
|
+
)}`;
|
|
109
|
+
if (process.env.NODE_ENV !== "production") {
|
|
110
|
+
console.debug(
|
|
111
|
+
"[portal-components] fetching custom branding via %s",
|
|
112
|
+
url
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
const response = await fetch(url, {
|
|
116
|
+
headers: {
|
|
117
|
+
accept: "application/json"
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`Custom branding request failed (${response.status} ${response.statusText})`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
const payload = await response.json();
|
|
126
|
+
const brandingData = payload?.branding_data;
|
|
127
|
+
if (typeof brandingData !== "string") {
|
|
128
|
+
throw new Error("Custom branding response missing branding_data string");
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
brandingData,
|
|
132
|
+
documentation: payload?.documentation ?? null
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
cache(fetchCustomBrandingImpl);
|
|
136
|
+
|
|
137
|
+
// src/utils/session.ts
|
|
138
|
+
async function validateSession(token) {
|
|
139
|
+
if (!token || typeof token !== "string" || !token.trim()) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const endpoint = `${getApiOrigin()}/v3/license`;
|
|
144
|
+
const response = await fetch(endpoint, {
|
|
145
|
+
method: "HEAD",
|
|
146
|
+
// Use HEAD to avoid downloading response body
|
|
147
|
+
headers: {
|
|
148
|
+
authorization: `Bearer ${token}`
|
|
149
|
+
},
|
|
150
|
+
// Short timeout for validation
|
|
151
|
+
signal: AbortSignal.timeout(5e3)
|
|
152
|
+
});
|
|
153
|
+
if (response.status === 401) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
return response.ok;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn("[portal-components] session validation error:", error);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
async function deleteSessionCookie() {
|
|
163
|
+
const { cookies } = await import('next/headers');
|
|
164
|
+
const cookieStore = await cookies();
|
|
165
|
+
cookieStore.delete("portal_session");
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { UnauthorizedError, authenticatedFetch, decodeBranding, deleteSessionCookie, isRedirectError, validateSession };
|
|
169
|
+
//# sourceMappingURL=index.js.map
|
|
170
|
+
//# sourceMappingURL=index.js.map
|