// Hero platform mock — styled to match the real ActiveManage admin UI
const { useState: useStateMock, useEffect: useEffectMock } = React;
// Fixed platform palette (the app is light + blue, regardless of site theme —
// this reads as a genuine screenshot of the product).
const PB = {
page: "#F6F8FA",
side: "#EAEDF1",
sideBorder: "#DDE2E8",
blue: "#1C82C4",
blueDark:"#176FA8",
blueTile:"#E7F1FB",
red: "#E5484D",
card: "#FFFFFF",
cardBorder: "#E5E9ED",
title: "#2B4257",
label: "#46535F",
faint: "#8A95A1",
line: "#EDF0F3",
};
const NAV_VIEWS = [
{ id: "home", label: "Admin Home" },
{ id: "datastores", label: "Datastores" },
{ id: "forms", label: "Forms" },
{ id: "dashboards", label: "Dashboards" },
{ id: "permissions",label: "Permissions" },
{ id: "communication", label: "Communication" },
{ id: "booking", label: "Booking" },
{ id: "ecommerce", label: "eCommerce" },
{ id: "api", label: "API Server" },
];
function PlatformMock() {
const [view, setView] = useStateMock(0);
const [tick, setTick] = useStateMock(0);
const [paused, setPaused] = useStateMock(false);
useEffectMock(() => {
if (paused) return;
// Home view dwells longer so the full feature list can scroll past
const dwell = NAV_VIEWS[view].id === "home" ? 16000 : 7000;
const t = setTimeout(() => setView(v => (v + 1) % NAV_VIEWS.length), dwell);
return () => clearTimeout(t);
}, [paused, view]);
useEffectMock(() => {
setTick(0);
const t = setInterval(() => setTick(v => v + 1), 600);
return () => clearInterval(t);
}, [view]);
const v = NAV_VIEWS[view].id;
return (
setPaused(true)} onMouseLeave={() => setPaused(false)}>
setView(i)} />
{/* faint warm top accent like the real app */}
{v === "home" &&
}
{v === "datastores" &&
}
{v === "forms" &&
}
{v === "dashboards" &&
}
{v === "permissions"&&
}
{v === "communication" &&
}
{v === "booking" &&
}
{v === "ecommerce" &&
}
{v === "api" &&
}
);
}
function PlatformSidebar({ view, onPick }) {
const pill = (label, active, i) => (
i != null && onPick(i)} style={{
width: "100%", textAlign: "center", padding: "7px 8px", borderRadius: 6, marginBottom: 5,
fontSize: 10.5, fontWeight: 600, cursor: "pointer", border: "none",
background: active ? PB.blue : "#fff",
color: active ? "#fff" : PB.blue,
boxShadow: active ? "none" : `inset 0 0 0 1px ${PB.cardBorder}`,
display: "flex", alignItems: "center", justifyContent: "center", gap: 5,
}}>{label}
);
const navItem = (label, viewId, idx) => {
const active = view === viewId;
return (
onPick(idx)} key={label} style={{
width: "100%", textAlign: "left", padding: "5px 8px", borderRadius: 5,
fontSize: 11, cursor: "pointer", border: "none",
background: active ? PB.blueTile : "transparent",
color: active ? PB.blue : PB.label,
fontWeight: active ? 600 : 450,
display: "flex", alignItems: "center", gap: 7,
}}>
{label}
);
};
return (
ActiveManage
{pill("Admin Home", view === "home", 0)}
{pill("Architect Panel", view !== "home" && view !== "status", null)}
{pill("Status Panel", false, null)}
ARCHITECT
{NAV_VIEWS.filter(nv => nv.id !== "home").map(nv => navItem(nv.label, nv.id, NAV_VIEWS.findIndex(x => x.id === nv.id)))}
SUPPORT
{["New Requests", "All Users", "Invoices"].map(l => (
{l}
))}
);
}
// Tiny blue line-icon set used in cards
function Ico({ name }) {
const p = {
shield: ,
form: <> >,
data: <> >,
layout: <> >,
activity: ,
plug: <> >,
cart: <> >,
chat: ,
cog: <> >,
cal: <> >,
api: <> >,
bolt: ,
search: <> >,
globe: <> >,
}[name] || ;
return (
{p}
);
}
function ArchitectHome({ tick }) {
// The full Architect Panel — every category, split across 3 columns,
// continuously scrolling so nothing has to be shrunk.
const columns = [
[
["Security", "shield", ["Permissions", "Authentication Methods", "Security Settings", "Blocked User Accounts", "Blocked IP Addresses"]],
["Subscriptions", "bolt", ["Feature Sets", "Features", "Subscription Packages"]],
["Searching", "search", ["Site Searching"]],
["Internationalisation", "globe", ["Languages"]],
],
[
["Forms", "form", ["Conditional Forms", "Dynamic Add Fields", "Custom Fields", "Custom Forms", "Line Item Builders", "User Input Views"]],
["Dashboards", "layout", ["Custom Dashboard Blocks", "Dashboard Styles", "Shared Dashboard Layout"]],
["Commercial", "cart", ["Carts"]],
["Communication", "chat", ["Manage Meetings", "Manage Chat Engines"]],
],
[
["Integration & Connections", "plug", ["Database Hosts", "E-mail Accounts", "API Server", "Table Sync", "Deployments", "API Clients", "Azure AD Tenants"]],
["Data", "data", ["Datastores", "File Stores", "Data Extraction", "File Import Routines"]],
["Layout & Pages", "layout", ["Pages", "Browse Views", "Document Templates", "Admin Panel Builder", "Header"]],
["Configuration", "cog", ["Site Settings", "Multitenancy", "Friendly URLs", "SEO"]],
["Activity", "activity", ["Activity Log", "E-mail Log", "Error Log", "SMS Log"]],
["Automation", "cal", ["Tasks", "Task Log", "Polling"]],
["Styling", "cog", ["Logo"]],
["Help & Support", "chat", ["Manage Articles"]],
],
];
const Grid = () => (
{columns.map((col, ci) => (
{col.map(([title, icon, items]) => (
{title}
{items.map(it => (
{it}
))}
))}
))}
);
return (
Architect Panel
60+ tools
);
}
function ScreenHeader({ title, sub, action }) {
return (
);
}
function DatastoresScreen({ tick }) {
const fields = [
["ref", "Random Alphanumeric", "PK"],
["patient_name", "Text Box", "req"],
["nhs_number", "Text Box · indexed", "idx"],
["dob", "Date", ""],
["address", "Postal Address", ""],
["consultant", "User Search", ""],
["consent", "Signature", ""],
["risk", "Slider · 1–10", ""],
["status", "Pills", ""],
];
const rows = [
["P-00481", "S. Patel", "Active"], ["P-00480", "M. Hughes", "Active"],
["P-00479", "R. Khan", "Triage"], ["P-00478", "T. Brown", "Active"],
["P-00477", "A. Singh", "Critical"], ["P-00476", "L. Wright", "Closed"],
];
const statusColour = (s) => s === "Critical" ? PB.red : s === "Triage" ? "#C77700" : s === "Closed" ? PB.faint : "#1F9254";
return (
{fields.map(([n, t, tag], i) => (
{n}
{tag && {tag} }{t}
))}
ref name status
{rows.map((r, i) => (
{r[0]}
{r[1]}
{r[2]}
))}
AUTO: form · API · search · audit
);
}
function FormsScreen({ tick }) {
const chips = ["Text Box", "Dropdown", "Date", "Slider", "Tickboxes", "Postal Address", "Signature", "AJAX Search", "File Upload", "Dynamic Add", "Conditional", "Line Items"];
return (
Field types
{chips.map((c, i) => (
{c}
))}
+38
{[["Full name", "Sarah Patel"], ["NHS number", "885 391 044"], ["Date of birth", "14 / 04 / 1986"]].map(([l, v]) => (
))}
Allergies
{["Penicillin", "Nuts", "Latex"].map((a, i) => (
{[0,2].includes(i) ? "✓ " : ""}{a}
))}
VALIDATED 14/14
Submit
);
}
function PLabel({ children }) {
return {children}
;
}
function PField({ label, value }) {
return (
);
}
function DashboardScreen({ tick }) {
const bars = [42, 58, 71, 65, 80, 92, 88, 95, 78, 90];
return (
{[["Active", "12,847", "+12%"], ["Triaged", "284", "+8"], ["Avg wait", "9m", "-2m"], ["P0", "0", "—"]].map(([l, v, d]) => (
))}
Throughput · 10 weeks
{bars.map((h, i) => (
))}
);
}
function PermissionsScreen({ tick }) {
const groups = ["Super Admin", "Clinician", "Reception", "Auditor", "Patient"];
const cols = ["Patients", "Appts", "Rx", "Audit", "Billing"];
const matrix = [
[3, 3, 3, 3, 3],
[2, 2, 2, 1, 0],
[2, 2, 0, 0, 1],
[1, 1, 1, 1, 1],
[1, 1, 0, 0, 1],
];
const lbl = ["—", "R", "RW", "ALL"];
const bg = (l) => l === 3 ? PB.blue : l === 2 ? "#9Fc6E6" : l === 2 ? "#9FC6E6" : l === 1 ? "#DCEAF6" : "#F0F2F5";
const fg = (l) => l === 3 ? "#fff" : "#46535F";
return (
Group
{cols.map(c =>
{c}
)}
{groups.map((g, i) => (
{g}
{matrix[i].map((l, j) => (
{lbl[l]}
))}
))}
Field-level:
nhs_number · masked for Reception
billing · hidden
);
}
function CommunicationScreen({ tick }) {
const msgs = [
["✉", "Appointment confirmation", "Delivered", "SendGrid", "#1F9254"],
["✉", "Renewal reminder · 14 days", "Opened", "SMTP", "#1C82C4"],
["☎", "2FA passcode", "Delivered", "SMS · Twilio", "#1F9254"],
["✉", "Invoice INV-2847", "Clicked", "MS Graph", "#1C82C4"],
["☎", "Booking reminder", "Queued", "SMS", "#C77700"],
["✉", "Password reset", "Delivered", "SES", "#1F9254"],
];
return (
{msgs.map((m, i) => (
))}
{[["Sent today", "1,284"], ["Open rate", "62%"], ["SMS delivered", "418"]].map(([k, v]) => (
))}
INBOX ACTION · parse → datastore
);
}
function BookingScreen({ tick }) {
const days = ["Mon", "Tue", "Wed", "Thu", "Fri"];
const slots = ["09:00", "10:00", "11:00", "13:00", "14:00", "15:00"];
// booking state per [slot][day]: 0 free, 1 booked, 2 held
const state = [
[1, 0, 1, 0, 2],
[0, 1, 0, 1, 0],
[1, 1, 0, 0, 1],
[0, 0, 2, 1, 0],
[1, 0, 1, 0, 1],
[0, 1, 0, 1, 0],
];
return (
{slots.map((s, si) => (
{s}
{days.map((d, di) => {
const st = state[si][di];
return (
{st === 1 ? "BKD" : st === 2 ? "HELD" : "·"}
);
})}
))}
Booked
Held
Free
Auto-confirm · e-mail + SMS
);
}
function EcommerceScreen({ tick }) {
const orders = [
["#4821", "A. Chen", "£128.00", "Paid", "#1F9254"],
["#4820", "R. Okafor", "£54.00", "Fulfilled", "#1C82C4"],
["#4819", "M. Díaz", "£312.00", "Paid", "#1F9254"],
["#4818", "K. Yamada", "£19.99", "Refunded", "#8A95A1"],
["#4817", "S. Ahmed", "£86.50", "Pending", "#C77700"],
];
return (
Order Customer Total Status
{orders.map((o, i) => (
{o[0]}
{o[1]}
{o[2]}
{o[3]}
))}
{[["Revenue · today", "£4,182"], ["Orders", "38"], ["Avg. basket", "£110"]].map(([k, v]) => (
))}
● Stripe connected
);
}
function ApiScreen({ tick }) {
const eps = [
["GET", "/api/patients", "list, filter"],
["GET", "/api/patients/:id", "single record"],
["POST", "/api/patients", "create + validate"],
["PATCH", "/api/patients/:id", "partial update"],
["DELETE", "/api/patients/:id", "soft delete"],
["GET", "/api/patients/export.csv", "CSV export"],
];
const mc = { GET: "#1F9254", POST: PB.blue, PATCH: "#C77700", DELETE: PB.red };
return (
{eps.map(([m, p, h], i) => (
{m}
{p}
))}
GET /api/patients/P-00481
← 200 OK · 142ms
{"{"}
{[["ref", "P-00481"], ["patient_name", "Sarah Patel"], ["nhs_number", "885 391 044"], ["risk", "6.2"], ["status", "Active"]].map(([k, v], i) => (
"{k}" : "{v}" {i < 4 ? "," : ""}
))}
{"}"}
);
}
/* =============================================================================
INTEGRATED ITSM — the support desk that lives inside every app we ship.
Styled with the fixed platform palette (PB) so it reads as a real screenshot
of the product, matching PlatformMock.
============================================================================= */
function ItsmSection({ onNavigate }) {
const shots = [
{ src: "images/itsm/tickettypes.png", step: "01", cap: "Pick the type of request", url: "support · new request" },
{ src: "images/itsm/lognewticket.png", step: "02", cap: "Log the details in-app", url: "support · new request" },
{ src: "images/itsm/mytickets.png", step: "03", cap: "Track every request & SLA", url: "support · my requests" },
];
const points = [
["In-app request logging", "No separate portal, no tier-1 queue, no offshore call centre — it's a panel inside the software you already use."],
["Incidents, changes & features", "Raise a bug fix, change request, feature idea or how-to question — each routed and tracked against its own SLA."],
["Approvals & full history", "Electronic authorisation, live status and a complete audit trail on every request and reply."],
["Straight to the engineers", "The people who built your software are the people who answer — nothing gets lost in translation."],
];
return (
Support, built in
Every app ships with its own
integrated service desk.
Our own ITSM is woven directly into the software we build for you. Log a bug, request a change or suggest a feature without ever leaving the app — it lands straight with the engineers who built it.
{/* Real screenshots of the built-in support desk, framed like the product */}
{shots.map((s) => (
{s.step} {s.cap}
))}
{points.map(([t, d]) => (
{t}
{d}
))}
onNavigate && onNavigate("contact")}>
Talk to us about support
);
}
Object.assign(window, { PlatformMock, ItsmSection });
const _mockScrollStyle = document.createElement("style");
_mockScrollStyle.innerHTML = `
.mock-vscroll { animation: mockVScroll 26s linear infinite; will-change: transform; }
@keyframes mockVScroll { 0% { transform: translateY(0); } 100% { transform: translateY(-50%); } }
.mock:hover .mock-vscroll { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .mock-vscroll { animation: none; } }
`;
document.head.appendChild(_mockScrollStyle);