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 path="/docs" element={<DocsLayout />}>
<Route index element={<DocsOverview />} /> <Route index element={<DocsOverview />} />
<Route path="tutorials" element={<DocsTutorials />} /> <Route path="tutorials" element={<DocsTutorials />} />
<Route path="getting-started" element={<DocsGettingStarted />} /> <Route
path="getting-started"
element={<DocsGettingStarted />}
/>
<Route path="platform" element={<DocsPlatform />} /> <Route path="platform" element={<DocsPlatform />} />
<Route path="api" element={<DocsApiReference />} /> <Route path="api" element={<DocsApiReference />} />
<Route path="cli" element={<DocsCli />} /> <Route path="cli" element={<DocsCli />} />

View file

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

View file

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

View file

@ -11,13 +11,7 @@ import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator"; import { Separator } from "@/components/ui/separator";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { import { Heart, MessageCircle, Share2, Volume2, VolumeX } from "lucide-react";
Heart,
MessageCircle,
Share2,
Volume2,
VolumeX,
} from "lucide-react";
import type { FeedItem } from "@/pages/Feed"; import type { FeedItem } from "@/pages/Feed";
interface FeedItemCardProps { interface FeedItemCardProps {
@ -42,7 +36,10 @@ export function FeedItemCard({
<div className="flex items-start justify-between gap-4"> <div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<Avatar className="h-12 w-12 ring-2 ring-aethex-500/30"> <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"> <AvatarFallback className="bg-aethex-500/10 text-aethex-300">
{item.authorName?.[0]?.toUpperCase() || "U"} {item.authorName?.[0]?.toUpperCase() || "U"}
</AvatarFallback> </AvatarFallback>
@ -132,7 +129,10 @@ export function FeedItemCard({
</Button> </Button>
</div> </div>
<div className="flex items-center gap-2"> <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" {item.mediaType === "video"
? "Video" ? "Video"
: item.mediaType === "image" : 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: '...' }; // Example override (console): window.__AETHEX_SKIP_AGENT_CONFIG = { src: 'https://example.com/agent.js', id: '...' };
const getRuntimeConfig = () => { const getRuntimeConfig = () => {
try { 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 { return (window as any).__AETHEX_SKIP_AGENT_CONFIG as {
src?: string; src?: string;
id?: string; id?: string;
@ -217,7 +220,10 @@ const createSkipAgentTheme = () => {
const isDocsPath = () => { const isDocsPath = () => {
try { try {
return typeof window !== "undefined" && window.location.pathname.startsWith("/docs"); return (
typeof window !== "undefined" &&
window.location.pathname.startsWith("/docs")
);
} catch (e) { } catch (e) {
return false; return false;
} }
@ -273,9 +279,9 @@ const isSkipAgentReachable = async (): Promise<boolean> => {
throw new Error(`Agent status request failed with ${response.status}`); throw new Error(`Agent status request failed with ${response.status}`);
} }
const payload = (await response.json().catch(() => null)) as const payload = (await response.json().catch(() => null)) as {
| { active?: boolean } active?: boolean;
| null; } | null;
if (payload && payload.active === false) { if (payload && payload.active === false) {
throw new Error("Agent reported inactive"); throw new Error("Agent reported inactive");
@ -413,7 +419,9 @@ const loadSkipAgent = async (): Promise<void> => {
} }
const scriptText = await response.text(); 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"); const script = document.createElement("script");
script.id = SKIP_AGENT_SCRIPT_ID; script.id = SKIP_AGENT_SCRIPT_ID;

View file

@ -171,12 +171,17 @@ export default function Admin() {
<CardHeader> <CardHeader>
<CardTitle className="text-red-400">Access denied</CardTitle> <CardTitle className="text-red-400">Access denied</CardTitle>
<CardDescription> <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> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex gap-2"> <CardContent className="flex gap-2">
<Button onClick={() => navigate("/dashboard")}>Go to dashboard</Button> <Button onClick={() => navigate("/dashboard")}>
<Button variant="outline" onClick={() => navigate("/support")}>Contact support</Button> Go to dashboard
</Button>
<Button variant="outline" onClick={() => navigate("/support")}>
Contact support
</Button>
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
@ -191,7 +196,8 @@ export default function Admin() {
const selectedMember = useMemo( const selectedMember = useMemo(
() => () =>
managedProfiles.find((profile) => profile.id === selectedMemberId) ?? null, managedProfiles.find((profile) => profile.id === selectedMemberId) ??
null,
[managedProfiles, selectedMemberId], [managedProfiles, selectedMemberId],
); );
@ -200,7 +206,9 @@ export default function Admin() {
const featuredStudios = studios.length; const featuredStudios = studios.length;
const pendingApplications = applications.filter((app) => { const pendingApplications = applications.filter((app) => {
const status = (app.status ?? "").toLowerCase(); const status = (app.status ?? "").toLowerCase();
return status !== "approved" && status !== "completed" && status !== "closed"; return (
status !== "approved" && status !== "completed" && status !== "closed"
);
}).length; }).length;
const overviewStats = useMemo( const overviewStats = useMemo(
@ -209,7 +217,9 @@ export default function Admin() {
title: "Total members", title: "Total members",
value: totalMembers ? totalMembers.toString() : "—", value: totalMembers ? totalMembers.toString() : "—",
description: "Profiles synced from AeThex identity service.", description: "Profiles synced from AeThex identity service.",
trend: totalMembers ? `${totalMembers} active profiles` : "Awaiting sync", trend: totalMembers
? `${totalMembers} active profiles`
: "Awaiting sync",
icon: Users, icon: Users,
tone: "blue" as const, 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="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="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
<div className="space-y-2"> <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"> <p className="text-muted-foreground">
Unified oversight for AeThex operations, content, and community. Unified oversight for AeThex operations, content, and community.
</p> </p>
<div className="flex flex-wrap gap-2"> <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 Owner
</Badge> </Badge>
<Badge variant="outline" className="border-blue-500/50 text-blue-300"> <Badge
variant="outline"
className="border-blue-500/50 text-blue-300"
>
Admin Admin
</Badge> </Badge>
<Badge variant="outline" className="border-purple-500/50 text-purple-300"> <Badge
variant="outline"
className="border-purple-500/50 text-purple-300"
>
Founder Founder
</Badge> </Badge>
</div> </div>
<p className="text-xs text-muted-foreground"> <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> </p>
</div> </div>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
@ -391,11 +415,17 @@ export default function Admin() {
<Button variant="outline" onClick={() => navigate("/profile")}> <Button variant="outline" onClick={() => navigate("/profile")}>
Profile Profile
</Button> </Button>
<Button onClick={() => setActiveTab("content")}>Create update</Button> <Button onClick={() => setActiveTab("content")}>
Create update
</Button>
</div> </div>
</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"> <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="overview">Overview</TabsTrigger>
<TabsTrigger value="content">Content</TabsTrigger> <TabsTrigger value="content">Content</TabsTrigger>
@ -425,23 +455,31 @@ export default function Admin() {
<Command className="h-5 w-5 text-aethex-300" /> <Command className="h-5 w-5 text-aethex-300" />
<CardTitle>Quick actions</CardTitle> <CardTitle>Quick actions</CardTitle>
</div> </div>
<CardDescription>Launch frequent administrative workflows.</CardDescription> <CardDescription>
Launch frequent administrative workflows.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="grid gap-3"> <CardContent className="grid gap-3">
{quickActions.map(({ label, description, icon: ActionIcon, action }) => ( {quickActions.map(
<button ({ label, description, icon: ActionIcon, action }) => (
key={label} <button
type="button" key={label}
onClick={action} type="button"
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" 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"> <ActionIcon className="mt-0.5 h-5 w-5 text-aethex-400 transition group-hover:text-aethex-200" />
<p className="font-medium text-foreground">{label}</p> <div className="space-y-1">
<p className="text-sm text-muted-foreground">{description}</p> <p className="font-medium text-foreground">
</div> {label}
</button> </p>
))} <p className="text-sm text-muted-foreground">
{description}
</p>
</div>
</button>
),
)}
</CardContent> </CardContent>
</Card> </Card>
@ -451,21 +489,37 @@ export default function Admin() {
<Shield className="h-5 w-5 text-green-400" /> <Shield className="h-5 w-5 text-green-400" />
<CardTitle>Access control</CardTitle> <CardTitle>Access control</CardTitle>
</div> </div>
<CardDescription>Owner-only access enforced via Supabase roles.</CardDescription> <CardDescription>
Owner-only access enforced via Supabase roles.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3 text-sm text-muted-foreground"> <CardContent className="space-y-3 text-sm text-muted-foreground">
<ul className="space-y-2 leading-relaxed"> <ul className="space-y-2 leading-relaxed">
<li> <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>
<li>Roles are provisioned automatically on owner sign-in.</li>
<li>Grant additional admins by updating Supabase role assignments.</li>
</ul> </ul>
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => setActiveTab("community")}> <Button
variant="outline"
size="sm"
onClick={() => setActiveTab("community")}
>
View members View members
</Button> </Button>
<Button variant="outline" size="sm" onClick={() => navigate("/support")}> <Button
variant="outline"
size="sm"
onClick={() => navigate("/support")}
>
Contact support Contact support
</Button> </Button>
</div> </div>
@ -482,12 +536,18 @@ export default function Admin() {
<CardTitle>Content overview</CardTitle> <CardTitle>Content overview</CardTitle>
</div> </div>
<CardDescription> <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> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-2"> <CardContent className="text-sm text-muted-foreground space-y-2">
<p> <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> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -498,7 +558,9 @@ export default function Admin() {
<PenTool className="h-5 w-5 text-aethex-400" /> <PenTool className="h-5 w-5 text-aethex-400" />
<CardTitle className="text-lg">Blog posts</CardTitle> <CardTitle className="text-lg">Blog posts</CardTitle>
</div> </div>
<CardDescription>Manage blog content stored in Supabase</CardDescription> <CardDescription>
Manage blog content stored in Supabase
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-3">
<div className="flex flex-wrap items-center gap-3"> <div className="flex flex-wrap items-center gap-3">
@ -538,7 +600,8 @@ export default function Admin() {
{blogPosts.length === 0 && ( {blogPosts.length === 0 && (
<p className="text-sm text-muted-foreground"> <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> </p>
)} )}
@ -680,14 +743,28 @@ export default function Admin() {
<UserCog className="h-5 w-5 text-teal-300" /> <UserCog className="h-5 w-5 text-teal-300" />
<CardTitle>Community actions</CardTitle> <CardTitle>Community actions</CardTitle>
</div> </div>
<CardDescription>Grow the network and celebrate contributors.</CardDescription> <CardDescription>
Grow the network and celebrate contributors.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex flex-wrap gap-2"> <CardContent className="flex flex-wrap gap-2">
<Button size="sm" onClick={() => navigate("/community")}>Open community hub</Button> <Button size="sm" onClick={() => navigate("/community")}>
<Button size="sm" variant="outline" onClick={() => navigate("/mentorship")}> Open community hub
</Button>
<Button
size="sm"
variant="outline"
onClick={() => navigate("/mentorship")}
>
Manage mentorships Manage mentorships
</Button> </Button>
<Button size="sm" variant="outline" onClick={() => navigate("/support")}>Support queue</Button> <Button
size="sm"
variant="outline"
onClick={() => navigate("/support")}
>
Support queue
</Button>
</CardContent> </CardContent>
</Card> </Card>
</TabsContent> </TabsContent>
@ -700,7 +777,9 @@ export default function Admin() {
<Settings className="h-5 w-5 text-yellow-300" /> <Settings className="h-5 w-5 text-yellow-300" />
<CardTitle>Featured studios</CardTitle> <CardTitle>Featured studios</CardTitle>
</div> </div>
<CardDescription>Control studios highlighted across AeThex experiences.</CardDescription> <CardDescription>
Control studios highlighted across AeThex experiences.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{studios.map((s, i) => ( {studios.map((s, i) => (
@ -762,7 +841,9 @@ export default function Admin() {
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => setStudios(studios.filter((_, idx) => idx !== i))} onClick={() =>
setStudios(studios.filter((_, idx) => idx !== i))
}
> >
Remove Remove
</Button> </Button>
@ -773,7 +854,9 @@ export default function Admin() {
<Button <Button
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => setStudios([...studios, { name: "New Studio" }])} onClick={() =>
setStudios([...studios, { name: "New Studio" }])
}
> >
Add studio Add studio
</Button> </Button>
@ -788,12 +871,14 @@ export default function Admin() {
if (!resp.ok) { if (!resp.ok) {
aethexToast.error({ aethexToast.error({
title: "Save failed", title: "Save failed",
description: "Unable to persist featured studios.", description:
"Unable to persist featured studios.",
}); });
} else { } else {
aethexToast.success({ aethexToast.success({
title: "Studios saved", 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" /> <ClipboardList className="h-5 w-5 text-sky-300" />
<CardTitle>Project applications</CardTitle> <CardTitle>Project applications</CardTitle>
</div> </div>
<CardDescription>Review collaboration requests and prioritize approvals.</CardDescription> <CardDescription>
Review collaboration requests and prioritize approvals.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3 text-sm text-muted-foreground"> <CardContent className="space-y-3 text-sm text-muted-foreground">
<div className="flex flex-wrap items-center justify-between gap-2"> <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"> <div className="flex flex-wrap items-center justify-between gap-2">
<p className="font-medium text-foreground"> <p className="font-medium text-foreground">
{app.applicant_name || app.applicant_email || "Unknown applicant"} {app.applicant_name ||
app.applicant_email ||
"Unknown applicant"}
</p> </p>
<Badge variant="outline" className="capitalize"> <Badge variant="outline" className="capitalize">
{(app.status ?? "pending").toLowerCase()} {(app.status ?? "pending").toLowerCase()}
@ -858,14 +947,18 @@ export default function Admin() {
</p> </p>
{app.created_at ? ( {app.created_at ? (
<p className="text-[11px] text-muted-foreground/80"> <p className="text-[11px] text-muted-foreground/80">
Submitted {new Date(app.created_at).toLocaleString()} Submitted{" "}
{new Date(app.created_at).toLocaleString()}
</p> </p>
) : null} ) : null}
</div> </div>
))} ))}
</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> </CardContent>
</Card> </Card>
@ -876,7 +969,9 @@ export default function Admin() {
<Activity className="h-5 w-5 text-orange-300" /> <Activity className="h-5 w-5 text-orange-300" />
<CardTitle>System status</CardTitle> <CardTitle>System status</CardTitle>
</div> </div>
<CardDescription>Auth, database, and realtime services.</CardDescription> <CardDescription>
Auth, database, and realtime services.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-1"> <CardContent className="text-sm text-muted-foreground space-y-1">
<p>Auth: Operational</p> <p>Auth: Operational</p>
@ -891,11 +986,16 @@ export default function Admin() {
<UserCog className="h-5 w-5 text-teal-300" /> <UserCog className="h-5 w-5 text-teal-300" />
<CardTitle>Your account</CardTitle> <CardTitle>Your account</CardTitle>
</div> </div>
<CardDescription>Owner privileges are active.</CardDescription> <CardDescription>
Owner privileges are active.
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="text-sm text-muted-foreground space-y-2"> <CardContent className="text-sm text-muted-foreground space-y-2">
<p>Signed in as {user.email}.</p> <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> </CardContent>
</Card> </Card>
</div> </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 [isLoading, setIsLoading] = useState(true);
const [following, setFollowing] = useState<string[]>([]); const [following, setFollowing] = useState<string[]>([]);
const [items, setItems] = useState<FeedItem[]>([]); const [items, setItems] = useState<FeedItem[]>([]);
const [activeFilter, setActiveFilter] = useState<"all" | "following" | "trending">( const [activeFilter, setActiveFilter] = useState<
"all", "all" | "following" | "trending"
); >("all");
const fetchFeed = useCallback(async () => { const fetchFeed = useCallback(async () => {
setIsLoading(true); setIsLoading(true);
@ -202,11 +202,14 @@ export default function Feed() {
const filteredItems = useMemo(() => { const filteredItems = useMemo(() => {
if (activeFilter === "following") { if (activeFilter === "following") {
return items.filter( return items.filter(
(item) => isFollowingAuthor(item.authorId) || item.authorId === user?.id, (item) =>
isFollowingAuthor(item.authorId) || item.authorId === user?.id,
); );
} }
if (activeFilter === "trending") { 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; return items;
}, [activeFilter, isFollowingAuthor, items, user?.id]); }, [activeFilter, isFollowingAuthor, items, user?.id]);
@ -259,7 +262,8 @@ export default function Feed() {
const totalEngagement = useMemo( const totalEngagement = useMemo(
() => () =>
items.reduce( 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, 0,
), ),
[items], [items],
@ -271,7 +275,10 @@ export default function Feed() {
}, [items.length, totalEngagement]); }, [items.length, totalEngagement]);
const handleScrollToComposer = useCallback(() => { const handleScrollToComposer = useCallback(() => {
composerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" }); composerRef.current?.scrollIntoView({
behavior: "smooth",
block: "center",
});
}, []); }, []);
const handleManualRefresh = useCallback(() => { const handleManualRefresh = useCallback(() => {
@ -280,7 +287,11 @@ export default function Feed() {
if (loading || (isLoading && items.length === 0)) { if (loading || (isLoading && items.length === 0)) {
return ( 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 Community Pulse
</h1> </h1>
<p className="mt-2 max-w-2xl text-sm text-muted-foreground sm:text-base"> <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> </p>
</div> </div>
<div className="flex flex-wrap items-center gap-3"> <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 Live updates enabled
</Badge> </Badge>
<Button <Button
@ -316,28 +331,26 @@ export default function Feed() {
</div> </div>
<div className="flex flex-wrap items-center gap-2"> <div className="flex flex-wrap items-center gap-2">
{( {[
[ {
{ key: "all" as const,
key: "all" as const, label: "All stories",
label: "All stories", icon: Sparkles,
icon: Sparkles, description: "Latest community activity",
description: "Latest community activity", },
}, {
{ key: "following" as const,
key: "following" as const, label: "Following",
label: "Following", icon: Users,
icon: Users, description: "People you follow",
description: "People you follow", },
}, {
{ key: "trending" as const,
key: "trending" as const, label: "Trending",
label: "Trending", icon: Flame,
icon: Flame, description: "Most engagement",
description: "Most engagement", },
}, ].map(({ key, label, icon: Icon, description }) => (
]
).map(({ key, label, icon: Icon, description }) => (
<Button <Button
key={key} key={key}
variant={activeFilter === key ? "default" : "outline"} variant={activeFilter === key ? "default" : "outline"}
@ -352,7 +365,9 @@ export default function Feed() {
> >
<Icon className="h-4 w-4" /> <Icon className="h-4 w-4" />
<span className="font-medium">{label}</span> <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> </Button>
))} ))}
</div> </div>
@ -368,9 +383,12 @@ export default function Feed() {
> >
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div> <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"> <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> </p>
</div> </div>
<Button <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 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"> <div className="flex items-center gap-2">
<Sparkles className="h-4 w-4 text-aethex-300" /> <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> </div>
<button <button
type="button" type="button"
@ -403,7 +422,8 @@ export default function Feed() {
<CardHeader> <CardHeader>
<CardTitle className="text-xl">No stories found</CardTitle> <CardTitle className="text-xl">No stories found</CardTitle>
<CardDescription> <CardDescription>
Try switching filters or follow more creators to personalize your feed. Try switching filters or follow more creators to
personalize your feed.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="pt-0"> <CardContent className="pt-0">
@ -434,7 +454,9 @@ export default function Feed() {
<aside className="space-y-6"> <aside className="space-y-6">
<Card className="rounded-3xl border-border/40 bg-background/70 shadow-xl backdrop-blur-lg"> <Card className="rounded-3xl border-border/40 bg-background/70 shadow-xl backdrop-blur-lg">
<CardHeader> <CardHeader>
<CardTitle className="text-lg">Your community snapshot</CardTitle> <CardTitle className="text-lg">
Your community snapshot
</CardTitle>
<CardDescription> <CardDescription>
Track how your network is evolving at a glance. Track how your network is evolving at a glance.
</CardDescription> </CardDescription>
@ -442,25 +464,33 @@ export default function Feed() {
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-3 text-sm"> <div className="grid grid-cols-2 gap-3 text-sm">
<div className="rounded-2xl border border-border/30 bg-background/60 p-4"> <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"> <p className="mt-1 text-2xl font-semibold text-foreground">
{items.length.toLocaleString()} {items.length.toLocaleString()}
</p> </p>
</div> </div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4"> <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"> <p className="mt-1 text-2xl font-semibold text-foreground">
{following.length.toLocaleString()} {following.length.toLocaleString()}
</p> </p>
</div> </div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4"> <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"> <p className="mt-1 text-2xl font-semibold text-foreground">
{totalEngagement.toLocaleString()} {totalEngagement.toLocaleString()}
</p> </p>
</div> </div>
<div className="rounded-2xl border border-border/30 bg-background/60 p-4"> <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"> <p className="mt-1 text-2xl font-semibold text-foreground">
{averageEngagement.toLocaleString()} {averageEngagement.toLocaleString()}
</p> </p>
@ -492,7 +522,8 @@ export default function Feed() {
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{trendingTopics.length === 0 ? ( {trendingTopics.length === 0 ? (
<p className="text-sm text-muted-foreground"> <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> </p>
) : ( ) : (
trendingTopics.map((topic, index) => ( trendingTopics.map((topic, index) => (
@ -505,7 +536,9 @@ export default function Feed() {
{index + 1} {index + 1}
</span> </span>
<div> <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"> <p className="text-xs text-muted-foreground">
{topic.count.toLocaleString()} mentions today {topic.count.toLocaleString()} mentions today
</p> </p>
@ -535,7 +568,8 @@ export default function Feed() {
<CardContent className="space-y-3"> <CardContent className="space-y-3">
{suggestedCreators.length === 0 ? ( {suggestedCreators.length === 0 ? (
<p className="text-sm text-muted-foreground"> <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> </p>
) : ( ) : (
suggestedCreators.map((creator) => ( suggestedCreators.map((creator) => (
@ -554,9 +588,12 @@ export default function Feed() {
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<div> <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"> <p className="text-xs text-muted-foreground">
{creator.posts.toLocaleString()} posts · {creator.likes.toLocaleString()} reactions {creator.posts.toLocaleString()} posts ·{" "}
{creator.likes.toLocaleString()} reactions
</p> </p>
</div> </div>
</div> </div>

View file

@ -69,11 +69,13 @@ const webhookTopics = [
}, },
{ {
event: "incident.opened", 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", 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" /> <ServerCog className="mr-2 h-3 w-3" />
API Reference API Reference
</Badge> </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"> <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 The REST API exposes every core capability of the AeThex platform.
personal access tokens, call idempotent endpoints, and subscribe to webhooks to react to changes in real Authenticate with OAuth 2.1 or personal access tokens, call idempotent
time. endpoints, and subscribe to webhooks to react to changes in real time.
</p> </p>
</section> </section>
@ -125,16 +129,20 @@ export default function DocsApiReference() {
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>
<CardContent className="space-y-4 text-gray-300"> <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"> <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 \ -u CLIENT_ID:CLIENT_SECRET \
-d "grant_type=client_credentials" \ -d "grant_type=client_credentials" \
-d "scope=projects:read deployments:write"`} -d "scope=projects:read deployments:write"`}
</pre> </pre>
<p> <p>
Prefer user-scoped access? Direct builders through the hosted OAuth consent screen and exchange their Prefer user-scoped access? Direct builders through the hosted
authorization code using the same endpoint. OAuth consent screen and exchange their authorization code using
the same endpoint.
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -148,10 +156,11 @@ export default function DocsApiReference() {
</CardHeader> </CardHeader>
<CardContent className="space-y-4 text-gray-300"> <CardContent className="space-y-4 text-gray-300">
<p className="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> </p>
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-sm text-teal-200"> <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: { headers: {
Authorization: "Bearer ${TOKEN}", Authorization: "Bearer ${TOKEN}",
"AeThex-Environment": "production", "AeThex-Environment": "production",
@ -162,9 +171,16 @@ export default function DocsApiReference() {
});`} });`}
</pre> </pre>
<p> <p>
Responses include <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-RateLimit-Remaining</code> Responses include{" "}
and <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-Request-ID</code> headers. Share the <code className="rounded bg-black/40 px-2 py-1 text-blue-200">
request ID when contacting support for faster triage. 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> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -189,7 +205,9 @@ export default function DocsApiReference() {
{apiEndpoints.map((endpoint) => ( {apiEndpoints.map((endpoint) => (
<TableRow key={`${endpoint.method}-${endpoint.path}`}> <TableRow key={`${endpoint.method}-${endpoint.path}`}>
<TableCell> <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>
<TableCell className="font-mono text-purple-200"> <TableCell className="font-mono text-purple-200">
{endpoint.path} {endpoint.path}
@ -201,7 +219,8 @@ export default function DocsApiReference() {
))} ))}
</TableBody> </TableBody>
<TableCaption> <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> </TableCaption>
</Table> </Table>
</CardContent> </CardContent>
@ -223,12 +242,23 @@ export default function DocsApiReference() {
className="rounded-lg border border-slate-700 bg-slate-950/40 p-4" 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="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> </div>
))} ))}
<p className="text-gray-400 text-sm"> <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>. Configure webhook destinations and signing secrets from the{" "}
Verify requests with the <code className="rounded bg-black/40 px-2 py-1 text-blue-200">AeThex-Signature</code> <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. header to guarantee authenticity.
</p> </p>
</CardContent> </CardContent>
@ -243,13 +273,22 @@ export default function DocsApiReference() {
</CardHeader> </CardHeader>
<CardContent className="space-y-4 text-gray-300"> <CardContent className="space-y-4 text-gray-300">
<p className="text-gray-300"> <p className="text-gray-300">
All endpoints are idempotent where appropriate and support conditional requests via the All endpoints are idempotent where appropriate and support
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">If-Match</code> header. conditional requests via the
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
If-Match
</code>{" "}
header.
</p> </p>
<div className="grid gap-3"> <div className="grid gap-3">
{errorExamples.map((error) => ( {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"> <div
<Badge className="bg-red-600/30 text-red-200">{error.code}</Badge> 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> <div>
<p className="text-white font-medium">{error.label}</p> <p className="text-white font-medium">{error.label}</p>
<p className="text-sm text-gray-400">{error.hint}</p> <p className="text-sm text-gray-400">{error.hint}</p>
@ -258,20 +297,28 @@ export default function DocsApiReference() {
))} ))}
</div> </div>
<p className="text-gray-300"> <p className="text-gray-300">
Monitor rate-limit headers and retry using an exponential backoff strategy. Persistent errors can be Monitor rate-limit headers and retry using an exponential backoff
escalated to the AeThex support team with the failing request ID. strategy. Persistent errors can be escalated to the AeThex support
team with the failing request ID.
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
</section> </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="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2"> <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"> <p className="text-gray-300 max-w-2xl">
Combine the REST API with event webhooks for a full-duplex integration pattern. Use the official Combine the REST API with event webhooks for a full-duplex
TypeScript SDK for typed helpers or generate your own client with the published OpenAPI schema. integration pattern. Use the official TypeScript SDK for typed
helpers or generate your own client with the published OpenAPI
schema.
</p> </p>
</div> </div>
<div className="flex gap-3"> <div className="flex gap-3">
@ -281,7 +328,12 @@ export default function DocsApiReference() {
OpenAPI explorer OpenAPI explorer
</Link> </Link>
</Button> </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"> <Link to="/docs/examples">
<AlertTriangle className="mr-2 h-5 w-5" /> <AlertTriangle className="mr-2 h-5 w-5" />
See implementation patterns See implementation patterns

View file

@ -42,7 +42,8 @@ const commands = [
{ {
command: "aethex deploy", command: "aethex deploy",
description: "Build and deploy the current project", 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", command: "aethex env pull",
@ -87,11 +88,14 @@ export default function DocsCli() {
<Terminal className="mr-2 h-3 w-3" /> <Terminal className="mr-2 h-3 w-3" />
CLI Tools CLI Tools
</Badge> </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"> <p className="text-gray-300 max-w-3xl">
The AeThex CLI automates local development, environment management, and production deployments. It is The AeThex CLI automates local development, environment management,
built with stability in mind, featuring transactional deploys, shell-friendly output, and native support for and production deployments. It is built with stability in mind,
Linux, macOS, and Windows. featuring transactional deploys, shell-friendly output, and native
support for Linux, macOS, and Windows.
</p> </p>
</section> </section>
@ -113,13 +117,22 @@ export default function DocsCli() {
<TableBody> <TableBody>
{commands.map((item) => ( {commands.map((item) => (
<TableRow key={item.command}> <TableRow key={item.command}>
<TableCell className="font-mono text-purple-200">{item.command}</TableCell> <TableCell className="font-mono text-purple-200">
<TableCell className="text-gray-300">{item.description}</TableCell> {item.command}
<TableCell className="text-gray-400 text-sm">{item.usage}</TableCell> </TableCell>
<TableCell className="text-gray-300">
{item.description}
</TableCell>
<TableCell className="text-gray-400 text-sm">
{item.usage}
</TableCell>
</TableRow> </TableRow>
))} ))}
</TableBody> </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> </Table>
</CardContent> </CardContent>
</Card> </Card>
@ -135,12 +148,13 @@ export default function DocsCli() {
</CardHeader> </CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm"> <CardContent className="text-gray-300 space-y-3 text-sm">
<p> <p>
Run <code className="bg-black/40 px-1">aethex dev</code> to start local services with hot reloading and the Run <code className="bg-black/40 px-1">aethex dev</code> to start
AeThex mock identity provider. Inspect logs via the integrated tail view. local services with hot reloading and the AeThex mock identity
provider. Inspect logs via the integrated tail view.
</p> </p>
<p> <p>
Use <code className="bg-black/40 px-1">aethex data seed</code> to populate sample datasets for QA or demo Use <code className="bg-black/40 px-1">aethex data seed</code> to
accounts. populate sample datasets for QA or demo accounts.
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -154,12 +168,16 @@ export default function DocsCli() {
</CardHeader> </CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm"> <CardContent className="text-gray-300 space-y-3 text-sm">
<p> <p>
Synchronize configuration with <code className="bg-black/40 px-1">aethex env pull</code> or populate remote Synchronize configuration with{" "}
environments from local files using <code className="bg-black/40 px-1">aethex env push</code>. <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>
<p> <p>
Inspect secrets securely through <code className="bg-black/40 px-1">aethex env inspect</code>. Output is redacted Inspect secrets securely through{" "}
by default, keeping sensitive data safe in terminal logs. <code className="bg-black/40 px-1">aethex env inspect</code>.
Output is redacted by default, keeping sensitive data safe in
terminal logs.
</p> </p>
</CardContent> </CardContent>
</Card> </Card>
@ -173,11 +191,13 @@ export default function DocsCli() {
</CardHeader> </CardHeader>
<CardContent className="text-gray-300 space-y-3 text-sm"> <CardContent className="text-gray-300 space-y-3 text-sm">
<p> <p>
Each deployment is transactional: a failure during build or migration automatically halts promotion and Each deployment is transactional: a failure during build or
emits alerts to subscribed channels. migration automatically halts promotion and emits alerts to
subscribed channels.
</p> </p>
<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. and enforced automatically by the CLI.
</p> </p>
</CardContent> </CardContent>
@ -187,13 +207,17 @@ export default function DocsCli() {
<section id="automation" className="space-y-4"> <section id="automation" className="space-y-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<CloudLightning className="h-6 w-6 text-amber-300" /> <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>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
{automationTips.map((tip) => ( {automationTips.map((tip) => (
<Card key={tip.title} className="bg-slate-900/60 border-slate-700"> <Card key={tip.title} className="bg-slate-900/60 border-slate-700">
<CardHeader> <CardHeader>
<CardTitle className="text-white text-lg">{tip.title}</CardTitle> <CardTitle className="text-white text-lg">
{tip.title}
</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<CardDescription className="text-sm text-gray-300"> <CardDescription className="text-sm text-gray-300">
@ -205,29 +229,49 @@ export default function DocsCli() {
</div> </div>
</section> </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="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2"> <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"> <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 The CLI signs every build artifact and enforces checksums during
policies to guarantee only trusted pipelines can trigger releases. deployment. Combine this with RBAC token policies to guarantee
only trusted pipelines can trigger releases.
</p> </p>
</div> </div>
<div className="flex flex-col gap-3 sm:flex-row"> <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"> <Link to="/docs/getting-started">
<ArrowRight className="mr-2 h-5 w-5" /> <ArrowRight className="mr-2 h-5 w-5" />
Return to setup guide Return to setup guide
</Link> </Link>
</Button> </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"> <Link to="/support">
<Shield className="mr-2 h-5 w-5" /> <Shield className="mr-2 h-5 w-5" />
Security best practices Security best practices
</Link> </Link>
</Button> </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"> <Link to="/docs/platform">
Discover platform features Discover platform features
<ArrowRight className="ml-2 h-5 w-5" /> <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" /> <Blocks className="mr-2 h-3 w-3" />
Examples & Templates Examples & Templates
</Badge> </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"> <p className="text-gray-300 max-w-3xl">
Explore curated examples covering backend services, realtime overlays, automation scripts, and workflow Explore curated examples covering backend services, realtime overlays,
integrations. Each project includes detailed READMEs, infrastructure diagrams, and deployment runbooks. automation scripts, and workflow integrations. Each project includes
detailed READMEs, infrastructure diagrams, and deployment runbooks.
</p> </p>
</section> </section>
<section id="code-gallery" className="grid gap-6 lg:grid-cols-3"> <section id="code-gallery" className="grid gap-6 lg:grid-cols-3">
{exampleSnippets.map((snippet) => ( {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"> <CardHeader className="space-y-2">
<div className="flex items-center justify-between"> <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> <Badge variant="outline">{snippet.language}</Badge>
</div> </div>
<CardDescription className="text-gray-300 text-sm"> <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"> <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> <code>{snippet.code}</code>
</pre> </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"> <Link to={snippet.href} target="_blank" rel="noreferrer">
<Github className="mr-2 h-4 w-4" /> <Github className="mr-2 h-4 w-4" />
Open repository Open repository
@ -180,13 +191,17 @@ export default function DocsExamples() {
<section id="templates" className="space-y-4"> <section id="templates" className="space-y-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Flame className="h-6 w-6 text-emerald-300" /> <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>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
{integrationIdeas.map((idea) => ( {integrationIdeas.map((idea) => (
<Card key={idea.title} className="bg-slate-900/60 border-slate-700"> <Card key={idea.title} className="bg-slate-900/60 border-slate-700">
<CardHeader> <CardHeader>
<CardTitle className="text-white text-base">{idea.title}</CardTitle> <CardTitle className="text-white text-base">
{idea.title}
</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<CardDescription className="text-gray-300 text-sm mb-4"> <CardDescription className="text-gray-300 text-sm mb-4">
@ -208,23 +223,38 @@ export default function DocsExamples() {
</div> </div>
</section> </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="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2"> <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"> <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 Publish your own templates or improvements by opening a pull
repository. Every accepted contribution is highlighted in the monthly creator spotlight. request to the public AeThex examples repository. Every accepted
contribution is highlighted in the monthly creator spotlight.
</p> </p>
</div> </div>
<div className="flex gap-3"> <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"> <Link to="https://github.com/aethex/examples" target="_blank">
<Code2 className="mr-2 h-5 w-5" /> <Code2 className="mr-2 h-5 w-5" />
Contribute on GitHub Contribute on GitHub
</Link> </Link>
</Button> </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"> <Link to="/community">
<Share2 className="mr-2 h-5 w-5" /> <Share2 className="mr-2 h-5 w-5" />
Showcase to the community Showcase to the community
@ -234,16 +264,27 @@ export default function DocsExamples() {
</div> </div>
</section> </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="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2"> <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"> <p className="text-gray-300 text-sm">
Our professional services team partners with studios to build tailored pipelines, analytics dashboards, Our professional services team partners with studios to build
and automation workflows on top of AeThex. tailored pipelines, analytics dashboards, and automation workflows
on top of AeThex.
</p> </p>
</div> </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"> <Link to="/consulting">
<Globe className="mr-2 h-5 w-5" /> <Globe className="mr-2 h-5 w-5" />
Talk to AeThex consultants Talk to AeThex consultants

View file

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

View file

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

View file

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

View file

@ -64,19 +64,22 @@ const collaborationWorkflows = [
label: "Onboard & align", label: "Onboard & align",
description: description:
"Welcome teammates through the guided onboarding flow, capture their interests, and assign the right mentorship programs from day one.", "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", label: "Build together",
description: description:
"Kick off projects with shared canvases, synced task boards, and CLI-generated environments. Use the realm switcher to target the correct workspace.", "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", label: "Launch & iterate",
description: description:
"Promote builds through AeThex Deploy, track KPIs in the analytics feed, and publish release notes via the changelog tools.", "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" /> <Sparkles className="mr-2 h-3 w-3" />
Platform Experience Platform Experience
</Badge> </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"> <p className="text-gray-300 max-w-3xl">
Beyond deployment pipelines and CLI tooling, AeThex bundles collaboration, identity, and live-ops systems so Beyond deployment pipelines and CLI tooling, AeThex bundles
teams can craft unforgettable experiences. Use this guide to orient new stakeholders and plan end-to-end collaboration, identity, and live-ops systems so teams can craft
platform rollouts. unforgettable experiences. Use this guide to orient new stakeholders
and plan end-to-end platform rollouts.
</p> </p>
</section> </section>
@ -183,14 +189,22 @@ export default function DocsPlatform() {
{platformPillars.map((pillar) => { {platformPillars.map((pillar) => {
const Icon = pillar.icon; const Icon = pillar.icon;
return ( 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> <CardHeader>
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Icon className="h-6 w-6 text-cyan-300" /> <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> </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 Platform
</Badge> </Badge>
</div> </div>
@ -219,13 +233,20 @@ export default function DocsPlatform() {
<section id="workflows" className="space-y-6"> <section id="workflows" className="space-y-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Workflow className="h-6 w-6 text-cyan-300" /> <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>
<div className="grid gap-6 md:grid-cols-3"> <div className="grid gap-6 md:grid-cols-3">
{collaborationWorkflows.map((stage, index) => ( {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"> <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"> <CardTitle className="text-white text-lg flex items-center gap-2">
<Globe className="h-5 w-5 text-cyan-300" /> <Globe className="h-5 w-5 text-cyan-300" />
{stage.label} {stage.label}
@ -247,15 +268,23 @@ export default function DocsPlatform() {
<section id="modules" className="space-y-6"> <section id="modules" className="space-y-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Compass className="h-6 w-6 text-cyan-300" /> <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>
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
{experienceModules.map((module) => ( {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> <CardHeader>
<CardTitle className="text-white text-base flex items-center justify-between"> <CardTitle className="text-white text-base flex items-center justify-between">
{module.name} {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 Platform
</Badge> </Badge>
</CardTitle> </CardTitle>
@ -283,17 +312,24 @@ export default function DocsPlatform() {
<section id="analytics" className="space-y-6"> <section id="analytics" className="space-y-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<BarChart3 className="h-6 w-6 text-cyan-300" /> <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>
<div className="grid gap-6 md:grid-cols-3"> <div className="grid gap-6 md:grid-cols-3">
{analyticsHighlights.map((item) => { {analyticsHighlights.map((item) => {
const Icon = item.icon; const Icon = item.icon;
return ( 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> <CardHeader>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Icon className="h-6 w-6 text-cyan-300" /> <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> </div>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
@ -310,7 +346,9 @@ export default function DocsPlatform() {
<section id="governance" className="space-y-6"> <section id="governance" className="space-y-6">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ShieldCheck className="h-6 w-6 text-cyan-300" /> <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> </div>
<Card className="bg-slate-900/60 border-slate-700"> <Card className="bg-slate-900/60 border-slate-700">
<CardContent> <CardContent>
@ -323,13 +361,19 @@ export default function DocsPlatform() {
</Card> </Card>
</section> </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="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
<div className="space-y-2"> <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"> <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, Share this page with non-technical teammates. It links out to
product, and operations groups can navigate confidently. every major surface area so marketing, product, and operations
groups can navigate confidently.
</p> </p>
</div> </div>
<div className="grid gap-3 md:grid-cols-2"> <div className="grid gap-3 md:grid-cols-2">

View file

@ -374,7 +374,10 @@ export default function DocsTutorials() {
</div> </div>
</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}> <Link to={tutorial.path}>
Start Tutorial Start Tutorial
<ChevronRight className="h-4 w-4 ml-2" /> <ChevronRight className="h-4 w-4 ml-2" />