Prettier format pending files

This commit is contained in:
Builder.io 2025-10-18 22:21:46 +00:00
parent 3b77c60d62
commit 85e96c5969
8 changed files with 467 additions and 108 deletions

View file

@ -61,7 +61,6 @@ import Staff from "./pages/Staff";
import Realms from "./pages/Realms";
import Investors from "./pages/Investors";
const queryClient = new QueryClient();
const App = () => (

View file

@ -62,7 +62,8 @@ export const changelogEntries: ChangelogEntry[] = [
date: "2025-10-18",
type: "major",
category: "Platform Enhancement",
title: "Realms consolidation, live Status, feed upgrades, and Investors revamp",
title:
"Realms consolidation, live Status, feed upgrades, and Investors revamp",
description:
"Introduces a dedicated Realms management page with realm-aware dashboards, a live system Status page backed by a real API, improved social feed (likes, comments, and trending tags), and a redesigned Investors page with clear legal guidance.",
author: "AeThex Development Team",
@ -112,7 +113,7 @@ export const changelogEntries: ChangelogEntry[] = [
{
type: "fixed",
description:
"Resolved \"invalid input value for Enum user_type_enum: \\\"staff\\\"\" by adding \"staff\" to enum via migration",
'Resolved "invalid input value for Enum user_type_enum: \\"staff\\"" by adding "staff" to enum via migration',
impact: "high",
},
],

View file

@ -139,7 +139,11 @@ export default function Dashboard() {
"staff",
];
const current = ((profile as any)?.user_type as RealmKey) ?? null;
if (paramRealm && validRealms.includes(paramRealm) && paramRealm !== current) {
if (
paramRealm &&
validRealms.includes(paramRealm) &&
paramRealm !== current
) {
(async () => {
try {
await updateProfile({ user_type: paramRealm } as any);
@ -663,7 +667,9 @@ export default function Dashboard() {
}
// Determine active realm for dashboard personalization
const activeRealm: RealmKey = (userRealm || ((profile as any)?.user_type as RealmKey) || "community_member") as RealmKey;
const activeRealm: RealmKey = (userRealm ||
((profile as any)?.user_type as RealmKey) ||
"community_member") as RealmKey;
return (
<Layout>
@ -718,18 +724,24 @@ export default function Dashboard() {
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
<div>
<h1 className="text-3xl font-bold text-gradient-purple">
{activeRealm === "game_developer" && "Game Development Dashboard"}
{activeRealm === "game_developer" &&
"Game Development Dashboard"}
{activeRealm === "client" && "Consulting Dashboard"}
{activeRealm === "community_member" && "Community Dashboard"}
{activeRealm === "customer" && "Get Started Dashboard"}
{activeRealm === "staff" && "Staff Dashboard"}
</h1>
<p className="text-muted-foreground">
Welcome back, {profile?.full_name || user.email?.split("@")[0]} {streakLabel}
Welcome back,{" "}
{profile?.full_name || user.email?.split("@")[0]} {" "}
{streakLabel}
</p>
{longestStreak > 0 && (
<div className="mt-3 flex flex-wrap gap-2">
<Badge variant="outline" className="border-aethex-400/40 text-aethex-200">
<Badge
variant="outline"
className="border-aethex-400/40 text-aethex-200"
>
Realm: {activeRealm.replace("_", " ")}
</Badge>
</div>
@ -887,8 +899,12 @@ export default function Dashboard() {
{activeRealm === "game_developer" && (
<Card className="bg-card/50 border-border/50 animate-fade-in">
<CardHeader>
<CardTitle className="text-gradient">Create a Post</CardTitle>
<CardDescription>Share updates, images, or videos</CardDescription>
<CardTitle className="text-gradient">
Create a Post
</CardTitle>
<CardDescription>
Share updates, images, or videos
</CardDescription>
</CardHeader>
<CardContent>
<PostComposer onPosted={loadDashboardData} />
@ -899,14 +915,28 @@ export default function Dashboard() {
{activeRealm === "community_member" && (
<Card className="bg-card/50 border-border/50 animate-fade-in">
<CardHeader>
<CardTitle className="text-gradient">Community actions</CardTitle>
<CardDescription>Post to the feed and explore trending topics</CardDescription>
<CardTitle className="text-gradient">
Community actions
</CardTitle>
<CardDescription>
Post to the feed and explore trending topics
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<PostComposer onPosted={() => {}} />
<div className="flex gap-2">
<Button variant="outline" onClick={() => navigate("/feed")}>Open Feed</Button>
<Button variant="outline" onClick={() => navigate("/community/mentorship")}>Mentorship</Button>
<Button
variant="outline"
onClick={() => navigate("/feed")}
>
Open Feed
</Button>
<Button
variant="outline"
onClick={() => navigate("/community/mentorship")}
>
Mentorship
</Button>
</div>
</CardContent>
</Card>
@ -915,13 +945,29 @@ export default function Dashboard() {
{activeRealm === "client" && (
<Card className="bg-card/50 border-border/50 animate-fade-in">
<CardHeader>
<CardTitle className="text-gradient">Project workspace</CardTitle>
<CardDescription>Kick off engagements and track delivery</CardDescription>
<CardTitle className="text-gradient">
Project workspace
</CardTitle>
<CardDescription>
Kick off engagements and track delivery
</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap gap-2">
<Button onClick={() => navigate("/projects/new")}>Start New Project</Button>
<Button variant="outline" onClick={() => navigate("/teams")}>Create Team</Button>
<Button variant="outline" onClick={() => navigate("/consulting")}>Consulting Overview</Button>
<Button onClick={() => navigate("/projects/new")}>
Start New Project
</Button>
<Button
variant="outline"
onClick={() => navigate("/teams")}
>
Create Team
</Button>
<Button
variant="outline"
onClick={() => navigate("/consulting")}
>
Consulting Overview
</Button>
</CardContent>
</Card>
)}
@ -930,11 +976,20 @@ export default function Dashboard() {
<Card className="bg-card/50 border-border/50 animate-fade-in">
<CardHeader>
<CardTitle className="text-gradient">Get started</CardTitle>
<CardDescription>Explore products and manage access</CardDescription>
<CardDescription>
Explore products and manage access
</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap gap-2">
<Button onClick={() => navigate("/get-started")}>Product onboarding</Button>
<Button variant="outline" onClick={() => navigate("/support")}>Support</Button>
<Button onClick={() => navigate("/get-started")}>
Product onboarding
</Button>
<Button
variant="outline"
onClick={() => navigate("/support")}
>
Support
</Button>
</CardContent>
</Card>
)}
@ -943,11 +998,17 @@ export default function Dashboard() {
<Card className="bg-card/50 border-border/50 animate-fade-in">
<CardHeader>
<CardTitle className="text-gradient">Operations</CardTitle>
<CardDescription>Moderation and internal tools</CardDescription>
<CardDescription>
Moderation and internal tools
</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap gap-2">
<Button onClick={() => navigate("/staff")}>Open Staff Console</Button>
<Button variant="outline" onClick={() => navigate("/feed")}>Community Feed</Button>
<Button onClick={() => navigate("/staff")}>
Open Staff Console
</Button>
<Button variant="outline" onClick={() => navigate("/feed")}>
Community Feed
</Button>
</CardContent>
</Card>
)}
@ -1204,9 +1265,16 @@ export default function Dashboard() {
<div className="rounded border border-border/40 p-3 flex items-center justify-between">
<div>
<div className="font-medium">Realm & Path</div>
<div className="text-sm text-muted-foreground">Manage your realm preferences on the Realms page.</div>
<div className="text-sm text-muted-foreground">
Manage your realm preferences on the Realms page.
</div>
</div>
<Button variant="outline" onClick={() => navigate("/realms")}>Open Realms</Button>
<Button
variant="outline"
onClick={() => navigate("/realms")}
>
Open Realms
</Button>
</div>
</TabsContent>

View file

@ -1,6 +1,12 @@
import Layout from "@/components/Layout";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
@ -8,7 +14,16 @@ import { useAuth } from "@/contexts/AuthContext";
import { useToast } from "@/hooks/use-toast";
import { useRef, useState } from "react";
import { Link } from "react-router-dom";
import { Flame, BarChart3, Layers, Shield, Handshake, Building2, Target, Rocket } from "lucide-react";
import {
Flame,
BarChart3,
Layers,
Shield,
Handshake,
Building2,
Target,
Rocket,
} from "lucide-react";
type ThesisPoint = { icon: JSX.Element; title: string; desc: string };
@ -26,7 +41,8 @@ export default function Investors() {
const isClientRealm = (profile as any)?.user_type === "client";
const scrollToForm = () => formRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
const scrollToForm = () =>
formRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
const submit = async () => {
if (!email.trim()) {
@ -41,10 +57,20 @@ export default function Investors() {
body: JSON.stringify({ name, email, amount, accredited, message }),
});
if (!resp.ok) throw new Error("Failed to submit");
toast({ title: "Thanks!", description: "Well follow up with next steps." });
setName(""); setEmail(""); setAmount(""); setMessage(""); setAccredited(false);
toast({
title: "Thanks!",
description: "Well follow up with next steps.",
});
setName("");
setEmail("");
setAmount("");
setMessage("");
setAccredited(false);
} catch (e: any) {
toast({ variant: "destructive", description: e?.message || "Try again later" });
toast({
variant: "destructive",
description: e?.message || "Try again later",
});
} finally {
setSubmitting(false);
}
@ -55,14 +81,29 @@ export default function Investors() {
await updateProfile({ user_type: "client" as any });
toast({ title: "Realm set", description: "Consulting realm activated" });
} catch (e: any) {
toast({ variant: "destructive", description: e?.message || "Could not update realm" });
toast({
variant: "destructive",
description: e?.message || "Could not update realm",
});
}
};
const thesis: ThesisPoint[] = [
{ icon: <Layers className="h-5 w-5" />, title: "Three Engines", desc: "Studios (services), Platform (community), and Labs (R&D) compound value together." },
{ icon: <Shield className="h-5 w-5" />, title: "Trust & Quality", desc: "Security-first engineering and measurable delivery keep churn low and NPS high." },
{ icon: <Target className="h-5 w-5" />, title: "Focused Markets", desc: "High-signal segments: games, real-time apps, and experience platforms." },
{
icon: <Layers className="h-5 w-5" />,
title: "Three Engines",
desc: "Studios (services), Platform (community), and Labs (R&D) compound value together.",
},
{
icon: <Shield className="h-5 w-5" />,
title: "Trust & Quality",
desc: "Security-first engineering and measurable delivery keep churn low and NPS high.",
},
{
icon: <Target className="h-5 w-5" />,
title: "Focused Markets",
desc: "High-signal segments: games, real-time apps, and experience platforms.",
},
];
const kpis = [
@ -83,18 +124,40 @@ export default function Investors() {
<section className="relative overflow-hidden py-20 lg:py-28">
<div className="container mx-auto max-w-6xl px-4 text-center">
<div className="mx-auto flex max-w-3xl flex-col items-center gap-8">
<Badge variant="outline" className="border-red-400/40 bg-red-500/10 text-red-300 shadow-[0_0_20px_rgba(239,68,68,0.2)]">
<Badge
variant="outline"
className="border-red-400/40 bg-red-500/10 text-red-300 shadow-[0_0_20px_rgba(239,68,68,0.2)]"
>
<span className="mr-2 inline-flex h-2 w-2 animate-pulse rounded-full bg-red-300" />
Investor Relations
</Badge>
<h1 className="text-4xl font-black tracking-tight text-red-300 sm:text-5xl lg:text-6xl">AeThex | Building With Conviction</h1>
<p className="text-lg text-red-100/90 sm:text-xl">We craft reliable, loved software and the platform that powers creators. Explore our thesis, traction, and how to participate in compliant offerings.</p>
<h1 className="text-4xl font-black tracking-tight text-red-300 sm:text-5xl lg:text-6xl">
AeThex | Building With Conviction
</h1>
<p className="text-lg text-red-100/90 sm:text-xl">
We craft reliable, loved software and the platform that powers
creators. Explore our thesis, traction, and how to participate
in compliant offerings.
</p>
<div className="flex flex-col gap-4 sm:flex-row">
<Button size="lg" className="bg-red-500 text-white shadow-[0_0_30px_rgba(239,68,68,0.35)] transition hover:bg-red-400" onClick={() => formRef.current?.scrollIntoView({ behavior: "smooth" })}>
<Button
size="lg"
className="bg-red-500 text-white shadow-[0_0_30px_rgba(239,68,68,0.35)] transition hover:bg-red-400"
onClick={() =>
formRef.current?.scrollIntoView({ behavior: "smooth" })
}
>
<Handshake className="mr-2 h-5 w-5" /> Request Investor Info
</Button>
<Button asChild size="lg" variant="outline" className="border-red-400/60 text-red-300 hover:bg-red-500/10">
<Link to="/about"><Rocket className="mr-2 h-5 w-5" /> Read About AeThex</Link>
<Button
asChild
size="lg"
variant="outline"
className="border-red-400/60 text-red-300 hover:bg-red-500/10"
>
<Link to="/about">
<Rocket className="mr-2 h-5 w-5" /> Read About AeThex
</Link>
</Button>
</div>
</div>
@ -106,17 +169,31 @@ export default function Investors() {
<div className="container mx-auto max-w-6xl px-4">
<div className="mb-12 flex items-start justify-between gap-6">
<div>
<h2 className="text-3xl font-bold text-red-300 sm:text-4xl">Investment Thesis</h2>
<p className="mt-3 max-w-2xl text-sm text-red-100/70 sm:text-base">Software creation is shifting to collaborative, real-time networks. AeThex aligns world-class services, platform, and research to accelerate outcomes for builders and brands.</p>
<h2 className="text-3xl font-bold text-red-300 sm:text-4xl">
Investment Thesis
</h2>
<p className="mt-3 max-w-2xl text-sm text-red-100/70 sm:text-base">
Software creation is shifting to collaborative, real-time
networks. AeThex aligns world-class services, platform, and
research to accelerate outcomes for builders and brands.
</p>
</div>
<Flame className="hidden h-10 w-10 text-red-400/70 sm:block" />
</div>
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
{thesis.map((point) => (
<Card key={point.title} className="border border-red-400/20 bg-black/60 backdrop-blur">
<Card
key={point.title}
className="border border-red-400/20 bg-black/60 backdrop-blur"
>
<CardHeader className="space-y-2">
<CardTitle className="flex items-center gap-2 text-red-100">{point.icon}{point.title}</CardTitle>
<CardDescription className="text-red-200/80">{point.desc}</CardDescription>
<CardTitle className="flex items-center gap-2 text-red-100">
{point.icon}
{point.title}
</CardTitle>
<CardDescription className="text-red-200/80">
{point.desc}
</CardDescription>
</CardHeader>
</Card>
))}
@ -129,10 +206,17 @@ export default function Investors() {
<div className="container mx-auto max-w-6xl px-4">
<div className="grid gap-4 md:grid-cols-4">
{kpis.map((m) => (
<Card key={m.label} className="border border-red-400/15 bg-black/70 text-center">
<Card
key={m.label}
className="border border-red-400/15 bg-black/70 text-center"
>
<CardContent className="p-6">
<div className="text-3xl font-bold text-red-300">{m.kpi}</div>
<div className="mt-1 text-sm text-red-100/80">{m.label}</div>
<div className="text-3xl font-bold text-red-300">
{m.kpi}
</div>
<div className="mt-1 text-sm text-red-100/80">
{m.label}
</div>
</CardContent>
</Card>
))}
@ -141,45 +225,118 @@ export default function Investors() {
</section>
{/* Interest + Realm */}
<section ref={formRef} className="border-y border-red-400/10 bg-black/85 py-16">
<section
ref={formRef}
className="border-y border-red-400/10 bg-black/85 py-16"
>
<div className="container mx-auto max-w-6xl px-4">
<div className="grid gap-6 md:grid-cols-2">
<Card className="border border-red-400/20 bg-black/70">
<CardHeader>
<CardTitle className="text-red-100">Investor interest</CardTitle>
<CardDescription className="text-red-200/80">Request our investor packet and updates</CardDescription>
<CardTitle className="text-red-100">
Investor interest
</CardTitle>
<CardDescription className="text-red-200/80">
Request our investor packet and updates
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<Input placeholder="Full name" value={name} onChange={(e) => setName(e.target.value)} />
<Input placeholder="Email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
<Input placeholder="Indicative amount (optional)" value={amount} onChange={(e) => setAmount(e.target.value)} />
<label className="flex items-center gap-2 text-sm text-red-100/80"><input type="checkbox" checked={accredited} onChange={(e) => setAccredited(e.target.checked)} /> I am an accredited investor (self-attested)</label>
<Textarea placeholder="Message (optional)" value={message} onChange={(e) => setMessage(e.target.value)} />
<Input
placeholder="Full name"
value={name}
onChange={(e) => setName(e.target.value)}
/>
<Input
placeholder="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<Input
placeholder="Indicative amount (optional)"
value={amount}
onChange={(e) => setAmount(e.target.value)}
/>
<label className="flex items-center gap-2 text-sm text-red-100/80">
<input
type="checkbox"
checked={accredited}
onChange={(e) => setAccredited(e.target.checked)}
/>{" "}
I am an accredited investor (self-attested)
</label>
<Textarea
placeholder="Message (optional)"
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
<div className="flex flex-wrap gap-2">
<Button onClick={submit} disabled={submitting} className="bg-red-500 hover:bg-red-400">{submitting ? "Sending…" : "Request Info"}</Button>
<Button variant="outline" asChild className="border-red-400/60 text-red-300 hover:bg-red-500/10"><Link to="/docs">View Docs</Link></Button>
<Button
onClick={submit}
disabled={submitting}
className="bg-red-500 hover:bg-red-400"
>
{submitting ? "Sending…" : "Request Info"}
</Button>
<Button
variant="outline"
asChild
className="border-red-400/60 text-red-300 hover:bg-red-500/10"
>
<Link to="/docs">View Docs</Link>
</Button>
</div>
<p className="text-xs text-red-200/70">This page is informational only and not an offer to sell securities.</p>
<p className="text-xs text-red-200/70">
This page is informational only and not an offer to sell
securities.
</p>
</CardContent>
</Card>
<Card className="border border-red-400/20 bg-black/70">
<CardHeader>
<CardTitle className="text-red-100">Realm for investors</CardTitle>
<CardDescription className="text-red-200/80">Investors map to the Client realm (strategic partners)</CardDescription>
<CardTitle className="text-red-100">
Realm for investors
</CardTitle>
<CardDescription className="text-red-200/80">
Investors map to the Client realm (strategic partners)
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-sm text-red-100/80">Client realm provides engagement dashboards, briefings, and investor updates. Switch realms anytime from Realms.</p>
<p className="text-sm text-red-100/80">
Client realm provides engagement dashboards, briefings,
and investor updates. Switch realms anytime from Realms.
</p>
{user ? (
<Button onClick={activateClientRealm} disabled={isClientRealm} className="bg-red-500 hover:bg-red-400">{isClientRealm ? "Consulting realm active" : "Activate Consulting realm"}</Button>
<Button
onClick={activateClientRealm}
disabled={isClientRealm}
className="bg-red-500 hover:bg-red-400"
>
{isClientRealm
? "Consulting realm active"
: "Activate Consulting realm"}
</Button>
) : (
<Button asChild className="bg-red-500 hover:bg-red-400"><Link to="/onboarding">Create account to activate</Link></Button>
<Button asChild className="bg-red-500 hover:bg-red-400">
<Link to="/onboarding">Create account to activate</Link>
</Button>
)}
<Button variant="outline" asChild className="border-red-400/60 text-red-300 hover:bg-red-500/10"><Link to="/realms">Open Realms</Link></Button>
<Button
variant="outline"
asChild
className="border-red-400/60 text-red-300 hover:bg-red-500/10"
>
<Link to="/realms">Open Realms</Link>
</Button>
<div className="rounded border border-red-400/20 bg-black/60 p-4">
<div className="mb-1 text-xs uppercase text-red-400/80">Why AeThex</div>
<div className="mb-1 text-xs uppercase text-red-400/80">
Why AeThex
</div>
<ul className="list-disc pl-5 text-sm text-red-100/80 space-y-1">
<li>End-to-end capabilities (services + platform + labs)</li>
<li>
End-to-end capabilities (services + platform + labs)
</li>
<li>Strong builder brand, ethical engineering</li>
<li>Clear roadmap to commerce (merch/digital goods)</li>
</ul>
@ -195,16 +352,35 @@ export default function Investors() {
<div className="container mx-auto max-w-6xl px-4">
<Card className="border border-red-400/15 bg-black/70">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-red-100"><Building2 className="h-5 w-5" /> How to invest (legal paths)</CardTitle>
<CardDescription className="text-red-200/80">We work with compliant frameworks</CardDescription>
<CardTitle className="flex items-center gap-2 text-red-100">
<Building2 className="h-5 w-5" /> How to invest (legal
paths)
</CardTitle>
<CardDescription className="text-red-200/80">
We work with compliant frameworks
</CardDescription>
</CardHeader>
<CardContent className="text-sm text-red-100/80 space-y-2">
<ul className="list-disc pl-5 space-y-1">
<li>Seed/SAFE via Reg D 506(c) for accredited investors (KYC/AML and accreditation checks required).</li>
<li>Community rounds via Reg CF on a registered funding portal (e.g., Wefunder/StartEngine) for non-accredited participants.</li>
<li>Larger public-ready rounds via Reg A+ with audited financials and a qualified offering circular.</li>
<li>
Seed/SAFE via Reg D 506(c) for accredited investors
(KYC/AML and accreditation checks required).
</li>
<li>
Community rounds via Reg CF on a registered funding portal
(e.g., Wefunder/StartEngine) for non-accredited
participants.
</li>
<li>
Larger public-ready rounds via Reg A+ with audited
financials and a qualified offering circular.
</li>
</ul>
<p>Investment funds are not accepted on this site. Product purchases (merch or digital goods) are separate consumer transactions and do not constitute securities.</p>
<p>
Investment funds are not accepted on this site. Product
purchases (merch or digital goods) are separate consumer
transactions and do not constitute securities.
</p>
</CardContent>
</Card>
</div>

View file

@ -1,5 +1,11 @@
import Layout from "@/components/Layout";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { REALM_OPTIONS, RealmKey } from "@/components/settings/RealmSwitcher";
@ -20,7 +26,12 @@ export default function Portal() {
const { profile, roles } = useAuth();
const lastRealm = (profile as any)?.user_type as RealmKey | undefined;
const canSeeStaff = useMemo(
() => roles.some((r) => ["owner", "admin", "founder", "staff", "employee"].includes(r.toLowerCase())),
() =>
roles.some((r) =>
["owner", "admin", "founder", "staff", "employee"].includes(
r.toLowerCase(),
),
),
[roles],
);
const visible = useMemo(
@ -32,20 +43,36 @@ export default function Portal() {
<Layout>
<div className="mx-auto w-full max-w-6xl px-4 py-10 lg:px-6">
<div className="mb-8">
<Badge variant="outline" className="mb-2">Portal</Badge>
<Badge variant="outline" className="mb-2">
Portal
</Badge>
<h1 className="text-3xl font-bold">Choose your realm</h1>
<p className="text-muted-foreground">Jump into the dashboard that matches your goals. Your last selection is highlighted.</p>
<p className="text-muted-foreground">
Jump into the dashboard that matches your goals. Your last selection
is highlighted.
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{visible.map((realm, i) => {
const Icon = realm.icon;
const active = lastRealm === realm.id;
return (
<Card key={realm.id} className={cn("relative border border-border/50 transition-all hover:border-aethex-400/50", active && "border-aethex-400/70 shadow-lg")}
style={{ animationDelay: `${i * 0.05}s` }}>
<Card
key={realm.id}
className={cn(
"relative border border-border/50 transition-all hover:border-aethex-400/50",
active && "border-aethex-400/70 shadow-lg",
)}
style={{ animationDelay: `${i * 0.05}s` }}
>
<CardHeader>
<div className="flex items-center gap-3">
<div className={cn("flex h-12 w-12 items-center justify-center rounded-xl text-white shadow", `bg-gradient-to-br ${realm.gradient}`)}>
<div
className={cn(
"flex h-12 w-12 items-center justify-center rounded-xl text-white shadow",
`bg-gradient-to-br ${realm.gradient}`,
)}
>
<Icon className="h-6 w-6" />
</div>
<div>
@ -55,10 +82,14 @@ export default function Portal() {
</div>
</CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">{realm.description}</p>
<p className="text-sm text-muted-foreground">
{realm.description}
</p>
<div className="flex flex-wrap gap-2">
{realm.highlights.slice(0, 3).map((h) => (
<Badge key={h} variant="outline" className="text-xs">{h}</Badge>
<Badge key={h} variant="outline" className="text-xs">
{h}
</Badge>
))}
</div>
<div className="pt-2">

View file

@ -1,8 +1,17 @@
import Layout from "@/components/Layout";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import RealmSwitcher, { REALM_OPTIONS, RealmKey } from "@/components/settings/RealmSwitcher";
import RealmSwitcher, {
REALM_OPTIONS,
RealmKey,
} from "@/components/settings/RealmSwitcher";
import { useAuth } from "@/contexts/AuthContext";
import { useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
@ -12,12 +21,21 @@ export default function Realms() {
const { user, profile, roles, updateProfile } = useAuth();
const navigate = useNavigate();
const [activating, setActivating] = useState<string | null>(null);
const [selectedRealm, setSelectedRealm] = useState<RealmKey | null>((profile as any)?.user_type ?? null);
const [experience, setExperience] = useState<string>((profile as any)?.experience_level || "beginner");
const [selectedRealm, setSelectedRealm] = useState<RealmKey | null>(
(profile as any)?.user_type ?? null,
);
const [experience, setExperience] = useState<string>(
(profile as any)?.experience_level || "beginner",
);
const [saving, setSaving] = useState(false);
const lastRealm = (profile as any)?.user_type as RealmKey | undefined;
const canSeeStaff = useMemo(
() => roles.some((r) => ["owner", "admin", "founder", "staff", "employee"].includes(r.toLowerCase())),
() =>
roles.some((r) =>
["owner", "admin", "founder", "staff", "employee"].includes(
r.toLowerCase(),
),
),
[roles],
);
const visible = useMemo(
@ -29,9 +47,14 @@ export default function Realms() {
<Layout>
<div className="mx-auto w-full max-w-6xl px-4 py-10 lg:px-6">
<div className="mb-8">
<Badge variant="outline" className="mb-2">Realms</Badge>
<Badge variant="outline" className="mb-2">
Realms
</Badge>
<h1 className="text-3xl font-bold">Choose your realm</h1>
<p className="text-muted-foreground">Your dashboard adapts to the selected realm. Last used realm is highlighted.</p>
<p className="text-muted-foreground">
Your dashboard adapts to the selected realm. Last used realm is
highlighted.
</p>
</div>
{/* Realm & Path manager */}
@ -41,7 +64,10 @@ export default function Realms() {
onRealmChange={setSelectedRealm}
selectedExperience={experience}
onExperienceChange={setExperience}
hasChanges={selectedRealm !== ((profile as any)?.user_type ?? null) || experience !== ((profile as any)?.experience_level || "beginner")}
hasChanges={
selectedRealm !== ((profile as any)?.user_type ?? null) ||
experience !== ((profile as any)?.experience_level || "beginner")
}
onSave={async () => {
if (!selectedRealm) return;
if (!user) {
@ -50,7 +76,10 @@ export default function Realms() {
}
setSaving(true);
try {
await updateProfile({ user_type: selectedRealm, experience_level: experience } as any);
await updateProfile({
user_type: selectedRealm,
experience_level: experience,
} as any);
navigate("/dashboard", { replace: true });
} finally {
setSaving(false);
@ -61,7 +90,9 @@ export default function Realms() {
</div>
<div className="mt-6 flex justify-end">
<Button onClick={() => navigate(user ? "/dashboard" : "/onboarding")}>{user ? "Open Dashboard" : "Start Onboarding"}</Button>
<Button onClick={() => navigate(user ? "/dashboard" : "/onboarding")}>
{user ? "Open Dashboard" : "Start Onboarding"}
</Button>
</div>
</div>
</Layout>

View file

@ -69,7 +69,9 @@ export default function Status() {
const resp = await fetch("/api/status");
if (!resp.ok) throw new Error("Status API failed");
const data = await resp.json();
const mappedMetrics: SystemMetric[] = (Array.isArray(data.metrics) ? data.metrics : []).map((it: any) => ({
const mappedMetrics: SystemMetric[] = (
Array.isArray(data.metrics) ? data.metrics : []
).map((it: any) => ({
name: String(it.name),
value: String(it.value ?? "--"),
unit: String(it.unit ?? ""),
@ -77,18 +79,30 @@ export default function Status() {
icon: iconFor(String(it.icon || "Activity")),
}));
setMetrics(mappedMetrics);
const mappedServices: ServiceStatus[] = (Array.isArray(data.services) ? data.services : []).map((it: any) => ({
const mappedServices: ServiceStatus[] = (
Array.isArray(data.services) ? data.services : []
).map((it: any) => ({
name: String(it.name),
status: (it.status ?? "operational") as any,
responseTime: Number(it.responseTime) || 0,
uptime: String(it.uptime ?? "--"),
lastCheck: new Date(it.lastCheck || data.updatedAt || Date.now()).toLocaleTimeString(),
lastCheck: new Date(
it.lastCheck || data.updatedAt || Date.now(),
).toLocaleTimeString(),
description: String(it.description || ""),
}));
setServices(mappedServices);
setLastUpdated(new Date(data.updatedAt || Date.now()));
} catch (e) {
setMetrics([{ name: "Global Uptime", value: "--", unit: "%", status: "warning", icon: Activity }]);
setMetrics([
{
name: "Global Uptime",
value: "--",
unit: "%",
status: "warning",
icon: Activity,
},
]);
}
};

View file

@ -1183,7 +1183,11 @@ export function createServer() {
// Investors: capture interest
app.post("/api/investors/interest", async (req, res) => {
const { name, email, amount, accredited, message } = (req.body || {}) as {
name?: string; email?: string; amount?: string; accredited?: boolean; message?: string;
name?: string;
email?: string;
amount?: string;
accredited?: boolean;
message?: string;
};
if (!email) return res.status(400).json({ error: "email required" });
try {
@ -1307,18 +1311,28 @@ export function createServer() {
await fn();
return { ok: true, ms: Date.now() - t0 };
} catch (e) {
return { ok: false, ms: Date.now() - t0, error: (e as any)?.message || String(e) };
return {
ok: false,
ms: Date.now() - t0,
error: (e as any)?.message || String(e),
};
}
};
// Database check (user_profiles)
const dbCheck = await time(async () => {
await adminSupabase.from("user_profiles").select("id", { head: true, count: "exact" }).limit(1);
await adminSupabase
.from("user_profiles")
.select("id", { head: true, count: "exact" })
.limit(1);
});
// API/Core check (community_posts)
const apiCheck = await time(async () => {
await adminSupabase.from("community_posts").select("id", { head: true, count: "exact" }).limit(1);
await adminSupabase
.from("community_posts")
.select("id", { head: true, count: "exact" })
.limit(1);
});
// Auth check
@ -1374,7 +1388,8 @@ export function createServer() {
];
const avgRt = Math.round(
services.reduce((a, s) => a + (Number(s.responseTime) || 0), 0) / services.length,
services.reduce((a, s) => a + (Number(s.responseTime) || 0), 0) /
services.length,
);
const errCount = services.filter((s) => s.status === "outage").length;
const warnCount = services.filter((s) => s.status === "degraded").length;
@ -1389,10 +1404,34 @@ export function createServer() {
} catch {}
const metrics = [
{ name: "Global Uptime", value: (errCount ? "99.5" : warnCount ? "99.9" : "99.99"), unit: "%", status: errCount ? "critical" : warnCount ? "warning" : "good", icon: "Activity" },
{ name: "Response Time", value: String(avgRt), unit: "ms", status: avgRt > 800 ? "critical" : avgRt > 400 ? "warning" : "good", icon: "Zap" },
{ name: "Active Users", value: activeUsers, unit: "", status: "good", icon: "Globe" },
{ name: "Error Rate", value: String(errCount), unit: " outages", status: errCount ? "critical" : warnCount ? "warning" : "good", icon: "Shield" },
{
name: "Global Uptime",
value: errCount ? "99.5" : warnCount ? "99.9" : "99.99",
unit: "%",
status: errCount ? "critical" : warnCount ? "warning" : "good",
icon: "Activity",
},
{
name: "Response Time",
value: String(avgRt),
unit: "ms",
status: avgRt > 800 ? "critical" : avgRt > 400 ? "warning" : "good",
icon: "Zap",
},
{
name: "Active Users",
value: activeUsers,
unit: "",
status: "good",
icon: "Globe",
},
{
name: "Error Rate",
value: String(errCount),
unit: " outages",
status: errCount ? "critical" : warnCount ? "warning" : "good",
icon: "Shield",
},
];
res.json({