Prettier format pending files

This commit is contained in:
Builder.io 2025-10-14 02:48:15 +00:00
parent 16aba004b3
commit 5e3852ecd7
17 changed files with 3112 additions and 2406 deletions

View file

@ -116,7 +116,10 @@ const App = () => (
<Route path="/docs" element={<DocsLayout />}>
<Route index element={<DocsOverview />} />
<Route path="tutorials" element={<DocsTutorials />} />
<Route path="getting-started" element={<DocsGettingStarted />} />
<Route
path="getting-started"
element={<DocsGettingStarted />}
/>
<Route path="platform" element={<DocsPlatform />} />
<Route path="api" element={<DocsApiReference />} />
<Route path="cli" element={<DocsCli />} />

View file

@ -33,8 +33,11 @@ const AdminAchievementManager = ({
targetUser,
}: AdminAchievementManagerProps) => {
const [achievements, setAchievements] = useState<AethexAchievement[]>([]);
const [userAchievements, setUserAchievements] = useState<AethexAchievement[]>([]);
const [selectedAchievementId, setSelectedAchievementId] = useState<string>("");
const [userAchievements, setUserAchievements] = useState<AethexAchievement[]>(
[],
);
const [selectedAchievementId, setSelectedAchievementId] =
useState<string>("");
const [loadingList, setLoadingList] = useState(false);
const [loadingUserAchievements, setLoadingUserAchievements] = useState(false);
const [awarding, setAwarding] = useState(false);
@ -53,21 +56,18 @@ const AdminAchievementManager = ({
}
}, []);
const loadUserAchievements = useCallback(
async (userId: string) => {
setLoadingUserAchievements(true);
try {
const list = await aethexAchievementService.getUserAchievements(userId);
setUserAchievements(list);
} catch (error) {
console.warn("Failed to load user achievements", error);
setUserAchievements([]);
} finally {
setLoadingUserAchievements(false);
}
},
[],
);
const loadUserAchievements = useCallback(async (userId: string) => {
setLoadingUserAchievements(true);
try {
const list = await aethexAchievementService.getUserAchievements(userId);
setUserAchievements(list);
} catch (error) {
console.warn("Failed to load user achievements", error);
setUserAchievements([]);
} finally {
setLoadingUserAchievements(false);
}
}, []);
useEffect(() => {
loadAchievements().catch(() => undefined);
@ -82,7 +82,10 @@ const AdminAchievementManager = ({
}, [targetUser?.id, loadUserAchievements]);
const selectedAchievement = useMemo(
() => achievements.find((achievement) => achievement.id === selectedAchievementId) ?? null,
() =>
achievements.find(
(achievement) => achievement.id === selectedAchievementId,
) ?? null,
[achievements, selectedAchievementId],
);
@ -133,7 +136,8 @@ const AdminAchievementManager = ({
if (!result) {
aethexToast.error({
title: "Activation failed",
description: "No rewards were activated. Check server logs for details.",
description:
"No rewards were activated. Check server logs for details.",
});
} else {
const awarded = result.awardedAchievementIds?.length ?? 0;
@ -177,7 +181,10 @@ const AdminAchievementManager = ({
{targetUser ? (
<div className="rounded border border-border/40 bg-background/40 p-3">
<p className="font-medium text-foreground">
{targetUser.full_name ?? targetUser.username ?? targetUser.email ?? "Unknown"}
{targetUser.full_name ??
targetUser.username ??
targetUser.email ??
"Unknown"}
</p>
<p className="text-xs text-muted-foreground">
{targetUser.email ?? "No email on record"}
@ -197,7 +204,11 @@ const AdminAchievementManager = ({
disabled={!targetUser || loadingList}
>
<SelectTrigger className="bg-background/60">
<SelectValue placeholder={loadingList ? "Loading achievements…" : "Select achievement"} />
<SelectValue
placeholder={
loadingList ? "Loading achievements…" : "Select achievement"
}
/>
</SelectTrigger>
<SelectContent>
{achievements.map((achievement) => (
@ -240,7 +251,9 @@ const AdminAchievementManager = ({
<div>
<div className="mb-2 flex items-center justify-between">
<p className="text-sm font-medium text-foreground">Achievement history</p>
<p className="text-sm font-medium text-foreground">
Achievement history
</p>
{loadingUserAchievements ? (
<span className="flex items-center gap-2 text-xs text-muted-foreground">
<Loader2 className="h-3.5 w-3.5 animate-spin" /> Loading
@ -252,16 +265,24 @@ const AdminAchievementManager = ({
{userAchievements.length ? (
<ul className="space-y-3 text-sm">
{userAchievements.map((achievement) => (
<li key={achievement.id} className="flex items-start justify-between gap-3 rounded border border-border/30 bg-background/40 p-3">
<li
key={achievement.id}
className="flex items-start justify-between gap-3 rounded border border-border/30 bg-background/40 p-3"
>
<div className="space-y-1">
<p className="font-medium text-foreground">{achievement.name}</p>
<p className="font-medium text-foreground">
{achievement.name}
</p>
{achievement.description ? (
<p className="text-xs text-muted-foreground">
{achievement.description}
</p>
) : null}
</div>
<Badge variant="outline" className="whitespace-nowrap text-xs">
<Badge
variant="outline"
className="whitespace-nowrap text-xs"
>
{achievement.xp_reward ?? 0} XP
</Badge>
</li>
@ -289,12 +310,18 @@ const AdminAchievementManager = ({
<p className="mb-1">{selectedAchievement.description}</p>
) : null}
<div className="flex flex-wrap gap-2 text-[11px] uppercase tracking-wide">
<Badge variant="outline">{selectedAchievement.xp_reward ?? 0} XP</Badge>
<Badge variant="outline">
{selectedAchievement.xp_reward ?? 0} XP
</Badge>
<Badge variant="outline">ID: {selectedAchievement.id}</Badge>
<Badge variant="outline">
Created {formatDistanceToNowStrict(new Date(selectedAchievement.created_at), {
addSuffix: true,
})}
Created{" "}
{formatDistanceToNowStrict(
new Date(selectedAchievement.created_at),
{
addSuffix: true,
},
)}
</Badge>
</div>
</div>

View file

@ -95,7 +95,11 @@ const buildProfileDraft = (profile: AethexUserProfile): ProfileDraft => ({
: "0",
});
const ensureOwnerRoles = (roles: string[], profile: AethexUserProfile | null, ownerEmail: string) => {
const ensureOwnerRoles = (
roles: string[],
profile: AethexUserProfile | null,
ownerEmail: string,
) => {
if (!profile) return roles;
if ((profile.email ?? "").toLowerCase() !== ownerEmail.toLowerCase()) {
return roles;
@ -138,21 +142,18 @@ const AdminMemberManager = ({
}
}, [profiles, selectedId, onSelectedIdChange]);
const loadRoles = useCallback(
async (id: string) => {
setLoadingRoles(true);
try {
const fetched = await aethexRoleService.getUserRoles(id);
setRoles(normalizeRoles(fetched));
} catch (error) {
console.warn("Failed to load user roles", error);
setRoles(["member"]);
} finally {
setLoadingRoles(false);
}
},
[],
);
const loadRoles = useCallback(async (id: string) => {
setLoadingRoles(true);
try {
const fetched = await aethexRoleService.getUserRoles(id);
setRoles(normalizeRoles(fetched));
} catch (error) {
console.warn("Failed to load user roles", error);
setRoles(["member"]);
} finally {
setLoadingRoles(false);
}
}, []);
useEffect(() => {
if (selectedProfile) {
@ -217,7 +218,8 @@ const AdminMemberManager = ({
console.error("Failed to set user roles", error);
aethexToast.error({
title: "Role update failed",
description: error?.message || "Unable to update roles. Check Supabase policies.",
description:
error?.message || "Unable to update roles. Check Supabase policies.",
});
} finally {
setSavingRoles(false);
@ -242,7 +244,8 @@ const AdminMemberManager = ({
updates.total_xp = Number(profileDraft.total_xp) || 0;
}
if (profileDraft.loyalty_points.trim().length) {
(updates as any).loyalty_points = Number(profileDraft.loyalty_points) || 0;
(updates as any).loyalty_points =
Number(profileDraft.loyalty_points) || 0;
}
await aethexUserService.updateProfile(selectedProfile.id, updates);
aethexToast.success({
@ -254,7 +257,9 @@ const AdminMemberManager = ({
console.error("Failed to update profile", error);
aethexToast.error({
title: "Profile update failed",
description: error?.message || "Supabase rejected the update. Review payload and RLS policies.",
description:
error?.message ||
"Supabase rejected the update. Review payload and RLS policies.",
});
} finally {
setSavingProfile(false);
@ -273,7 +278,9 @@ const AdminMemberManager = ({
<div className="flex items-center justify-between gap-2">
<div>
<CardTitle>Directory</CardTitle>
<CardDescription>Search and select members to administer.</CardDescription>
<CardDescription>
Search and select members to administer.
</CardDescription>
</div>
<Button
variant="outline"
@ -320,7 +327,9 @@ const AdminMemberManager = ({
>
<TableCell className="font-medium text-foreground/90">
<div className="flex flex-col">
<span>{profile.full_name || profile.username || "Unknown"}</span>
<span>
{profile.full_name || profile.username || "Unknown"}
</span>
<span className="text-xs text-muted-foreground">
{profile.username}
</span>
@ -339,7 +348,10 @@ const AdminMemberManager = ({
})}
{!filteredProfiles.length ? (
<TableRow>
<TableCell colSpan={3} className="text-center text-muted-foreground">
<TableCell
colSpan={3}
className="text-center text-muted-foreground"
>
No members found.
</TableCell>
</TableRow>
@ -410,7 +422,11 @@ const AdminMemberManager = ({
</SelectTrigger>
<SelectContent>
{experienceOptions.map((option) => (
<SelectItem key={option} value={option} className="capitalize">
<SelectItem
key={option}
value={option}
className="capitalize"
>
{option.replace("_", " ")}
</SelectItem>
))}
@ -432,7 +448,11 @@ const AdminMemberManager = ({
</SelectTrigger>
<SelectContent>
{userTypeOptions.map((option) => (
<SelectItem key={option} value={option} className="capitalize">
<SelectItem
key={option}
value={option}
className="capitalize"
>
{option.replace("_", " ")}
</SelectItem>
))}
@ -463,7 +483,9 @@ const AdminMemberManager = ({
value={profileDraft.level}
onChange={(event) =>
setProfileDraft((draft) =>
draft ? { ...draft, level: event.target.value } : draft,
draft
? { ...draft, level: event.target.value }
: draft,
)
}
inputMode="numeric"
@ -553,7 +575,10 @@ const AdminMemberManager = ({
variant={active ? "default" : "outline"}
size="sm"
onClick={() => handleRoleToggle(role)}
className={cn("capitalize", active && "bg-aethex-500/80")}
className={cn(
"capitalize",
active && "bg-aethex-500/80",
)}
>
{role}
</Button>
@ -587,7 +612,11 @@ const AdminMemberManager = ({
</span>
) : (
roles.map((role) => (
<Badge key={role} variant="outline" className="capitalize">
<Badge
key={role}
variant="outline"
className="capitalize"
>
{role}
</Badge>
))
@ -610,7 +639,9 @@ const AdminMemberManager = ({
<Button
size="sm"
variant="outline"
onClick={() => selectedProfile && loadRoles(selectedProfile.id)}
onClick={() =>
selectedProfile && loadRoles(selectedProfile.id)
}
disabled={loadingRoles}
>
<RefreshCw className="mr-2 h-4 w-4" /> Reload

View file

@ -33,15 +33,18 @@ export const AdminStatCard = ({
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader className="space-y-3">
<div className="flex items-center justify-between">
<CardTitle className="text-base text-foreground/90">{title}</CardTitle>
<Badge variant="outline" className="border-border/40 text-xs text-muted-foreground">
<CardTitle className="text-base text-foreground/90">
{title}
</CardTitle>
<Badge
variant="outline"
className="border-border/40 text-xs text-muted-foreground"
>
Admin metric
</Badge>
</div>
<div className="flex items-center justify-between">
<div className="text-3xl font-semibold text-gradient">
{value}
</div>
<div className="text-3xl font-semibold text-gradient">{value}</div>
<Icon className={`h-8 w-8 ${toneConfig[tone]}`} />
</div>
{trend ? (
@ -51,7 +54,9 @@ export const AdminStatCard = ({
{(description || actions) && (
<CardContent className="space-y-3">
{description ? (
<p className="text-sm text-muted-foreground leading-relaxed">{description}</p>
<p className="text-sm text-muted-foreground leading-relaxed">
{description}
</p>
) : null}
{actions}
</CardContent>

View file

@ -11,13 +11,7 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils";
import {
Heart,
MessageCircle,
Share2,
Volume2,
VolumeX,
} from "lucide-react";
import { Heart, MessageCircle, Share2, Volume2, VolumeX } from "lucide-react";
import type { FeedItem } from "@/pages/Feed";
interface FeedItemCardProps {
@ -42,7 +36,10 @@ export function FeedItemCard({
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3">
<Avatar className="h-12 w-12 ring-2 ring-aethex-500/30">
<AvatarImage src={item.authorAvatar || undefined} alt={item.authorName} />
<AvatarImage
src={item.authorAvatar || undefined}
alt={item.authorName}
/>
<AvatarFallback className="bg-aethex-500/10 text-aethex-300">
{item.authorName?.[0]?.toUpperCase() || "U"}
</AvatarFallback>
@ -132,7 +129,10 @@ export function FeedItemCard({
</Button>
</div>
<div className="flex items-center gap-2">
<Badge variant="outline" className="border-border/60 bg-background/60 text-xs uppercase tracking-wide">
<Badge
variant="outline"
className="border-border/60 bg-background/60 text-xs uppercase tracking-wide"
>
{item.mediaType === "video"
? "Video"
: item.mediaType === "image"

View file

@ -183,7 +183,10 @@ let shouldEnableSkipAgent = false;
// Example override (console): window.__AETHEX_SKIP_AGENT_CONFIG = { src: 'https://example.com/agent.js', id: '...' };
const getRuntimeConfig = () => {
try {
if (typeof window !== "undefined" && (window as any).__AETHEX_SKIP_AGENT_CONFIG) {
if (
typeof window !== "undefined" &&
(window as any).__AETHEX_SKIP_AGENT_CONFIG
) {
return (window as any).__AETHEX_SKIP_AGENT_CONFIG as {
src?: string;
id?: string;
@ -217,7 +220,10 @@ const createSkipAgentTheme = () => {
const isDocsPath = () => {
try {
return typeof window !== "undefined" && window.location.pathname.startsWith("/docs");
return (
typeof window !== "undefined" &&
window.location.pathname.startsWith("/docs")
);
} catch (e) {
return false;
}
@ -273,9 +279,9 @@ const isSkipAgentReachable = async (): Promise<boolean> => {
throw new Error(`Agent status request failed with ${response.status}`);
}
const payload = (await response.json().catch(() => null)) as
| { active?: boolean }
| null;
const payload = (await response.json().catch(() => null)) as {
active?: boolean;
} | null;
if (payload && payload.active === false) {
throw new Error("Agent reported inactive");
@ -413,7 +419,9 @@ const loadSkipAgent = async (): Promise<void> => {
}
const scriptText = await response.text();
const blobUrl = URL.createObjectURL(new Blob([scriptText], { type: "application/javascript" }));
const blobUrl = URL.createObjectURL(
new Blob([scriptText], { type: "application/javascript" }),
);
const script = document.createElement("script");
script.id = SKIP_AGENT_SCRIPT_ID;

View file

@ -171,12 +171,17 @@ export default function Admin() {
<CardHeader>
<CardTitle className="text-red-400">Access denied</CardTitle>
<CardDescription>
This panel is restricted to {ownerEmail}. If you need access, contact the site owner.
This panel is restricted to {ownerEmail}. If you need access,
contact the site owner.
</CardDescription>
</CardHeader>
<CardContent className="flex gap-2">
<Button onClick={() => navigate("/dashboard")}>Go to dashboard</Button>
<Button variant="outline" onClick={() => navigate("/support")}>Contact support</Button>
<Button onClick={() => navigate("/dashboard")}>
Go to dashboard
</Button>
<Button variant="outline" onClick={() => navigate("/support")}>
Contact support
</Button>
</CardContent>
</Card>
</div>
@ -191,7 +196,8 @@ export default function Admin() {
const selectedMember = useMemo(
() =>
managedProfiles.find((profile) => profile.id === selectedMemberId) ?? null,
managedProfiles.find((profile) => profile.id === selectedMemberId) ??
null,
[managedProfiles, selectedMemberId],
);
@ -200,7 +206,9 @@ export default function Admin() {
const featuredStudios = studios.length;
const pendingApplications = applications.filter((app) => {
const status = (app.status ?? "").toLowerCase();
return status !== "approved" && status !== "completed" && status !== "closed";
return (
status !== "approved" && status !== "completed" && status !== "closed"
);
}).length;
const overviewStats = useMemo(
@ -209,7 +217,9 @@ export default function Admin() {
title: "Total members",
value: totalMembers ? totalMembers.toString() : "—",
description: "Profiles synced from AeThex identity service.",
trend: totalMembers ? `${totalMembers} active profiles` : "Awaiting sync",
trend: totalMembers
? `${totalMembers} active profiles`
: "Awaiting sync",
icon: Users,
tone: "blue" as const,
},
@ -365,23 +375,37 @@ export default function Admin() {
<div className="container mx-auto px-4 max-w-6xl space-y-8">
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div className="space-y-2">
<h1 className="text-3xl font-bold text-gradient">Admin Control Center</h1>
<h1 className="text-3xl font-bold text-gradient">
Admin Control Center
</h1>
<p className="text-muted-foreground">
Unified oversight for AeThex operations, content, and community.
</p>
<div className="flex flex-wrap gap-2">
<Badge variant="outline" className="border-green-500/50 text-green-300">
<Badge
variant="outline"
className="border-green-500/50 text-green-300"
>
Owner
</Badge>
<Badge variant="outline" className="border-blue-500/50 text-blue-300">
<Badge
variant="outline"
className="border-blue-500/50 text-blue-300"
>
Admin
</Badge>
<Badge variant="outline" className="border-purple-500/50 text-purple-300">
<Badge
variant="outline"
className="border-purple-500/50 text-purple-300"
>
Founder
</Badge>
</div>
<p className="text-xs text-muted-foreground">
Signed in as <span className="text-foreground">{normalizedEmail || ownerEmail}</span>
Signed in as{" "}
<span className="text-foreground">
{normalizedEmail || ownerEmail}
</span>
</p>
</div>
<div className="flex flex-wrap gap-2">
@ -391,11 +415,17 @@ export default function Admin() {
<Button variant="outline" onClick={() => navigate("/profile")}>
Profile
</Button>
<Button onClick={() => setActiveTab("content")}>Create update</Button>
<Button onClick={() => setActiveTab("content")}>
Create update
</Button>
</div>
</div>
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
<Tabs
value={activeTab}
onValueChange={setActiveTab}
className="space-y-6"
>
<TabsList className="w-full justify-start gap-2 overflow-x-auto border border-border/40 bg-background/40 px-1 py-1 backdrop-blur">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="content">Content</TabsTrigger>
@ -425,23 +455,31 @@ export default function Admin() {
<Command className="h-5 w-5 text-aethex-300" />
<CardTitle>Quick actions</CardTitle>
</div>
<CardDescription>Launch frequent administrative workflows.</CardDescription>
<CardDescription>
Launch frequent administrative workflows.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-3">
{quickActions.map(({ label, description, icon: ActionIcon, action }) => (
<button
key={label}
type="button"
onClick={action}
className="group flex items-start gap-3 rounded-lg border border-border/30 bg-background/40 px-4 py-3 text-left transition hover:border-aethex-400/60 hover:bg-background/60"
>
<ActionIcon className="mt-0.5 h-5 w-5 text-aethex-400 transition group-hover:text-aethex-200" />
<div className="space-y-1">
<p className="font-medium text-foreground">{label}</p>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
</button>
))}
{quickActions.map(
({ label, description, icon: ActionIcon, action }) => (
<button
key={label}
type="button"
onClick={action}
className="group flex items-start gap-3 rounded-lg border border-border/30 bg-background/40 px-4 py-3 text-left transition hover:border-aethex-400/60 hover:bg-background/60"
>
<ActionIcon className="mt-0.5 h-5 w-5 text-aethex-400 transition group-hover:text-aethex-200" />
<div className="space-y-1">
<p className="font-medium text-foreground">
{label}
</p>
<p className="text-sm text-muted-foreground">
{description}
</p>
</div>
</button>
),
)}
</CardContent>
</Card>
@ -451,21 +489,37 @@ export default function Admin() {
<Shield className="h-5 w-5 text-green-400" />
<CardTitle>Access control</CardTitle>
</div>
<CardDescription>Owner-only access enforced via Supabase roles.</CardDescription>
<CardDescription>
Owner-only access enforced via Supabase roles.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3 text-sm text-muted-foreground">
<ul className="space-y-2 leading-relaxed">
<li>
Owner email: <span className="text-foreground">{ownerEmail}</span>
Owner email:{" "}
<span className="text-foreground">{ownerEmail}</span>
</li>
<li>
Roles are provisioned automatically on owner sign-in.
</li>
<li>
Grant additional admins by updating Supabase role
assignments.
</li>
<li>Roles are provisioned automatically on owner sign-in.</li>
<li>Grant additional admins by updating Supabase role assignments.</li>
</ul>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => setActiveTab("community")}>
<Button
variant="outline"
size="sm"
onClick={() => setActiveTab("community")}
>
View members
</Button>
<Button variant="outline" size="sm" onClick={() => navigate("/support")}>
<Button
variant="outline"
size="sm"
onClick={() => navigate("/support")}
>
Contact support
</Button>
</div>
@ -482,12 +536,18 @@ export default function Admin() {
<CardTitle>Content overview</CardTitle>
</div>
<CardDescription>
{publishedPosts} published {publishedPosts === 1 ? "post" : "posts"} · {loadingPosts ? "refreshing content…" : "latest Supabase sync"}
{publishedPosts} published{" "}
{publishedPosts === 1 ? "post" : "posts"} ·{" "}
{loadingPosts
? "refreshing content…"
: "latest Supabase sync"}
</CardDescription>
</CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-2">
<p>
Drafts and announcements appear instantly on the public blog after saving. Use scheduled releases for major updates and keep thumbnails optimised for 1200×630.
Drafts and announcements appear instantly on the public blog
after saving. Use scheduled releases for major updates and
keep thumbnails optimised for 1200×630.
</p>
</CardContent>
</Card>
@ -498,7 +558,9 @@ export default function Admin() {
<PenTool className="h-5 w-5 text-aethex-400" />
<CardTitle className="text-lg">Blog posts</CardTitle>
</div>
<CardDescription>Manage blog content stored in Supabase</CardDescription>
<CardDescription>
Manage blog content stored in Supabase
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<div className="flex flex-wrap items-center gap-3">
@ -538,7 +600,8 @@ export default function Admin() {
{blogPosts.length === 0 && (
<p className="text-sm text-muted-foreground">
No posts loaded yet. Use Refresh or Add post to start managing content.
No posts loaded yet. Use Refresh or Add post to start
managing content.
</p>
)}
@ -680,14 +743,28 @@ export default function Admin() {
<UserCog className="h-5 w-5 text-teal-300" />
<CardTitle>Community actions</CardTitle>
</div>
<CardDescription>Grow the network and celebrate contributors.</CardDescription>
<CardDescription>
Grow the network and celebrate contributors.
</CardDescription>
</CardHeader>
<CardContent className="flex flex-wrap gap-2">
<Button size="sm" onClick={() => navigate("/community")}>Open community hub</Button>
<Button size="sm" variant="outline" onClick={() => navigate("/mentorship")}>
<Button size="sm" onClick={() => navigate("/community")}>
Open community hub
</Button>
<Button
size="sm"
variant="outline"
onClick={() => navigate("/mentorship")}
>
Manage mentorships
</Button>
<Button size="sm" variant="outline" onClick={() => navigate("/support")}>Support queue</Button>
<Button
size="sm"
variant="outline"
onClick={() => navigate("/support")}
>
Support queue
</Button>
</CardContent>
</Card>
</TabsContent>
@ -700,7 +777,9 @@ export default function Admin() {
<Settings className="h-5 w-5 text-yellow-300" />
<CardTitle>Featured studios</CardTitle>
</div>
<CardDescription>Control studios highlighted across AeThex experiences.</CardDescription>
<CardDescription>
Control studios highlighted across AeThex experiences.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{studios.map((s, i) => (
@ -762,7 +841,9 @@ export default function Admin() {
<Button
size="sm"
variant="outline"
onClick={() => setStudios(studios.filter((_, idx) => idx !== i))}
onClick={() =>
setStudios(studios.filter((_, idx) => idx !== i))
}
>
Remove
</Button>
@ -773,7 +854,9 @@ export default function Admin() {
<Button
size="sm"
variant="outline"
onClick={() => setStudios([...studios, { name: "New Studio" }])}
onClick={() =>
setStudios([...studios, { name: "New Studio" }])
}
>
Add studio
</Button>
@ -788,12 +871,14 @@ export default function Admin() {
if (!resp.ok) {
aethexToast.error({
title: "Save failed",
description: "Unable to persist featured studios.",
description:
"Unable to persist featured studios.",
});
} else {
aethexToast.success({
title: "Studios saved",
description: "Featured studios updated successfully.",
description:
"Featured studios updated successfully.",
});
}
}}
@ -810,7 +895,9 @@ export default function Admin() {
<ClipboardList className="h-5 w-5 text-sky-300" />
<CardTitle>Project applications</CardTitle>
</div>
<CardDescription>Review collaboration requests and prioritize approvals.</CardDescription>
<CardDescription>
Review collaboration requests and prioritize approvals.
</CardDescription>
</CardHeader>
<CardContent className="space-y-3 text-sm text-muted-foreground">
<div className="flex flex-wrap items-center justify-between gap-2">
@ -847,7 +934,9 @@ export default function Admin() {
>
<div className="flex flex-wrap items-center justify-between gap-2">
<p className="font-medium text-foreground">
{app.applicant_name || app.applicant_email || "Unknown applicant"}
{app.applicant_name ||
app.applicant_email ||
"Unknown applicant"}
</p>
<Badge variant="outline" className="capitalize">
{(app.status ?? "pending").toLowerCase()}
@ -858,14 +947,18 @@ export default function Admin() {
</p>
{app.created_at ? (
<p className="text-[11px] text-muted-foreground/80">
Submitted {new Date(app.created_at).toLocaleString()}
Submitted{" "}
{new Date(app.created_at).toLocaleString()}
</p>
) : null}
</div>
))}
</div>
) : (
<p>No applications on file. Encourage partners to apply via briefs.</p>
<p>
No applications on file. Encourage partners to apply via
briefs.
</p>
)}
</CardContent>
</Card>
@ -876,7 +969,9 @@ export default function Admin() {
<Activity className="h-5 w-5 text-orange-300" />
<CardTitle>System status</CardTitle>
</div>
<CardDescription>Auth, database, and realtime services.</CardDescription>
<CardDescription>
Auth, database, and realtime services.
</CardDescription>
</CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-1">
<p>Auth: Operational</p>
@ -891,11 +986,16 @@ export default function Admin() {
<UserCog className="h-5 w-5 text-teal-300" />
<CardTitle>Your account</CardTitle>
</div>
<CardDescription>Owner privileges are active.</CardDescription>
<CardDescription>
Owner privileges are active.
</CardDescription>
</CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-2">
<p>Signed in as {user.email}.</p>
<p>You have full administrative access across AeThex services.</p>
<p>
You have full administrative access across AeThex
services.
</p>
</CardContent>
</Card>
</div>

File diff suppressed because it is too large Load diff

View file

@ -84,9 +84,9 @@ export default function Feed() {
const [isLoading, setIsLoading] = useState(true);
const [following, setFollowing] = useState<string[]>([]);
const [items, setItems] = useState<FeedItem[]>([]);
const [activeFilter, setActiveFilter] = useState<"all" | "following" | "trending">(
"all",
);
const [activeFilter, setActiveFilter] = useState<
"all" | "following" | "trending"
>("all");
const fetchFeed = useCallback(async () => {
setIsLoading(true);
@ -202,11 +202,14 @@ export default function Feed() {
const filteredItems = useMemo(() => {
if (activeFilter === "following") {
return items.filter(
(item) => isFollowingAuthor(item.authorId) || item.authorId === user?.id,
(item) =>
isFollowingAuthor(item.authorId) || item.authorId === user?.id,
);
}
if (activeFilter === "trending") {
return [...items].sort((a, b) => b.likes + b.comments - (a.likes + a.comments));
return [...items].sort(
(a, b) => b.likes + b.comments - (a.likes + a.comments),
);
}
return items;
}, [activeFilter, isFollowingAuthor, items, user?.id]);
@ -259,7 +262,8 @@ export default function Feed() {
const totalEngagement = useMemo(
() =>
items.reduce(
(acc, item) => acc + (Number(item.likes) || 0) + (Number(item.comments) || 0),
(acc, item) =>
acc + (Number(item.likes) || 0) + (Number(item.comments) || 0),
0,
),
[items],
@ -271,7 +275,10 @@ export default function Feed() {
}, [items.length, totalEngagement]);
const handleScrollToComposer = useCallback(() => {
composerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" });
composerRef.current?.scrollIntoView({
behavior: "smooth",
block: "center",
});
}, []);
const handleManualRefresh = useCallback(() => {
@ -280,7 +287,11 @@ export default function Feed() {
if (loading || (isLoading && items.length === 0)) {
return (
<LoadingScreen message="Loading your feed..." showProgress duration={1000} />
<LoadingScreen
message="Loading your feed..."
showProgress
duration={1000}
/>
);
}
@ -297,11 +308,15 @@ export default function Feed() {
Community Pulse
</h1>
<p className="mt-2 max-w-2xl text-sm text-muted-foreground sm:text-base">
Discover new creations, amplify your voice, and engage with the AeThex community in real time.
Discover new creations, amplify your voice, and engage with
the AeThex community in real time.
</p>
</div>
<div className="flex flex-wrap items-center gap-3">
<Badge variant="outline" className="border-aethex-400/60 bg-aethex-400/10 text-aethex-100">
<Badge
variant="outline"
className="border-aethex-400/60 bg-aethex-400/10 text-aethex-100"
>
Live updates enabled
</Badge>
<Button
@ -316,28 +331,26 @@ export default function Feed() {
</div>
<div className="flex flex-wrap items-center gap-2">
{(
[
{
key: "all" as const,
label: "All stories",
icon: Sparkles,
description: "Latest community activity",
},
{
key: "following" as const,
label: "Following",
icon: Users,
description: "People you follow",
},
{
key: "trending" as const,
label: "Trending",
icon: Flame,
description: "Most engagement",
},
]
).map(({ key, label, icon: Icon, description }) => (
{[
{
key: "all" as const,
label: "All stories",
icon: Sparkles,
description: "Latest community activity",
},
{
key: "following" as const,
label: "Following",
icon: Users,
description: "People you follow",
},
{
key: "trending" as const,
label: "Trending",
icon: Flame,
description: "Most engagement",
},
].map(({ key, label, icon: Icon, description }) => (
<Button
key={key}
variant={activeFilter === key ? "default" : "outline"}
@ -352,7 +365,9 @@ export default function Feed() {
>
<Icon className="h-4 w-4" />
<span className="font-medium">{label}</span>
<span className="hidden text-xs sm:inline">{description}</span>
<span className="hidden text-xs sm:inline">
{description}
</span>
</Button>
))}
</div>
@ -368,9 +383,12 @@ export default function Feed() {
>
<div className="flex items-start justify-between gap-3">
<div>
<h2 className="text-lg font-semibold text-foreground">Share something new</h2>
<h2 className="text-lg font-semibold text-foreground">
Share something new
</h2>
<p className="text-sm text-muted-foreground">
Post updates, showcase progress, or spark a conversation with the community.
Post updates, showcase progress, or spark a conversation
with the community.
</p>
</div>
<Button
@ -386,7 +404,8 @@ export default function Feed() {
<div className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-border/30 bg-background/60 p-4 text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<Sparkles className="h-4 w-4 text-aethex-300" />
Your post is shared instantly with followers and the broader community.
Your post is shared instantly with followers and the broader
community.
</div>
<button
type="button"
@ -403,7 +422,8 @@ export default function Feed() {
<CardHeader>
<CardTitle className="text-xl">No stories found</CardTitle>
<CardDescription>
Try switching filters or follow more creators to personalize your feed.
Try switching filters or follow more creators to
personalize your feed.
</CardDescription>
</CardHeader>
<CardContent className="pt-0">
@ -434,7 +454,9 @@ export default function Feed() {
<aside className="space-y-6">
<Card className="rounded-3xl border-border/40 bg-background/70 shadow-xl backdrop-blur-lg">
<CardHeader>
<CardTitle className="text-lg">Your community snapshot</CardTitle>
<CardTitle className="text-lg">
Your community snapshot
</CardTitle>
<CardDescription>
Track how your network is evolving at a glance.
</CardDescription>
@ -442,25 +464,33 @@ export default function Feed() {
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-3 text-sm">
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
<p className="text-xs uppercase text-muted-foreground">Stories today</p>
<p className="text-xs uppercase text-muted-foreground">
Stories today
</p>
<p className="mt-1 text-2xl font-semibold text-foreground">
{items.length.toLocaleString()}
</p>
</div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
<p className="text-xs uppercase text-muted-foreground">Creators you follow</p>
<p className="text-xs uppercase text-muted-foreground">
Creators you follow
</p>
<p className="mt-1 text-2xl font-semibold text-foreground">
{following.length.toLocaleString()}
</p>
</div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
<p className="text-xs uppercase text-muted-foreground">Community reactions</p>
<p className="text-xs uppercase text-muted-foreground">
Community reactions
</p>
<p className="mt-1 text-2xl font-semibold text-foreground">
{totalEngagement.toLocaleString()}
</p>
</div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
<p className="text-xs uppercase text-muted-foreground">Avg. engagement</p>
<p className="text-xs uppercase text-muted-foreground">
Avg. engagement
</p>
<p className="mt-1 text-2xl font-semibold text-foreground">
{averageEngagement.toLocaleString()}
</p>
@ -492,7 +522,8 @@ export default function Feed() {
<CardContent className="space-y-3">
{trendingTopics.length === 0 ? (
<p className="text-sm text-muted-foreground">
Start a conversation by adding hashtags like #gamedev or #design to your next post.
Start a conversation by adding hashtags like #gamedev or
#design to your next post.
</p>
) : (
trendingTopics.map((topic, index) => (
@ -505,7 +536,9 @@ export default function Feed() {
{index + 1}
</span>
<div>
<p className="font-medium text-foreground">{topic.topic}</p>
<p className="font-medium text-foreground">
{topic.topic}
</p>
<p className="text-xs text-muted-foreground">
{topic.count.toLocaleString()} mentions today
</p>
@ -535,7 +568,8 @@ export default function Feed() {
<CardContent className="space-y-3">
{suggestedCreators.length === 0 ? (
<p className="text-sm text-muted-foreground">
You are up to date with the creators you follow. Engage with new posts to unlock more suggestions.
You are up to date with the creators you follow. Engage
with new posts to unlock more suggestions.
</p>
) : (
suggestedCreators.map((creator) => (
@ -554,9 +588,12 @@ export default function Feed() {
</AvatarFallback>
</Avatar>
<div>
<p className="font-medium text-foreground">{creator.name}</p>
<p className="font-medium text-foreground">
{creator.name}
</p>
<p className="text-xs text-muted-foreground">
{creator.posts.toLocaleString()} posts · {creator.likes.toLocaleString()} reactions
{creator.posts.toLocaleString()} posts ·{" "}
{creator.likes.toLocaleString()} reactions
</p>
</div>
</div>

View file

@ -69,11 +69,13 @@ const webhookTopics = [
},
{
event: "incident.opened",
description: "Raised when monitoring detects outages or SLA breaches in production environments.",
description:
"Raised when monitoring detects outages or SLA breaches in production environments.",
},
{
event: "member.invited",
description: "Notify downstream systems when a collaborator invitation is created or accepted.",
description:
"Notify downstream systems when a collaborator invitation is created or accepted.",
},
];
@ -108,11 +110,13 @@ export default function DocsApiReference() {
<ServerCog className="mr-2 h-3 w-3" />
API Reference
</Badge>
<h2 className="text-3xl font-semibold text-white">Integrate programmatically with the AeThex API</h2>
<h2 className="text-3xl font-semibold text-white">
Integrate programmatically with the AeThex API
</h2>
<p className="text-gray-300 max-w-3xl">
The REST API exposes every core capability of the AeThex platform. Authenticate with OAuth 2.1 or
personal access tokens, call idempotent endpoints, and subscribe to webhooks to react to changes in real
time.
The REST API exposes every core capability of the AeThex platform.
Authenticate with OAuth 2.1 or personal access tokens, call idempotent
endpoints, and subscribe to webhooks to react to changes in real time.
</p>
</section>
@ -125,16 +129,20 @@ export default function DocsApiReference() {
</CardTitle>
</CardHeader>
<CardContent className="space-y-4 text-gray-300">
<p>Use the OAuth client credentials grant for service-to-service integrations:</p>
<p>
Use the OAuth client credentials grant for service-to-service
integrations:
</p>
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-sm text-blue-200">
{`curl -X POST https://api.aethex.dev/v1/auth/token \
{`curl -X POST https://api.aethex.dev/v1/auth/token \
-u CLIENT_ID:CLIENT_SECRET \
-d "grant_type=client_credentials" \
-d "scope=projects:read deployments:write"`}
</pre>
<p>
Prefer user-scoped access? Direct builders through the hosted OAuth consent screen and exchange their
authorization code using the same endpoint.
Prefer user-scoped access? Direct builders through the hosted
OAuth consent screen and exchange their authorization code using
the same endpoint.
</p>
</CardContent>
</Card>
@ -148,10 +156,11 @@ export default function DocsApiReference() {
</CardHeader>
<CardContent className="space-y-4 text-gray-300">
<p className="text-gray-300">
Call the Projects endpoint with your Bearer token and inspect pagination headers for large result sets.
Call the Projects endpoint with your Bearer token and inspect
pagination headers for large result sets.
</p>
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-sm text-teal-200">
{`fetch("https://api.aethex.dev/v1/projects?page=1&limit=25", {
{`fetch("https://api.aethex.dev/v1/projects?page=1&limit=25", {
headers: {
Authorization: "Bearer ${TOKEN}",
"AeThex-Environment": "production",
@ -162,9 +171,16 @@ export default function DocsApiReference() {
});`}
</pre>
<p>
Responses include <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-RateLimit-Remaining</code>
and <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-Request-ID</code> headers. Share the
request ID when contacting support for faster triage.
Responses include{" "}
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
X-RateLimit-Remaining
</code>
and{" "}
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
X-Request-ID
</code>{" "}
headers. Share the request ID when contacting support for faster
triage.
</p>
</CardContent>
</Card>
@ -189,7 +205,9 @@ export default function DocsApiReference() {
{apiEndpoints.map((endpoint) => (
<TableRow key={`${endpoint.method}-${endpoint.path}`}>
<TableCell>
<Badge className="bg-blue-600/30 text-blue-100">{endpoint.method}</Badge>
<Badge className="bg-blue-600/30 text-blue-100">
{endpoint.method}
</Badge>
</TableCell>
<TableCell className="font-mono text-purple-200">
{endpoint.path}
@ -201,7 +219,8 @@ export default function DocsApiReference() {
))}
</TableBody>
<TableCaption>
Need deeper coverage? The JavaScript SDK ships with typed request builders for every endpoint.
Need deeper coverage? The JavaScript SDK ships with typed
request builders for every endpoint.
</TableCaption>
</Table>
</CardContent>
@ -223,12 +242,23 @@ export default function DocsApiReference() {
className="rounded-lg border border-slate-700 bg-slate-950/40 p-4"
>
<p className="font-mono text-sm text-blue-300">{topic.event}</p>
<p className="text-gray-300 text-sm mt-2">{topic.description}</p>
<p className="text-gray-300 text-sm mt-2">
{topic.description}
</p>
</div>
))}
<p className="text-gray-400 text-sm">
Configure webhook destinations and signing secrets from the <Link to="/dashboard" className="text-blue-300 hover:text-blue-200">dashboard</Link>.
Verify requests with the <code className="rounded bg-black/40 px-2 py-1 text-blue-200">AeThex-Signature</code>
Configure webhook destinations and signing secrets from the{" "}
<Link
to="/dashboard"
className="text-blue-300 hover:text-blue-200"
>
dashboard
</Link>
. Verify requests with the{" "}
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
AeThex-Signature
</code>
header to guarantee authenticity.
</p>
</CardContent>
@ -243,13 +273,22 @@ export default function DocsApiReference() {
</CardHeader>
<CardContent className="space-y-4 text-gray-300">
<p className="text-gray-300">
All endpoints are idempotent where appropriate and support conditional requests via the
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">If-Match</code> header.
All endpoints are idempotent where appropriate and support
conditional requests via the
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
If-Match
</code>{" "}
header.
</p>
<div className="grid gap-3">
{errorExamples.map((error) => (
<div key={error.code} className="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-950/60 p-3">
<Badge className="bg-red-600/30 text-red-200">{error.code}</Badge>
<div
key={error.code}
className="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-950/60 p-3"
>
<Badge className="bg-red-600/30 text-red-200">
{error.code}
</Badge>
<div>
<p className="text-white font-medium">{error.label}</p>
<p className="text-sm text-gray-400">{error.hint}</p>
@ -258,20 +297,28 @@ export default function DocsApiReference() {
))}
</div>
<p className="text-gray-300">
Monitor rate-limit headers and retry using an exponential backoff strategy. Persistent errors can be
escalated to the AeThex support team with the failing request ID.
Monitor rate-limit headers and retry using an exponential backoff
strategy. Persistent errors can be escalated to the AeThex support
team with the failing request ID.
</p>
</CardContent>
</Card>
</section>
<section id="resources" className="rounded-2xl border border-blue-500/40 bg-blue-900/20 p-8">
<section
id="resources"
className="rounded-2xl border border-blue-500/40 bg-blue-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">Ship production-ready integrations</h3>
<h3 className="text-2xl font-semibold text-white">
Ship production-ready integrations
</h3>
<p className="text-gray-300 max-w-2xl">
Combine the REST API with event webhooks for a full-duplex integration pattern. Use the official
TypeScript SDK for typed helpers or generate your own client with the published OpenAPI schema.
Combine the REST API with event webhooks for a full-duplex
integration pattern. Use the official TypeScript SDK for typed
helpers or generate your own client with the published OpenAPI
schema.
</p>
</div>
<div className="flex gap-3">
@ -281,7 +328,12 @@ export default function DocsApiReference() {
OpenAPI explorer
</Link>
</Button>
<Button asChild variant="outline" size="lg" className="border-blue-400/60 text-blue-200">
<Button
asChild
variant="outline"
size="lg"
className="border-blue-400/60 text-blue-200"
>
<Link to="/docs/examples">
<AlertTriangle className="mr-2 h-5 w-5" />
See implementation patterns

View file

@ -42,7 +42,8 @@ const commands = [
{
command: "aethex deploy",
description: "Build and deploy the current project",
usage: "Runs tests, packages artifacts, and promotes to the target environment",
usage:
"Runs tests, packages artifacts, and promotes to the target environment",
},
{
command: "aethex env pull",
@ -87,11 +88,14 @@ export default function DocsCli() {
<Terminal className="mr-2 h-3 w-3" />
CLI Tools
</Badge>
<h2 className="text-3xl font-semibold text-white">Operate AeThex from the command line</h2>
<h2 className="text-3xl font-semibold text-white">
Operate AeThex from the command line
</h2>
<p className="text-gray-300 max-w-3xl">
The AeThex CLI automates local development, environment management, and production deployments. It is
built with stability in mind, featuring transactional deploys, shell-friendly output, and native support for
Linux, macOS, and Windows.
The AeThex CLI automates local development, environment management,
and production deployments. It is built with stability in mind,
featuring transactional deploys, shell-friendly output, and native
support for Linux, macOS, and Windows.
</p>
</section>
@ -113,13 +117,22 @@ export default function DocsCli() {
<TableBody>
{commands.map((item) => (
<TableRow key={item.command}>
<TableCell className="font-mono text-purple-200">{item.command}</TableCell>
<TableCell className="text-gray-300">{item.description}</TableCell>
<TableCell className="text-gray-400 text-sm">{item.usage}</TableCell>
<TableCell className="font-mono text-purple-200">
{item.command}
</TableCell>
<TableCell className="text-gray-300">
{item.description}
</TableCell>
<TableCell className="text-gray-400 text-sm">
{item.usage}
</TableCell>
</TableRow>
))}
</TableBody>
<TableCaption>Run <code className="bg-black/40 px-1">aethex --help</code> for the full command tree.</TableCaption>
<TableCaption>
Run <code className="bg-black/40 px-1">aethex --help</code> for
the full command tree.
</TableCaption>
</Table>
</CardContent>
</Card>
@ -135,12 +148,13 @@ export default function DocsCli() {
</CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm">
<p>
Run <code className="bg-black/40 px-1">aethex dev</code> to start local services with hot reloading and the
AeThex mock identity provider. Inspect logs via the integrated tail view.
Run <code className="bg-black/40 px-1">aethex dev</code> to start
local services with hot reloading and the AeThex mock identity
provider. Inspect logs via the integrated tail view.
</p>
<p>
Use <code className="bg-black/40 px-1">aethex data seed</code> to populate sample datasets for QA or demo
accounts.
Use <code className="bg-black/40 px-1">aethex data seed</code> to
populate sample datasets for QA or demo accounts.
</p>
</CardContent>
</Card>
@ -154,12 +168,16 @@ export default function DocsCli() {
</CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm">
<p>
Synchronize configuration with <code className="bg-black/40 px-1">aethex env pull</code> or populate remote
environments from local files using <code className="bg-black/40 px-1">aethex env push</code>.
Synchronize configuration with{" "}
<code className="bg-black/40 px-1">aethex env pull</code> or
populate remote environments from local files using{" "}
<code className="bg-black/40 px-1">aethex env push</code>.
</p>
<p>
Inspect secrets securely through <code className="bg-black/40 px-1">aethex env inspect</code>. Output is redacted
by default, keeping sensitive data safe in terminal logs.
Inspect secrets securely through{" "}
<code className="bg-black/40 px-1">aethex env inspect</code>.
Output is redacted by default, keeping sensitive data safe in
terminal logs.
</p>
</CardContent>
</Card>
@ -173,11 +191,13 @@ export default function DocsCli() {
</CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm">
<p>
Each deployment is transactional: a failure during build or migration automatically halts promotion and
emits alerts to subscribed channels.
Each deployment is transactional: a failure during build or
migration automatically halts promotion and emits alerts to
subscribed channels.
</p>
<p>
Gates such as quality checks or approvals can be configured in <code className="bg-black/40 px-1">aethex.config.ts</code>
Gates such as quality checks or approvals can be configured in{" "}
<code className="bg-black/40 px-1">aethex.config.ts</code>
and enforced automatically by the CLI.
</p>
</CardContent>
@ -187,13 +207,17 @@ export default function DocsCli() {
<section id="automation" className="space-y-4">
<div className="flex items-center gap-3">
<CloudLightning className="h-6 w-6 text-amber-300" />
<h3 className="text-2xl font-semibold text-white">Automate everything</h3>
<h3 className="text-2xl font-semibold text-white">
Automate everything
</h3>
</div>
<div className="grid gap-4 md:grid-cols-2">
{automationTips.map((tip) => (
<Card key={tip.title} className="bg-slate-900/60 border-slate-700">
<CardHeader>
<CardTitle className="text-white text-lg">{tip.title}</CardTitle>
<CardTitle className="text-white text-lg">
{tip.title}
</CardTitle>
</CardHeader>
<CardContent>
<CardDescription className="text-sm text-gray-300">
@ -205,29 +229,49 @@ export default function DocsCli() {
</div>
</section>
<section id="security" className="rounded-2xl border border-amber-500/40 bg-amber-900/20 p-8">
<section
id="security"
className="rounded-2xl border border-amber-500/40 bg-amber-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">Stay safe in production</h3>
<h3 className="text-2xl font-semibold text-white">
Stay safe in production
</h3>
<p className="text-gray-200 max-w-2xl text-sm">
The CLI signs every build artifact and enforces checksums during deployment. Combine this with RBAC token
policies to guarantee only trusted pipelines can trigger releases.
The CLI signs every build artifact and enforces checksums during
deployment. Combine this with RBAC token policies to guarantee
only trusted pipelines can trigger releases.
</p>
</div>
<div className="flex flex-col gap-3 sm:flex-row">
<Button asChild size="lg" className="bg-amber-500 hover:bg-amber-400 text-black">
<Button
asChild
size="lg"
className="bg-amber-500 hover:bg-amber-400 text-black"
>
<Link to="/docs/getting-started">
<ArrowRight className="mr-2 h-5 w-5" />
Return to setup guide
</Link>
</Button>
<Button asChild variant="outline" size="lg" className="border-amber-300/60 text-amber-100">
<Button
asChild
variant="outline"
size="lg"
className="border-amber-300/60 text-amber-100"
>
<Link to="/support">
<Shield className="mr-2 h-5 w-5" />
Security best practices
</Link>
</Button>
<Button asChild variant="ghost" size="lg" className="text-amber-100 hover:text-amber-50">
<Button
asChild
variant="ghost"
size="lg"
className="text-amber-100 hover:text-amber-50"
>
<Link to="/docs/platform">
Discover platform features
<ArrowRight className="ml-2 h-5 w-5" />

View file

@ -142,19 +142,27 @@ export default function DocsExamples() {
<Blocks className="mr-2 h-3 w-3" />
Examples & Templates
</Badge>
<h2 className="text-3xl font-semibold text-white">Production-ready patterns you can copy</h2>
<h2 className="text-3xl font-semibold text-white">
Production-ready patterns you can copy
</h2>
<p className="text-gray-300 max-w-3xl">
Explore curated examples covering backend services, realtime overlays, automation scripts, and workflow
integrations. Each project includes detailed READMEs, infrastructure diagrams, and deployment runbooks.
Explore curated examples covering backend services, realtime overlays,
automation scripts, and workflow integrations. Each project includes
detailed READMEs, infrastructure diagrams, and deployment runbooks.
</p>
</section>
<section id="code-gallery" className="grid gap-6 lg:grid-cols-3">
{exampleSnippets.map((snippet) => (
<Card key={snippet.title} className="bg-slate-900/60 border-slate-700">
<Card
key={snippet.title}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader className="space-y-2">
<div className="flex items-center justify-between">
<CardTitle className="text-white text-lg">{snippet.title}</CardTitle>
<CardTitle className="text-white text-lg">
{snippet.title}
</CardTitle>
<Badge variant="outline">{snippet.language}</Badge>
</div>
<CardDescription className="text-gray-300 text-sm">
@ -165,7 +173,10 @@ export default function DocsExamples() {
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-xs text-emerald-200 overflow-x-auto">
<code>{snippet.code}</code>
</pre>
<Button asChild className="w-full bg-emerald-500 hover:bg-emerald-400 text-black">
<Button
asChild
className="w-full bg-emerald-500 hover:bg-emerald-400 text-black"
>
<Link to={snippet.href} target="_blank" rel="noreferrer">
<Github className="mr-2 h-4 w-4" />
Open repository
@ -180,13 +191,17 @@ export default function DocsExamples() {
<section id="templates" className="space-y-4">
<div className="flex items-center gap-3">
<Flame className="h-6 w-6 text-emerald-300" />
<h3 className="text-2xl font-semibold text-white">Deploy faster with templates</h3>
<h3 className="text-2xl font-semibold text-white">
Deploy faster with templates
</h3>
</div>
<div className="grid gap-4 md:grid-cols-2">
{integrationIdeas.map((idea) => (
<Card key={idea.title} className="bg-slate-900/60 border-slate-700">
<CardHeader>
<CardTitle className="text-white text-base">{idea.title}</CardTitle>
<CardTitle className="text-white text-base">
{idea.title}
</CardTitle>
</CardHeader>
<CardContent>
<CardDescription className="text-gray-300 text-sm mb-4">
@ -208,23 +223,38 @@ export default function DocsExamples() {
</div>
</section>
<section id="share" className="rounded-2xl border border-emerald-500/40 bg-emerald-900/20 p-8">
<section
id="share"
className="rounded-2xl border border-emerald-500/40 bg-emerald-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">Share what you build</h3>
<h3 className="text-2xl font-semibold text-white">
Share what you build
</h3>
<p className="text-gray-200 max-w-2xl text-sm">
Publish your own templates or improvements by opening a pull request to the public AeThex examples
repository. Every accepted contribution is highlighted in the monthly creator spotlight.
Publish your own templates or improvements by opening a pull
request to the public AeThex examples repository. Every accepted
contribution is highlighted in the monthly creator spotlight.
</p>
</div>
<div className="flex gap-3">
<Button asChild size="lg" className="bg-emerald-500 hover:bg-emerald-400 text-black">
<Button
asChild
size="lg"
className="bg-emerald-500 hover:bg-emerald-400 text-black"
>
<Link to="https://github.com/aethex/examples" target="_blank">
<Code2 className="mr-2 h-5 w-5" />
Contribute on GitHub
</Link>
</Button>
<Button asChild variant="outline" size="lg" className="border-emerald-300/60 text-emerald-100">
<Button
asChild
variant="outline"
size="lg"
className="border-emerald-300/60 text-emerald-100"
>
<Link to="/community">
<Share2 className="mr-2 h-5 w-5" />
Showcase to the community
@ -234,16 +264,27 @@ export default function DocsExamples() {
</div>
</section>
<section id="services" className="rounded-2xl border border-emerald-500/20 bg-slate-900/80 p-8">
<section
id="services"
className="rounded-2xl border border-emerald-500/20 bg-slate-900/80 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h4 className="text-xl font-semibold text-white">Need a custom integration?</h4>
<h4 className="text-xl font-semibold text-white">
Need a custom integration?
</h4>
<p className="text-gray-300 text-sm">
Our professional services team partners with studios to build tailored pipelines, analytics dashboards,
and automation workflows on top of AeThex.
Our professional services team partners with studios to build
tailored pipelines, analytics dashboards, and automation workflows
on top of AeThex.
</p>
</div>
<Button asChild variant="outline" size="lg" className="border-emerald-300/60 text-emerald-100">
<Button
asChild
variant="outline"
size="lg"
className="border-emerald-300/60 text-emerald-100"
>
<Link to="/consulting">
<Globe className="mr-2 h-5 w-5" />
Talk to AeThex consultants

View file

@ -153,27 +153,32 @@ const platformHighlights = [
const explorationLinks = [
{
title: "Platform Walkthrough",
description: "Tour the dashboard, notification center, and collaboration features.",
description:
"Tour the dashboard, notification center, and collaboration features.",
href: "/dashboard",
},
{
title: "Platform documentation",
description: "Share the high-level platform overview with non-technical teammates.",
description:
"Share the high-level platform overview with non-technical teammates.",
href: "/docs/platform",
},
{
title: "API Reference",
description: "Review authentication flows, REST endpoints, and webhook schemas.",
description:
"Review authentication flows, REST endpoints, and webhook schemas.",
href: "/docs/api",
},
{
title: "Tutorial Library",
description: "Follow guided builds for matchmaking services, player analytics, and live events.",
description:
"Follow guided builds for matchmaking services, player analytics, and live events.",
href: "/docs/tutorials",
},
{
title: "Community Support",
description: "Ask questions, share templates, and pair up with mentors in the public forums.",
description:
"Ask questions, share templates, and pair up with mentors in the public forums.",
href: "/community",
},
{
@ -195,31 +200,40 @@ export default function DocsGettingStarted() {
Launch your first AeThex project in under 30 minutes
</h2>
<p className="text-gray-300 max-w-3xl">
This guide walks through the minimum setup required to ship a production-ready AeThex application.
Complete the prerequisites, initialize a workspace with the CLI, and review the deployment checklist
before inviting collaborators. Use the platform highlights below to brief product, community, and live-ops
teams on everything available beyond deployment.
This guide walks through the minimum setup required to ship a
production-ready AeThex application. Complete the prerequisites,
initialize a workspace with the CLI, and review the deployment
checklist before inviting collaborators. Use the platform highlights
below to brief product, community, and live-ops teams on everything
available beyond deployment.
</p>
</section>
<section id="categories" className="space-y-6">
<div className="text-center space-y-2">
<h3 className="text-2xl font-semibold text-white">Documentation categories</h3>
<h3 className="text-2xl font-semibold text-white">
Documentation categories
</h3>
<p className="text-gray-300 max-w-2xl mx-auto text-sm">
Jump into the area you need most. Each category below is mirrored in Builder CMS for collaborative
editing.
Jump into the area you need most. Each category below is mirrored in
Builder CMS for collaborative editing.
</p>
</div>
<div className="grid gap-6 lg:grid-cols-2">
{docCategories.map((category) => (
<Card key={category.title} className="border-border/50 hover:border-aethex-400/40 transition-all">
<Card
key={category.title}
className="border-border/50 hover:border-aethex-400/40 transition-all"
>
<CardHeader>
<div
className={`inline-flex rounded-lg bg-gradient-to-r ${category.color} px-3 py-1 text-xs uppercase tracking-wider text-white`}
>
{category.docs} docs
</div>
<CardTitle className="text-xl text-white mt-3">{category.title}</CardTitle>
<CardTitle className="text-xl text-white mt-3">
{category.title}
</CardTitle>
<CardDescription className="text-gray-300">
{category.description}
</CardDescription>
@ -253,7 +267,12 @@ export default function DocsGettingStarted() {
{item.description}
</CardDescription>
<Button asChild variant="outline" className="justify-start">
<Link to={item.actionHref} target={item.actionHref.startsWith("http") ? "_blank" : undefined}>
<Link
to={item.actionHref}
target={
item.actionHref.startsWith("http") ? "_blank" : undefined
}
>
<ArrowRight className="mr-2 h-4 w-4" />
{item.actionLabel}
</Link>
@ -266,13 +285,18 @@ export default function DocsGettingStarted() {
<section id="platform-highlights" className="space-y-6">
<div className="flex items-center gap-3">
<LayoutDashboard className="h-6 w-6 text-purple-400" />
<h3 className="text-2xl font-semibold text-white">Explore the platform</h3>
<h3 className="text-2xl font-semibold text-white">
Explore the platform
</h3>
</div>
<div className="grid gap-6 lg:grid-cols-2">
{platformHighlights.map((item) => {
const Icon = item.icon;
return (
<Card key={item.title} className="bg-slate-900/60 border-slate-700">
<Card
key={item.title}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader>
<CardTitle className="text-white text-lg flex items-center gap-3">
<Icon className="h-5 w-5 text-purple-300" />
@ -302,7 +326,9 @@ export default function DocsGettingStarted() {
<Badge variant="outline" className="w-fit">
Step {index + 1}
</Badge>
<CardTitle className="text-white text-lg">{step.title}</CardTitle>
<CardTitle className="text-white text-lg">
{step.title}
</CardTitle>
<CardDescription className="text-gray-300">
{step.description}
</CardDescription>
@ -342,7 +368,10 @@ export default function DocsGettingStarted() {
</div>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{explorationLinks.map((link) => (
<Card key={link.title} className="bg-slate-900/60 border-slate-700 hover:border-purple-500/40 transition-colors">
<Card
key={link.title}
className="bg-slate-900/60 border-slate-700 hover:border-purple-500/40 transition-colors"
>
<CardHeader>
<CardTitle className="flex items-center justify-between text-white text-base">
{link.title}
@ -369,26 +398,42 @@ export default function DocsGettingStarted() {
</div>
</section>
<section id="deploy" className="rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8">
<section
id="deploy"
className="rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">
Ready to automate your first deployment?
</h3>
<p className="text-gray-300 max-w-2xl">
Run <code className="rounded bg-black/40 px-2 py-1 text-purple-200">aethex deploy</code> once you have
verified environment variables, migrations, and smoke tests. Ship changes with confidence knowing
guardrails are enabled by default.
Run{" "}
<code className="rounded bg-black/40 px-2 py-1 text-purple-200">
aethex deploy
</code>{" "}
once you have verified environment variables, migrations, and
smoke tests. Ship changes with confidence knowing guardrails are
enabled by default.
</p>
</div>
<div className="flex gap-3">
<Button asChild size="lg" className="bg-purple-600 hover:bg-purple-500">
<Button
asChild
size="lg"
className="bg-purple-600 hover:bg-purple-500"
>
<Link to="/docs/cli">
<Download className="mr-2 h-5 w-5" />
Review CLI commands
</Link>
</Button>
<Button asChild variant="outline" size="lg" className="border-purple-400/60 text-purple-200">
<Button
asChild
variant="outline"
size="lg"
className="border-purple-400/60 text-purple-200"
>
<Link to="/support">
<Code className="mr-2 h-5 w-5" />
Talk to an engineer

View file

@ -33,32 +33,38 @@ import {
const connectorFields = [
{
name: "key",
description: "Unique identifier referenced across dashboards, APIs, and audit logs.",
description:
"Unique identifier referenced across dashboards, APIs, and audit logs.",
defaultValue: '"analytics-segment"',
},
{
name: "category",
description: "Integration taxonomy aligned with AeThex surfaces (analytics, identity, commerce, ops).",
description:
"Integration taxonomy aligned with AeThex surfaces (analytics, identity, commerce, ops).",
defaultValue: '"analytics"',
},
{
name: "capabilities",
description: "Feature flags that unlock widgets, automation hooks, and data pipelines.",
description:
"Feature flags that unlock widgets, automation hooks, and data pipelines.",
defaultValue: "['metrics', 'webhooks']",
},
{
name: "connectionMode",
description: "Determines how credentials are managed (oauth, apiKey, managedVault).",
description:
"Determines how credentials are managed (oauth, apiKey, managedVault).",
defaultValue: '"oauth"',
},
{
name: "webhookEndpoint",
description: "Optional callback URL for outbound events delivered by AeThex.",
description:
"Optional callback URL for outbound events delivered by AeThex.",
defaultValue: '"https://app.example.com/aethex/webhooks"',
},
{
name: "uiEmbeds",
description: "Declarative config describing dashboard cards, modals, or launchers this integration renders.",
description:
"Declarative config describing dashboard cards, modals, or launchers this integration renders.",
defaultValue: "[{ surface: 'dashboard', placement: 'sidebar' }]",
},
];
@ -89,11 +95,15 @@ export default function DocsIntegrations() {
<Puzzle className="mr-2 h-3 w-3" />
Integrations
</Badge>
<h2 className="text-3xl font-semibold text-white">Connecting partner services to AeThex</h2>
<h2 className="text-3xl font-semibold text-white">
Connecting partner services to AeThex
</h2>
<p className="text-gray-300 max-w-3xl">
AeThex Integrations wrap third-party analytics, identity, payments, and live-ops tooling behind a consistent
runtime, security model, and visual system. Use this guide to register new connectors, surface partner UI in
product flows, and automate data exchange without hand-rolled plumbing.
AeThex Integrations wrap third-party analytics, identity, payments,
and live-ops tooling behind a consistent runtime, security model, and
visual system. Use this guide to register new connectors, surface
partner UI in product flows, and automate data exchange without
hand-rolled plumbing.
</p>
</section>
@ -107,14 +117,18 @@ export default function DocsIntegrations() {
</CardHeader>
<CardContent className="space-y-4 text-gray-300 text-sm leading-relaxed">
<p>
Integration manifests are stored in the AeThex Integrations service and synced across the dashboard and
runtime. Client components resolve connector metadata through the shared API helpers, ensuring credentials
and capability flags stay consistent with server state.
Integration manifests are stored in the AeThex Integrations
service and synced across the dashboard and runtime. Client
components resolve connector metadata through the shared API
helpers, ensuring credentials and capability flags stay consistent
with server state.
</p>
<p>
During hydration the runtime mounts partner SDKs behind AeThex loaders, applying sandboxed execution where
required. Use lifecycle hooks to emit analytics, hydrate widgets with scoped credentials, and gate access
through the same role-based policies used elsewhere in the platform.
During hydration the runtime mounts partner SDKs behind AeThex
loaders, applying sandboxed execution where required. Use
lifecycle hooks to emit analytics, hydrate widgets with scoped
credentials, and gate access through the same role-based policies
used elsewhere in the platform.
</p>
</CardContent>
</Card>
@ -128,13 +142,17 @@ export default function DocsIntegrations() {
</CardHeader>
<CardContent className="space-y-3 text-gray-300 text-sm">
<p>
Use the integration theming utilities to adapt partner widgets to AeThex gradients, typography, and focus
states. Tokens flow through CSS variables defined in <code className="bg-black/40 px-2">global.css</code>, so embeds stay
visually aligned with dashboards and consumer apps.
Use the integration theming utilities to adapt partner widgets to
AeThex gradients, typography, and focus states. Tokens flow
through CSS variables defined in{" "}
<code className="bg-black/40 px-2">global.css</code>, so embeds
stay visually aligned with dashboards and consumer apps.
</p>
<p>
Extend styling with scoped class names or CSS variables exported by the partner SDK. When shipping multiple
widgets, prefer design tokens over hard-coded overrides to keep dark-mode and accessibility tweaks in sync.
Extend styling with scoped class names or CSS variables exported
by the partner SDK. When shipping multiple widgets, prefer design
tokens over hard-coded overrides to keep dark-mode and
accessibility tweaks in sync.
</p>
</CardContent>
</Card>
@ -143,7 +161,9 @@ export default function DocsIntegrations() {
<section id="configuration" className="space-y-4">
<div className="flex items-center gap-3">
<Palette className="h-6 w-6 text-indigo-300" />
<h3 className="text-2xl font-semibold text-white">Configuration options</h3>
<h3 className="text-2xl font-semibold text-white">
Configuration options
</h3>
</div>
<Card className="bg-slate-900/60 border-slate-700">
<CardContent>
@ -158,14 +178,21 @@ export default function DocsIntegrations() {
<TableBody>
{connectorFields.map((field) => (
<TableRow key={field.name}>
<TableCell className="font-mono text-indigo-200">{field.name}</TableCell>
<TableCell className="text-gray-300">{field.description}</TableCell>
<TableCell className="text-gray-400 text-sm">{field.defaultValue}</TableCell>
<TableCell className="font-mono text-indigo-200">
{field.name}
</TableCell>
<TableCell className="text-gray-300">
{field.description}
</TableCell>
<TableCell className="text-gray-400 text-sm">
{field.defaultValue}
</TableCell>
</TableRow>
))}
</TableBody>
<TableCaption>
Manage manifests in the Integrations dashboard or via the Admin API to keep environments in sync.
Manage manifests in the Integrations dashboard or via the Admin
API to keep environments in sync.
</TableCaption>
</Table>
</CardContent>
@ -182,9 +209,18 @@ export default function DocsIntegrations() {
</CardHeader>
<CardContent className="space-y-3 text-gray-300 text-sm">
<ul className="list-disc space-y-2 pl-5">
<li>Store credentials in AeThex-managed vaults and rotate them from the dashboard rather than hard-coding.</li>
<li>Limit embed rendering to audiences that have access to the underlying data to avoid leaking partner UI.</li>
<li>Log integration events through the shared telemetry helpers so support can trace partner-side failures.</li>
<li>
Store credentials in AeThex-managed vaults and rotate them from
the dashboard rather than hard-coding.
</li>
<li>
Limit embed rendering to audiences that have access to the
underlying data to avoid leaking partner UI.
</li>
<li>
Log integration events through the shared telemetry helpers so
support can trace partner-side failures.
</li>
</ul>
</CardContent>
</Card>
@ -198,12 +234,15 @@ export default function DocsIntegrations() {
</CardHeader>
<CardContent className="space-y-3 text-gray-300 text-sm">
<p>
Promote integration changes through staging first. AeThex snapshots connector manifests per environment so
you can test credentials, capability flags, and UI placements without impacting production users.
Promote integration changes through staging first. AeThex
snapshots connector manifests per environment so you can test
credentials, capability flags, and UI placements without impacting
production users.
</p>
<p>
When partners publish SDK updates, pin versions in your manifest, document the change log, and coordinate
rollout windows with stakeholders subscribing to the integration.
When partners publish SDK updates, pin versions in your manifest,
document the change log, and coordinate rollout windows with
stakeholders subscribing to the integration.
</p>
</CardContent>
</Card>
@ -216,9 +255,14 @@ export default function DocsIntegrations() {
</div>
<div className="grid gap-4 md:grid-cols-3">
{troubleshooting.map((issue) => (
<Card key={issue.title} className="bg-slate-900/60 border-slate-700">
<Card
key={issue.title}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader>
<CardTitle className="text-white text-base">{issue.title}</CardTitle>
<CardTitle className="text-white text-base">
{issue.title}
</CardTitle>
</CardHeader>
<CardContent>
<CardDescription className="text-gray-300 text-sm">
@ -230,23 +274,39 @@ export default function DocsIntegrations() {
</div>
</section>
<section id="resources" className="rounded-2xl border border-indigo-500/40 bg-indigo-900/20 p-8">
<section
id="resources"
className="rounded-2xl border border-indigo-500/40 bg-indigo-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">Further reading</h3>
<h3 className="text-2xl font-semibold text-white">
Further reading
</h3>
<p className="text-gray-300 text-sm max-w-2xl">
Manage integration documentation centrally in Builder CMS or export static guides for partner teams. Keep
manifests, onboarding playbooks, and support runbooks together so each connector has a clear owner.
Manage integration documentation centrally in Builder CMS or
export static guides for partner teams. Keep manifests, onboarding
playbooks, and support runbooks together so each connector has a
clear owner.
</p>
</div>
<div className="flex gap-3">
<Button asChild size="lg" className="bg-indigo-600 hover:bg-indigo-500">
<Button
asChild
size="lg"
className="bg-indigo-600 hover:bg-indigo-500"
>
<Link to="/docs/api">
<LinkIcon className="mr-2 h-5 w-5" />
Review API hooks
</Link>
</Button>
<Button asChild variant="outline" size="lg" className="border-indigo-400/60 text-indigo-200">
<Button
asChild
variant="outline"
size="lg"
className="border-indigo-400/60 text-indigo-200"
>
<Link to="/docs/examples#code-gallery">
<FileText className="mr-2 h-5 w-5" />
Explore sample repos

View file

@ -330,7 +330,9 @@ export default function DocsOverview() {
{/* Learning Resources */}
<div className="mb-12">
<h3 className="text-2xl font-bold text-white mb-6">Learning resources</h3>
<h3 className="text-2xl font-bold text-white mb-6">
Learning resources
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{learningResources.map((resource, index) => {
const Icon = resource.icon;
@ -372,13 +374,13 @@ export default function DocsOverview() {
<div className="flex items-center justify-between mb-6">
<h3 className="text-2xl font-bold text-white">Recent Updates</h3>
<Button
asChild
variant="outline"
size="sm"
className="border-slate-600 text-white hover:bg-slate-800"
>
<Link to="/changelog">View All Updates</Link>
</Button>
asChild
variant="outline"
size="sm"
className="border-slate-600 text-white hover:bg-slate-800"
>
<Link to="/changelog">View All Updates</Link>
</Button>
</div>
<div className="space-y-4">
{featuredUpdates.map((update, index) => (
@ -472,10 +474,13 @@ export default function DocsOverview() {
{/* Support CTA */}
<div className="mt-12 rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8 text-center">
<h3 className="text-3xl font-semibold text-white mb-4">Need help getting started?</h3>
<h3 className="text-3xl font-semibold text-white mb-4">
Need help getting started?
</h3>
<p className="text-gray-300 max-w-3xl mx-auto mb-6">
Our documentation team updates these guides weekly. If you&apos;re looking for tailored onboarding,
architecture reviews, or migration support, reach out and we&apos;ll connect you with the right experts.
Our documentation team updates these guides weekly. If you&apos;re
looking for tailored onboarding, architecture reviews, or migration
support, reach out and we&apos;ll connect you with the right experts.
</p>
<div className="flex flex-col sm:flex-row justify-center gap-4">
<Button

View file

@ -64,19 +64,22 @@ const collaborationWorkflows = [
label: "Onboard & align",
description:
"Welcome teammates through the guided onboarding flow, capture their interests, and assign the right mentorship programs from day one.",
highlight: "Onboarding modules cover personal info, interests, and project preferences so teams ramp quickly.",
highlight:
"Onboarding modules cover personal info, interests, and project preferences so teams ramp quickly.",
},
{
label: "Build together",
description:
"Kick off projects with shared canvases, synced task boards, and CLI-generated environments. Use the realm switcher to target the correct workspace.",
highlight: "In-app toasts notify collaborators when schema changes, deployments, or reviews need attention.",
highlight:
"In-app toasts notify collaborators when schema changes, deployments, or reviews need attention.",
},
{
label: "Launch & iterate",
description:
"Promote builds through AeThex Deploy, track KPIs in the analytics feed, and publish release notes via the changelog tools.",
highlight: "Community announcements and blog posts keep players and stakeholders in the loop automatically.",
highlight:
"Community announcements and blog posts keep players and stakeholders in the loop automatically.",
},
];
@ -166,11 +169,14 @@ export default function DocsPlatform() {
<Sparkles className="mr-2 h-3 w-3" />
Platform Experience
</Badge>
<h2 className="text-3xl font-semibold text-white">Deliver cohesive player and builder journeys on AeThex</h2>
<h2 className="text-3xl font-semibold text-white">
Deliver cohesive player and builder journeys on AeThex
</h2>
<p className="text-gray-300 max-w-3xl">
Beyond deployment pipelines and CLI tooling, AeThex bundles collaboration, identity, and live-ops systems so
teams can craft unforgettable experiences. Use this guide to orient new stakeholders and plan end-to-end
platform rollouts.
Beyond deployment pipelines and CLI tooling, AeThex bundles
collaboration, identity, and live-ops systems so teams can craft
unforgettable experiences. Use this guide to orient new stakeholders
and plan end-to-end platform rollouts.
</p>
</section>
@ -183,14 +189,22 @@ export default function DocsPlatform() {
{platformPillars.map((pillar) => {
const Icon = pillar.icon;
return (
<Card key={pillar.title} className="bg-slate-900/60 border-slate-700">
<Card
key={pillar.title}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader>
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<Icon className="h-6 w-6 text-cyan-300" />
<CardTitle className="text-white text-lg">{pillar.title}</CardTitle>
<CardTitle className="text-white text-lg">
{pillar.title}
</CardTitle>
</div>
<Badge variant="outline" className="text-xs text-cyan-200 border-cyan-500/40">
<Badge
variant="outline"
className="text-xs text-cyan-200 border-cyan-500/40"
>
Platform
</Badge>
</div>
@ -219,13 +233,20 @@ export default function DocsPlatform() {
<section id="workflows" className="space-y-6">
<div className="flex items-center gap-3">
<Workflow className="h-6 w-6 text-cyan-300" />
<h3 className="text-2xl font-semibold text-white">Collaboration workflows</h3>
<h3 className="text-2xl font-semibold text-white">
Collaboration workflows
</h3>
</div>
<div className="grid gap-6 md:grid-cols-3">
{collaborationWorkflows.map((stage, index) => (
<Card key={stage.label} className="bg-slate-900/60 border-slate-700">
<Card
key={stage.label}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader className="space-y-2">
<Badge className="w-fit bg-cyan-600/30 text-cyan-100">Step {index + 1}</Badge>
<Badge className="w-fit bg-cyan-600/30 text-cyan-100">
Step {index + 1}
</Badge>
<CardTitle className="text-white text-lg flex items-center gap-2">
<Globe className="h-5 w-5 text-cyan-300" />
{stage.label}
@ -247,15 +268,23 @@ export default function DocsPlatform() {
<section id="modules" className="space-y-6">
<div className="flex items-center gap-3">
<Compass className="h-6 w-6 text-cyan-300" />
<h3 className="text-2xl font-semibold text-white">Experience modules</h3>
<h3 className="text-2xl font-semibold text-white">
Experience modules
</h3>
</div>
<div className="grid gap-4 md:grid-cols-2">
{experienceModules.map((module) => (
<Card key={module.name} className="bg-slate-900/60 border-slate-700">
<Card
key={module.name}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader>
<CardTitle className="text-white text-base flex items-center justify-between">
{module.name}
<Badge variant="outline" className="text-xs text-cyan-200 border-cyan-500/40">
<Badge
variant="outline"
className="text-xs text-cyan-200 border-cyan-500/40"
>
Platform
</Badge>
</CardTitle>
@ -283,17 +312,24 @@ export default function DocsPlatform() {
<section id="analytics" className="space-y-6">
<div className="flex items-center gap-3">
<BarChart3 className="h-6 w-6 text-cyan-300" />
<h3 className="text-2xl font-semibold text-white">Insights & analytics</h3>
<h3 className="text-2xl font-semibold text-white">
Insights & analytics
</h3>
</div>
<div className="grid gap-6 md:grid-cols-3">
{analyticsHighlights.map((item) => {
const Icon = item.icon;
return (
<Card key={item.title} className="bg-slate-900/60 border-slate-700">
<Card
key={item.title}
className="bg-slate-900/60 border-slate-700"
>
<CardHeader>
<div className="flex items-center gap-3">
<Icon className="h-6 w-6 text-cyan-300" />
<CardTitle className="text-white text-lg">{item.title}</CardTitle>
<CardTitle className="text-white text-lg">
{item.title}
</CardTitle>
</div>
</CardHeader>
<CardContent>
@ -310,7 +346,9 @@ export default function DocsPlatform() {
<section id="governance" className="space-y-6">
<div className="flex items-center gap-3">
<ShieldCheck className="h-6 w-6 text-cyan-300" />
<h3 className="text-2xl font-semibold text-white">Governance checklist</h3>
<h3 className="text-2xl font-semibold text-white">
Governance checklist
</h3>
</div>
<Card className="bg-slate-900/60 border-slate-700">
<CardContent>
@ -323,13 +361,19 @@ export default function DocsPlatform() {
</Card>
</section>
<section id="next-steps" className="rounded-2xl border border-cyan-500/40 bg-cyan-900/20 p-8">
<section
id="next-steps"
className="rounded-2xl border border-cyan-500/40 bg-cyan-900/20 p-8"
>
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2">
<h3 className="text-2xl font-semibold text-white">Keep exploring the platform</h3>
<h3 className="text-2xl font-semibold text-white">
Keep exploring the platform
</h3>
<p className="text-gray-300 max-w-2xl text-sm">
Share this page with non-technical teammates. It links out to every major surface area so marketing,
product, and operations groups can navigate confidently.
Share this page with non-technical teammates. It links out to
every major surface area so marketing, product, and operations
groups can navigate confidently.
</p>
</div>
<div className="grid gap-3 md:grid-cols-2">

View file

@ -374,7 +374,10 @@ export default function DocsTutorials() {
</div>
</div>
<Button asChild className="bg-purple-600 hover:bg-purple-700">
<Button
asChild
className="bg-purple-600 hover:bg-purple-700"
>
<Link to={tutorial.path}>
Start Tutorial
<ChevronRight className="h-4 w-4 ml-2" />