Prettier format pending files
This commit is contained in:
parent
16aba004b3
commit
5e3852ecd7
17 changed files with 3112 additions and 2406 deletions
|
|
@ -116,7 +116,10 @@ const App = () => (
|
|||
<Route path="/docs" element={<DocsLayout />}>
|
||||
<Route index element={<DocsOverview />} />
|
||||
<Route path="tutorials" element={<DocsTutorials />} />
|
||||
<Route path="getting-started" element={<DocsGettingStarted />} />
|
||||
<Route
|
||||
path="getting-started"
|
||||
element={<DocsGettingStarted />}
|
||||
/>
|
||||
<Route path="platform" element={<DocsPlatform />} />
|
||||
<Route path="api" element={<DocsApiReference />} />
|
||||
<Route path="cli" element={<DocsCli />} />
|
||||
|
|
|
|||
|
|
@ -33,8 +33,11 @@ const AdminAchievementManager = ({
|
|||
targetUser,
|
||||
}: AdminAchievementManagerProps) => {
|
||||
const [achievements, setAchievements] = useState<AethexAchievement[]>([]);
|
||||
const [userAchievements, setUserAchievements] = useState<AethexAchievement[]>([]);
|
||||
const [selectedAchievementId, setSelectedAchievementId] = useState<string>("");
|
||||
const [userAchievements, setUserAchievements] = useState<AethexAchievement[]>(
|
||||
[],
|
||||
);
|
||||
const [selectedAchievementId, setSelectedAchievementId] =
|
||||
useState<string>("");
|
||||
const [loadingList, setLoadingList] = useState(false);
|
||||
const [loadingUserAchievements, setLoadingUserAchievements] = useState(false);
|
||||
const [awarding, setAwarding] = useState(false);
|
||||
|
|
@ -53,21 +56,18 @@ const AdminAchievementManager = ({
|
|||
}
|
||||
}, []);
|
||||
|
||||
const loadUserAchievements = useCallback(
|
||||
async (userId: string) => {
|
||||
setLoadingUserAchievements(true);
|
||||
try {
|
||||
const list = await aethexAchievementService.getUserAchievements(userId);
|
||||
setUserAchievements(list);
|
||||
} catch (error) {
|
||||
console.warn("Failed to load user achievements", error);
|
||||
setUserAchievements([]);
|
||||
} finally {
|
||||
setLoadingUserAchievements(false);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
const loadUserAchievements = useCallback(async (userId: string) => {
|
||||
setLoadingUserAchievements(true);
|
||||
try {
|
||||
const list = await aethexAchievementService.getUserAchievements(userId);
|
||||
setUserAchievements(list);
|
||||
} catch (error) {
|
||||
console.warn("Failed to load user achievements", error);
|
||||
setUserAchievements([]);
|
||||
} finally {
|
||||
setLoadingUserAchievements(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
loadAchievements().catch(() => undefined);
|
||||
|
|
@ -82,7 +82,10 @@ const AdminAchievementManager = ({
|
|||
}, [targetUser?.id, loadUserAchievements]);
|
||||
|
||||
const selectedAchievement = useMemo(
|
||||
() => achievements.find((achievement) => achievement.id === selectedAchievementId) ?? null,
|
||||
() =>
|
||||
achievements.find(
|
||||
(achievement) => achievement.id === selectedAchievementId,
|
||||
) ?? null,
|
||||
[achievements, selectedAchievementId],
|
||||
);
|
||||
|
||||
|
|
@ -133,7 +136,8 @@ const AdminAchievementManager = ({
|
|||
if (!result) {
|
||||
aethexToast.error({
|
||||
title: "Activation failed",
|
||||
description: "No rewards were activated. Check server logs for details.",
|
||||
description:
|
||||
"No rewards were activated. Check server logs for details.",
|
||||
});
|
||||
} else {
|
||||
const awarded = result.awardedAchievementIds?.length ?? 0;
|
||||
|
|
@ -177,7 +181,10 @@ const AdminAchievementManager = ({
|
|||
{targetUser ? (
|
||||
<div className="rounded border border-border/40 bg-background/40 p-3">
|
||||
<p className="font-medium text-foreground">
|
||||
{targetUser.full_name ?? targetUser.username ?? targetUser.email ?? "Unknown"}
|
||||
{targetUser.full_name ??
|
||||
targetUser.username ??
|
||||
targetUser.email ??
|
||||
"Unknown"}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{targetUser.email ?? "No email on record"}
|
||||
|
|
@ -197,7 +204,11 @@ const AdminAchievementManager = ({
|
|||
disabled={!targetUser || loadingList}
|
||||
>
|
||||
<SelectTrigger className="bg-background/60">
|
||||
<SelectValue placeholder={loadingList ? "Loading achievements…" : "Select achievement"} />
|
||||
<SelectValue
|
||||
placeholder={
|
||||
loadingList ? "Loading achievements…" : "Select achievement"
|
||||
}
|
||||
/>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{achievements.map((achievement) => (
|
||||
|
|
@ -240,7 +251,9 @@ const AdminAchievementManager = ({
|
|||
|
||||
<div>
|
||||
<div className="mb-2 flex items-center justify-between">
|
||||
<p className="text-sm font-medium text-foreground">Achievement history</p>
|
||||
<p className="text-sm font-medium text-foreground">
|
||||
Achievement history
|
||||
</p>
|
||||
{loadingUserAchievements ? (
|
||||
<span className="flex items-center gap-2 text-xs text-muted-foreground">
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" /> Loading…
|
||||
|
|
@ -252,16 +265,24 @@ const AdminAchievementManager = ({
|
|||
{userAchievements.length ? (
|
||||
<ul className="space-y-3 text-sm">
|
||||
{userAchievements.map((achievement) => (
|
||||
<li key={achievement.id} className="flex items-start justify-between gap-3 rounded border border-border/30 bg-background/40 p-3">
|
||||
<li
|
||||
key={achievement.id}
|
||||
className="flex items-start justify-between gap-3 rounded border border-border/30 bg-background/40 p-3"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium text-foreground">{achievement.name}</p>
|
||||
<p className="font-medium text-foreground">
|
||||
{achievement.name}
|
||||
</p>
|
||||
{achievement.description ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{achievement.description}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
<Badge variant="outline" className="whitespace-nowrap text-xs">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="whitespace-nowrap text-xs"
|
||||
>
|
||||
{achievement.xp_reward ?? 0} XP
|
||||
</Badge>
|
||||
</li>
|
||||
|
|
@ -289,12 +310,18 @@ const AdminAchievementManager = ({
|
|||
<p className="mb-1">{selectedAchievement.description}</p>
|
||||
) : null}
|
||||
<div className="flex flex-wrap gap-2 text-[11px] uppercase tracking-wide">
|
||||
<Badge variant="outline">{selectedAchievement.xp_reward ?? 0} XP</Badge>
|
||||
<Badge variant="outline">
|
||||
{selectedAchievement.xp_reward ?? 0} XP
|
||||
</Badge>
|
||||
<Badge variant="outline">ID: {selectedAchievement.id}</Badge>
|
||||
<Badge variant="outline">
|
||||
Created {formatDistanceToNowStrict(new Date(selectedAchievement.created_at), {
|
||||
addSuffix: true,
|
||||
})}
|
||||
Created{" "}
|
||||
{formatDistanceToNowStrict(
|
||||
new Date(selectedAchievement.created_at),
|
||||
{
|
||||
addSuffix: true,
|
||||
},
|
||||
)}
|
||||
</Badge>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,11 @@ const buildProfileDraft = (profile: AethexUserProfile): ProfileDraft => ({
|
|||
: "0",
|
||||
});
|
||||
|
||||
const ensureOwnerRoles = (roles: string[], profile: AethexUserProfile | null, ownerEmail: string) => {
|
||||
const ensureOwnerRoles = (
|
||||
roles: string[],
|
||||
profile: AethexUserProfile | null,
|
||||
ownerEmail: string,
|
||||
) => {
|
||||
if (!profile) return roles;
|
||||
if ((profile.email ?? "").toLowerCase() !== ownerEmail.toLowerCase()) {
|
||||
return roles;
|
||||
|
|
@ -138,21 +142,18 @@ const AdminMemberManager = ({
|
|||
}
|
||||
}, [profiles, selectedId, onSelectedIdChange]);
|
||||
|
||||
const loadRoles = useCallback(
|
||||
async (id: string) => {
|
||||
setLoadingRoles(true);
|
||||
try {
|
||||
const fetched = await aethexRoleService.getUserRoles(id);
|
||||
setRoles(normalizeRoles(fetched));
|
||||
} catch (error) {
|
||||
console.warn("Failed to load user roles", error);
|
||||
setRoles(["member"]);
|
||||
} finally {
|
||||
setLoadingRoles(false);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
const loadRoles = useCallback(async (id: string) => {
|
||||
setLoadingRoles(true);
|
||||
try {
|
||||
const fetched = await aethexRoleService.getUserRoles(id);
|
||||
setRoles(normalizeRoles(fetched));
|
||||
} catch (error) {
|
||||
console.warn("Failed to load user roles", error);
|
||||
setRoles(["member"]);
|
||||
} finally {
|
||||
setLoadingRoles(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedProfile) {
|
||||
|
|
@ -217,7 +218,8 @@ const AdminMemberManager = ({
|
|||
console.error("Failed to set user roles", error);
|
||||
aethexToast.error({
|
||||
title: "Role update failed",
|
||||
description: error?.message || "Unable to update roles. Check Supabase policies.",
|
||||
description:
|
||||
error?.message || "Unable to update roles. Check Supabase policies.",
|
||||
});
|
||||
} finally {
|
||||
setSavingRoles(false);
|
||||
|
|
@ -242,7 +244,8 @@ const AdminMemberManager = ({
|
|||
updates.total_xp = Number(profileDraft.total_xp) || 0;
|
||||
}
|
||||
if (profileDraft.loyalty_points.trim().length) {
|
||||
(updates as any).loyalty_points = Number(profileDraft.loyalty_points) || 0;
|
||||
(updates as any).loyalty_points =
|
||||
Number(profileDraft.loyalty_points) || 0;
|
||||
}
|
||||
await aethexUserService.updateProfile(selectedProfile.id, updates);
|
||||
aethexToast.success({
|
||||
|
|
@ -254,7 +257,9 @@ const AdminMemberManager = ({
|
|||
console.error("Failed to update profile", error);
|
||||
aethexToast.error({
|
||||
title: "Profile update failed",
|
||||
description: error?.message || "Supabase rejected the update. Review payload and RLS policies.",
|
||||
description:
|
||||
error?.message ||
|
||||
"Supabase rejected the update. Review payload and RLS policies.",
|
||||
});
|
||||
} finally {
|
||||
setSavingProfile(false);
|
||||
|
|
@ -273,7 +278,9 @@ const AdminMemberManager = ({
|
|||
<div className="flex items-center justify-between gap-2">
|
||||
<div>
|
||||
<CardTitle>Directory</CardTitle>
|
||||
<CardDescription>Search and select members to administer.</CardDescription>
|
||||
<CardDescription>
|
||||
Search and select members to administer.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
|
|
@ -320,7 +327,9 @@ const AdminMemberManager = ({
|
|||
>
|
||||
<TableCell className="font-medium text-foreground/90">
|
||||
<div className="flex flex-col">
|
||||
<span>{profile.full_name || profile.username || "Unknown"}</span>
|
||||
<span>
|
||||
{profile.full_name || profile.username || "Unknown"}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{profile.username}
|
||||
</span>
|
||||
|
|
@ -339,7 +348,10 @@ const AdminMemberManager = ({
|
|||
})}
|
||||
{!filteredProfiles.length ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="text-center text-muted-foreground">
|
||||
<TableCell
|
||||
colSpan={3}
|
||||
className="text-center text-muted-foreground"
|
||||
>
|
||||
No members found.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
@ -410,7 +422,11 @@ const AdminMemberManager = ({
|
|||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{experienceOptions.map((option) => (
|
||||
<SelectItem key={option} value={option} className="capitalize">
|
||||
<SelectItem
|
||||
key={option}
|
||||
value={option}
|
||||
className="capitalize"
|
||||
>
|
||||
{option.replace("_", " ")}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
@ -432,7 +448,11 @@ const AdminMemberManager = ({
|
|||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{userTypeOptions.map((option) => (
|
||||
<SelectItem key={option} value={option} className="capitalize">
|
||||
<SelectItem
|
||||
key={option}
|
||||
value={option}
|
||||
className="capitalize"
|
||||
>
|
||||
{option.replace("_", " ")}
|
||||
</SelectItem>
|
||||
))}
|
||||
|
|
@ -463,7 +483,9 @@ const AdminMemberManager = ({
|
|||
value={profileDraft.level}
|
||||
onChange={(event) =>
|
||||
setProfileDraft((draft) =>
|
||||
draft ? { ...draft, level: event.target.value } : draft,
|
||||
draft
|
||||
? { ...draft, level: event.target.value }
|
||||
: draft,
|
||||
)
|
||||
}
|
||||
inputMode="numeric"
|
||||
|
|
@ -553,7 +575,10 @@ const AdminMemberManager = ({
|
|||
variant={active ? "default" : "outline"}
|
||||
size="sm"
|
||||
onClick={() => handleRoleToggle(role)}
|
||||
className={cn("capitalize", active && "bg-aethex-500/80")}
|
||||
className={cn(
|
||||
"capitalize",
|
||||
active && "bg-aethex-500/80",
|
||||
)}
|
||||
>
|
||||
{role}
|
||||
</Button>
|
||||
|
|
@ -587,7 +612,11 @@ const AdminMemberManager = ({
|
|||
</span>
|
||||
) : (
|
||||
roles.map((role) => (
|
||||
<Badge key={role} variant="outline" className="capitalize">
|
||||
<Badge
|
||||
key={role}
|
||||
variant="outline"
|
||||
className="capitalize"
|
||||
>
|
||||
{role}
|
||||
</Badge>
|
||||
))
|
||||
|
|
@ -610,7 +639,9 @@ const AdminMemberManager = ({
|
|||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => selectedProfile && loadRoles(selectedProfile.id)}
|
||||
onClick={() =>
|
||||
selectedProfile && loadRoles(selectedProfile.id)
|
||||
}
|
||||
disabled={loadingRoles}
|
||||
>
|
||||
<RefreshCw className="mr-2 h-4 w-4" /> Reload
|
||||
|
|
|
|||
|
|
@ -33,15 +33,18 @@ export const AdminStatCard = ({
|
|||
<Card className="bg-card/60 border-border/40 backdrop-blur">
|
||||
<CardHeader className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-base text-foreground/90">{title}</CardTitle>
|
||||
<Badge variant="outline" className="border-border/40 text-xs text-muted-foreground">
|
||||
<CardTitle className="text-base text-foreground/90">
|
||||
{title}
|
||||
</CardTitle>
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-border/40 text-xs text-muted-foreground"
|
||||
>
|
||||
Admin metric
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="text-3xl font-semibold text-gradient">
|
||||
{value}
|
||||
</div>
|
||||
<div className="text-3xl font-semibold text-gradient">{value}</div>
|
||||
<Icon className={`h-8 w-8 ${toneConfig[tone]}`} />
|
||||
</div>
|
||||
{trend ? (
|
||||
|
|
@ -51,7 +54,9 @@ export const AdminStatCard = ({
|
|||
{(description || actions) && (
|
||||
<CardContent className="space-y-3">
|
||||
{description ? (
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">{description}</p>
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
{description}
|
||||
</p>
|
||||
) : null}
|
||||
{actions}
|
||||
</CardContent>
|
||||
|
|
|
|||
|
|
@ -11,13 +11,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Heart,
|
||||
MessageCircle,
|
||||
Share2,
|
||||
Volume2,
|
||||
VolumeX,
|
||||
} from "lucide-react";
|
||||
import { Heart, MessageCircle, Share2, Volume2, VolumeX } from "lucide-react";
|
||||
import type { FeedItem } from "@/pages/Feed";
|
||||
|
||||
interface FeedItemCardProps {
|
||||
|
|
@ -42,7 +36,10 @@ export function FeedItemCard({
|
|||
<div className="flex items-start justify-between gap-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<Avatar className="h-12 w-12 ring-2 ring-aethex-500/30">
|
||||
<AvatarImage src={item.authorAvatar || undefined} alt={item.authorName} />
|
||||
<AvatarImage
|
||||
src={item.authorAvatar || undefined}
|
||||
alt={item.authorName}
|
||||
/>
|
||||
<AvatarFallback className="bg-aethex-500/10 text-aethex-300">
|
||||
{item.authorName?.[0]?.toUpperCase() || "U"}
|
||||
</AvatarFallback>
|
||||
|
|
@ -132,7 +129,10 @@ export function FeedItemCard({
|
|||
</Button>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant="outline" className="border-border/60 bg-background/60 text-xs uppercase tracking-wide">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-border/60 bg-background/60 text-xs uppercase tracking-wide"
|
||||
>
|
||||
{item.mediaType === "video"
|
||||
? "Video"
|
||||
: item.mediaType === "image"
|
||||
|
|
|
|||
|
|
@ -183,7 +183,10 @@ let shouldEnableSkipAgent = false;
|
|||
// Example override (console): window.__AETHEX_SKIP_AGENT_CONFIG = { src: 'https://example.com/agent.js', id: '...' };
|
||||
const getRuntimeConfig = () => {
|
||||
try {
|
||||
if (typeof window !== "undefined" && (window as any).__AETHEX_SKIP_AGENT_CONFIG) {
|
||||
if (
|
||||
typeof window !== "undefined" &&
|
||||
(window as any).__AETHEX_SKIP_AGENT_CONFIG
|
||||
) {
|
||||
return (window as any).__AETHEX_SKIP_AGENT_CONFIG as {
|
||||
src?: string;
|
||||
id?: string;
|
||||
|
|
@ -217,7 +220,10 @@ const createSkipAgentTheme = () => {
|
|||
|
||||
const isDocsPath = () => {
|
||||
try {
|
||||
return typeof window !== "undefined" && window.location.pathname.startsWith("/docs");
|
||||
return (
|
||||
typeof window !== "undefined" &&
|
||||
window.location.pathname.startsWith("/docs")
|
||||
);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -273,9 +279,9 @@ const isSkipAgentReachable = async (): Promise<boolean> => {
|
|||
throw new Error(`Agent status request failed with ${response.status}`);
|
||||
}
|
||||
|
||||
const payload = (await response.json().catch(() => null)) as
|
||||
| { active?: boolean }
|
||||
| null;
|
||||
const payload = (await response.json().catch(() => null)) as {
|
||||
active?: boolean;
|
||||
} | null;
|
||||
|
||||
if (payload && payload.active === false) {
|
||||
throw new Error("Agent reported inactive");
|
||||
|
|
@ -413,7 +419,9 @@ const loadSkipAgent = async (): Promise<void> => {
|
|||
}
|
||||
|
||||
const scriptText = await response.text();
|
||||
const blobUrl = URL.createObjectURL(new Blob([scriptText], { type: "application/javascript" }));
|
||||
const blobUrl = URL.createObjectURL(
|
||||
new Blob([scriptText], { type: "application/javascript" }),
|
||||
);
|
||||
|
||||
const script = document.createElement("script");
|
||||
script.id = SKIP_AGENT_SCRIPT_ID;
|
||||
|
|
|
|||
|
|
@ -171,12 +171,17 @@ export default function Admin() {
|
|||
<CardHeader>
|
||||
<CardTitle className="text-red-400">Access denied</CardTitle>
|
||||
<CardDescription>
|
||||
This panel is restricted to {ownerEmail}. If you need access, contact the site owner.
|
||||
This panel is restricted to {ownerEmail}. If you need access,
|
||||
contact the site owner.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex gap-2">
|
||||
<Button onClick={() => navigate("/dashboard")}>Go to dashboard</Button>
|
||||
<Button variant="outline" onClick={() => navigate("/support")}>Contact support</Button>
|
||||
<Button onClick={() => navigate("/dashboard")}>
|
||||
Go to dashboard
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => navigate("/support")}>
|
||||
Contact support
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
@ -191,7 +196,8 @@ export default function Admin() {
|
|||
|
||||
const selectedMember = useMemo(
|
||||
() =>
|
||||
managedProfiles.find((profile) => profile.id === selectedMemberId) ?? null,
|
||||
managedProfiles.find((profile) => profile.id === selectedMemberId) ??
|
||||
null,
|
||||
[managedProfiles, selectedMemberId],
|
||||
);
|
||||
|
||||
|
|
@ -200,7 +206,9 @@ export default function Admin() {
|
|||
const featuredStudios = studios.length;
|
||||
const pendingApplications = applications.filter((app) => {
|
||||
const status = (app.status ?? "").toLowerCase();
|
||||
return status !== "approved" && status !== "completed" && status !== "closed";
|
||||
return (
|
||||
status !== "approved" && status !== "completed" && status !== "closed"
|
||||
);
|
||||
}).length;
|
||||
|
||||
const overviewStats = useMemo(
|
||||
|
|
@ -209,7 +217,9 @@ export default function Admin() {
|
|||
title: "Total members",
|
||||
value: totalMembers ? totalMembers.toString() : "—",
|
||||
description: "Profiles synced from AeThex identity service.",
|
||||
trend: totalMembers ? `${totalMembers} active profiles` : "Awaiting sync",
|
||||
trend: totalMembers
|
||||
? `${totalMembers} active profiles`
|
||||
: "Awaiting sync",
|
||||
icon: Users,
|
||||
tone: "blue" as const,
|
||||
},
|
||||
|
|
@ -365,23 +375,37 @@ export default function Admin() {
|
|||
<div className="container mx-auto px-4 max-w-6xl space-y-8">
|
||||
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h1 className="text-3xl font-bold text-gradient">Admin Control Center</h1>
|
||||
<h1 className="text-3xl font-bold text-gradient">
|
||||
Admin Control Center
|
||||
</h1>
|
||||
<p className="text-muted-foreground">
|
||||
Unified oversight for AeThex operations, content, and community.
|
||||
</p>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Badge variant="outline" className="border-green-500/50 text-green-300">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-green-500/50 text-green-300"
|
||||
>
|
||||
Owner
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-blue-500/50 text-blue-300">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-blue-500/50 text-blue-300"
|
||||
>
|
||||
Admin
|
||||
</Badge>
|
||||
<Badge variant="outline" className="border-purple-500/50 text-purple-300">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-purple-500/50 text-purple-300"
|
||||
>
|
||||
Founder
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Signed in as <span className="text-foreground">{normalizedEmail || ownerEmail}</span>
|
||||
Signed in as{" "}
|
||||
<span className="text-foreground">
|
||||
{normalizedEmail || ownerEmail}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
|
|
@ -391,11 +415,17 @@ export default function Admin() {
|
|||
<Button variant="outline" onClick={() => navigate("/profile")}>
|
||||
Profile
|
||||
</Button>
|
||||
<Button onClick={() => setActiveTab("content")}>Create update</Button>
|
||||
<Button onClick={() => setActiveTab("content")}>
|
||||
Create update
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab} className="space-y-6">
|
||||
<Tabs
|
||||
value={activeTab}
|
||||
onValueChange={setActiveTab}
|
||||
className="space-y-6"
|
||||
>
|
||||
<TabsList className="w-full justify-start gap-2 overflow-x-auto border border-border/40 bg-background/40 px-1 py-1 backdrop-blur">
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="content">Content</TabsTrigger>
|
||||
|
|
@ -425,23 +455,31 @@ export default function Admin() {
|
|||
<Command className="h-5 w-5 text-aethex-300" />
|
||||
<CardTitle>Quick actions</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Launch frequent administrative workflows.</CardDescription>
|
||||
<CardDescription>
|
||||
Launch frequent administrative workflows.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="grid gap-3">
|
||||
{quickActions.map(({ label, description, icon: ActionIcon, action }) => (
|
||||
<button
|
||||
key={label}
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="group flex items-start gap-3 rounded-lg border border-border/30 bg-background/40 px-4 py-3 text-left transition hover:border-aethex-400/60 hover:bg-background/60"
|
||||
>
|
||||
<ActionIcon className="mt-0.5 h-5 w-5 text-aethex-400 transition group-hover:text-aethex-200" />
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium text-foreground">{label}</p>
|
||||
<p className="text-sm text-muted-foreground">{description}</p>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
{quickActions.map(
|
||||
({ label, description, icon: ActionIcon, action }) => (
|
||||
<button
|
||||
key={label}
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="group flex items-start gap-3 rounded-lg border border-border/30 bg-background/40 px-4 py-3 text-left transition hover:border-aethex-400/60 hover:bg-background/60"
|
||||
>
|
||||
<ActionIcon className="mt-0.5 h-5 w-5 text-aethex-400 transition group-hover:text-aethex-200" />
|
||||
<div className="space-y-1">
|
||||
<p className="font-medium text-foreground">
|
||||
{label}
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
</button>
|
||||
),
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
|
@ -451,21 +489,37 @@ export default function Admin() {
|
|||
<Shield className="h-5 w-5 text-green-400" />
|
||||
<CardTitle>Access control</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Owner-only access enforced via Supabase roles.</CardDescription>
|
||||
<CardDescription>
|
||||
Owner-only access enforced via Supabase roles.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-sm text-muted-foreground">
|
||||
<ul className="space-y-2 leading-relaxed">
|
||||
<li>
|
||||
Owner email: <span className="text-foreground">{ownerEmail}</span>
|
||||
Owner email:{" "}
|
||||
<span className="text-foreground">{ownerEmail}</span>
|
||||
</li>
|
||||
<li>
|
||||
Roles are provisioned automatically on owner sign-in.
|
||||
</li>
|
||||
<li>
|
||||
Grant additional admins by updating Supabase role
|
||||
assignments.
|
||||
</li>
|
||||
<li>Roles are provisioned automatically on owner sign-in.</li>
|
||||
<li>Grant additional admins by updating Supabase role assignments.</li>
|
||||
</ul>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={() => setActiveTab("community")}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => setActiveTab("community")}
|
||||
>
|
||||
View members
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={() => navigate("/support")}>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => navigate("/support")}
|
||||
>
|
||||
Contact support
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -482,12 +536,18 @@ export default function Admin() {
|
|||
<CardTitle>Content overview</CardTitle>
|
||||
</div>
|
||||
<CardDescription>
|
||||
{publishedPosts} published {publishedPosts === 1 ? "post" : "posts"} · {loadingPosts ? "refreshing content…" : "latest Supabase sync"}
|
||||
{publishedPosts} published{" "}
|
||||
{publishedPosts === 1 ? "post" : "posts"} ·{" "}
|
||||
{loadingPosts
|
||||
? "refreshing content…"
|
||||
: "latest Supabase sync"}
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="text-sm text-muted-foreground space-y-2">
|
||||
<p>
|
||||
Drafts and announcements appear instantly on the public blog after saving. Use scheduled releases for major updates and keep thumbnails optimised for 1200×630.
|
||||
Drafts and announcements appear instantly on the public blog
|
||||
after saving. Use scheduled releases for major updates and
|
||||
keep thumbnails optimised for 1200×630.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -498,7 +558,9 @@ export default function Admin() {
|
|||
<PenTool className="h-5 w-5 text-aethex-400" />
|
||||
<CardTitle className="text-lg">Blog posts</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Manage blog content stored in Supabase</CardDescription>
|
||||
<CardDescription>
|
||||
Manage blog content stored in Supabase
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
|
|
@ -538,7 +600,8 @@ export default function Admin() {
|
|||
|
||||
{blogPosts.length === 0 && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
No posts loaded yet. Use “Refresh” or “Add post” to start managing content.
|
||||
No posts loaded yet. Use “Refresh” or “Add post” to start
|
||||
managing content.
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
|
@ -680,14 +743,28 @@ export default function Admin() {
|
|||
<UserCog className="h-5 w-5 text-teal-300" />
|
||||
<CardTitle>Community actions</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Grow the network and celebrate contributors.</CardDescription>
|
||||
<CardDescription>
|
||||
Grow the network and celebrate contributors.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap gap-2">
|
||||
<Button size="sm" onClick={() => navigate("/community")}>Open community hub</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => navigate("/mentorship")}>
|
||||
<Button size="sm" onClick={() => navigate("/community")}>
|
||||
Open community hub
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => navigate("/mentorship")}
|
||||
>
|
||||
Manage mentorships
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => navigate("/support")}>Support queue</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => navigate("/support")}
|
||||
>
|
||||
Support queue
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
|
@ -700,7 +777,9 @@ export default function Admin() {
|
|||
<Settings className="h-5 w-5 text-yellow-300" />
|
||||
<CardTitle>Featured studios</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Control studios highlighted across AeThex experiences.</CardDescription>
|
||||
<CardDescription>
|
||||
Control studios highlighted across AeThex experiences.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3">
|
||||
{studios.map((s, i) => (
|
||||
|
|
@ -762,7 +841,9 @@ export default function Admin() {
|
|||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setStudios(studios.filter((_, idx) => idx !== i))}
|
||||
onClick={() =>
|
||||
setStudios(studios.filter((_, idx) => idx !== i))
|
||||
}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
|
|
@ -773,7 +854,9 @@ export default function Admin() {
|
|||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setStudios([...studios, { name: "New Studio" }])}
|
||||
onClick={() =>
|
||||
setStudios([...studios, { name: "New Studio" }])
|
||||
}
|
||||
>
|
||||
Add studio
|
||||
</Button>
|
||||
|
|
@ -788,12 +871,14 @@ export default function Admin() {
|
|||
if (!resp.ok) {
|
||||
aethexToast.error({
|
||||
title: "Save failed",
|
||||
description: "Unable to persist featured studios.",
|
||||
description:
|
||||
"Unable to persist featured studios.",
|
||||
});
|
||||
} else {
|
||||
aethexToast.success({
|
||||
title: "Studios saved",
|
||||
description: "Featured studios updated successfully.",
|
||||
description:
|
||||
"Featured studios updated successfully.",
|
||||
});
|
||||
}
|
||||
}}
|
||||
|
|
@ -810,7 +895,9 @@ export default function Admin() {
|
|||
<ClipboardList className="h-5 w-5 text-sky-300" />
|
||||
<CardTitle>Project applications</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Review collaboration requests and prioritize approvals.</CardDescription>
|
||||
<CardDescription>
|
||||
Review collaboration requests and prioritize approvals.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-sm text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
|
|
@ -847,7 +934,9 @@ export default function Admin() {
|
|||
>
|
||||
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||
<p className="font-medium text-foreground">
|
||||
{app.applicant_name || app.applicant_email || "Unknown applicant"}
|
||||
{app.applicant_name ||
|
||||
app.applicant_email ||
|
||||
"Unknown applicant"}
|
||||
</p>
|
||||
<Badge variant="outline" className="capitalize">
|
||||
{(app.status ?? "pending").toLowerCase()}
|
||||
|
|
@ -858,14 +947,18 @@ export default function Admin() {
|
|||
</p>
|
||||
{app.created_at ? (
|
||||
<p className="text-[11px] text-muted-foreground/80">
|
||||
Submitted {new Date(app.created_at).toLocaleString()}
|
||||
Submitted{" "}
|
||||
{new Date(app.created_at).toLocaleString()}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p>No applications on file. Encourage partners to apply via briefs.</p>
|
||||
<p>
|
||||
No applications on file. Encourage partners to apply via
|
||||
briefs.
|
||||
</p>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -876,7 +969,9 @@ export default function Admin() {
|
|||
<Activity className="h-5 w-5 text-orange-300" />
|
||||
<CardTitle>System status</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Auth, database, and realtime services.</CardDescription>
|
||||
<CardDescription>
|
||||
Auth, database, and realtime services.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="text-sm text-muted-foreground space-y-1">
|
||||
<p>Auth: Operational</p>
|
||||
|
|
@ -891,11 +986,16 @@ export default function Admin() {
|
|||
<UserCog className="h-5 w-5 text-teal-300" />
|
||||
<CardTitle>Your account</CardTitle>
|
||||
</div>
|
||||
<CardDescription>Owner privileges are active.</CardDescription>
|
||||
<CardDescription>
|
||||
Owner privileges are active.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="text-sm text-muted-foreground space-y-2">
|
||||
<p>Signed in as {user.email}.</p>
|
||||
<p>You have full administrative access across AeThex services.</p>
|
||||
<p>
|
||||
You have full administrative access across AeThex
|
||||
services.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -84,9 +84,9 @@ export default function Feed() {
|
|||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [following, setFollowing] = useState<string[]>([]);
|
||||
const [items, setItems] = useState<FeedItem[]>([]);
|
||||
const [activeFilter, setActiveFilter] = useState<"all" | "following" | "trending">(
|
||||
"all",
|
||||
);
|
||||
const [activeFilter, setActiveFilter] = useState<
|
||||
"all" | "following" | "trending"
|
||||
>("all");
|
||||
|
||||
const fetchFeed = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
|
|
@ -202,11 +202,14 @@ export default function Feed() {
|
|||
const filteredItems = useMemo(() => {
|
||||
if (activeFilter === "following") {
|
||||
return items.filter(
|
||||
(item) => isFollowingAuthor(item.authorId) || item.authorId === user?.id,
|
||||
(item) =>
|
||||
isFollowingAuthor(item.authorId) || item.authorId === user?.id,
|
||||
);
|
||||
}
|
||||
if (activeFilter === "trending") {
|
||||
return [...items].sort((a, b) => b.likes + b.comments - (a.likes + a.comments));
|
||||
return [...items].sort(
|
||||
(a, b) => b.likes + b.comments - (a.likes + a.comments),
|
||||
);
|
||||
}
|
||||
return items;
|
||||
}, [activeFilter, isFollowingAuthor, items, user?.id]);
|
||||
|
|
@ -259,7 +262,8 @@ export default function Feed() {
|
|||
const totalEngagement = useMemo(
|
||||
() =>
|
||||
items.reduce(
|
||||
(acc, item) => acc + (Number(item.likes) || 0) + (Number(item.comments) || 0),
|
||||
(acc, item) =>
|
||||
acc + (Number(item.likes) || 0) + (Number(item.comments) || 0),
|
||||
0,
|
||||
),
|
||||
[items],
|
||||
|
|
@ -271,7 +275,10 @@ export default function Feed() {
|
|||
}, [items.length, totalEngagement]);
|
||||
|
||||
const handleScrollToComposer = useCallback(() => {
|
||||
composerRef.current?.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
composerRef.current?.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleManualRefresh = useCallback(() => {
|
||||
|
|
@ -280,7 +287,11 @@ export default function Feed() {
|
|||
|
||||
if (loading || (isLoading && items.length === 0)) {
|
||||
return (
|
||||
<LoadingScreen message="Loading your feed..." showProgress duration={1000} />
|
||||
<LoadingScreen
|
||||
message="Loading your feed..."
|
||||
showProgress
|
||||
duration={1000}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -297,11 +308,15 @@ export default function Feed() {
|
|||
Community Pulse
|
||||
</h1>
|
||||
<p className="mt-2 max-w-2xl text-sm text-muted-foreground sm:text-base">
|
||||
Discover new creations, amplify your voice, and engage with the AeThex community in real time.
|
||||
Discover new creations, amplify your voice, and engage with
|
||||
the AeThex community in real time.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<Badge variant="outline" className="border-aethex-400/60 bg-aethex-400/10 text-aethex-100">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="border-aethex-400/60 bg-aethex-400/10 text-aethex-100"
|
||||
>
|
||||
Live updates enabled
|
||||
</Badge>
|
||||
<Button
|
||||
|
|
@ -316,28 +331,26 @@ export default function Feed() {
|
|||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
{(
|
||||
[
|
||||
{
|
||||
key: "all" as const,
|
||||
label: "All stories",
|
||||
icon: Sparkles,
|
||||
description: "Latest community activity",
|
||||
},
|
||||
{
|
||||
key: "following" as const,
|
||||
label: "Following",
|
||||
icon: Users,
|
||||
description: "People you follow",
|
||||
},
|
||||
{
|
||||
key: "trending" as const,
|
||||
label: "Trending",
|
||||
icon: Flame,
|
||||
description: "Most engagement",
|
||||
},
|
||||
]
|
||||
).map(({ key, label, icon: Icon, description }) => (
|
||||
{[
|
||||
{
|
||||
key: "all" as const,
|
||||
label: "All stories",
|
||||
icon: Sparkles,
|
||||
description: "Latest community activity",
|
||||
},
|
||||
{
|
||||
key: "following" as const,
|
||||
label: "Following",
|
||||
icon: Users,
|
||||
description: "People you follow",
|
||||
},
|
||||
{
|
||||
key: "trending" as const,
|
||||
label: "Trending",
|
||||
icon: Flame,
|
||||
description: "Most engagement",
|
||||
},
|
||||
].map(({ key, label, icon: Icon, description }) => (
|
||||
<Button
|
||||
key={key}
|
||||
variant={activeFilter === key ? "default" : "outline"}
|
||||
|
|
@ -352,7 +365,9 @@ export default function Feed() {
|
|||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
<span className="font-medium">{label}</span>
|
||||
<span className="hidden text-xs sm:inline">{description}</span>
|
||||
<span className="hidden text-xs sm:inline">
|
||||
{description}
|
||||
</span>
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
|
|
@ -368,9 +383,12 @@ export default function Feed() {
|
|||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-foreground">Share something new</h2>
|
||||
<h2 className="text-lg font-semibold text-foreground">
|
||||
Share something new
|
||||
</h2>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Post updates, showcase progress, or spark a conversation with the community.
|
||||
Post updates, showcase progress, or spark a conversation
|
||||
with the community.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
|
|
@ -386,7 +404,8 @@ export default function Feed() {
|
|||
<div className="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-border/30 bg-background/60 p-4 text-sm text-muted-foreground">
|
||||
<div className="flex items-center gap-2">
|
||||
<Sparkles className="h-4 w-4 text-aethex-300" />
|
||||
Your post is shared instantly with followers and the broader community.
|
||||
Your post is shared instantly with followers and the broader
|
||||
community.
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
|
|
@ -403,7 +422,8 @@ export default function Feed() {
|
|||
<CardHeader>
|
||||
<CardTitle className="text-xl">No stories found</CardTitle>
|
||||
<CardDescription>
|
||||
Try switching filters or follow more creators to personalize your feed.
|
||||
Try switching filters or follow more creators to
|
||||
personalize your feed.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-0">
|
||||
|
|
@ -434,7 +454,9 @@ export default function Feed() {
|
|||
<aside className="space-y-6">
|
||||
<Card className="rounded-3xl border-border/40 bg-background/70 shadow-xl backdrop-blur-lg">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Your community snapshot</CardTitle>
|
||||
<CardTitle className="text-lg">
|
||||
Your community snapshot
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Track how your network is evolving at a glance.
|
||||
</CardDescription>
|
||||
|
|
@ -442,25 +464,33 @@ export default function Feed() {
|
|||
<CardContent className="space-y-4">
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
|
||||
<p className="text-xs uppercase text-muted-foreground">Stories today</p>
|
||||
<p className="text-xs uppercase text-muted-foreground">
|
||||
Stories today
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-foreground">
|
||||
{items.length.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
|
||||
<p className="text-xs uppercase text-muted-foreground">Creators you follow</p>
|
||||
<p className="text-xs uppercase text-muted-foreground">
|
||||
Creators you follow
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-foreground">
|
||||
{following.length.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
|
||||
<p className="text-xs uppercase text-muted-foreground">Community reactions</p>
|
||||
<p className="text-xs uppercase text-muted-foreground">
|
||||
Community reactions
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-foreground">
|
||||
{totalEngagement.toLocaleString()}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-2xl border border-border/30 bg-background/60 p-4">
|
||||
<p className="text-xs uppercase text-muted-foreground">Avg. engagement</p>
|
||||
<p className="text-xs uppercase text-muted-foreground">
|
||||
Avg. engagement
|
||||
</p>
|
||||
<p className="mt-1 text-2xl font-semibold text-foreground">
|
||||
{averageEngagement.toLocaleString()}
|
||||
</p>
|
||||
|
|
@ -492,7 +522,8 @@ export default function Feed() {
|
|||
<CardContent className="space-y-3">
|
||||
{trendingTopics.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Start a conversation by adding hashtags like #gamedev or #design to your next post.
|
||||
Start a conversation by adding hashtags like #gamedev or
|
||||
#design to your next post.
|
||||
</p>
|
||||
) : (
|
||||
trendingTopics.map((topic, index) => (
|
||||
|
|
@ -505,7 +536,9 @@ export default function Feed() {
|
|||
{index + 1}
|
||||
</span>
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{topic.topic}</p>
|
||||
<p className="font-medium text-foreground">
|
||||
{topic.topic}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{topic.count.toLocaleString()} mentions today
|
||||
</p>
|
||||
|
|
@ -535,7 +568,8 @@ export default function Feed() {
|
|||
<CardContent className="space-y-3">
|
||||
{suggestedCreators.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
You are up to date with the creators you follow. Engage with new posts to unlock more suggestions.
|
||||
You are up to date with the creators you follow. Engage
|
||||
with new posts to unlock more suggestions.
|
||||
</p>
|
||||
) : (
|
||||
suggestedCreators.map((creator) => (
|
||||
|
|
@ -554,9 +588,12 @@ export default function Feed() {
|
|||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div>
|
||||
<p className="font-medium text-foreground">{creator.name}</p>
|
||||
<p className="font-medium text-foreground">
|
||||
{creator.name}
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{creator.posts.toLocaleString()} posts · {creator.likes.toLocaleString()} reactions
|
||||
{creator.posts.toLocaleString()} posts ·{" "}
|
||||
{creator.likes.toLocaleString()} reactions
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -69,11 +69,13 @@ const webhookTopics = [
|
|||
},
|
||||
{
|
||||
event: "incident.opened",
|
||||
description: "Raised when monitoring detects outages or SLA breaches in production environments.",
|
||||
description:
|
||||
"Raised when monitoring detects outages or SLA breaches in production environments.",
|
||||
},
|
||||
{
|
||||
event: "member.invited",
|
||||
description: "Notify downstream systems when a collaborator invitation is created or accepted.",
|
||||
description:
|
||||
"Notify downstream systems when a collaborator invitation is created or accepted.",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -108,11 +110,13 @@ export default function DocsApiReference() {
|
|||
<ServerCog className="mr-2 h-3 w-3" />
|
||||
API Reference
|
||||
</Badge>
|
||||
<h2 className="text-3xl font-semibold text-white">Integrate programmatically with the AeThex API</h2>
|
||||
<h2 className="text-3xl font-semibold text-white">
|
||||
Integrate programmatically with the AeThex API
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
The REST API exposes every core capability of the AeThex platform. Authenticate with OAuth 2.1 or
|
||||
personal access tokens, call idempotent endpoints, and subscribe to webhooks to react to changes in real
|
||||
time.
|
||||
The REST API exposes every core capability of the AeThex platform.
|
||||
Authenticate with OAuth 2.1 or personal access tokens, call idempotent
|
||||
endpoints, and subscribe to webhooks to react to changes in real time.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -125,16 +129,20 @@ export default function DocsApiReference() {
|
|||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 text-gray-300">
|
||||
<p>Use the OAuth client credentials grant for service-to-service integrations:</p>
|
||||
<p>
|
||||
Use the OAuth client credentials grant for service-to-service
|
||||
integrations:
|
||||
</p>
|
||||
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-sm text-blue-200">
|
||||
{`curl -X POST https://api.aethex.dev/v1/auth/token \
|
||||
{`curl -X POST https://api.aethex.dev/v1/auth/token \
|
||||
-u CLIENT_ID:CLIENT_SECRET \
|
||||
-d "grant_type=client_credentials" \
|
||||
-d "scope=projects:read deployments:write"`}
|
||||
</pre>
|
||||
<p>
|
||||
Prefer user-scoped access? Direct builders through the hosted OAuth consent screen and exchange their
|
||||
authorization code using the same endpoint.
|
||||
Prefer user-scoped access? Direct builders through the hosted
|
||||
OAuth consent screen and exchange their authorization code using
|
||||
the same endpoint.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -148,10 +156,11 @@ export default function DocsApiReference() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4 text-gray-300">
|
||||
<p className="text-gray-300">
|
||||
Call the Projects endpoint with your Bearer token and inspect pagination headers for large result sets.
|
||||
Call the Projects endpoint with your Bearer token and inspect
|
||||
pagination headers for large result sets.
|
||||
</p>
|
||||
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-sm text-teal-200">
|
||||
{`fetch("https://api.aethex.dev/v1/projects?page=1&limit=25", {
|
||||
{`fetch("https://api.aethex.dev/v1/projects?page=1&limit=25", {
|
||||
headers: {
|
||||
Authorization: "Bearer ${TOKEN}",
|
||||
"AeThex-Environment": "production",
|
||||
|
|
@ -162,9 +171,16 @@ export default function DocsApiReference() {
|
|||
});`}
|
||||
</pre>
|
||||
<p>
|
||||
Responses include <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-RateLimit-Remaining</code>
|
||||
and <code className="rounded bg-black/40 px-2 py-1 text-blue-200">X-Request-ID</code> headers. Share the
|
||||
request ID when contacting support for faster triage.
|
||||
Responses include{" "}
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
|
||||
X-RateLimit-Remaining
|
||||
</code>
|
||||
and{" "}
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
|
||||
X-Request-ID
|
||||
</code>{" "}
|
||||
headers. Share the request ID when contacting support for faster
|
||||
triage.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -189,7 +205,9 @@ export default function DocsApiReference() {
|
|||
{apiEndpoints.map((endpoint) => (
|
||||
<TableRow key={`${endpoint.method}-${endpoint.path}`}>
|
||||
<TableCell>
|
||||
<Badge className="bg-blue-600/30 text-blue-100">{endpoint.method}</Badge>
|
||||
<Badge className="bg-blue-600/30 text-blue-100">
|
||||
{endpoint.method}
|
||||
</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-mono text-purple-200">
|
||||
{endpoint.path}
|
||||
|
|
@ -201,7 +219,8 @@ export default function DocsApiReference() {
|
|||
))}
|
||||
</TableBody>
|
||||
<TableCaption>
|
||||
Need deeper coverage? The JavaScript SDK ships with typed request builders for every endpoint.
|
||||
Need deeper coverage? The JavaScript SDK ships with typed
|
||||
request builders for every endpoint.
|
||||
</TableCaption>
|
||||
</Table>
|
||||
</CardContent>
|
||||
|
|
@ -223,12 +242,23 @@ export default function DocsApiReference() {
|
|||
className="rounded-lg border border-slate-700 bg-slate-950/40 p-4"
|
||||
>
|
||||
<p className="font-mono text-sm text-blue-300">{topic.event}</p>
|
||||
<p className="text-gray-300 text-sm mt-2">{topic.description}</p>
|
||||
<p className="text-gray-300 text-sm mt-2">
|
||||
{topic.description}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
<p className="text-gray-400 text-sm">
|
||||
Configure webhook destinations and signing secrets from the <Link to="/dashboard" className="text-blue-300 hover:text-blue-200">dashboard</Link>.
|
||||
Verify requests with the <code className="rounded bg-black/40 px-2 py-1 text-blue-200">AeThex-Signature</code>
|
||||
Configure webhook destinations and signing secrets from the{" "}
|
||||
<Link
|
||||
to="/dashboard"
|
||||
className="text-blue-300 hover:text-blue-200"
|
||||
>
|
||||
dashboard
|
||||
</Link>
|
||||
. Verify requests with the{" "}
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
|
||||
AeThex-Signature
|
||||
</code>
|
||||
header to guarantee authenticity.
|
||||
</p>
|
||||
</CardContent>
|
||||
|
|
@ -243,13 +273,22 @@ export default function DocsApiReference() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4 text-gray-300">
|
||||
<p className="text-gray-300">
|
||||
All endpoints are idempotent where appropriate and support conditional requests via the
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">If-Match</code> header.
|
||||
All endpoints are idempotent where appropriate and support
|
||||
conditional requests via the
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-blue-200">
|
||||
If-Match
|
||||
</code>{" "}
|
||||
header.
|
||||
</p>
|
||||
<div className="grid gap-3">
|
||||
{errorExamples.map((error) => (
|
||||
<div key={error.code} className="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-950/60 p-3">
|
||||
<Badge className="bg-red-600/30 text-red-200">{error.code}</Badge>
|
||||
<div
|
||||
key={error.code}
|
||||
className="flex items-start gap-3 rounded-lg border border-slate-700 bg-slate-950/60 p-3"
|
||||
>
|
||||
<Badge className="bg-red-600/30 text-red-200">
|
||||
{error.code}
|
||||
</Badge>
|
||||
<div>
|
||||
<p className="text-white font-medium">{error.label}</p>
|
||||
<p className="text-sm text-gray-400">{error.hint}</p>
|
||||
|
|
@ -258,20 +297,28 @@ export default function DocsApiReference() {
|
|||
))}
|
||||
</div>
|
||||
<p className="text-gray-300">
|
||||
Monitor rate-limit headers and retry using an exponential backoff strategy. Persistent errors can be
|
||||
escalated to the AeThex support team with the failing request ID.
|
||||
Monitor rate-limit headers and retry using an exponential backoff
|
||||
strategy. Persistent errors can be escalated to the AeThex support
|
||||
team with the failing request ID.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</section>
|
||||
|
||||
<section id="resources" className="rounded-2xl border border-blue-500/40 bg-blue-900/20 p-8">
|
||||
<section
|
||||
id="resources"
|
||||
className="rounded-2xl border border-blue-500/40 bg-blue-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Ship production-ready integrations</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Ship production-ready integrations
|
||||
</h3>
|
||||
<p className="text-gray-300 max-w-2xl">
|
||||
Combine the REST API with event webhooks for a full-duplex integration pattern. Use the official
|
||||
TypeScript SDK for typed helpers or generate your own client with the published OpenAPI schema.
|
||||
Combine the REST API with event webhooks for a full-duplex
|
||||
integration pattern. Use the official TypeScript SDK for typed
|
||||
helpers or generate your own client with the published OpenAPI
|
||||
schema.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
|
|
@ -281,7 +328,12 @@ export default function DocsApiReference() {
|
|||
OpenAPI explorer
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="border-blue-400/60 text-blue-200">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-blue-400/60 text-blue-200"
|
||||
>
|
||||
<Link to="/docs/examples">
|
||||
<AlertTriangle className="mr-2 h-5 w-5" />
|
||||
See implementation patterns
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ const commands = [
|
|||
{
|
||||
command: "aethex deploy",
|
||||
description: "Build and deploy the current project",
|
||||
usage: "Runs tests, packages artifacts, and promotes to the target environment",
|
||||
usage:
|
||||
"Runs tests, packages artifacts, and promotes to the target environment",
|
||||
},
|
||||
{
|
||||
command: "aethex env pull",
|
||||
|
|
@ -87,11 +88,14 @@ export default function DocsCli() {
|
|||
<Terminal className="mr-2 h-3 w-3" />
|
||||
CLI Tools
|
||||
</Badge>
|
||||
<h2 className="text-3xl font-semibold text-white">Operate AeThex from the command line</h2>
|
||||
<h2 className="text-3xl font-semibold text-white">
|
||||
Operate AeThex from the command line
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
The AeThex CLI automates local development, environment management, and production deployments. It is
|
||||
built with stability in mind, featuring transactional deploys, shell-friendly output, and native support for
|
||||
Linux, macOS, and Windows.
|
||||
The AeThex CLI automates local development, environment management,
|
||||
and production deployments. It is built with stability in mind,
|
||||
featuring transactional deploys, shell-friendly output, and native
|
||||
support for Linux, macOS, and Windows.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -113,13 +117,22 @@ export default function DocsCli() {
|
|||
<TableBody>
|
||||
{commands.map((item) => (
|
||||
<TableRow key={item.command}>
|
||||
<TableCell className="font-mono text-purple-200">{item.command}</TableCell>
|
||||
<TableCell className="text-gray-300">{item.description}</TableCell>
|
||||
<TableCell className="text-gray-400 text-sm">{item.usage}</TableCell>
|
||||
<TableCell className="font-mono text-purple-200">
|
||||
{item.command}
|
||||
</TableCell>
|
||||
<TableCell className="text-gray-300">
|
||||
{item.description}
|
||||
</TableCell>
|
||||
<TableCell className="text-gray-400 text-sm">
|
||||
{item.usage}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableCaption>Run <code className="bg-black/40 px-1">aethex --help</code> for the full command tree.</TableCaption>
|
||||
<TableCaption>
|
||||
Run <code className="bg-black/40 px-1">aethex --help</code> for
|
||||
the full command tree.
|
||||
</TableCaption>
|
||||
</Table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -135,12 +148,13 @@ export default function DocsCli() {
|
|||
</CardHeader>
|
||||
<CardContent className="text-gray-300 space-y-3 text-sm">
|
||||
<p>
|
||||
Run <code className="bg-black/40 px-1">aethex dev</code> to start local services with hot reloading and the
|
||||
AeThex mock identity provider. Inspect logs via the integrated tail view.
|
||||
Run <code className="bg-black/40 px-1">aethex dev</code> to start
|
||||
local services with hot reloading and the AeThex mock identity
|
||||
provider. Inspect logs via the integrated tail view.
|
||||
</p>
|
||||
<p>
|
||||
Use <code className="bg-black/40 px-1">aethex data seed</code> to populate sample datasets for QA or demo
|
||||
accounts.
|
||||
Use <code className="bg-black/40 px-1">aethex data seed</code> to
|
||||
populate sample datasets for QA or demo accounts.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -154,12 +168,16 @@ export default function DocsCli() {
|
|||
</CardHeader>
|
||||
<CardContent className="text-gray-300 space-y-3 text-sm">
|
||||
<p>
|
||||
Synchronize configuration with <code className="bg-black/40 px-1">aethex env pull</code> or populate remote
|
||||
environments from local files using <code className="bg-black/40 px-1">aethex env push</code>.
|
||||
Synchronize configuration with{" "}
|
||||
<code className="bg-black/40 px-1">aethex env pull</code> or
|
||||
populate remote environments from local files using{" "}
|
||||
<code className="bg-black/40 px-1">aethex env push</code>.
|
||||
</p>
|
||||
<p>
|
||||
Inspect secrets securely through <code className="bg-black/40 px-1">aethex env inspect</code>. Output is redacted
|
||||
by default, keeping sensitive data safe in terminal logs.
|
||||
Inspect secrets securely through{" "}
|
||||
<code className="bg-black/40 px-1">aethex env inspect</code>.
|
||||
Output is redacted by default, keeping sensitive data safe in
|
||||
terminal logs.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -173,11 +191,13 @@ export default function DocsCli() {
|
|||
</CardHeader>
|
||||
<CardContent className="text-gray-300 space-y-3 text-sm">
|
||||
<p>
|
||||
Each deployment is transactional: a failure during build or migration automatically halts promotion and
|
||||
emits alerts to subscribed channels.
|
||||
Each deployment is transactional: a failure during build or
|
||||
migration automatically halts promotion and emits alerts to
|
||||
subscribed channels.
|
||||
</p>
|
||||
<p>
|
||||
Gates such as quality checks or approvals can be configured in <code className="bg-black/40 px-1">aethex.config.ts</code>
|
||||
Gates such as quality checks or approvals can be configured in{" "}
|
||||
<code className="bg-black/40 px-1">aethex.config.ts</code>
|
||||
and enforced automatically by the CLI.
|
||||
</p>
|
||||
</CardContent>
|
||||
|
|
@ -187,13 +207,17 @@ export default function DocsCli() {
|
|||
<section id="automation" className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<CloudLightning className="h-6 w-6 text-amber-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Automate everything</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Automate everything
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{automationTips.map((tip) => (
|
||||
<Card key={tip.title} className="bg-slate-900/60 border-slate-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white text-lg">{tip.title}</CardTitle>
|
||||
<CardTitle className="text-white text-lg">
|
||||
{tip.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="text-sm text-gray-300">
|
||||
|
|
@ -205,29 +229,49 @@ export default function DocsCli() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section id="security" className="rounded-2xl border border-amber-500/40 bg-amber-900/20 p-8">
|
||||
<section
|
||||
id="security"
|
||||
className="rounded-2xl border border-amber-500/40 bg-amber-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Stay safe in production</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Stay safe in production
|
||||
</h3>
|
||||
<p className="text-gray-200 max-w-2xl text-sm">
|
||||
The CLI signs every build artifact and enforces checksums during deployment. Combine this with RBAC token
|
||||
policies to guarantee only trusted pipelines can trigger releases.
|
||||
The CLI signs every build artifact and enforces checksums during
|
||||
deployment. Combine this with RBAC token policies to guarantee
|
||||
only trusted pipelines can trigger releases.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row">
|
||||
<Button asChild size="lg" className="bg-amber-500 hover:bg-amber-400 text-black">
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="bg-amber-500 hover:bg-amber-400 text-black"
|
||||
>
|
||||
<Link to="/docs/getting-started">
|
||||
<ArrowRight className="mr-2 h-5 w-5" />
|
||||
Return to setup guide
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="border-amber-300/60 text-amber-100">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-amber-300/60 text-amber-100"
|
||||
>
|
||||
<Link to="/support">
|
||||
<Shield className="mr-2 h-5 w-5" />
|
||||
Security best practices
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="ghost" size="lg" className="text-amber-100 hover:text-amber-50">
|
||||
<Button
|
||||
asChild
|
||||
variant="ghost"
|
||||
size="lg"
|
||||
className="text-amber-100 hover:text-amber-50"
|
||||
>
|
||||
<Link to="/docs/platform">
|
||||
Discover platform features
|
||||
<ArrowRight className="ml-2 h-5 w-5" />
|
||||
|
|
|
|||
|
|
@ -142,19 +142,27 @@ export default function DocsExamples() {
|
|||
<Blocks className="mr-2 h-3 w-3" />
|
||||
Examples & Templates
|
||||
</Badge>
|
||||
<h2 className="text-3xl font-semibold text-white">Production-ready patterns you can copy</h2>
|
||||
<h2 className="text-3xl font-semibold text-white">
|
||||
Production-ready patterns you can copy
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
Explore curated examples covering backend services, realtime overlays, automation scripts, and workflow
|
||||
integrations. Each project includes detailed READMEs, infrastructure diagrams, and deployment runbooks.
|
||||
Explore curated examples covering backend services, realtime overlays,
|
||||
automation scripts, and workflow integrations. Each project includes
|
||||
detailed READMEs, infrastructure diagrams, and deployment runbooks.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="code-gallery" className="grid gap-6 lg:grid-cols-3">
|
||||
{exampleSnippets.map((snippet) => (
|
||||
<Card key={snippet.title} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={snippet.title}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-white text-lg">{snippet.title}</CardTitle>
|
||||
<CardTitle className="text-white text-lg">
|
||||
{snippet.title}
|
||||
</CardTitle>
|
||||
<Badge variant="outline">{snippet.language}</Badge>
|
||||
</div>
|
||||
<CardDescription className="text-gray-300 text-sm">
|
||||
|
|
@ -165,7 +173,10 @@ export default function DocsExamples() {
|
|||
<pre className="rounded-lg border border-slate-700 bg-slate-950/60 p-4 text-xs text-emerald-200 overflow-x-auto">
|
||||
<code>{snippet.code}</code>
|
||||
</pre>
|
||||
<Button asChild className="w-full bg-emerald-500 hover:bg-emerald-400 text-black">
|
||||
<Button
|
||||
asChild
|
||||
className="w-full bg-emerald-500 hover:bg-emerald-400 text-black"
|
||||
>
|
||||
<Link to={snippet.href} target="_blank" rel="noreferrer">
|
||||
<Github className="mr-2 h-4 w-4" />
|
||||
Open repository
|
||||
|
|
@ -180,13 +191,17 @@ export default function DocsExamples() {
|
|||
<section id="templates" className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Flame className="h-6 w-6 text-emerald-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Deploy faster with templates</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Deploy faster with templates
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{integrationIdeas.map((idea) => (
|
||||
<Card key={idea.title} className="bg-slate-900/60 border-slate-700">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white text-base">{idea.title}</CardTitle>
|
||||
<CardTitle className="text-white text-base">
|
||||
{idea.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="text-gray-300 text-sm mb-4">
|
||||
|
|
@ -208,23 +223,38 @@ export default function DocsExamples() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section id="share" className="rounded-2xl border border-emerald-500/40 bg-emerald-900/20 p-8">
|
||||
<section
|
||||
id="share"
|
||||
className="rounded-2xl border border-emerald-500/40 bg-emerald-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Share what you build</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Share what you build
|
||||
</h3>
|
||||
<p className="text-gray-200 max-w-2xl text-sm">
|
||||
Publish your own templates or improvements by opening a pull request to the public AeThex examples
|
||||
repository. Every accepted contribution is highlighted in the monthly creator spotlight.
|
||||
Publish your own templates or improvements by opening a pull
|
||||
request to the public AeThex examples repository. Every accepted
|
||||
contribution is highlighted in the monthly creator spotlight.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button asChild size="lg" className="bg-emerald-500 hover:bg-emerald-400 text-black">
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="bg-emerald-500 hover:bg-emerald-400 text-black"
|
||||
>
|
||||
<Link to="https://github.com/aethex/examples" target="_blank">
|
||||
<Code2 className="mr-2 h-5 w-5" />
|
||||
Contribute on GitHub
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="border-emerald-300/60 text-emerald-100">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-emerald-300/60 text-emerald-100"
|
||||
>
|
||||
<Link to="/community">
|
||||
<Share2 className="mr-2 h-5 w-5" />
|
||||
Showcase to the community
|
||||
|
|
@ -234,16 +264,27 @@ export default function DocsExamples() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section id="services" className="rounded-2xl border border-emerald-500/20 bg-slate-900/80 p-8">
|
||||
<section
|
||||
id="services"
|
||||
className="rounded-2xl border border-emerald-500/20 bg-slate-900/80 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h4 className="text-xl font-semibold text-white">Need a custom integration?</h4>
|
||||
<h4 className="text-xl font-semibold text-white">
|
||||
Need a custom integration?
|
||||
</h4>
|
||||
<p className="text-gray-300 text-sm">
|
||||
Our professional services team partners with studios to build tailored pipelines, analytics dashboards,
|
||||
and automation workflows on top of AeThex.
|
||||
Our professional services team partners with studios to build
|
||||
tailored pipelines, analytics dashboards, and automation workflows
|
||||
on top of AeThex.
|
||||
</p>
|
||||
</div>
|
||||
<Button asChild variant="outline" size="lg" className="border-emerald-300/60 text-emerald-100">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-emerald-300/60 text-emerald-100"
|
||||
>
|
||||
<Link to="/consulting">
|
||||
<Globe className="mr-2 h-5 w-5" />
|
||||
Talk to AeThex consultants
|
||||
|
|
|
|||
|
|
@ -153,27 +153,32 @@ const platformHighlights = [
|
|||
const explorationLinks = [
|
||||
{
|
||||
title: "Platform Walkthrough",
|
||||
description: "Tour the dashboard, notification center, and collaboration features.",
|
||||
description:
|
||||
"Tour the dashboard, notification center, and collaboration features.",
|
||||
href: "/dashboard",
|
||||
},
|
||||
{
|
||||
title: "Platform documentation",
|
||||
description: "Share the high-level platform overview with non-technical teammates.",
|
||||
description:
|
||||
"Share the high-level platform overview with non-technical teammates.",
|
||||
href: "/docs/platform",
|
||||
},
|
||||
{
|
||||
title: "API Reference",
|
||||
description: "Review authentication flows, REST endpoints, and webhook schemas.",
|
||||
description:
|
||||
"Review authentication flows, REST endpoints, and webhook schemas.",
|
||||
href: "/docs/api",
|
||||
},
|
||||
{
|
||||
title: "Tutorial Library",
|
||||
description: "Follow guided builds for matchmaking services, player analytics, and live events.",
|
||||
description:
|
||||
"Follow guided builds for matchmaking services, player analytics, and live events.",
|
||||
href: "/docs/tutorials",
|
||||
},
|
||||
{
|
||||
title: "Community Support",
|
||||
description: "Ask questions, share templates, and pair up with mentors in the public forums.",
|
||||
description:
|
||||
"Ask questions, share templates, and pair up with mentors in the public forums.",
|
||||
href: "/community",
|
||||
},
|
||||
{
|
||||
|
|
@ -195,31 +200,40 @@ export default function DocsGettingStarted() {
|
|||
Launch your first AeThex project in under 30 minutes
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
This guide walks through the minimum setup required to ship a production-ready AeThex application.
|
||||
Complete the prerequisites, initialize a workspace with the CLI, and review the deployment checklist
|
||||
before inviting collaborators. Use the platform highlights below to brief product, community, and live-ops
|
||||
teams on everything available beyond deployment.
|
||||
This guide walks through the minimum setup required to ship a
|
||||
production-ready AeThex application. Complete the prerequisites,
|
||||
initialize a workspace with the CLI, and review the deployment
|
||||
checklist before inviting collaborators. Use the platform highlights
|
||||
below to brief product, community, and live-ops teams on everything
|
||||
available beyond deployment.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="categories" className="space-y-6">
|
||||
<div className="text-center space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Documentation categories</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Documentation categories
|
||||
</h3>
|
||||
<p className="text-gray-300 max-w-2xl mx-auto text-sm">
|
||||
Jump into the area you need most. Each category below is mirrored in Builder CMS for collaborative
|
||||
editing.
|
||||
Jump into the area you need most. Each category below is mirrored in
|
||||
Builder CMS for collaborative editing.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
{docCategories.map((category) => (
|
||||
<Card key={category.title} className="border-border/50 hover:border-aethex-400/40 transition-all">
|
||||
<Card
|
||||
key={category.title}
|
||||
className="border-border/50 hover:border-aethex-400/40 transition-all"
|
||||
>
|
||||
<CardHeader>
|
||||
<div
|
||||
className={`inline-flex rounded-lg bg-gradient-to-r ${category.color} px-3 py-1 text-xs uppercase tracking-wider text-white`}
|
||||
>
|
||||
{category.docs} docs
|
||||
</div>
|
||||
<CardTitle className="text-xl text-white mt-3">{category.title}</CardTitle>
|
||||
<CardTitle className="text-xl text-white mt-3">
|
||||
{category.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-gray-300">
|
||||
{category.description}
|
||||
</CardDescription>
|
||||
|
|
@ -253,7 +267,12 @@ export default function DocsGettingStarted() {
|
|||
{item.description}
|
||||
</CardDescription>
|
||||
<Button asChild variant="outline" className="justify-start">
|
||||
<Link to={item.actionHref} target={item.actionHref.startsWith("http") ? "_blank" : undefined}>
|
||||
<Link
|
||||
to={item.actionHref}
|
||||
target={
|
||||
item.actionHref.startsWith("http") ? "_blank" : undefined
|
||||
}
|
||||
>
|
||||
<ArrowRight className="mr-2 h-4 w-4" />
|
||||
{item.actionLabel}
|
||||
</Link>
|
||||
|
|
@ -266,13 +285,18 @@ export default function DocsGettingStarted() {
|
|||
<section id="platform-highlights" className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<LayoutDashboard className="h-6 w-6 text-purple-400" />
|
||||
<h3 className="text-2xl font-semibold text-white">Explore the platform</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Explore the platform
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-6 lg:grid-cols-2">
|
||||
{platformHighlights.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Card key={item.title} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={item.title}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white text-lg flex items-center gap-3">
|
||||
<Icon className="h-5 w-5 text-purple-300" />
|
||||
|
|
@ -302,7 +326,9 @@ export default function DocsGettingStarted() {
|
|||
<Badge variant="outline" className="w-fit">
|
||||
Step {index + 1}
|
||||
</Badge>
|
||||
<CardTitle className="text-white text-lg">{step.title}</CardTitle>
|
||||
<CardTitle className="text-white text-lg">
|
||||
{step.title}
|
||||
</CardTitle>
|
||||
<CardDescription className="text-gray-300">
|
||||
{step.description}
|
||||
</CardDescription>
|
||||
|
|
@ -342,7 +368,10 @@ export default function DocsGettingStarted() {
|
|||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{explorationLinks.map((link) => (
|
||||
<Card key={link.title} className="bg-slate-900/60 border-slate-700 hover:border-purple-500/40 transition-colors">
|
||||
<Card
|
||||
key={link.title}
|
||||
className="bg-slate-900/60 border-slate-700 hover:border-purple-500/40 transition-colors"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center justify-between text-white text-base">
|
||||
{link.title}
|
||||
|
|
@ -369,26 +398,42 @@ export default function DocsGettingStarted() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section id="deploy" className="rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8">
|
||||
<section
|
||||
id="deploy"
|
||||
className="rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Ready to automate your first deployment?
|
||||
</h3>
|
||||
<p className="text-gray-300 max-w-2xl">
|
||||
Run <code className="rounded bg-black/40 px-2 py-1 text-purple-200">aethex deploy</code> once you have
|
||||
verified environment variables, migrations, and smoke tests. Ship changes with confidence knowing
|
||||
guardrails are enabled by default.
|
||||
Run{" "}
|
||||
<code className="rounded bg-black/40 px-2 py-1 text-purple-200">
|
||||
aethex deploy
|
||||
</code>{" "}
|
||||
once you have verified environment variables, migrations, and
|
||||
smoke tests. Ship changes with confidence knowing guardrails are
|
||||
enabled by default.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button asChild size="lg" className="bg-purple-600 hover:bg-purple-500">
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="bg-purple-600 hover:bg-purple-500"
|
||||
>
|
||||
<Link to="/docs/cli">
|
||||
<Download className="mr-2 h-5 w-5" />
|
||||
Review CLI commands
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="border-purple-400/60 text-purple-200">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-purple-400/60 text-purple-200"
|
||||
>
|
||||
<Link to="/support">
|
||||
<Code className="mr-2 h-5 w-5" />
|
||||
Talk to an engineer
|
||||
|
|
|
|||
|
|
@ -33,32 +33,38 @@ import {
|
|||
const connectorFields = [
|
||||
{
|
||||
name: "key",
|
||||
description: "Unique identifier referenced across dashboards, APIs, and audit logs.",
|
||||
description:
|
||||
"Unique identifier referenced across dashboards, APIs, and audit logs.",
|
||||
defaultValue: '"analytics-segment"',
|
||||
},
|
||||
{
|
||||
name: "category",
|
||||
description: "Integration taxonomy aligned with AeThex surfaces (analytics, identity, commerce, ops).",
|
||||
description:
|
||||
"Integration taxonomy aligned with AeThex surfaces (analytics, identity, commerce, ops).",
|
||||
defaultValue: '"analytics"',
|
||||
},
|
||||
{
|
||||
name: "capabilities",
|
||||
description: "Feature flags that unlock widgets, automation hooks, and data pipelines.",
|
||||
description:
|
||||
"Feature flags that unlock widgets, automation hooks, and data pipelines.",
|
||||
defaultValue: "['metrics', 'webhooks']",
|
||||
},
|
||||
{
|
||||
name: "connectionMode",
|
||||
description: "Determines how credentials are managed (oauth, apiKey, managedVault).",
|
||||
description:
|
||||
"Determines how credentials are managed (oauth, apiKey, managedVault).",
|
||||
defaultValue: '"oauth"',
|
||||
},
|
||||
{
|
||||
name: "webhookEndpoint",
|
||||
description: "Optional callback URL for outbound events delivered by AeThex.",
|
||||
description:
|
||||
"Optional callback URL for outbound events delivered by AeThex.",
|
||||
defaultValue: '"https://app.example.com/aethex/webhooks"',
|
||||
},
|
||||
{
|
||||
name: "uiEmbeds",
|
||||
description: "Declarative config describing dashboard cards, modals, or launchers this integration renders.",
|
||||
description:
|
||||
"Declarative config describing dashboard cards, modals, or launchers this integration renders.",
|
||||
defaultValue: "[{ surface: 'dashboard', placement: 'sidebar' }]",
|
||||
},
|
||||
];
|
||||
|
|
@ -89,11 +95,15 @@ export default function DocsIntegrations() {
|
|||
<Puzzle className="mr-2 h-3 w-3" />
|
||||
Integrations
|
||||
</Badge>
|
||||
<h2 className="text-3xl font-semibold text-white">Connecting partner services to AeThex</h2>
|
||||
<h2 className="text-3xl font-semibold text-white">
|
||||
Connecting partner services to AeThex
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
AeThex Integrations wrap third-party analytics, identity, payments, and live-ops tooling behind a consistent
|
||||
runtime, security model, and visual system. Use this guide to register new connectors, surface partner UI in
|
||||
product flows, and automate data exchange without hand-rolled plumbing.
|
||||
AeThex Integrations wrap third-party analytics, identity, payments,
|
||||
and live-ops tooling behind a consistent runtime, security model, and
|
||||
visual system. Use this guide to register new connectors, surface
|
||||
partner UI in product flows, and automate data exchange without
|
||||
hand-rolled plumbing.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -107,14 +117,18 @@ export default function DocsIntegrations() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-4 text-gray-300 text-sm leading-relaxed">
|
||||
<p>
|
||||
Integration manifests are stored in the AeThex Integrations service and synced across the dashboard and
|
||||
runtime. Client components resolve connector metadata through the shared API helpers, ensuring credentials
|
||||
and capability flags stay consistent with server state.
|
||||
Integration manifests are stored in the AeThex Integrations
|
||||
service and synced across the dashboard and runtime. Client
|
||||
components resolve connector metadata through the shared API
|
||||
helpers, ensuring credentials and capability flags stay consistent
|
||||
with server state.
|
||||
</p>
|
||||
<p>
|
||||
During hydration the runtime mounts partner SDKs behind AeThex loaders, applying sandboxed execution where
|
||||
required. Use lifecycle hooks to emit analytics, hydrate widgets with scoped credentials, and gate access
|
||||
through the same role-based policies used elsewhere in the platform.
|
||||
During hydration the runtime mounts partner SDKs behind AeThex
|
||||
loaders, applying sandboxed execution where required. Use
|
||||
lifecycle hooks to emit analytics, hydrate widgets with scoped
|
||||
credentials, and gate access through the same role-based policies
|
||||
used elsewhere in the platform.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -128,13 +142,17 @@ export default function DocsIntegrations() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-gray-300 text-sm">
|
||||
<p>
|
||||
Use the integration theming utilities to adapt partner widgets to AeThex gradients, typography, and focus
|
||||
states. Tokens flow through CSS variables defined in <code className="bg-black/40 px-2">global.css</code>, so embeds stay
|
||||
visually aligned with dashboards and consumer apps.
|
||||
Use the integration theming utilities to adapt partner widgets to
|
||||
AeThex gradients, typography, and focus states. Tokens flow
|
||||
through CSS variables defined in{" "}
|
||||
<code className="bg-black/40 px-2">global.css</code>, so embeds
|
||||
stay visually aligned with dashboards and consumer apps.
|
||||
</p>
|
||||
<p>
|
||||
Extend styling with scoped class names or CSS variables exported by the partner SDK. When shipping multiple
|
||||
widgets, prefer design tokens over hard-coded overrides to keep dark-mode and accessibility tweaks in sync.
|
||||
Extend styling with scoped class names or CSS variables exported
|
||||
by the partner SDK. When shipping multiple widgets, prefer design
|
||||
tokens over hard-coded overrides to keep dark-mode and
|
||||
accessibility tweaks in sync.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -143,7 +161,9 @@ export default function DocsIntegrations() {
|
|||
<section id="configuration" className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<Palette className="h-6 w-6 text-indigo-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Configuration options</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Configuration options
|
||||
</h3>
|
||||
</div>
|
||||
<Card className="bg-slate-900/60 border-slate-700">
|
||||
<CardContent>
|
||||
|
|
@ -158,14 +178,21 @@ export default function DocsIntegrations() {
|
|||
<TableBody>
|
||||
{connectorFields.map((field) => (
|
||||
<TableRow key={field.name}>
|
||||
<TableCell className="font-mono text-indigo-200">{field.name}</TableCell>
|
||||
<TableCell className="text-gray-300">{field.description}</TableCell>
|
||||
<TableCell className="text-gray-400 text-sm">{field.defaultValue}</TableCell>
|
||||
<TableCell className="font-mono text-indigo-200">
|
||||
{field.name}
|
||||
</TableCell>
|
||||
<TableCell className="text-gray-300">
|
||||
{field.description}
|
||||
</TableCell>
|
||||
<TableCell className="text-gray-400 text-sm">
|
||||
{field.defaultValue}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableCaption>
|
||||
Manage manifests in the Integrations dashboard or via the Admin API to keep environments in sync.
|
||||
Manage manifests in the Integrations dashboard or via the Admin
|
||||
API to keep environments in sync.
|
||||
</TableCaption>
|
||||
</Table>
|
||||
</CardContent>
|
||||
|
|
@ -182,9 +209,18 @@ export default function DocsIntegrations() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-gray-300 text-sm">
|
||||
<ul className="list-disc space-y-2 pl-5">
|
||||
<li>Store credentials in AeThex-managed vaults and rotate them from the dashboard rather than hard-coding.</li>
|
||||
<li>Limit embed rendering to audiences that have access to the underlying data to avoid leaking partner UI.</li>
|
||||
<li>Log integration events through the shared telemetry helpers so support can trace partner-side failures.</li>
|
||||
<li>
|
||||
Store credentials in AeThex-managed vaults and rotate them from
|
||||
the dashboard rather than hard-coding.
|
||||
</li>
|
||||
<li>
|
||||
Limit embed rendering to audiences that have access to the
|
||||
underlying data to avoid leaking partner UI.
|
||||
</li>
|
||||
<li>
|
||||
Log integration events through the shared telemetry helpers so
|
||||
support can trace partner-side failures.
|
||||
</li>
|
||||
</ul>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -198,12 +234,15 @@ export default function DocsIntegrations() {
|
|||
</CardHeader>
|
||||
<CardContent className="space-y-3 text-gray-300 text-sm">
|
||||
<p>
|
||||
Promote integration changes through staging first. AeThex snapshots connector manifests per environment so
|
||||
you can test credentials, capability flags, and UI placements without impacting production users.
|
||||
Promote integration changes through staging first. AeThex
|
||||
snapshots connector manifests per environment so you can test
|
||||
credentials, capability flags, and UI placements without impacting
|
||||
production users.
|
||||
</p>
|
||||
<p>
|
||||
When partners publish SDK updates, pin versions in your manifest, document the change log, and coordinate
|
||||
rollout windows with stakeholders subscribing to the integration.
|
||||
When partners publish SDK updates, pin versions in your manifest,
|
||||
document the change log, and coordinate rollout windows with
|
||||
stakeholders subscribing to the integration.
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
@ -216,9 +255,14 @@ export default function DocsIntegrations() {
|
|||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-3">
|
||||
{troubleshooting.map((issue) => (
|
||||
<Card key={issue.title} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={issue.title}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white text-base">{issue.title}</CardTitle>
|
||||
<CardTitle className="text-white text-base">
|
||||
{issue.title}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<CardDescription className="text-gray-300 text-sm">
|
||||
|
|
@ -230,23 +274,39 @@ export default function DocsIntegrations() {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<section id="resources" className="rounded-2xl border border-indigo-500/40 bg-indigo-900/20 p-8">
|
||||
<section
|
||||
id="resources"
|
||||
className="rounded-2xl border border-indigo-500/40 bg-indigo-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Further reading</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Further reading
|
||||
</h3>
|
||||
<p className="text-gray-300 text-sm max-w-2xl">
|
||||
Manage integration documentation centrally in Builder CMS or export static guides for partner teams. Keep
|
||||
manifests, onboarding playbooks, and support runbooks together so each connector has a clear owner.
|
||||
Manage integration documentation centrally in Builder CMS or
|
||||
export static guides for partner teams. Keep manifests, onboarding
|
||||
playbooks, and support runbooks together so each connector has a
|
||||
clear owner.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<Button asChild size="lg" className="bg-indigo-600 hover:bg-indigo-500">
|
||||
<Button
|
||||
asChild
|
||||
size="lg"
|
||||
className="bg-indigo-600 hover:bg-indigo-500"
|
||||
>
|
||||
<Link to="/docs/api">
|
||||
<LinkIcon className="mr-2 h-5 w-5" />
|
||||
Review API hooks
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="border-indigo-400/60 text-indigo-200">
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="lg"
|
||||
className="border-indigo-400/60 text-indigo-200"
|
||||
>
|
||||
<Link to="/docs/examples#code-gallery">
|
||||
<FileText className="mr-2 h-5 w-5" />
|
||||
Explore sample repos
|
||||
|
|
|
|||
|
|
@ -330,7 +330,9 @@ export default function DocsOverview() {
|
|||
|
||||
{/* Learning Resources */}
|
||||
<div className="mb-12">
|
||||
<h3 className="text-2xl font-bold text-white mb-6">Learning resources</h3>
|
||||
<h3 className="text-2xl font-bold text-white mb-6">
|
||||
Learning resources
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{learningResources.map((resource, index) => {
|
||||
const Icon = resource.icon;
|
||||
|
|
@ -372,13 +374,13 @@ export default function DocsOverview() {
|
|||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-2xl font-bold text-white">Recent Updates</h3>
|
||||
<Button
|
||||
asChild
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-slate-600 text-white hover:bg-slate-800"
|
||||
>
|
||||
<Link to="/changelog">View All Updates</Link>
|
||||
</Button>
|
||||
asChild
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-slate-600 text-white hover:bg-slate-800"
|
||||
>
|
||||
<Link to="/changelog">View All Updates</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
{featuredUpdates.map((update, index) => (
|
||||
|
|
@ -472,10 +474,13 @@ export default function DocsOverview() {
|
|||
|
||||
{/* Support CTA */}
|
||||
<div className="mt-12 rounded-2xl border border-purple-500/40 bg-purple-900/20 p-8 text-center">
|
||||
<h3 className="text-3xl font-semibold text-white mb-4">Need help getting started?</h3>
|
||||
<h3 className="text-3xl font-semibold text-white mb-4">
|
||||
Need help getting started?
|
||||
</h3>
|
||||
<p className="text-gray-300 max-w-3xl mx-auto mb-6">
|
||||
Our documentation team updates these guides weekly. If you're looking for tailored onboarding,
|
||||
architecture reviews, or migration support, reach out and we'll connect you with the right experts.
|
||||
Our documentation team updates these guides weekly. If you're
|
||||
looking for tailored onboarding, architecture reviews, or migration
|
||||
support, reach out and we'll connect you with the right experts.
|
||||
</p>
|
||||
<div className="flex flex-col sm:flex-row justify-center gap-4">
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -64,19 +64,22 @@ const collaborationWorkflows = [
|
|||
label: "Onboard & align",
|
||||
description:
|
||||
"Welcome teammates through the guided onboarding flow, capture their interests, and assign the right mentorship programs from day one.",
|
||||
highlight: "Onboarding modules cover personal info, interests, and project preferences so teams ramp quickly.",
|
||||
highlight:
|
||||
"Onboarding modules cover personal info, interests, and project preferences so teams ramp quickly.",
|
||||
},
|
||||
{
|
||||
label: "Build together",
|
||||
description:
|
||||
"Kick off projects with shared canvases, synced task boards, and CLI-generated environments. Use the realm switcher to target the correct workspace.",
|
||||
highlight: "In-app toasts notify collaborators when schema changes, deployments, or reviews need attention.",
|
||||
highlight:
|
||||
"In-app toasts notify collaborators when schema changes, deployments, or reviews need attention.",
|
||||
},
|
||||
{
|
||||
label: "Launch & iterate",
|
||||
description:
|
||||
"Promote builds through AeThex Deploy, track KPIs in the analytics feed, and publish release notes via the changelog tools.",
|
||||
highlight: "Community announcements and blog posts keep players and stakeholders in the loop automatically.",
|
||||
highlight:
|
||||
"Community announcements and blog posts keep players and stakeholders in the loop automatically.",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -166,11 +169,14 @@ export default function DocsPlatform() {
|
|||
<Sparkles className="mr-2 h-3 w-3" />
|
||||
Platform Experience
|
||||
</Badge>
|
||||
<h2 className="text-3xl font-semibold text-white">Deliver cohesive player and builder journeys on AeThex</h2>
|
||||
<h2 className="text-3xl font-semibold text-white">
|
||||
Deliver cohesive player and builder journeys on AeThex
|
||||
</h2>
|
||||
<p className="text-gray-300 max-w-3xl">
|
||||
Beyond deployment pipelines and CLI tooling, AeThex bundles collaboration, identity, and live-ops systems so
|
||||
teams can craft unforgettable experiences. Use this guide to orient new stakeholders and plan end-to-end
|
||||
platform rollouts.
|
||||
Beyond deployment pipelines and CLI tooling, AeThex bundles
|
||||
collaboration, identity, and live-ops systems so teams can craft
|
||||
unforgettable experiences. Use this guide to orient new stakeholders
|
||||
and plan end-to-end platform rollouts.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -183,14 +189,22 @@ export default function DocsPlatform() {
|
|||
{platformPillars.map((pillar) => {
|
||||
const Icon = pillar.icon;
|
||||
return (
|
||||
<Card key={pillar.title} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={pillar.title}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Icon className="h-6 w-6 text-cyan-300" />
|
||||
<CardTitle className="text-white text-lg">{pillar.title}</CardTitle>
|
||||
<CardTitle className="text-white text-lg">
|
||||
{pillar.title}
|
||||
</CardTitle>
|
||||
</div>
|
||||
<Badge variant="outline" className="text-xs text-cyan-200 border-cyan-500/40">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs text-cyan-200 border-cyan-500/40"
|
||||
>
|
||||
Platform
|
||||
</Badge>
|
||||
</div>
|
||||
|
|
@ -219,13 +233,20 @@ export default function DocsPlatform() {
|
|||
<section id="workflows" className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Workflow className="h-6 w-6 text-cyan-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Collaboration workflows</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Collaboration workflows
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{collaborationWorkflows.map((stage, index) => (
|
||||
<Card key={stage.label} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={stage.label}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader className="space-y-2">
|
||||
<Badge className="w-fit bg-cyan-600/30 text-cyan-100">Step {index + 1}</Badge>
|
||||
<Badge className="w-fit bg-cyan-600/30 text-cyan-100">
|
||||
Step {index + 1}
|
||||
</Badge>
|
||||
<CardTitle className="text-white text-lg flex items-center gap-2">
|
||||
<Globe className="h-5 w-5 text-cyan-300" />
|
||||
{stage.label}
|
||||
|
|
@ -247,15 +268,23 @@ export default function DocsPlatform() {
|
|||
<section id="modules" className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<Compass className="h-6 w-6 text-cyan-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Experience modules</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Experience modules
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{experienceModules.map((module) => (
|
||||
<Card key={module.name} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={module.name}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-white text-base flex items-center justify-between">
|
||||
{module.name}
|
||||
<Badge variant="outline" className="text-xs text-cyan-200 border-cyan-500/40">
|
||||
<Badge
|
||||
variant="outline"
|
||||
className="text-xs text-cyan-200 border-cyan-500/40"
|
||||
>
|
||||
Platform
|
||||
</Badge>
|
||||
</CardTitle>
|
||||
|
|
@ -283,17 +312,24 @@ export default function DocsPlatform() {
|
|||
<section id="analytics" className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<BarChart3 className="h-6 w-6 text-cyan-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Insights & analytics</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Insights & analytics
|
||||
</h3>
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{analyticsHighlights.map((item) => {
|
||||
const Icon = item.icon;
|
||||
return (
|
||||
<Card key={item.title} className="bg-slate-900/60 border-slate-700">
|
||||
<Card
|
||||
key={item.title}
|
||||
className="bg-slate-900/60 border-slate-700"
|
||||
>
|
||||
<CardHeader>
|
||||
<div className="flex items-center gap-3">
|
||||
<Icon className="h-6 w-6 text-cyan-300" />
|
||||
<CardTitle className="text-white text-lg">{item.title}</CardTitle>
|
||||
<CardTitle className="text-white text-lg">
|
||||
{item.title}
|
||||
</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
|
@ -310,7 +346,9 @@ export default function DocsPlatform() {
|
|||
<section id="governance" className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<ShieldCheck className="h-6 w-6 text-cyan-300" />
|
||||
<h3 className="text-2xl font-semibold text-white">Governance checklist</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Governance checklist
|
||||
</h3>
|
||||
</div>
|
||||
<Card className="bg-slate-900/60 border-slate-700">
|
||||
<CardContent>
|
||||
|
|
@ -323,13 +361,19 @@ export default function DocsPlatform() {
|
|||
</Card>
|
||||
</section>
|
||||
|
||||
<section id="next-steps" className="rounded-2xl border border-cyan-500/40 bg-cyan-900/20 p-8">
|
||||
<section
|
||||
id="next-steps"
|
||||
className="rounded-2xl border border-cyan-500/40 bg-cyan-900/20 p-8"
|
||||
>
|
||||
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-2xl font-semibold text-white">Keep exploring the platform</h3>
|
||||
<h3 className="text-2xl font-semibold text-white">
|
||||
Keep exploring the platform
|
||||
</h3>
|
||||
<p className="text-gray-300 max-w-2xl text-sm">
|
||||
Share this page with non-technical teammates. It links out to every major surface area so marketing,
|
||||
product, and operations groups can navigate confidently.
|
||||
Share this page with non-technical teammates. It links out to
|
||||
every major surface area so marketing, product, and operations
|
||||
groups can navigate confidently.
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-3 md:grid-cols-2">
|
||||
|
|
|
|||
|
|
@ -374,7 +374,10 @@ export default function DocsTutorials() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Button asChild className="bg-purple-600 hover:bg-purple-700">
|
||||
<Button
|
||||
asChild
|
||||
className="bg-purple-600 hover:bg-purple-700"
|
||||
>
|
||||
<Link to={tutorial.path}>
|
||||
Start Tutorial
|
||||
<ChevronRight className="h-4 w-4 ml-2" />
|
||||
|
|
|
|||
Loading…
Reference in a new issue