Prettier format pending files
This commit is contained in:
parent
3b77c60d62
commit
85e96c5969
8 changed files with 467 additions and 108 deletions
|
|
@ -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 = () => (
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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: "We’ll follow up with next steps." });
|
||||
setName(""); setEmail(""); setAmount(""); setMessage(""); setAccredited(false);
|
||||
toast({
|
||||
title: "Thanks!",
|
||||
description: "We’ll 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>
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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({
|
||||
|
|
|
|||
Loading…
Reference in a new issue