@replicated/portal-components 0.0.19 → 0.0.21
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 +2 -2
- package/components/metadata/registry.md +2 -2
- package/dist/actions/change-team.js +66 -7
- package/dist/actions/change-team.js.map +1 -1
- package/dist/actions/index.d.mts +3 -1
- package/dist/actions/index.d.ts +3 -1
- package/dist/actions/index.js +182 -465
- package/dist/actions/index.js.map +1 -1
- package/dist/actions/install-actions.d.mts +3 -1
- package/dist/actions/install-actions.d.ts +3 -1
- package/dist/actions/install-actions.js +58 -5
- package/dist/actions/install-actions.js.map +1 -1
- package/dist/actions/service-account.d.mts +3 -1
- package/dist/actions/service-account.d.ts +3 -1
- package/dist/actions/service-account.js +58 -5
- package/dist/actions/service-account.js.map +1 -1
- package/dist/actions/support-bundles.d.mts +3 -1
- package/dist/actions/support-bundles.d.ts +3 -1
- package/dist/actions/support-bundles.js +58 -5
- package/dist/actions/support-bundles.js.map +1 -1
- package/dist/actions/team-settings.d.mts +3 -1
- package/dist/actions/team-settings.d.ts +3 -1
- package/dist/actions/team-settings.js +91 -27
- package/dist/actions/team-settings.js.map +1 -1
- package/dist/actions/trial-signup.d.mts +24 -0
- package/dist/actions/trial-signup.d.ts +24 -0
- package/dist/actions/trial-signup.js +482 -0
- package/dist/actions/trial-signup.js.map +1 -0
- package/dist/actions/user-settings.d.mts +3 -1
- package/dist/actions/user-settings.d.ts +3 -1
- package/dist/actions/user-settings.js +58 -5
- package/dist/actions/user-settings.js.map +1 -1
- package/dist/airgap-instances.d.mts +3 -1
- package/dist/airgap-instances.d.ts +3 -1
- package/dist/airgap-instances.js +41 -112
- package/dist/airgap-instances.js.map +1 -1
- package/dist/branding-BsMSywts.d.mts +36 -0
- package/dist/branding-BsMSywts.d.ts +36 -0
- package/dist/error-page.js +10 -2
- package/dist/error-page.js.map +1 -1
- package/dist/error.js +10 -2
- package/dist/error.js.map +1 -1
- package/dist/esm/actions/change-team.js +66 -7
- package/dist/esm/actions/change-team.js.map +1 -1
- package/dist/esm/actions/index.js +181 -462
- package/dist/esm/actions/index.js.map +1 -1
- package/dist/esm/actions/install-actions.js +58 -5
- package/dist/esm/actions/install-actions.js.map +1 -1
- package/dist/esm/actions/service-account.js +58 -5
- package/dist/esm/actions/service-account.js.map +1 -1
- package/dist/esm/actions/support-bundles.js +58 -5
- package/dist/esm/actions/support-bundles.js.map +1 -1
- package/dist/esm/actions/team-settings.js +91 -27
- package/dist/esm/actions/team-settings.js.map +1 -1
- package/dist/esm/actions/trial-signup.js +478 -0
- package/dist/esm/actions/trial-signup.js.map +1 -0
- package/dist/esm/actions/user-settings.js +58 -5
- package/dist/esm/actions/user-settings.js.map +1 -1
- package/dist/esm/airgap-instances.js +40 -112
- package/dist/esm/airgap-instances.js.map +1 -1
- package/dist/esm/error-page.js +10 -2
- package/dist/esm/error-page.js.map +1 -1
- package/dist/esm/error.js +10 -2
- package/dist/esm/error.js.map +1 -1
- package/dist/esm/helm-install-wizard.js +118 -79
- package/dist/esm/helm-install-wizard.js.map +1 -1
- package/dist/esm/index.js +706 -438
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/install-actions.js +40 -5
- package/dist/esm/install-actions.js.map +1 -1
- package/dist/esm/install-card.js +9 -6
- package/dist/esm/install-card.js.map +1 -1
- package/dist/esm/install-targets.js +9 -2
- package/dist/esm/install-targets.js.map +1 -1
- package/dist/esm/instance-card.js +39 -111
- package/dist/esm/instance-card.js.map +1 -1
- package/dist/esm/join-team.js +9 -3
- package/dist/esm/join-team.js.map +1 -1
- package/dist/esm/license-card.js +24 -22
- package/dist/esm/license-card.js.map +1 -1
- package/dist/esm/license-details.js +128 -334
- package/dist/esm/license-details.js.map +1 -1
- package/dist/esm/linux-install-wizard.js +95 -41
- package/dist/esm/linux-install-wizard.js.map +1 -1
- package/dist/esm/login.js +20 -4
- package/dist/esm/login.js.map +1 -1
- package/dist/esm/middleware.js +33 -0
- package/dist/esm/middleware.js.map +1 -0
- package/dist/esm/online-instance-list.js +40 -112
- package/dist/esm/online-instance-list.js.map +1 -1
- package/dist/esm/release-history-panel.js +27 -14
- package/dist/esm/release-history-panel.js.map +1 -1
- package/dist/esm/saml-callback-client.js +82 -0
- package/dist/esm/saml-callback-client.js.map +1 -0
- package/dist/esm/saml-handlers.js +138 -0
- package/dist/esm/saml-handlers.js.map +1 -0
- package/dist/esm/security-card.js +53 -38
- package/dist/esm/security-card.js.map +1 -1
- package/dist/esm/service-accounts-tab.js +800 -0
- package/dist/esm/service-accounts-tab.js.map +1 -0
- package/dist/esm/support-bundle-collection-card.js +48 -24
- package/dist/esm/support-bundle-collection-card.js.map +1 -1
- package/dist/esm/support-bundles-card.js +10 -5
- package/dist/esm/support-bundles-card.js.map +1 -1
- package/dist/esm/support-card.js +37 -5
- package/dist/esm/support-card.js.map +1 -1
- package/dist/esm/team-selection.js +5 -1
- package/dist/esm/team-selection.js.map +1 -1
- package/dist/esm/team-settings-card.js +5 -2
- package/dist/esm/team-settings-card.js.map +1 -1
- package/dist/esm/team-settings.js +7 -2
- package/dist/esm/team-settings.js.map +1 -1
- package/dist/esm/top-nav-user-menu.js +5 -1
- package/dist/esm/top-nav-user-menu.js.map +1 -1
- package/dist/esm/top-nav.js +175 -62
- package/dist/esm/top-nav.js.map +1 -1
- package/dist/esm/trial-signup.js +256 -0
- package/dist/esm/trial-signup.js.map +1 -0
- package/dist/esm/update-layout.js +175 -62
- package/dist/esm/update-layout.js.map +1 -1
- package/dist/esm/updates-card.js +15 -4
- package/dist/esm/updates-card.js.map +1 -1
- package/dist/esm/upload-support-bundle-modal.js +9 -4
- package/dist/esm/upload-support-bundle-modal.js.map +1 -1
- package/dist/esm/user-settings-card.js +5 -2
- package/dist/esm/user-settings-card.js.map +1 -1
- package/dist/esm/user-settings.js +12 -6
- package/dist/esm/user-settings.js.map +1 -1
- package/dist/esm/utils/index.js +204 -13
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/fetch-license-iTyF7_GY.d.mts +81 -0
- package/dist/fetch-license-iTyF7_GY.d.ts +81 -0
- package/dist/helm-install-wizard.d.mts +11 -3
- package/dist/helm-install-wizard.d.ts +11 -3
- package/dist/helm-install-wizard.js +118 -79
- package/dist/helm-install-wizard.js.map +1 -1
- package/dist/{index-BAiVrSSR.d.mts → index-DyzJ0yKD.d.mts} +48 -50
- package/dist/{index-DWt-N5od.d.ts → index-sMbq94M7.d.ts} +48 -50
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +726 -438
- package/dist/index.js.map +1 -1
- package/dist/install-actions.d.mts +4 -2
- package/dist/install-actions.d.ts +4 -2
- package/dist/install-actions.js +40 -5
- package/dist/install-actions.js.map +1 -1
- package/dist/install-card.d.mts +2 -3
- package/dist/install-card.d.ts +2 -3
- package/dist/install-card.js +9 -6
- package/dist/install-card.js.map +1 -1
- package/dist/install-targets.js +9 -2
- package/dist/install-targets.js.map +1 -1
- package/dist/instance-card.d.mts +3 -1
- package/dist/instance-card.d.ts +3 -1
- package/dist/instance-card.js +40 -111
- package/dist/instance-card.js.map +1 -1
- package/dist/join-team.js +9 -3
- package/dist/join-team.js.map +1 -1
- package/dist/license-card.d.mts +2 -3
- package/dist/license-card.d.ts +2 -3
- package/dist/license-card.js +24 -22
- package/dist/license-card.js.map +1 -1
- package/dist/license-details.js +128 -334
- package/dist/license-details.js.map +1 -1
- package/dist/linux-install-wizard.d.mts +9 -3
- package/dist/linux-install-wizard.d.ts +9 -3
- package/dist/linux-install-wizard.js +95 -41
- package/dist/linux-install-wizard.js.map +1 -1
- package/dist/login.d.mts +4 -0
- package/dist/login.d.ts +4 -0
- package/dist/login.js +20 -4
- package/dist/login.js.map +1 -1
- package/dist/middleware.d.mts +13 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.js +35 -0
- package/dist/middleware.js.map +1 -0
- package/dist/online-instance-list.d.mts +3 -1
- package/dist/online-instance-list.d.ts +3 -1
- package/dist/online-instance-list.js +41 -112
- package/dist/online-instance-list.js.map +1 -1
- package/dist/pending-installations.d.mts +3 -1
- package/dist/pending-installations.d.ts +3 -1
- package/dist/release-history-panel.js +27 -14
- package/dist/release-history-panel.js.map +1 -1
- package/dist/saml-callback-client.d.mts +36 -0
- package/dist/saml-callback-client.d.ts +36 -0
- package/dist/saml-callback-client.js +88 -0
- package/dist/saml-callback-client.js.map +1 -0
- package/dist/saml-handlers.d.mts +50 -0
- package/dist/saml-handlers.d.ts +50 -0
- package/dist/saml-handlers.js +141 -0
- package/dist/saml-handlers.js.map +1 -0
- package/dist/security-card.d.mts +3 -1
- package/dist/security-card.d.ts +3 -1
- package/dist/security-card.js +53 -38
- package/dist/security-card.js.map +1 -1
- package/dist/service-accounts-tab.d.mts +51 -0
- package/dist/service-accounts-tab.d.ts +51 -0
- package/dist/service-accounts-tab.js +802 -0
- package/dist/service-accounts-tab.js.map +1 -0
- package/dist/styles.css +375 -127
- package/dist/support-bundle-collection-card.d.mts +1 -1
- package/dist/support-bundle-collection-card.d.ts +1 -1
- package/dist/support-bundle-collection-card.js +47 -23
- package/dist/support-bundle-collection-card.js.map +1 -1
- package/dist/support-bundles-card.d.mts +4 -2
- package/dist/support-bundles-card.d.ts +4 -2
- package/dist/support-bundles-card.js +10 -5
- package/dist/support-bundles-card.js.map +1 -1
- package/dist/support-card.js +37 -5
- package/dist/support-card.js.map +1 -1
- package/dist/team-selection.js +5 -1
- package/dist/team-selection.js.map +1 -1
- package/dist/team-settings-card.js +5 -2
- package/dist/team-settings-card.js.map +1 -1
- package/dist/team-settings.js +7 -2
- package/dist/team-settings.js.map +1 -1
- package/dist/{top-nav-IRIn66wS.d.ts → top-nav-BUQAGoG1.d.mts} +14 -2
- package/dist/{top-nav-IRIn66wS.d.mts → top-nav-CEqw0KpO.d.ts} +14 -2
- package/dist/top-nav-user-menu.js +5 -1
- package/dist/top-nav-user-menu.js.map +1 -1
- package/dist/top-nav.d.mts +2 -1
- package/dist/top-nav.d.ts +2 -1
- package/dist/top-nav.js +175 -62
- package/dist/top-nav.js.map +1 -1
- package/dist/trial-signup.d.mts +31 -0
- package/dist/trial-signup.d.ts +31 -0
- package/dist/trial-signup.js +258 -0
- package/dist/trial-signup.js.map +1 -0
- package/dist/update-layout.js +175 -62
- package/dist/update-layout.js.map +1 -1
- package/dist/updates-card.js +15 -4
- package/dist/updates-card.js.map +1 -1
- package/dist/upload-support-bundle-modal.js +9 -4
- package/dist/upload-support-bundle-modal.js.map +1 -1
- package/dist/user-settings-card.js +5 -2
- package/dist/user-settings-card.js.map +1 -1
- package/dist/user-settings.js +12 -6
- package/dist/user-settings.js.map +1 -1
- package/dist/utils/index.d.mts +74 -16
- package/dist/utils/index.d.ts +74 -16
- package/dist/utils/index.js +215 -12
- package/dist/utils/index.js.map +1 -1
- package/package.json +37 -2
package/dist/esm/join-team.js
CHANGED
|
@@ -61,6 +61,9 @@ var Button = forwardRef(
|
|
|
61
61
|
}
|
|
62
62
|
);
|
|
63
63
|
Button.displayName = "Button";
|
|
64
|
+
|
|
65
|
+
// src/utils/constants.ts
|
|
66
|
+
var DEFAULT_SECONDARY_COLOR = "#6366f1";
|
|
64
67
|
var JoinTeam = forwardRef(
|
|
65
68
|
({
|
|
66
69
|
logo,
|
|
@@ -182,7 +185,8 @@ var JoinTeam = forwardRef(
|
|
|
182
185
|
{
|
|
183
186
|
type: "submit",
|
|
184
187
|
size: "lg",
|
|
185
|
-
className: "w-full justify-center rounded-xl
|
|
188
|
+
className: "w-full justify-center rounded-xl text-white hover:opacity-90",
|
|
189
|
+
style: { backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` },
|
|
186
190
|
disabled: !effectiveCode || isSubmitting,
|
|
187
191
|
isLoading: isSubmitting,
|
|
188
192
|
children: "Accept invite"
|
|
@@ -194,7 +198,8 @@ var JoinTeam = forwardRef(
|
|
|
194
198
|
type: "button",
|
|
195
199
|
onClick: handleRefresh,
|
|
196
200
|
disabled: isRefreshing,
|
|
197
|
-
className: "text-sm font-medium
|
|
201
|
+
className: "text-sm font-medium hover:underline disabled:opacity-50",
|
|
202
|
+
style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` },
|
|
198
203
|
children: isRefreshing ? "Sending..." : "Refresh invite"
|
|
199
204
|
}
|
|
200
205
|
) }),
|
|
@@ -202,7 +207,8 @@ var JoinTeam = forwardRef(
|
|
|
202
207
|
Link,
|
|
203
208
|
{
|
|
204
209
|
href: loginUrl,
|
|
205
|
-
className: "text-sm font-medium
|
|
210
|
+
className: "text-sm font-medium hover:underline",
|
|
211
|
+
style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` },
|
|
206
212
|
children: "or login"
|
|
207
213
|
}
|
|
208
214
|
) })
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/button.tsx","../../src/components/join-team.tsx"],"names":["forwardRef","jsxs","jsx"],"mappings":"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","file":"join-team.js","sourcesContent":["import {\n forwardRef,\n type ComponentPropsWithoutRef,\n type ReactNode\n} from \"react\";\n\nconst buttonVariants = [\"primary\", \"secondary\", \"ghost\", \"destructive\"] as const;\nconst buttonSizes = [\"sm\", \"md\", \"lg\"] as const;\n\nconst variantStyles: Record<ButtonVariant, string> = {\n primary:\n \"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary\",\n secondary:\n \"bg-secondary/20 text-secondary-foreground hover:bg-secondary/30 focus-visible:ring-secondary\",\n ghost:\n \"bg-transparent text-primary hover:bg-primary/10 focus-visible:ring-primary/60\",\n destructive:\n \"bg-danger text-white hover:bg-danger/90 focus-visible:ring-danger\"\n};\n\nconst sizeStyles: Record<ButtonSize, string> = {\n sm: \"h-8 px-3 text-sm\",\n md: \"h-10 px-4 text-sm\",\n lg: \"h-12 px-6 text-base\"\n};\n\nconst inlineFlexBase =\n \"inline-flex items-center justify-center gap-2 rounded-md font-medium tracking-tight transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-60\";\n\nconst Spinner = () => (\n <span className=\"inline-flex h-3.5 w-3.5 animate-spin items-center justify-center\">\n <span className=\"h-3 w-3 rounded-full border-2 border-transparent border-t-current\" />\n </span>\n);\n\nexport type ButtonVariant = (typeof buttonVariants)[number];\nexport type ButtonSize = (typeof buttonSizes)[number];\n\nexport interface ButtonProps extends ComponentPropsWithoutRef<\"button\"> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n isLoading?: boolean;\n leadingIcon?: ReactNode;\n trailingIcon?: ReactNode;\n}\n\nconst composeClassName = (\n ...values: Array<string | undefined | false>\n): string => values.filter(Boolean).join(\" \");\n\n/**\n * Button is the primary interactive primitive for triggering portal actions.\n * It is theme aware via CSS variables generated from portal tokens.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"md\",\n type = \"button\",\n isLoading = false,\n leadingIcon,\n trailingIcon,\n disabled,\n className,\n children,\n ...props\n },\n ref\n ) => {\n const computedLeading = isLoading ? <Spinner /> : leadingIcon;\n const computedDisabled = disabled ?? isLoading;\n\n return (\n <button\n ref={ref}\n type={type}\n className={composeClassName(\n inlineFlexBase,\n variantStyles[variant],\n sizeStyles[size],\n className\n )}\n aria-busy={isLoading || undefined}\n disabled={computedDisabled}\n {...props}\n >\n {computedLeading ? (\n <span aria-hidden=\"true\" className=\"inline-flex\">\n {computedLeading}\n </span>\n ) : null}\n <span className=\"flex-1 whitespace-nowrap\">{children}</span>\n {trailingIcon ? (\n <span aria-hidden=\"true\" className=\"inline-flex\">\n {trailingIcon}\n </span>\n ) : null}\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n","'use client';\n\nimport {\n forwardRef,\n useEffect,\n useState,\n type ComponentPropsWithoutRef,\n type FormEvent,\n type ReactNode\n} from \"react\";\nimport Link from \"next/link\";\n\nimport { Button } from \"./button\";\n\nexport interface JoinTeamProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /** Logo element to display at the top */\n logo?: ReactNode;\n /** Team/product name to display in the heading */\n teamName?: string;\n /** Invite code from URL fragment (if available) */\n initialCode?: string;\n /** Whether an accept operation is in progress */\n isSubmitting?: boolean;\n /** Error message from a previous accept attempt */\n error?: string | null;\n /** Whether the invite has expired */\n isExpired?: boolean;\n /** Called when user clicks \"Accept invite\" */\n onAcceptInvite?: (code: string) => Promise<void> | void;\n /** Called when user clicks \"Refresh invite\" for expired invites */\n onRefreshInvite?: (code: string) => Promise<void> | void;\n /** URL to navigate to for login (when no code in URL) */\n loginUrl?: string;\n}\n\n/**\n * JoinTeam renders a form for accepting team invitations.\n * Supports both URL-based codes (from email links) and manual code entry.\n */\nexport const JoinTeam = forwardRef<HTMLDivElement, JoinTeamProps>(\n (\n {\n logo,\n teamName = \"Enterprise Portal\",\n initialCode = \"\",\n isSubmitting = false,\n error = null,\n isExpired = false,\n onAcceptInvite,\n onRefreshInvite,\n loginUrl = \"/login\",\n className,\n ...props\n },\n ref\n ) => {\n const [manualCode, setManualCode] = useState(\"\");\n const [localError, setLocalError] = useState<string | null>(null);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [refreshSuccess, setRefreshSuccess] = useState(false);\n // Track whether to use URL code - can be cleared after refresh\n const [useUrlCode, setUseUrlCode] = useState(true);\n\n // Use code from URL if available and not cleared, otherwise use manual input\n const hasUrlCode = Boolean(initialCode) && useUrlCode;\n const effectiveCode = hasUrlCode ? initialCode : manualCode.trim();\n\n // Clear local error when code changes\n useEffect(() => {\n if (manualCode) {\n setLocalError(null);\n }\n }, [manualCode]);\n\n const handleSubmit = async (event: FormEvent) => {\n event.preventDefault();\n\n if (!effectiveCode) {\n setLocalError(\"Please enter your invite code\");\n return;\n }\n\n if (!onAcceptInvite) {\n return;\n }\n\n try {\n setLocalError(null);\n await onAcceptInvite(effectiveCode);\n } catch (err) {\n if (!hasUrlCode) {\n // Only show local error for manual entry\n const message =\n err instanceof Error\n ? err.message\n : \"Invalid or expired code. Please check the code and try again.\";\n setLocalError(message);\n }\n }\n };\n\n const handleRefresh = async () => {\n if (!onRefreshInvite || !effectiveCode) {\n return;\n }\n\n try {\n setIsRefreshing(true);\n setRefreshSuccess(false);\n await onRefreshInvite(effectiveCode);\n setRefreshSuccess(true);\n \n // After refresh, clear URL hash and switch to manual entry mode\n // This matches Vandoor's behavior where href=\"#\" clears the hash\n if (typeof window !== \"undefined\") {\n window.history.replaceState(null, \"\", window.location.pathname + window.location.search);\n }\n setUseUrlCode(false);\n } catch {\n // Refresh errors are typically silent per the API design\n } finally {\n setIsRefreshing(false);\n }\n };\n\n const displayError = error || localError;\n const showRefreshLink = hasUrlCode && (isExpired || displayError);\n\n return (\n <div\n ref={ref}\n className={[\n \"w-full max-w-xl rounded-3xl border-2 border-gray-900 bg-white p-12 shadow-xl\",\n \"text-gray-900 transition-shadow\",\n className\n ]\n .filter(Boolean)\n .join(\" \")}\n {...props}\n >\n <div className=\"flex flex-col items-center gap-6 text-center\">\n {logo ?? (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-blue-500 to-violet-500 text-lg font-semibold leading-tight text-white\">\n EP\n </div>\n )}\n <div>\n <h1 className=\"text-3xl font-bold tracking-tight text-gray-900\">\n Join the {teamName} team\n </h1>\n <p className=\"mt-3 text-base text-gray-600\">\n Accept your invitation to get started\n </p>\n </div>\n </div>\n\n <form onSubmit={handleSubmit} className=\"mt-10 space-y-4\">\n {/* Show input field only when no code in URL */}\n {!hasUrlCode && (\n <>\n <label\n htmlFor=\"invite-code\"\n className=\"block text-sm font-medium text-gray-700\"\n >\n Paste your invite code\n </label>\n <input\n id=\"invite-code\"\n type=\"text\"\n placeholder=\"Paste code from your email\"\n value={manualCode}\n onChange={(e) => setManualCode(e.target.value)}\n className={[\n \"portal-input w-full px-5 py-4 text-base font-mono\",\n displayError ? \"border-red-500\" : \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n autoFocus\n disabled={isSubmitting}\n />\n </>\n )}\n\n {/* Error message */}\n {displayError && (\n <p className=\"text-sm font-medium text-red-600\">{displayError}</p>\n )}\n\n {/* Refresh success message */}\n {refreshSuccess && (\n <p className=\"text-sm font-medium text-green-600\">\n A new invitation has been sent to your email.\n </p>\n )}\n\n {/* Accept button */}\n <Button\n type=\"submit\"\n size=\"lg\"\n className=\"w-full justify-center rounded-xl bg-indigo-600 text-white hover:bg-indigo-700\"\n disabled={!effectiveCode || isSubmitting}\n isLoading={isSubmitting}\n >\n Accept invite\n </Button>\n\n {/* Refresh invite link (shown on error for URL-based codes) */}\n {showRefreshLink && onRefreshInvite && (\n <div className=\"text-center\">\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"text-sm font-medium text-indigo-600 hover:text-indigo-700 hover:underline disabled:opacity-50\"\n >\n {isRefreshing ? \"Sending...\" : \"Refresh invite\"}\n </button>\n </div>\n )}\n\n {/* Login link (shown when no URL code) */}\n {!hasUrlCode && (\n <div className=\"text-center\">\n <Link\n href={loginUrl}\n className=\"text-sm font-medium text-indigo-600 hover:text-indigo-700 hover:underline\"\n >\n or login\n </Link>\n </div>\n )}\n </form>\n </div>\n );\n }\n);\n\nJoinTeam.displayName = \"JoinTeam\";\n\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/components/button.tsx","../../src/utils/constants.ts","../../src/components/join-team.tsx"],"names":["forwardRef","jsxs","jsx"],"mappings":"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","file":"join-team.js","sourcesContent":["import {\n forwardRef,\n type ComponentPropsWithoutRef,\n type ReactNode\n} from \"react\";\n\nconst buttonVariants = [\"primary\", \"secondary\", \"ghost\", \"destructive\"] as const;\nconst buttonSizes = [\"sm\", \"md\", \"lg\"] as const;\n\nconst variantStyles: Record<ButtonVariant, string> = {\n primary:\n \"bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary\",\n secondary:\n \"bg-secondary/20 text-secondary-foreground hover:bg-secondary/30 focus-visible:ring-secondary\",\n ghost:\n \"bg-transparent text-primary hover:bg-primary/10 focus-visible:ring-primary/60\",\n destructive:\n \"bg-danger text-white hover:bg-danger/90 focus-visible:ring-danger\"\n};\n\nconst sizeStyles: Record<ButtonSize, string> = {\n sm: \"h-8 px-3 text-sm\",\n md: \"h-10 px-4 text-sm\",\n lg: \"h-12 px-6 text-base\"\n};\n\nconst inlineFlexBase =\n \"inline-flex items-center justify-center gap-2 rounded-md font-medium tracking-tight transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-60\";\n\nconst Spinner = () => (\n <span className=\"inline-flex h-3.5 w-3.5 animate-spin items-center justify-center\">\n <span className=\"h-3 w-3 rounded-full border-2 border-transparent border-t-current\" />\n </span>\n);\n\nexport type ButtonVariant = (typeof buttonVariants)[number];\nexport type ButtonSize = (typeof buttonSizes)[number];\n\nexport interface ButtonProps extends ComponentPropsWithoutRef<\"button\"> {\n variant?: ButtonVariant;\n size?: ButtonSize;\n isLoading?: boolean;\n leadingIcon?: ReactNode;\n trailingIcon?: ReactNode;\n}\n\nconst composeClassName = (\n ...values: Array<string | undefined | false>\n): string => values.filter(Boolean).join(\" \");\n\n/**\n * Button is the primary interactive primitive for triggering portal actions.\n * It is theme aware via CSS variables generated from portal tokens.\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n variant = \"primary\",\n size = \"md\",\n type = \"button\",\n isLoading = false,\n leadingIcon,\n trailingIcon,\n disabled,\n className,\n children,\n ...props\n },\n ref\n ) => {\n const computedLeading = isLoading ? <Spinner /> : leadingIcon;\n const computedDisabled = disabled ?? isLoading;\n\n return (\n <button\n ref={ref}\n type={type}\n className={composeClassName(\n inlineFlexBase,\n variantStyles[variant],\n sizeStyles[size],\n className\n )}\n aria-busy={isLoading || undefined}\n disabled={computedDisabled}\n {...props}\n >\n {computedLeading ? (\n <span aria-hidden=\"true\" className=\"inline-flex\">\n {computedLeading}\n </span>\n ) : null}\n <span className=\"flex-1 whitespace-nowrap\">{children}</span>\n {trailingIcon ? (\n <span aria-hidden=\"true\" className=\"inline-flex\">\n {trailingIcon}\n </span>\n ) : null}\n </button>\n );\n }\n);\n\nButton.displayName = \"Button\";\n","/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","'use client';\n\nimport {\n forwardRef,\n useEffect,\n useState,\n type ComponentPropsWithoutRef,\n type FormEvent,\n type ReactNode\n} from \"react\";\nimport Link from \"next/link\";\n\nimport { Button } from \"./button\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\n\nexport interface JoinTeamProps\n extends Omit<ComponentPropsWithoutRef<\"div\">, \"children\"> {\n /** Logo element to display at the top */\n logo?: ReactNode;\n /** Team/product name to display in the heading */\n teamName?: string;\n /** Invite code from URL fragment (if available) */\n initialCode?: string;\n /** Whether an accept operation is in progress */\n isSubmitting?: boolean;\n /** Error message from a previous accept attempt */\n error?: string | null;\n /** Whether the invite has expired */\n isExpired?: boolean;\n /** Called when user clicks \"Accept invite\" */\n onAcceptInvite?: (code: string) => Promise<void> | void;\n /** Called when user clicks \"Refresh invite\" for expired invites */\n onRefreshInvite?: (code: string) => Promise<void> | void;\n /** URL to navigate to for login (when no code in URL) */\n loginUrl?: string;\n}\n\n/**\n * JoinTeam renders a form for accepting team invitations.\n * Supports both URL-based codes (from email links) and manual code entry.\n */\nexport const JoinTeam = forwardRef<HTMLDivElement, JoinTeamProps>(\n (\n {\n logo,\n teamName = \"Enterprise Portal\",\n initialCode = \"\",\n isSubmitting = false,\n error = null,\n isExpired = false,\n onAcceptInvite,\n onRefreshInvite,\n loginUrl = \"/login\",\n className,\n ...props\n },\n ref\n ) => {\n const [manualCode, setManualCode] = useState(\"\");\n const [localError, setLocalError] = useState<string | null>(null);\n const [isRefreshing, setIsRefreshing] = useState(false);\n const [refreshSuccess, setRefreshSuccess] = useState(false);\n // Track whether to use URL code - can be cleared after refresh\n const [useUrlCode, setUseUrlCode] = useState(true);\n\n // Use code from URL if available and not cleared, otherwise use manual input\n const hasUrlCode = Boolean(initialCode) && useUrlCode;\n const effectiveCode = hasUrlCode ? initialCode : manualCode.trim();\n\n // Clear local error when code changes\n useEffect(() => {\n if (manualCode) {\n setLocalError(null);\n }\n }, [manualCode]);\n\n const handleSubmit = async (event: FormEvent) => {\n event.preventDefault();\n\n if (!effectiveCode) {\n setLocalError(\"Please enter your invite code\");\n return;\n }\n\n if (!onAcceptInvite) {\n return;\n }\n\n try {\n setLocalError(null);\n await onAcceptInvite(effectiveCode);\n } catch (err) {\n if (!hasUrlCode) {\n // Only show local error for manual entry\n const message =\n err instanceof Error\n ? err.message\n : \"Invalid or expired code. Please check the code and try again.\";\n setLocalError(message);\n }\n }\n };\n\n const handleRefresh = async () => {\n if (!onRefreshInvite || !effectiveCode) {\n return;\n }\n\n try {\n setIsRefreshing(true);\n setRefreshSuccess(false);\n await onRefreshInvite(effectiveCode);\n setRefreshSuccess(true);\n \n // After refresh, clear URL hash and switch to manual entry mode\n // This matches Vandoor's behavior where href=\"#\" clears the hash\n if (typeof window !== \"undefined\") {\n window.history.replaceState(null, \"\", window.location.pathname + window.location.search);\n }\n setUseUrlCode(false);\n } catch {\n // Refresh errors are typically silent per the API design\n } finally {\n setIsRefreshing(false);\n }\n };\n\n const displayError = error || localError;\n const showRefreshLink = hasUrlCode && (isExpired || displayError);\n\n return (\n <div\n ref={ref}\n className={[\n \"w-full max-w-xl rounded-3xl border-2 border-gray-900 bg-white p-12 shadow-xl\",\n \"text-gray-900 transition-shadow\",\n className\n ]\n .filter(Boolean)\n .join(\" \")}\n {...props}\n >\n <div className=\"flex flex-col items-center gap-6 text-center\">\n {logo ?? (\n <div className=\"flex h-16 w-16 items-center justify-center rounded-2xl bg-gradient-to-br from-blue-500 to-violet-500 text-lg font-semibold leading-tight text-white\">\n EP\n </div>\n )}\n <div>\n <h1 className=\"text-3xl font-bold tracking-tight text-gray-900\">\n Join the {teamName} team\n </h1>\n <p className=\"mt-3 text-base text-gray-600\">\n Accept your invitation to get started\n </p>\n </div>\n </div>\n\n <form onSubmit={handleSubmit} className=\"mt-10 space-y-4\">\n {/* Show input field only when no code in URL */}\n {!hasUrlCode && (\n <>\n <label\n htmlFor=\"invite-code\"\n className=\"block text-sm font-medium text-gray-700\"\n >\n Paste your invite code\n </label>\n <input\n id=\"invite-code\"\n type=\"text\"\n placeholder=\"Paste code from your email\"\n value={manualCode}\n onChange={(e) => setManualCode(e.target.value)}\n className={[\n \"portal-input w-full px-5 py-4 text-base font-mono\",\n displayError ? \"border-red-500\" : \"\"\n ]\n .filter(Boolean)\n .join(\" \")}\n autoFocus\n disabled={isSubmitting}\n />\n </>\n )}\n\n {/* Error message */}\n {displayError && (\n <p className=\"text-sm font-medium text-red-600\">{displayError}</p>\n )}\n\n {/* Refresh success message */}\n {refreshSuccess && (\n <p className=\"text-sm font-medium text-green-600\">\n A new invitation has been sent to your email.\n </p>\n )}\n\n {/* Accept button */}\n <Button\n type=\"submit\"\n size=\"lg\"\n className=\"w-full justify-center rounded-xl text-white hover:opacity-90\"\n style={{ backgroundColor: `var(--portal-branding-primary, ${DEFAULT_SECONDARY_COLOR})` }}\n disabled={!effectiveCode || isSubmitting}\n isLoading={isSubmitting}\n >\n Accept invite\n </Button>\n\n {/* Refresh invite link (shown on error for URL-based codes) */}\n {showRefreshLink && onRefreshInvite && (\n <div className=\"text-center\">\n <button\n type=\"button\"\n onClick={handleRefresh}\n disabled={isRefreshing}\n className=\"text-sm font-medium hover:underline disabled:opacity-50\"\n style={{ color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }}\n >\n {isRefreshing ? \"Sending...\" : \"Refresh invite\"}\n </button>\n </div>\n )}\n\n {/* Login link (shown when no URL code) */}\n {!hasUrlCode && (\n <div className=\"text-center\">\n <Link\n href={loginUrl}\n className=\"text-sm font-medium hover:underline\"\n style={{ color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }}\n >\n or login\n </Link>\n </div>\n )}\n </form>\n </div>\n );\n }\n);\n\nJoinTeam.displayName = \"JoinTeam\";\n\n"]}
|
package/dist/esm/license-card.js
CHANGED
|
@@ -8,6 +8,23 @@ import { jsxs, jsx } from 'react/jsx-runtime';
|
|
|
8
8
|
* This file is generated by tsup. Do not edit manually.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
|
|
12
|
+
// src/utils/constants.ts
|
|
13
|
+
var DEFAULT_SECONDARY_COLOR = "#6366f1";
|
|
14
|
+
|
|
15
|
+
// src/utils/format.ts
|
|
16
|
+
function formatDateShort(dateString) {
|
|
17
|
+
if (!dateString) return "Never";
|
|
18
|
+
const date = new Date(dateString);
|
|
19
|
+
if (isNaN(date.getTime())) {
|
|
20
|
+
return dateString;
|
|
21
|
+
}
|
|
22
|
+
return date.toLocaleDateString("en-US", {
|
|
23
|
+
year: "numeric",
|
|
24
|
+
month: "2-digit",
|
|
25
|
+
day: "2-digit"
|
|
26
|
+
});
|
|
27
|
+
}
|
|
11
28
|
var FileIcon = (props) => /* @__PURE__ */ jsx(
|
|
12
29
|
"svg",
|
|
13
30
|
{
|
|
@@ -43,22 +60,7 @@ var headingClass = "text-lg font-semibold text-gray-900";
|
|
|
43
60
|
var contentClass = "mt-4 flex-1 space-y-3";
|
|
44
61
|
var itemClass = "flex items-center gap-3 text-sm text-gray-600";
|
|
45
62
|
var iconClass = "h-5 w-5 text-gray-500";
|
|
46
|
-
var footerClass = "mt-6 flex justify-end text-sm font-semibold
|
|
47
|
-
var formatDate = (dateString) => {
|
|
48
|
-
if (!dateString) {
|
|
49
|
-
return "Never";
|
|
50
|
-
}
|
|
51
|
-
try {
|
|
52
|
-
const date = new Date(dateString);
|
|
53
|
-
return date.toLocaleDateString("en-US", {
|
|
54
|
-
year: "numeric",
|
|
55
|
-
month: "2-digit",
|
|
56
|
-
day: "2-digit"
|
|
57
|
-
});
|
|
58
|
-
} catch {
|
|
59
|
-
return dateString;
|
|
60
|
-
}
|
|
61
|
-
};
|
|
63
|
+
var footerClass = "mt-6 flex justify-end text-sm font-semibold hover:opacity-80";
|
|
62
64
|
var formatLicenseType = (type) => {
|
|
63
65
|
if (!type || type === "Unknown") {
|
|
64
66
|
return "Unknown";
|
|
@@ -80,11 +82,11 @@ var LicenseCard = ({
|
|
|
80
82
|
return;
|
|
81
83
|
}
|
|
82
84
|
try {
|
|
83
|
-
const
|
|
84
|
-
setType(
|
|
85
|
-
setExpiresAt(
|
|
85
|
+
const license = await fetchLicenseSummaryAction();
|
|
86
|
+
setType(license.licenseType);
|
|
87
|
+
setExpiresAt(license.expireAt || null);
|
|
86
88
|
} catch (error) {
|
|
87
|
-
console.error("[license-card] Failed to fetch license
|
|
89
|
+
console.error("[license-card] Failed to fetch license", error);
|
|
88
90
|
}
|
|
89
91
|
};
|
|
90
92
|
fetchSummary();
|
|
@@ -114,11 +116,11 @@ var LicenseCard = ({
|
|
|
114
116
|
/* @__PURE__ */ jsx(CalendarIcon, { className: iconClass }),
|
|
115
117
|
/* @__PURE__ */ jsxs("span", { children: [
|
|
116
118
|
"Expiration: ",
|
|
117
|
-
|
|
119
|
+
formatDateShort(expiresAt)
|
|
118
120
|
] })
|
|
119
121
|
] })
|
|
120
122
|
] }),
|
|
121
|
-
/* @__PURE__ */ jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsx(Link, { href: "/license", children: "View license \u2192" }) })
|
|
123
|
+
/* @__PURE__ */ jsx("footer", { className: footerClass, children: /* @__PURE__ */ jsx(Link, { href: "/license", style: { color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }, children: "View license \u2192" }) })
|
|
122
124
|
] });
|
|
123
125
|
};
|
|
124
126
|
LicenseCard.displayName = "LicenseCard";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/license-card.tsx"],"names":[],"mappings":"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","file":"license-card.js","sourcesContent":["\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { SVGProps } from \"react\";\nimport Link from \"next/link\";\nimport type { FetchLicenseSummaryResult } from \"../actions\";\n\nconst FileIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\" />\n </svg>\n);\n\nconst CalendarIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5\" />\n </svg>\n);\n\nconst baseCardClass =\n \"flex h-full flex-col rounded-xl border border-gray-200 bg-white p-6 shadow-[0_16px_32px_rgba(15,23,42,0.05)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst contentClass = \"mt-4 flex-1 space-y-3\";\nconst itemClass = \"flex items-center gap-3 text-sm text-gray-600\";\nconst iconClass = \"h-5 w-5 text-gray-500\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold text-primary hover:text-primary/80\";\n\nexport interface LicenseCardProps {\n fetchLicenseSummaryAction: () => Promise<FetchLicenseSummaryResult>;\n initialType?: string;\n initialExpiresAt?: string | null;\n pollIntervalMs?: number;\n}\n\nconst formatDate = (dateString: string | null): string => {\n if (!dateString) {\n return \"Never\";\n }\n \n try {\n const date = new Date(dateString);\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n } catch {\n return dateString;\n }\n};\n\nconst formatLicenseType = (type: string): string => {\n if (!type || type === \"Unknown\") {\n return \"Unknown\";\n }\n \n // Capitalize first letter and add \" license\"\n const capitalized = type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();\n return `${capitalized} license`;\n};\n\nexport const LicenseCard = ({\n fetchLicenseSummaryAction,\n initialType = \"Unknown\",\n initialExpiresAt = null,\n pollIntervalMs = 2000\n}: LicenseCardProps) => {\n const [type, setType] = useState(initialType);\n const [expiresAt, setExpiresAt] = useState(initialExpiresAt);\n\n useEffect(() => {\n const fetchSummary = async () => {\n // Skip if tab is not visible\n if (document.hidden) {\n return;\n }\n \n try {\n const result = await fetchLicenseSummaryAction();\n setType(result.type);\n setExpiresAt(result.expiresAt);\n } catch (error) {\n console.error(\"[license-card] Failed to fetch license summary\", error);\n }\n };\n\n // Fetch immediately\n fetchSummary();\n\n // Set up polling interval\n const intervalId = setInterval(fetchSummary, pollIntervalMs);\n\n // Also fetch when tab becomes visible again\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n fetchSummary();\n }\n };\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n // Cleanup on unmount\n return () => {\n clearInterval(intervalId);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [fetchLicenseSummaryAction, pollIntervalMs]);\n\n return (\n <section className={baseCardClass} aria-labelledby=\"license-card-heading\">\n <header>\n <h2 id=\"license-card-heading\" className={headingClass}>\n License\n </h2>\n </header>\n <div className={contentClass}>\n <div className={itemClass}>\n <FileIcon className={iconClass} />\n <span>Type: {formatLicenseType(type)}</span>\n </div>\n <div className={itemClass}>\n <CalendarIcon className={iconClass} />\n <span>Expiration: {formatDate(expiresAt)}</span>\n </div>\n </div>\n <footer className={footerClass}>\n <Link href=\"/license\">View license →</Link>\n </footer>\n </section>\n );\n};\n\nLicenseCard.displayName = \"LicenseCard\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/constants.ts","../../src/utils/format.ts","../../src/components/license-card.tsx"],"names":[],"mappings":"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","file":"license-card.js","sourcesContent":["/**\n * Default globe favicon matching the CiGlobe icon (Circum Icons)\n * Used as fallback when custom branding doesn't provide a favicon\n */\nexport const DEFAULT_FAVICON = \"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%239ca3af' stroke-width='1.5'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cellipse cx='12' cy='12' rx='4' ry='10'/%3E%3Cpath d='M2 12h20'/%3E%3C/svg%3E\";\n\n/**\n * Default primary brand color\n */\nexport const DEFAULT_PRIMARY_COLOR = \"#4f46e5\";\n\n/**\n * Default secondary brand color\n */\nexport const DEFAULT_SECONDARY_COLOR = \"#6366f1\";\n\n/**\n * Check if the API origin is HTTP (used for repldev environments)\n * This determines cookie security settings for cross-origin iframes\n * @returns true if the API origin starts with http:// (not https://)\n */\nexport const isHttpApiOrigin = (): boolean => {\n return process.env.REPLICATED_APP_ORIGIN?.startsWith('http://') || false;\n};\n","/**\n * Format bytes to human-readable string\n * @param bytes - Number of bytes\n * @param decimals - Number of decimal places (default: 1)\n * @returns Formatted string (e.g., \"1.5 MB\")\n */\nexport function formatBytes(bytes: number, decimals = 1): string {\n if (bytes === 0) return \"0 Bytes\";\n\n const k = 1024;\n const dm = decimals < 0 ? 0 : decimals;\n const sizes = [\"Bytes\", \"KB\", \"MB\", \"GB\", \"TB\"];\n\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n\n return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;\n}\n\n/**\n * Format date string to short numeric date (no time)\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"01/27/2026\") or \"Never\" if no date\n */\nexport function formatDateShort(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\"\n });\n}\n\n/**\n * Format date string to human-readable local timestamp\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"Jan 27, 2026, 10:32 PM\") or \"Never\" if no date\n */\nexport function formatDate(dateString?: string | null): string {\n if (!dateString) return \"Never\";\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n}\n\n/**\n * Format date string to human-readable UTC timestamp\n * @param dateString - ISO date string\n * @returns Formatted string (e.g., \"01/27/2026, 22:32:39 UTC\")\n */\nexport function formatDateTime(dateString: string): string {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleString(\"en-US\", {\n timeZone: \"UTC\",\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false\n }) + \" UTC\";\n}\n\n/**\n * Format date string to user-friendly local timestamp with 12-hour time\n * @param dateString - ISO date string or null/undefined\n * @returns Formatted string (e.g., \"1/27/2026, 10:32 PM\") or \"N/A\" if not provided\n */\nexport function formatDateTimeLocal(dateString?: string | null): string {\n if (!dateString) return \"N/A\";\n\n try {\n const date = new Date(dateString);\n if (isNaN(date.getTime())) {\n return dateString;\n }\n return date.toLocaleDateString(\"en-US\", {\n month: \"numeric\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n hour12: true\n });\n } catch {\n return \"N/A\";\n }\n}\n","\"use client\";\n\nimport { useEffect, useState } from \"react\";\nimport type { SVGProps } from \"react\";\nimport Link from \"next/link\";\nimport type { License } from \"../actions\";\nimport { DEFAULT_SECONDARY_COLOR } from \"../utils/constants\";\nimport { formatDateShort } from \"../utils/format\";\n\nconst FileIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z\" />\n </svg>\n);\n\nconst CalendarIcon = (props: SVGProps<SVGSVGElement>) => (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.5}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <path d=\"M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 012.25-2.25h13.5A2.25 2.25 0 0121 7.5v11.25m-18 0A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75m-18 0v-7.5A2.25 2.25 0 015.25 9h13.5A2.25 2.25 0 0121 11.25v7.5\" />\n </svg>\n);\n\nconst baseCardClass =\n \"flex h-full flex-col rounded-xl border border-gray-200 bg-white p-6 shadow-[0_16px_32px_rgba(15,23,42,0.05)]\";\nconst headingClass = \"text-lg font-semibold text-gray-900\";\nconst contentClass = \"mt-4 flex-1 space-y-3\";\nconst itemClass = \"flex items-center gap-3 text-sm text-gray-600\";\nconst iconClass = \"h-5 w-5 text-gray-500\";\nconst footerClass =\n \"mt-6 flex justify-end text-sm font-semibold hover:opacity-80\";\n\nexport interface LicenseCardProps {\n fetchLicenseSummaryAction: () => Promise<License>;\n initialType?: string;\n initialExpiresAt?: string | null;\n pollIntervalMs?: number;\n}\n\nconst formatLicenseType = (type: string): string => {\n if (!type || type === \"Unknown\") {\n return \"Unknown\";\n }\n \n // Capitalize first letter and add \" license\"\n const capitalized = type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();\n return `${capitalized} license`;\n};\n\nexport const LicenseCard = ({\n fetchLicenseSummaryAction,\n initialType = \"Unknown\",\n initialExpiresAt = null,\n pollIntervalMs = 2000\n}: LicenseCardProps) => {\n const [type, setType] = useState(initialType);\n const [expiresAt, setExpiresAt] = useState(initialExpiresAt);\n\n useEffect(() => {\n const fetchSummary = async () => {\n // Skip if tab is not visible\n if (document.hidden) {\n return;\n }\n \n try {\n const license = await fetchLicenseSummaryAction();\n setType(license.licenseType);\n setExpiresAt(license.expireAt || null);\n } catch (error) {\n console.error(\"[license-card] Failed to fetch license\", error);\n }\n };\n\n // Fetch immediately\n fetchSummary();\n\n // Set up polling interval\n const intervalId = setInterval(fetchSummary, pollIntervalMs);\n\n // Also fetch when tab becomes visible again\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n fetchSummary();\n }\n };\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n // Cleanup on unmount\n return () => {\n clearInterval(intervalId);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n };\n }, [fetchLicenseSummaryAction, pollIntervalMs]);\n\n return (\n <section className={baseCardClass} aria-labelledby=\"license-card-heading\">\n <header>\n <h2 id=\"license-card-heading\" className={headingClass}>\n License\n </h2>\n </header>\n <div className={contentClass}>\n <div className={itemClass}>\n <FileIcon className={iconClass} />\n <span>Type: {formatLicenseType(type)}</span>\n </div>\n <div className={itemClass}>\n <CalendarIcon className={iconClass} />\n <span>Expiration: {formatDateShort(expiresAt)}</span>\n </div>\n </div>\n <footer className={footerClass}>\n <Link href=\"/license\" style={{ color: `var(--portal-branding-secondary, ${DEFAULT_SECONDARY_COLOR})` }}>View license →</Link>\n </footer>\n </section>\n );\n};\n\nLicenseCard.displayName = \"LicenseCard\";\n"]}
|