mirror of
https://github.com/okieselbach/AutopilotMonitor.git
synced 2026-10-25 13:10:13 +00:00
updated layout for plans and sidebar navigation added
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import { useState } from "react";
|
||||
import { useTenantConfig } from "../../TenantConfigContext";
|
||||
import { editionLabel, trialDaysLeft } from "@/lib/edition";
|
||||
import { trialDaysLeft } from "@/lib/edition";
|
||||
|
||||
/**
|
||||
* Self-service Enterprise trial switch. The Enterprise feature set is not finalized yet, so the
|
||||
@@ -12,21 +12,59 @@ import { editionLabel, trialDaysLeft } from "@/lib/edition";
|
||||
*/
|
||||
const TRIAL_SELF_SERVICE_ENABLED = false;
|
||||
|
||||
function CheckIcon({ className }: { className: string }) {
|
||||
return (
|
||||
<svg className={className} fill="none" stroke="currentColor" viewBox="0 0 24 24" strokeWidth={2.5}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function FeatureList({ features, checkClass }: { features: string[]; checkClass: string }) {
|
||||
return (
|
||||
<ul className="space-y-2.5">
|
||||
{features.map((f) => (
|
||||
<li key={f} className="flex items-start gap-2 text-sm text-gray-700">
|
||||
<CheckIcon className={`w-4 h-4 mt-0.5 shrink-0 ${checkClass}`} />
|
||||
<span>{f}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plan section: shows the tenant's current plan (Community / Enterprise / Enterprise Trial)
|
||||
* and teases what the Enterprise plan adds.
|
||||
* Plan section: two side-by-side plan cards (Community and Enterprise). The tenant's current plan
|
||||
* is highlighted so the edition is obvious at a glance; the Enterprise card teases what it adds.
|
||||
*/
|
||||
export function SectionPlan() {
|
||||
const { editionInfo, startTrial, startingTrial, user } = useTenantConfig();
|
||||
const [confirming, setConfirming] = useState(false);
|
||||
|
||||
const isEnterprise = editionInfo.edition === "enterprise";
|
||||
const label = editionLabel(editionInfo);
|
||||
const retention = editionInfo.entitlements.retentionCapDays;
|
||||
const daysLeft = editionInfo.isTrial ? trialDaysLeft(editionInfo.trialExpiresUtc) : 0;
|
||||
const trialConsumed = !isEnterprise && !editionInfo.trialAvailable;
|
||||
const canStartTrial =
|
||||
editionInfo.trialAvailable && (user?.isTenantAdmin === true || user?.isGlobalAdmin === true);
|
||||
|
||||
const communityFeatures = [
|
||||
"Live session monitoring & progress portal",
|
||||
"Full rules engine, including custom rules",
|
||||
"Fleet analytics, notifications & diagnostics",
|
||||
"AI integration (MCP) within usage limits",
|
||||
`${retention}-day data retention`,
|
||||
"Community support (GitHub)",
|
||||
];
|
||||
|
||||
const enterpriseFeatures = [
|
||||
`Extended data retention — 365 days (vs ${retention})`,
|
||||
"Higher portal & agent API rate limits",
|
||||
"Larger AI (MCP) usage quota",
|
||||
"Delegated (MSP) administration across tenants",
|
||||
"Reliability commitments & priority support",
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="p-6 border-b border-gray-200 bg-gradient-to-r from-purple-50 to-indigo-50">
|
||||
@@ -41,112 +79,135 @@ export function SectionPlan() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-6 space-y-8">
|
||||
{/* Current plan */}
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-700 mb-2">Current plan</h3>
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className={`inline-flex items-center px-3 py-1 rounded-full text-sm font-semibold ${
|
||||
isEnterprise
|
||||
? "bg-purple-100 text-purple-800 border border-purple-300"
|
||||
: "bg-gray-100 text-gray-700 border border-gray-300"
|
||||
}`}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
{editionInfo.isTrial && editionInfo.trialExpiresUtc && (
|
||||
<span className="text-sm text-gray-500">
|
||||
Trial ends {new Date(editionInfo.trialExpiresUtc).toLocaleDateString()}
|
||||
{daysLeft > 0 && ` (${daysLeft} day${daysLeft === 1 ? "" : "s"} left)`}
|
||||
<div className="p-6">
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
|
||||
{/* Community card */}
|
||||
<div
|
||||
className={`rounded-xl border p-6 flex flex-col ${
|
||||
!isEnterprise ? "border-gray-800 ring-1 ring-gray-800 bg-gray-50/60" : "border-gray-200"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">Community</h3>
|
||||
<p className="text-sm text-gray-500 mt-0.5">The full product, free</p>
|
||||
</div>
|
||||
{!isEnterprise && (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold bg-gray-900 text-white">
|
||||
Current plan
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 mb-5">
|
||||
<span className="text-2xl font-bold text-gray-900">Free</span>
|
||||
<span className="text-sm text-gray-500"> — and stays free</span>
|
||||
</div>
|
||||
|
||||
<FeatureList features={communityFeatures} checkClass="text-emerald-500" />
|
||||
</div>
|
||||
|
||||
{/* Enterprise card */}
|
||||
<div
|
||||
className={`rounded-xl border p-6 flex flex-col ${
|
||||
isEnterprise ? "border-purple-500 ring-1 ring-purple-500 bg-purple-50/40" : "border-purple-200 bg-purple-50/20"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<h3 className="text-lg font-semibold text-purple-900">Enterprise</h3>
|
||||
<p className="text-sm text-gray-500 mt-0.5">Higher limits, support & MSP</p>
|
||||
</div>
|
||||
{isEnterprise ? (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold bg-purple-600 text-white">
|
||||
{editionInfo.isTrial ? `Trial — ${daysLeft} day${daysLeft === 1 ? "" : "s"} left` : "Current plan"}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium bg-purple-100 text-purple-700 border border-purple-200">
|
||||
Coming soon
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 mb-5">
|
||||
<span className="text-2xl font-bold text-purple-900">
|
||||
{isEnterprise ? "Active" : "Pricing TBA"}
|
||||
</span>
|
||||
{!isEnterprise && <span className="text-sm text-gray-500"> — to be announced</span>}
|
||||
</div>
|
||||
|
||||
<p className="text-xs font-medium uppercase tracking-wide text-gray-400 mb-2.5">
|
||||
Everything in Community, plus
|
||||
</p>
|
||||
<FeatureList features={enterpriseFeatures} checkClass="text-purple-500" />
|
||||
|
||||
{/* CTA — only meaningful while the tenant is on Community */}
|
||||
{!isEnterprise && (
|
||||
<div className="mt-auto pt-5">
|
||||
{trialConsumed ? (
|
||||
<p className="text-sm text-gray-500">
|
||||
This tenant has already used its Enterprise trial. To move to Enterprise,{" "}
|
||||
<a
|
||||
href="https://github.com/okieselbach/Autopilot-Monitor/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-700 hover:underline"
|
||||
>
|
||||
get in touch
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
) : !TRIAL_SELF_SERVICE_ENABLED ? (
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
title="Available soon — the Enterprise trial opens once the feature set is finalized."
|
||||
className="w-full text-sm font-medium text-white bg-purple-400 rounded-lg px-4 py-2.5 cursor-not-allowed opacity-70"
|
||||
>
|
||||
Start 30-day Enterprise trial — coming soon
|
||||
</button>
|
||||
) : canStartTrial && !confirming ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirming(true)}
|
||||
className="w-full text-sm font-medium text-white bg-purple-600 rounded-lg px-4 py-2.5 hover:bg-purple-700 transition-colors"
|
||||
>
|
||||
Start 30-day Enterprise trial
|
||||
</button>
|
||||
) : canStartTrial && confirming ? (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="text-gray-600">One-time trial — start now?</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
const ok = await startTrial();
|
||||
if (ok) setConfirming(false);
|
||||
}}
|
||||
disabled={startingTrial}
|
||||
className="font-medium text-white bg-purple-600 rounded-lg px-3 py-1.5 hover:bg-purple-700 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{startingTrial ? "Starting…" : "Confirm"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirming(false)}
|
||||
disabled={startingTrial}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-gray-500 mt-3">
|
||||
{isEnterprise
|
||||
? editionInfo.isTrial
|
||||
? "Your Enterprise trial is active — all Enterprise capabilities are unlocked. When the trial ends, the tenant returns to Community automatically."
|
||||
: "This tenant is on the Enterprise plan — all Enterprise capabilities are unlocked."
|
||||
: `This tenant is on the Community plan — the full product, free, with a ${editionInfo.entitlements.retentionCapDays}-day data retention window.`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Enterprise teaser — only for Community tenants */}
|
||||
{!isEnterprise && (
|
||||
<div className="rounded-lg border border-purple-200 bg-purple-50/50 p-5">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<h3 className="text-base font-semibold text-purple-900">Enterprise</h3>
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-700 border border-purple-200">
|
||||
Coming soon
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-gray-700 leading-relaxed">
|
||||
A commercial plan for organizations that need more than the preview can promise —
|
||||
reliability commitments and support, plus higher operating limits: data retention
|
||||
extended from {editionInfo.entitlements.retentionCapDays} to 365 days, raised portal
|
||||
and agent API rate limits, a larger AI (MCP) usage quota, and delegated (MSP)
|
||||
administration for managing multiple tenants from one place. The full feature set and
|
||||
pricing will be announced here.
|
||||
</p>
|
||||
|
||||
<div className="mt-4">
|
||||
{trialConsumed ? (
|
||||
<p className="text-sm text-gray-500">
|
||||
This tenant has already used its Enterprise trial. To move to Enterprise,{" "}
|
||||
<a
|
||||
href="https://github.com/okieselbach/Autopilot-Monitor/issues"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-purple-700 hover:underline"
|
||||
>
|
||||
get in touch
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
) : !TRIAL_SELF_SERVICE_ENABLED ? (
|
||||
<button
|
||||
type="button"
|
||||
disabled
|
||||
title="Available soon — the Enterprise trial opens once the feature set is finalized."
|
||||
className="text-sm font-medium text-white bg-purple-400 rounded-full px-4 py-1.5 cursor-not-allowed opacity-70"
|
||||
>
|
||||
Start 30-day Enterprise trial — coming soon
|
||||
</button>
|
||||
) : canStartTrial && !confirming ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirming(true)}
|
||||
className="text-sm font-medium text-white bg-purple-600 rounded-full px-4 py-1.5 hover:bg-purple-700 transition-colors"
|
||||
>
|
||||
Start 30-day Enterprise trial
|
||||
</button>
|
||||
) : canStartTrial && confirming ? (
|
||||
<span className="flex items-center gap-2 text-sm">
|
||||
<span className="text-gray-600">One-time trial — start now?</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
const ok = await startTrial();
|
||||
if (ok) setConfirming(false);
|
||||
}}
|
||||
disabled={startingTrial}
|
||||
className="font-medium text-white bg-purple-600 rounded-full px-3 py-1 hover:bg-purple-700 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{startingTrial ? "Starting…" : "Confirm"}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirming(false)}
|
||||
disabled={startingTrial}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<p className="text-xs text-gray-400 mt-5">
|
||||
{isEnterprise && editionInfo.isTrial
|
||||
? "When the trial ends, the tenant returns to Community automatically."
|
||||
: "Scope, pricing and timeline for Enterprise will be announced. Community stays free."}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export const TENANT_NAV_SECTIONS = [
|
||||
{ id: "plan", label: "Plan", description: "Your current plan and what Enterprise adds" },
|
||||
{ id: "access-management", label: "Access Management", description: "Manage tenant admins and operators" },
|
||||
{ id: "autopilot", label: "Autopilot Validation", description: "Autopilot device and corporate identifier validation" },
|
||||
{ id: "hardware-whitelist", label: "Hardware Whitelist", description: "Manufacturer and model whitelist configuration" },
|
||||
@@ -7,6 +6,7 @@ export const TENANT_NAV_SECTIONS = [
|
||||
{ id: "sla-targets", label: "SLA Targets", description: "SLA targets and breach notification settings" },
|
||||
{ id: "bootstrap-sessions", label: "Bootstrap Sessions", description: "Create and manage bootstrap tokens" },
|
||||
{ id: "graph-permissions", label: "Optional Graph capabilities", description: "Grant additional Microsoft Graph permissions for optional features" },
|
||||
{ id: "plan", label: "Plan", description: "Your current plan and what Enterprise adds" },
|
||||
{ id: "support", label: "Submit Logs", description: "Send diagnostic files to the Autopilot Monitor team" },
|
||||
] as const;
|
||||
|
||||
|
||||
@@ -208,6 +208,7 @@ export const EXPANDABLE_NAV_GROUPS: ExpandableNavGroup[] = [
|
||||
{ id: "cfg-sla-targets", label: "SLA Targets", href: "/settings/tenant/sla-targets" },
|
||||
{ id: "cfg-bootstrap-sessions", label: "Bootstrap Sessions", href: "/settings/tenant/bootstrap-sessions" },
|
||||
{ id: "cfg-graph-permissions", label: "Optional Graph capabilities", href: "/settings/tenant/graph-permissions" },
|
||||
{ id: "cfg-plan", label: "Plan", href: "/settings/tenant/plan" },
|
||||
{ id: "cfg-support", label: "Submit Logs", href: "/settings/tenant/support" },
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user