diff --git a/api/achievements/activate.ts b/api/achievements/activate.ts
index b483f811..e52c6500 100644
--- a/api/achievements/activate.ts
+++ b/api/achievements/activate.ts
@@ -48,10 +48,7 @@ const CORE_ACHIEVEMENTS = [
const DEFAULT_TARGET_EMAIL = "mrpiglr@gmail.com";
const DEFAULT_TARGET_USERNAME = "mrpiglr";
-export default async function handler(
- req: VercelRequest,
- res: VercelResponse,
-) {
+export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== "POST") {
return res.status(405).json({ error: "Method not allowed" });
}
@@ -68,20 +65,18 @@ export default async function handler(
// Ensure core achievements exist
const achievementResults = await Promise.all(
CORE_ACHIEVEMENTS.map(async (achievement) => {
- const { error } = await admin
- .from("achievements")
- .upsert(
- {
- id: achievement.id,
- name: achievement.name,
- description: achievement.description,
- icon: achievement.icon,
- badge_color: achievement.badgeColor,
- xp_reward: achievement.xpReward,
- created_at: nowIso,
- },
- { onConflict: "id" },
- );
+ const { error } = await admin.from("achievements").upsert(
+ {
+ id: achievement.id,
+ name: achievement.name,
+ description: achievement.description,
+ icon: achievement.icon,
+ badge_color: achievement.badgeColor,
+ xp_reward: achievement.xpReward,
+ created_at: nowIso,
+ },
+ { onConflict: "id" },
+ );
if (error) {
throw error;
@@ -92,14 +87,8 @@ export default async function handler(
// Normalise profile progression defaults
await Promise.all([
- admin
- .from("user_profiles")
- .update({ level: 1 })
- .is("level", null),
- admin
- .from("user_profiles")
- .update({ total_xp: 0 })
- .is("total_xp", null),
+ admin.from("user_profiles").update({ level: 1 }).is("level", null),
+ admin.from("user_profiles").update({ total_xp: 0 }).is("total_xp", null),
admin
.from("user_profiles")
.update({ user_type: "game_developer" })
@@ -108,7 +97,9 @@ export default async function handler(
// Locate target user
const normalizedEmail = (targetEmail || DEFAULT_TARGET_EMAIL).toLowerCase();
- const normalizedUsername = (targetUsername || DEFAULT_TARGET_USERNAME).toLowerCase();
+ const normalizedUsername = (
+ targetUsername || DEFAULT_TARGET_USERNAME
+ ).toLowerCase();
let targetUserId: string | null = null;
@@ -171,7 +162,9 @@ export default async function handler(
throw existingError;
}
- const existingIds = new Set((existingRows ?? []).map((row: any) => row.achievement_id));
+ const existingIds = new Set(
+ (existingRows ?? []).map((row: any) => row.achievement_id),
+ );
for (const achievement of CORE_ACHIEVEMENTS) {
if (existingIds.has(achievement.id)) {
diff --git a/client/App.tsx b/client/App.tsx
index 381a74b1..5977f666 100644
--- a/client/App.tsx
+++ b/client/App.tsx
@@ -67,12 +67,18 @@ const App = () => (
} />
} />
- } />
+ }
+ />
}
/>
- } />
+ }
+ />
} />
- {isLegendary ? "Legendary Status" : `Progress to Level ${level + 1}`}
+ {isLegendary
+ ? "Legendary Status"
+ : `Progress to Level ${level + 1}`}
- {isLegendary ? "MAX" : `${(progressToNextLevel || 0).toFixed(0)}%`}
+ {isLegendary
+ ? "MAX"
+ : `${(progressToNextLevel || 0).toFixed(0)}%`}
diff --git a/client/lib/aethex-database-adapter.ts b/client/lib/aethex-database-adapter.ts
index 2573a0ac..5309c114 100644
--- a/client/lib/aethex-database-adapter.ts
+++ b/client/lib/aethex-database-adapter.ts
@@ -34,7 +34,9 @@ const ensureSupabase = () => {
const MS_PER_DAY = 1000 * 60 * 60 * 24;
const startOfUTC = (date: Date) =>
- new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()));
+ new Date(
+ Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate()),
+ );
const isoDate = (date: Date) => date.toISOString().slice(0, 10);
@@ -50,14 +52,14 @@ const normalizeProfile = (
): AethexUserProfile => ({
...(row as AethexUserProfile),
email: email ?? (row as any)?.email,
- username:
- (row as any)?.username ?? email?.split("@")[0] ?? "user",
+ username: (row as any)?.username ?? email?.split("@")[0] ?? "user",
onboarded: true,
role: (row as any)?.role ?? "developer",
loyalty_points: (row as any)?.loyalty_points ?? 0,
current_streak: (row as any)?.current_streak ?? 0,
longest_streak:
- (row as any)?.longest_streak ?? Math.max((row as any)?.current_streak ?? 0, 0),
+ (row as any)?.longest_streak ??
+ Math.max((row as any)?.current_streak ?? 0, 0),
last_streak_at: (row as any)?.last_streak_at ?? null,
});
@@ -302,16 +304,15 @@ export const aethexUserService = {
return normalizeProfile(data);
},
- async getProfileByUsername(username: string): Promise {
+ async getProfileByUsername(
+ username: string,
+ ): Promise {
const normalized = username?.trim();
if (!normalized) return null;
ensureSupabase();
- const {
- data,
- error,
- } = await supabase
+ const { data, error } = await supabase
.from("user_profiles")
.select("*")
.eq("username", normalized)
@@ -332,10 +333,7 @@ export const aethexUserService = {
return normalizeProfile(data);
}
- const {
- data: fallback,
- error: fallbackError,
- } = await supabase
+ const { data: fallback, error: fallbackError } = await supabase
.from("user_profiles")
.select("*")
.ilike("username", normalized)
@@ -379,13 +377,11 @@ export const aethexUserService = {
}
return ((data as any[]) || []).map((row) =>
- normalizeProfile(
- {
- ...(row as AethexUserProfile),
- user_type: (row as any).user_type || "game_developer",
- experience_level: (row as any).experience_level || "beginner",
- },
- ),
+ normalizeProfile({
+ ...(row as AethexUserProfile),
+ user_type: (row as any).user_type || "game_developer",
+ experience_level: (row as any).experience_level || "beginner",
+ }),
);
},
@@ -693,7 +689,10 @@ export const aethexAchievementService = {
}
},
- async updateUserXPAndLevel(userId: string, xpGained: number | null = null): Promise {
+ async updateUserXPAndLevel(
+ userId: string,
+ xpGained: number | null = null,
+ ): Promise {
ensureSupabase();
const { data: profile, error } = await supabase
@@ -719,7 +718,8 @@ export const aethexAchievementService = {
const updates: Record = {};
if ("total_xp" in currentProfile) updates.total_xp = newTotalXP;
if ("level" in currentProfile) updates.level = newLevel;
- if ("loyalty_points" in currentProfile) updates.loyalty_points = newLoyaltyPoints;
+ if ("loyalty_points" in currentProfile)
+ updates.loyalty_points = newLoyaltyPoints;
if (Object.keys(updates).length > 0) {
const { error: updateError } = await supabase
@@ -748,11 +748,16 @@ export const aethexAchievementService = {
return;
}
} catch (error) {
- console.warn("Edge function award failed, attempting direct Supabase insert", error);
+ console.warn(
+ "Edge function award failed, attempting direct Supabase insert",
+ error,
+ );
}
const achievements = await this.getAllAchievements();
- const byName = new Map(achievements.map((item) => [item.name, item.id] as const));
+ const byName = new Map(
+ achievements.map((item) => [item.name, item.id] as const),
+ );
const names = ["Welcome to AeThex", "AeThex Explorer"];
for (const name of names) {
diff --git a/client/lib/onboarding.integration.spec.ts b/client/lib/onboarding.integration.spec.ts
index dc9649e5..b5abeb9f 100644
--- a/client/lib/onboarding.integration.spec.ts
+++ b/client/lib/onboarding.integration.spec.ts
@@ -36,7 +36,8 @@ const fetchMock = fetch as unknown as Mock;
vi.mock("@/lib/supabase", () => {
const userProfiles = new Map();
- const userAchievements: Array<{ user_id: string; achievement_id: string }> = [];
+ const userAchievements: Array<{ user_id: string; achievement_id: string }> =
+ [];
const achievementsCatalog = [
{
@@ -59,8 +60,12 @@ vi.mock("@/lib/supabase", () => {
},
];
- const achievementsById = new Map(achievementsCatalog.map((item) => [item.id, item] as const));
- const achievementsByName = new Map(achievementsCatalog.map((item) => [item.name, item] as const));
+ const achievementsById = new Map(
+ achievementsCatalog.map((item) => [item.id, item] as const),
+ );
+ const achievementsByName = new Map(
+ achievementsCatalog.map((item) => [item.name, item] as const),
+ );
const profileDefaults = (id: string) => ({
id,
@@ -201,7 +206,8 @@ vi.mock("@/lib/supabase", () => {
for (const entry of entries) {
const exists = userAchievements.some(
(item) =>
- item.user_id === entry.user_id && item.achievement_id === entry.achievement_id,
+ item.user_id === entry.user_id &&
+ item.achievement_id === entry.achievement_id,
);
if (exists) {
error = { code: "23505" };
@@ -215,10 +221,12 @@ vi.mock("@/lib/supabase", () => {
return {
eq(_column: string, userId: string) {
return {
- data: userAchievements.map((entry) => ({
- ...entry,
- achievements: achievementsById.get(entry.achievement_id),
- })).filter((entry) => entry.user_id === userId),
+ data: userAchievements
+ .map((entry) => ({
+ ...entry,
+ achievements: achievementsById.get(entry.achievement_id),
+ }))
+ .filter((entry) => entry.user_id === userId),
error: null,
};
},
@@ -251,9 +259,11 @@ vi.mock("@/lib/supabase", () => {
}),
},
from(table: string) {
- return tableMap[table] ?? {
- select: () => ({ data: [], error: null }),
- };
+ return (
+ tableMap[table] ?? {
+ select: () => ({ data: [], error: null }),
+ }
+ );
},
channel: () => ({
on: () => ({}),
diff --git a/client/pages/Community.tsx b/client/pages/Community.tsx
index 50a1bede..91c86d1c 100644
--- a/client/pages/Community.tsx
+++ b/client/pages/Community.tsx
@@ -24,7 +24,13 @@ import LoadingScreen from "@/components/LoadingScreen";
import { aethexToast } from "@/lib/aethex-toast";
import { cn } from "@/lib/utils";
import { Link } from "react-router-dom";
-import { useCallback, useEffect, useRef, useState, type FormEvent } from "react";
+import {
+ useCallback,
+ useEffect,
+ useRef,
+ useState,
+ type FormEvent,
+} from "react";
import {
AlertTriangle,
ArrowDown,
@@ -285,14 +291,17 @@ function EventCard({
};
const statusStyles: Record = {
- "Registration Open": "bg-gradient-to-r from-emerald-500/20 to-aethex-500/30 text-emerald-200 border border-emerald-400/40",
+ "Registration Open":
+ "bg-gradient-to-r from-emerald-500/20 to-aethex-500/30 text-emerald-200 border border-emerald-400/40",
Recurring: "bg-blue-500/10 text-blue-200 border border-blue-400/40",
Upcoming: "bg-orange-500/10 text-orange-200 border border-orange-400/40",
Waitlist: "bg-amber-500/10 text-amber-200 border border-amber-400/40",
};
const buttonLabel = isRegistered ? "Manage Registration" : "Register";
- const submitLabel = isRegistered ? "Update Registration" : "Confirm Registration";
+ const submitLabel = isRegistered
+ ? "Update Registration"
+ : "Confirm Registration";
return (
-
{event.title}
-
+
+ {event.title}
+
+
{event.status}
{isRegistered && (
@@ -347,7 +363,9 @@ function EventCard({
{buttonLabel}
@@ -367,7 +385,10 @@ function EventCard({
id={`name-${event.id}`}
value={form.name}
onChange={(eventChange) =>
- setForm((prev) => ({ ...prev, name: eventChange.target.value }))
+ setForm((prev) => ({
+ ...prev,
+ name: eventChange.target.value,
+ }))
}
placeholder="Enter your name"
required
@@ -380,14 +401,19 @@ function EventCard({
type="email"
value={form.email}
onChange={(eventChange) =>
- setForm((prev) => ({ ...prev, email: eventChange.target.value }))
+ setForm((prev) => ({
+ ...prev,
+ email: eventChange.target.value,
+ }))
}
placeholder="you@example.com"
required
/>
- Team or studio (optional)
+
+ Team or studio (optional)
+
- Notes for the organizers
+
+ Notes for the organizers
+
- {error && (
-
{error}
- )}
+ {error &&
{error}
}
-
+
{submitting ? (
@@ -434,8 +464,17 @@ function EventCard({
{event.registrationUrl && (
-
-
+
+
Event details
@@ -449,7 +488,10 @@ function EventCard({
{event.agenda.map((item) => (
-
+
{item}
@@ -468,16 +510,27 @@ interface SectionHeaderProps {
align?: "center" | "left";
}
-function SectionHeader({ badge, title, description, align = "center" }: SectionHeaderProps) {
- const containerClass = align === "center" ? "mx-auto text-center" : "text-left";
+function SectionHeader({
+ badge,
+ title,
+ description,
+ align = "center",
+}: SectionHeaderProps) {
+ const containerClass =
+ align === "center" ? "mx-auto text-center" : "text-left";
return (
-
+
{badge && (
{badge}
@@ -496,7 +549,10 @@ interface PollCardProps {
}
function PollCard({ poll, selectedOption, onSelect }: PollCardProps) {
- const totalVotes = poll.options.reduce((sum, option) => sum + option.votes, 0);
+ const totalVotes = poll.options.reduce(
+ (sum, option) => sum + option.votes,
+ 0,
+ );
return (
@@ -511,14 +567,24 @@ function PollCard({ poll, selectedOption, onSelect }: PollCardProps) {
{poll.closesIn}
-
{poll.question}
+
+ {poll.question}
+
{poll.options.map((option) => {
- const percentage = totalVotes === 0 ? 0 : Math.round((option.votes / totalVotes) * 100);
+ const percentage =
+ totalVotes === 0
+ ? 0
+ : Math.round((option.votes / totalVotes) * 100);
const isSelected = selectedOption === option.id;
- const TrendIcon = option.trend === "up" ? TrendingUp : option.trend === "down" ? ArrowDown : ArrowRight;
+ const TrendIcon =
+ option.trend === "up"
+ ? TrendingUp
+ : option.trend === "down"
+ ? ArrowDown
+ : ArrowRight;
return (
onSelect(option.id)}
>
@@ -544,16 +610,17 @@ function PollCard({ poll, selectedOption, onSelect }: PollCardProps) {
{option.label}
-
- {percentage}%
-
+ {percentage}%
);
})}
{totalVotes} votes recorded
-
+
Live insight
@@ -565,16 +632,34 @@ function PollCard({ poll, selectedOption, onSelect }: PollCardProps) {
export default function Community() {
const [isLoading, setIsLoading] = useState(true);
const toastShownRef = useRef(false);
- const [registeredEvents, setRegisteredEvents] = useState>({});
+ const [registeredEvents, setRegisteredEvents] = useState<
+ Record
+ >({});
const [polls, setPolls] = useState(() => [
{
id: "winter-cosmetic",
- question: "Which cosmetic drop should we fast-track for the winter update?",
+ question:
+ "Which cosmetic drop should we fast-track for the winter update?",
closesIn: "Closes in 3 days",
options: [
- { id: "frostbyte-operative", label: "Frostbyte Operative Skin", votes: 342, trend: "up" },
- { id: "aurora-trails", label: "Aurora Hover Trails", votes: 287, trend: "steady" },
- { id: "synthwave-lobby", label: "Synthwave Lobby Music Pack", votes: 198, trend: "down" },
+ {
+ id: "frostbyte-operative",
+ label: "Frostbyte Operative Skin",
+ votes: 342,
+ trend: "up",
+ },
+ {
+ id: "aurora-trails",
+ label: "Aurora Hover Trails",
+ votes: 287,
+ trend: "steady",
+ },
+ {
+ id: "synthwave-lobby",
+ label: "Synthwave Lobby Music Pack",
+ votes: 198,
+ trend: "down",
+ },
],
},
{
@@ -582,13 +667,30 @@ export default function Community() {
question: "What should the team prioritise next for the companion app?",
closesIn: "Closes in 6 days",
options: [
- { id: "build-tracker", label: "Squad build tracking & loadouts", votes: 264, trend: "up" },
- { id: "voice-integrations", label: "Native voice channel integration", votes: 192, trend: "steady" },
- { id: "marketplace", label: "Creator marketplace beta", votes: 148, trend: "up" },
+ {
+ id: "build-tracker",
+ label: "Squad build tracking & loadouts",
+ votes: 264,
+ trend: "up",
+ },
+ {
+ id: "voice-integrations",
+ label: "Native voice channel integration",
+ votes: 192,
+ trend: "steady",
+ },
+ {
+ id: "marketplace",
+ label: "Creator marketplace beta",
+ votes: 148,
+ trend: "up",
+ },
],
},
]);
- const [pollSelections, setPollSelections] = useState>({});
+ const [pollSelections, setPollSelections] = useState>(
+ {},
+ );
const [reportForm, setReportForm] = useState({ reason: "", details: "" });
useEffect(() => {
@@ -725,7 +827,8 @@ export default function Community() {
{
id: "strategy-lab",
name: "Strategy Lab",
- description: "Break down balance changes and evolving metas with systems designers.",
+ description:
+ "Break down balance changes and evolving metas with systems designers.",
threads: 182,
activeToday: 46,
latestThread: {
@@ -738,7 +841,8 @@ export default function Community() {
{
id: "build-clinic",
name: "Build Clinic",
- description: "Share pipelines, CI setups, and performance wins with fellow builders.",
+ description:
+ "Share pipelines, CI setups, and performance wins with fellow builders.",
threads: 241,
activeToday: 63,
latestThread: {
@@ -751,7 +855,8 @@ export default function Community() {
{
id: "playtest-reports",
name: "Playtest Reports",
- description: "Swap qualitative feedback, telemetry, and next-step experiments.",
+ description:
+ "Swap qualitative feedback, telemetry, and next-step experiments.",
threads: 96,
activeToday: 22,
latestThread: {
@@ -767,7 +872,8 @@ export default function Community() {
{
id: "bug-reports",
title: "Bug Reports",
- description: "Track crashes, blockers, and gameplay issues spotted by players.",
+ description:
+ "Track crashes, blockers, and gameplay issues spotted by players.",
submissionsThisWeek: 52,
statuses: [
{ label: "New", count: 7, tone: "warning" },
@@ -780,7 +886,8 @@ export default function Community() {
{
id: "balance-insights",
title: "Balance Insights",
- description: "Surface tuning feedback for weapons, archetypes, and encounters.",
+ description:
+ "Surface tuning feedback for weapons, archetypes, and encounters.",
submissionsThisWeek: 38,
statuses: [
{ label: "In Review", count: 15, tone: "neutral" },
@@ -793,7 +900,8 @@ export default function Community() {
{
id: "feature-ideas",
title: "Ideas & Features",
- description: "Prioritise new systems, cosmetics, and social tools suggested by the community.",
+ description:
+ "Prioritise new systems, cosmetics, and social tools suggested by the community.",
submissionsThisWeek: 61,
statuses: [
{ label: "Ideation", count: 24, tone: "neutral" },
@@ -809,7 +917,8 @@ export default function Community() {
{
id: "squad-voice",
name: "Squad Voice Lounge",
- description: "Hop into matchmaking-ready rooms with spatial audio support.",
+ description:
+ "Hop into matchmaking-ready rooms with spatial audio support.",
participants: 640,
activeNow: 132,
icon: Headphones,
@@ -836,7 +945,8 @@ export default function Community() {
{
id: "portfolio",
title: "Unified Portfolio",
- description: "Link GitHub, AeThex achievements, and live builds in one developer profile.",
+ description:
+ "Link GitHub, AeThex achievements, and live builds in one developer profile.",
metricLabel: "Portfolio views",
metricValue: "3.2K / wk",
icon: Layers,
@@ -844,7 +954,8 @@ export default function Community() {
{
id: "streaks",
title: "Contribution Streaks",
- description: "Track commits, bug reports, and mentoring sessions automatically.",
+ description:
+ "Track commits, bug reports, and mentoring sessions automatically.",
metricLabel: "Longest streak",
metricValue: "48 days",
icon: Sparkles,
@@ -852,7 +963,8 @@ export default function Community() {
{
id: "reputation",
title: "Reputation Signals",
- description: "Surface kudos, endorsements, and verified roles in the community.",
+ description:
+ "Surface kudos, endorsements, and verified roles in the community.",
metricLabel: "Reputation",
metricValue: "4.9 ★",
icon: UserCircle,
@@ -869,7 +981,8 @@ export default function Community() {
badge: "🏆 Top Contributor",
speciality: "Game Development",
bio: "Hosts the weekly build clinics and mentors new squads shipping co-op missions.",
- recentContribution: "Rolled out the Adaptive Matchmaking toolkit for Season 4.",
+ recentContribution:
+ "Rolled out the Adaptive Matchmaking toolkit for Season 4.",
reputation: "4.9/5 player rating",
profileUrl: "/passport/alex-developer",
},
@@ -882,7 +995,8 @@ export default function Community() {
badge: "📚 Knowledge Hero",
speciality: "Technical Writing",
bio: "Maintains the AeThex SDK quickstarts and keeps localisation packs up to date.",
- recentContribution: "Launched the multiplayer onboarding walkthrough in seven languages.",
+ recentContribution:
+ "Launched the multiplayer onboarding walkthrough in seven languages.",
reputation: "4.8/5 creator rating",
profileUrl: "/passport/sarah-coder",
},
@@ -895,7 +1009,8 @@ export default function Community() {
badge: "🧠 AI Pioneer",
speciality: "Machine Learning",
bio: "Builds guardrailed inference pipelines and shares evaluation playbooks weekly.",
- recentContribution: "Released the Procedural Dialogue orchestrator adopted by 40+ teams.",
+ recentContribution:
+ "Released the Procedural Dialogue orchestrator adopted by 40+ teams.",
reputation: "4.9/5 mentor rating",
profileUrl: "/passport/jordan-ai",
},
@@ -905,7 +1020,8 @@ export default function Community() {
{
id: "photon-ridge",
title: "Photon Ridge Raid",
- description: "Modded endgame encounter balancing puzzle mechanics with co-op combat.",
+ description:
+ "Modded endgame encounter balancing puzzle mechanics with co-op combat.",
downloads: 12045,
rating: 4.8,
author: "NebulaFox",
@@ -914,7 +1030,8 @@ export default function Community() {
{
id: "neon-run",
title: "Neon Run Track Pack",
- description: "Five synthwave-inspired courses with dynamic weather scripting.",
+ description:
+ "Five synthwave-inspired courses with dynamic weather scripting.",
downloads: 8742,
rating: 4.7,
author: "CircuitBreaker",
@@ -923,7 +1040,8 @@ export default function Community() {
{
id: "dynamic-hubs",
title: "Dynamic Hub Templates",
- description: "Drop-in lobby layouts with reactive NPC chatter and map voting.",
+ description:
+ "Drop-in lobby layouts with reactive NPC chatter and map voting.",
downloads: 6403,
rating: 4.9,
author: "Astra",
@@ -936,7 +1054,8 @@ export default function Community() {
id: "screenshot-1",
title: "Aurora Rift",
type: "Screenshot",
- thumbnail: "https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1469474968028-56623f02e42e?w=600&h=400&fit=crop",
author: "Nova",
likes: 684,
},
@@ -944,7 +1063,8 @@ export default function Community() {
id: "artwork-1",
title: "Character concept: Vox",
type: "Artwork",
- thumbnail: "https://images.unsplash.com/photo-1604079628040-94301bb21b11?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1604079628040-94301bb21b11?w=600&h=400&fit=crop",
author: "Sketchbyte",
likes: 542,
},
@@ -952,7 +1072,8 @@ export default function Community() {
id: "video-1",
title: "Speedrun showcase",
type: "Video",
- thumbnail: "https://images.unsplash.com/photo-1511512578047-dfb367046420?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1511512578047-dfb367046420?w=600&h=400&fit=crop",
author: "Dash",
likes: 452,
},
@@ -960,7 +1081,8 @@ export default function Community() {
id: "screenshot-2",
title: "Photon Ridge sunset",
type: "Screenshot",
- thumbnail: "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1517694712202-14dd9538aa97?w=600&h=400&fit=crop",
author: "NebulaFox",
likes: 398,
},
@@ -968,7 +1090,8 @@ export default function Community() {
id: "artwork-2",
title: "Kairos armor set",
type: "Artwork",
- thumbnail: "https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1498050108023-c5249f4df085?w=600&h=400&fit=crop",
author: "Synth",
likes: 356,
},
@@ -976,7 +1099,8 @@ export default function Community() {
id: "video-2",
title: "Live jam timelapse",
type: "Video",
- thumbnail: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=600&h=400&fit=crop",
+ thumbnail:
+ "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=600&h=400&fit=crop",
author: "BeatSync",
likes: 312,
},
@@ -987,9 +1111,11 @@ export default function Community() {
id: "nebulafox",
name: "NebulaFox",
role: "Systems Designer",
- highlight: "Created the Photon Ridge raid encounter adopted by 54% of squads.",
+ highlight:
+ "Created the Photon Ridge raid encounter adopted by 54% of squads.",
link: "/community/spotlight/nebulafox",
- avatar: "https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?w=120&h=120&fit=crop&crop=face",
+ avatar:
+ "https://images.unsplash.com/photo-1529626455594-4ff0802cfb7e?w=120&h=120&fit=crop&crop=face",
metrics: [
{ label: "Downloads", value: "12K" },
{ label: "Avg Rating", value: "4.8" },
@@ -1000,9 +1126,11 @@ export default function Community() {
id: "lunatech",
name: "LunaTech",
role: "Technical Artist",
- highlight: "Delivered the AeThex holographic shader pack powering six community events.",
+ highlight:
+ "Delivered the AeThex holographic shader pack powering six community events.",
link: "/community/spotlight/lunatech",
- avatar: "https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=120&h=120&fit=crop&crop=face",
+ avatar:
+ "https://images.unsplash.com/photo-1527980965255-d3b416303d12?w=120&h=120&fit=crop&crop=face",
metrics: [
{ label: "Adopted Studios", value: "32" },
{ label: "Workshop Forks", value: "210" },
@@ -1044,7 +1172,8 @@ export default function Community() {
const moderationGuidelines = [
{
title: "Respect every player",
- description: "Harassment, hate speech, and discrimination are removed without debate.",
+ description:
+ "Harassment, hate speech, and discrimination are removed without debate.",
},
{
title: "Keep feedback actionable",
@@ -1052,7 +1181,8 @@ export default function Community() {
},
{
title: "Collaborate transparently",
- description: "Disclose monetisation, affiliations, or sensitive data sources when sharing work.",
+ description:
+ "Disclose monetisation, affiliations, or sensitive data sources when sharing work.",
},
];
@@ -1060,19 +1190,22 @@ export default function Community() {
{
id: "auto-shields",
title: "Automated Shields",
- description: "Realtime filters flag slurs, spam, and phishing before they hit the feed.",
+ description:
+ "Realtime filters flag slurs, spam, and phishing before they hit the feed.",
icon: Shield,
},
{
id: "casebook",
title: "Casebook Workflows",
- description: "Escalate complex cases with evidence bundles and audit trails.",
+ description:
+ "Escalate complex cases with evidence bundles and audit trails.",
icon: Gavel,
},
{
id: "report-routing",
title: "Report Routing",
- description: "Route player reports to dedicated channel owners for rapid follow-up.",
+ description:
+ "Route player reports to dedicated channel owners for rapid follow-up.",
icon: Flag,
},
];
@@ -1081,27 +1214,34 @@ export default function Community() {
{
id: "harassment",
label: "Harassment or hateful content",
- description: "Toxic language, slurs, or targeted harassment toward a player or group.",
+ description:
+ "Toxic language, slurs, or targeted harassment toward a player or group.",
},
{
id: "cheating",
label: "Cheating or exploit",
- description: "Sharing hacks, stolen assets, or instructions to bypass fair play.",
+ description:
+ "Sharing hacks, stolen assets, or instructions to bypass fair play.",
},
{
id: "inappropriate",
label: "Inappropriate media",
- description: "Adult content, personal data leaks, or otherwise unsafe material.",
+ description:
+ "Adult content, personal data leaks, or otherwise unsafe material.",
},
];
- const feedbackToneStyles: Record<"neutral" | "positive" | "warning", string> = {
- neutral: "bg-blue-500/10 text-blue-200 border border-blue-400/40",
- positive: "bg-emerald-500/10 text-emerald-200 border border-emerald-400/40",
- warning: "bg-amber-500/10 text-amber-200 border border-amber-400/40",
- };
+ const feedbackToneStyles: Record<"neutral" | "positive" | "warning", string> =
+ {
+ neutral: "bg-blue-500/10 text-blue-200 border border-blue-400/40",
+ positive:
+ "bg-emerald-500/10 text-emerald-200 border border-emerald-400/40",
+ warning: "bg-amber-500/10 text-amber-200 border border-amber-400/40",
+ };
- const selectedReportReason = reportReasons.find((reason) => reason.id === reportForm.reason);
+ const selectedReportReason = reportReasons.find(
+ (reason) => reason.id === reportForm.reason,
+ );
const handleEventRegistration = useCallback(
(eventData: CommunityEvent, payload: EventRegistrationPayload) => {
@@ -1165,11 +1305,15 @@ export default function Community() {
}
if (!trimmedDetails) {
- aethexToast.system("Add a brief description so moderators can respond quickly.");
+ aethexToast.system(
+ "Add a brief description so moderators can respond quickly.",
+ );
return;
}
- aethexToast.system("Report submitted. Our moderation team will review shortly.");
+ aethexToast.system(
+ "Report submitted. Our moderation team will review shortly.",
+ );
setReportForm({ reason: "", details: "" });
},
[reportForm],
@@ -1342,9 +1486,9 @@ export default function Community() {
Introducing DevConnect
- DevConnect is our dedicated hub for platform teams building the
- next wave of AeThex experiences. Launch collabs, monitor live
- services, and activate the Studio network — all from one
+ DevConnect is our dedicated hub for platform teams building
+ the next wave of AeThex experiences. Launch collabs, monitor
+ live services, and activate the Studio network — all from one
command center.
@@ -1363,7 +1507,12 @@ export default function Community() {
-
+
Talk with AeThex Team
@@ -1394,9 +1543,9 @@ export default function Community() {
})}
- DevConnect syncs directly with AeThex developer profiles, so your
- activity, streaks, and achievements follow you across every
- build.
+ DevConnect syncs directly with AeThex developer profiles, so
+ your activity, streaks, and achievements follow you across
+ every build.
@@ -1429,8 +1578,12 @@ export default function Community() {
- {space.name}
- {space.description}
+
+ {space.name}
+
+
+ {space.description}
+
@@ -1450,10 +1603,15 @@ export default function Community() {
{space.latestThread.title}
- by {space.latestThread.author} • {space.latestThread.timeAgo}
+ by {space.latestThread.author} •{" "}
+ {space.latestThread.timeAgo}
-
+
Enter space
@@ -1481,7 +1639,10 @@ export default function Community() {
{feedbackChannels.map((channel) => {
const Icon = channel.icon;
return (
-
+
@@ -1489,11 +1650,18 @@ export default function Community() {
- {channel.title}
- {channel.description}
+
+ {channel.title}
+
+
+ {channel.description}
+
-
+
{channel.submissionsThisWeek} this week
@@ -1506,13 +1674,20 @@ export default function Community() {
{channel.statuses.map((status) => (
{status.label}: {status.count}
))}
-
+
Submit feedback
@@ -1527,7 +1702,8 @@ export default function Community() {
Live roadmap snapshot
- Track what the team is shipping next based on player momentum.
+ Track what the team is shipping next based on player
+ momentum.
@@ -1537,13 +1713,23 @@ export default function Community() {
className="flex flex-col gap-2 rounded-lg border border-border/40 bg-background/80 p-4 sm:flex-row sm:items-center sm:justify-between"
>
-
{item.title}
-
{item.eta}
+
+ {item.title}
+
+
+ {item.eta}
+
- {item.status}
+
+ {item.status}
+
))}
-
+
Open public roadmap
@@ -1588,7 +1774,8 @@ export default function Community() {
Active channels
- See who’s online and jump into the conversations that matter right now.
+ See who’s online and jump into the conversations that matter
+ right now.
@@ -1605,18 +1792,26 @@ export default function Community() {
-
{channel.name}
-
{channel.description}
+
+ {channel.name}
+
+
+ {channel.description}
+
-
+
{channel.participants} members
Synced with Discord
- {channel.activeNow} live now
+ {channel.activeNow}{" "}
+ live now
@@ -1628,7 +1823,8 @@ export default function Community() {
Why players love integrated chat
- Keep every squad aligned across devices with presence, threads, and recordings.
+ Keep every squad aligned across devices with presence,
+ threads, and recordings.
@@ -1666,7 +1862,9 @@ export default function Community() {
animationDelay={index * 0.12}
isRegistered={Boolean(registeredEvents[event.id])}
registrant={registeredEvents[event.id]}
- onRegister={(payload) => handleEventRegistration(event, payload)}
+ onRegister={(payload) =>
+ handleEventRegistration(event, payload)
+ }
/>
))}
@@ -1686,12 +1884,17 @@ export default function Community() {
{profileHighlights.map((highlight) => {
const Icon = highlight.icon;
return (
-
+
- {highlight.title}
+
+ {highlight.title}
+
{highlight.description}
@@ -1759,7 +1962,11 @@ export default function Community() {
{contributor.badge}
-
+
View passport
@@ -1782,7 +1989,10 @@ export default function Community() {
{workshopItems.map((item) => {
const Icon = item.icon;
return (
-
+
@@ -1799,12 +2009,18 @@ export default function Community() {
Rating
- {item.rating.toFixed(1)}
+
+ {item.rating.toFixed(1)}
+
Created by {item.author}
-
+
View asset
@@ -1829,7 +2045,10 @@ export default function Community() {
/>
{mediaGallery.map((item) => (
-
+
-
{item.title}
-
by {item.author}
+
+ {item.title}
+
+
+ by {item.author}
+
@@ -1873,7 +2096,10 @@ export default function Community() {
/>
{spotlightCreators.map((creator) => (
-
+
@@ -1886,7 +2112,9 @@ export default function Community() {
{creator.name}
-
{creator.role}
+
+ {creator.role}
+
@@ -1935,20 +2163,32 @@ export default function Community() {
Community guidelines
- Expectations for every player and contributor.
+
+ Expectations for every player and contributor.
+
-
+
Updated weekly
{moderationGuidelines.map((guideline) => (
-
+
-
{guideline.title}
-
{guideline.description}
+
+ {guideline.title}
+
+
+ {guideline.description}
+
))}
@@ -1960,7 +2200,8 @@ export default function Community() {
Moderation toolkit
- Equip community managers with actionable, auditable controls.
+ Equip community managers with actionable, auditable
+ controls.
@@ -1974,8 +2215,12 @@ export default function Community() {
- {tool.title}
- {tool.description}
+
+ {tool.title}
+
+
+ {tool.description}
+
);
})}
@@ -1985,7 +2230,8 @@ export default function Community() {
Submit a report
- Reports are routed to the right channel owners for fast follow-up.
+ Reports are routed to the right channel owners for fast
+ follow-up.
@@ -1996,7 +2242,10 @@ export default function Community() {
id="report-reason"
value={reportForm.reason}
onChange={(event) =>
- setReportForm((prev) => ({ ...prev, reason: event.target.value }))
+ setReportForm((prev) => ({
+ ...prev,
+ reason: event.target.value,
+ }))
}
className="w-full rounded-lg border border-border/50 bg-background/80 px-3 py-2 text-sm focus:border-aethex-400 focus:outline-none focus:ring-2 focus:ring-aethex-400/40"
>
@@ -2019,7 +2268,10 @@ export default function Community() {
id="report-details"
value={reportForm.details}
onChange={(event) =>
- setReportForm((prev) => ({ ...prev, details: event.target.value }))
+ setReportForm((prev) => ({
+ ...prev,
+ details: event.target.value,
+ }))
}
placeholder="Share links, timestamps, or context so moderators can follow up quickly."
rows={4}
diff --git a/client/pages/DevelopersDirectory.tsx b/client/pages/DevelopersDirectory.tsx
index 917a22ef..ed85793a 100644
--- a/client/pages/DevelopersDirectory.tsx
+++ b/client/pages/DevelopersDirectory.tsx
@@ -58,7 +58,9 @@ const DeveloperCard = ({ profile }: DeveloperCardProps) => {
-
+
{profile.user_type.replace("_", " ")}
{isGodMode && (
@@ -124,7 +126,10 @@ const DeveloperCard = ({ profile }: DeveloperCardProps) => {
variant="outline"
className="w-full border-slate-700/70 text-slate-100 transition-colors hover:border-aethex-400/60 hover:text-white"
>
-
+
View Passport
@@ -193,8 +198,8 @@ const DevelopersDirectory = () => {
Discover AeThex developers
- Browse verified builders, clients, and community members across
- every AeThex realm.
+ Browse verified builders, clients, and community members
+ across every AeThex realm.
diff --git a/client/pages/Profile.tsx b/client/pages/Profile.tsx
index f9aa5a6a..a007d80d 100644
--- a/client/pages/Profile.tsx
+++ b/client/pages/Profile.tsx
@@ -51,7 +51,7 @@ import {
interface SiteStatus {
name: string;
url: string;
- status: 'online' | 'offline' | 'maintenance';
+ status: "online" | "offline" | "maintenance";
lastCheck: string;
responseTime: number;
version: string;
@@ -61,7 +61,7 @@ interface CrossSiteCommunication {
siteName: string;
lastSync: string;
dataExchanged: number;
- status: 'connected' | 'disconnected' | 'syncing';
+ status: "connected" | "disconnected" | "syncing";
}
export default function Profile() {
@@ -70,17 +70,17 @@ export default function Profile() {
const { success: toastSuccess, error: toastError } = useAethexToast();
const [isLoading, setIsLoading] = useState(true);
const [isSaving, setIsSaving] = useState(false);
-
+
// Profile settings state
const [profileData, setProfileData] = useState({
- displayName: '',
- bio: '',
- company: '',
- location: '',
- website: '',
- githubUsername: '',
- twitterUsername: '',
- linkedinUrl: '',
+ displayName: "",
+ bio: "",
+ company: "",
+ location: "",
+ website: "",
+ githubUsername: "",
+ twitterUsername: "",
+ linkedinUrl: "",
});
// Notification settings
@@ -94,7 +94,7 @@ export default function Profile() {
// Privacy settings
const [privacy, setPrivacy] = useState({
- profileVisibility: 'public',
+ profileVisibility: "public",
showEmail: false,
showProjects: true,
allowAnalytics: true,
@@ -114,50 +114,50 @@ export default function Profile() {
// Cross-site communication data
const [crossSiteData, setCrossSiteData] = useState
([
{
- siteName: 'AeThex Labs',
- lastSync: '2 minutes ago',
+ siteName: "AeThex Labs",
+ lastSync: "2 minutes ago",
dataExchanged: 1.2,
- status: 'connected',
+ status: "connected",
},
{
- siteName: 'Development Portal',
- lastSync: '5 minutes ago',
+ siteName: "Development Portal",
+ lastSync: "5 minutes ago",
dataExchanged: 0.8,
- status: 'syncing',
+ status: "syncing",
},
{
- siteName: 'Community Hub',
- lastSync: '12 minutes ago',
+ siteName: "Community Hub",
+ lastSync: "12 minutes ago",
dataExchanged: 2.1,
- status: 'connected',
+ status: "connected",
},
]);
// Site monitoring data
const [siteStatuses, setSiteStatuses] = useState([
{
- name: 'Main Site',
- url: 'https://core.aethex.biz',
- status: 'online',
- lastCheck: 'Just now',
+ name: "Main Site",
+ url: "https://core.aethex.biz",
+ status: "online",
+ lastCheck: "Just now",
responseTime: 245,
- version: '1.0.0',
+ version: "1.0.0",
},
{
- name: 'API Gateway',
- url: 'https://api.aethex.biz',
- status: 'online',
- lastCheck: '30 seconds ago',
+ name: "API Gateway",
+ url: "https://api.aethex.biz",
+ status: "online",
+ lastCheck: "30 seconds ago",
responseTime: 123,
- version: '2.1.3',
+ version: "2.1.3",
},
{
- name: 'Labs Portal',
- url: 'https://labs.aethex.biz',
- status: 'maintenance',
- lastCheck: '2 minutes ago',
+ name: "Labs Portal",
+ url: "https://labs.aethex.biz",
+ status: "maintenance",
+ lastCheck: "2 minutes ago",
responseTime: 0,
- version: '1.5.2',
+ version: "1.5.2",
},
]);
@@ -166,20 +166,20 @@ export default function Profile() {
useEffect(() => {
if (!authLoading && !user) {
- navigate('/login');
+ navigate("/login");
return;
}
if (profile) {
setProfileData({
- displayName: profile.full_name || '',
- bio: profile.bio || '',
- company: (profile as any).company || '',
- location: profile.location || '',
- website: (profile as any).website || '',
- githubUsername: (profile as any).github_username || '',
- twitterUsername: (profile as any).twitter_username || '',
- linkedinUrl: profile.linkedin_url || '',
+ displayName: profile.full_name || "",
+ bio: profile.bio || "",
+ company: (profile as any).company || "",
+ location: profile.location || "",
+ website: (profile as any).website || "",
+ githubUsername: (profile as any).github_username || "",
+ twitterUsername: (profile as any).twitter_username || "",
+ linkedinUrl: profile.linkedin_url || "",
});
}
@@ -193,7 +193,7 @@ export default function Profile() {
console.log("Starting profile save...", {
user: user.id,
profile: !!profile,
- profileData
+ profileData,
});
try {
@@ -204,13 +204,20 @@ export default function Profile() {
try {
// Try using the AuthContext updateProfile (which works with mock too)
await updateProfile({
- username: profileData.displayName || user.email?.split('@')[0] || `user_${Date.now()}`,
+ username:
+ profileData.displayName ||
+ user.email?.split("@")[0] ||
+ `user_${Date.now()}`,
full_name: profileData.displayName,
bio: profileData.bio,
location: profileData.location,
linkedin_url: profileData.linkedinUrl,
- github_url: profileData.githubUsername ? `https://github.com/${profileData.githubUsername}` : null,
- twitter_url: profileData.twitterUsername ? `https://twitter.com/${profileData.twitterUsername}` : null,
+ github_url: profileData.githubUsername
+ ? `https://github.com/${profileData.githubUsername}`
+ : null,
+ twitter_url: profileData.twitterUsername
+ ? `https://twitter.com/${profileData.twitterUsername}`
+ : null,
user_type: "game_developer",
experience_level: "beginner",
level: 1,
@@ -219,22 +226,32 @@ export default function Profile() {
console.log("Profile created successfully via AuthContext");
} catch (authError) {
- console.warn("AuthContext creation failed, trying direct database:", authError);
+ console.warn(
+ "AuthContext creation failed, trying direct database:",
+ authError,
+ );
// Fallback to direct database call
const { data, error } = await supabase
.from("user_profiles")
.insert({
id: user.id,
- username: profileData.displayName || user.email?.split('@')[0] || `user_${Date.now()}`,
+ username:
+ profileData.displayName ||
+ user.email?.split("@")[0] ||
+ `user_${Date.now()}`,
user_type: "game_developer",
experience_level: "beginner",
full_name: profileData.displayName,
bio: profileData.bio,
location: profileData.location,
linkedin_url: profileData.linkedinUrl,
- github_url: profileData.githubUsername ? `https://github.com/${profileData.githubUsername}` : null,
- twitter_url: profileData.twitterUsername ? `https://twitter.com/${profileData.twitterUsername}` : null,
+ github_url: profileData.githubUsername
+ ? `https://github.com/${profileData.githubUsername}`
+ : null,
+ twitter_url: profileData.twitterUsername
+ ? `https://twitter.com/${profileData.twitterUsername}`
+ : null,
level: 1,
total_xp: 0,
created_at: new Date().toISOString(),
@@ -257,8 +274,12 @@ export default function Profile() {
bio: profileData.bio,
location: profileData.location,
linkedin_url: profileData.linkedinUrl,
- github_url: profileData.githubUsername ? `https://github.com/${profileData.githubUsername}` : null,
- twitter_url: profileData.twitterUsername ? `https://twitter.com/${profileData.twitterUsername}` : null,
+ github_url: profileData.githubUsername
+ ? `https://github.com/${profileData.githubUsername}`
+ : null,
+ twitter_url: profileData.twitterUsername
+ ? `https://twitter.com/${profileData.twitterUsername}`
+ : null,
updated_at: new Date().toISOString(),
} as any);
}
@@ -271,7 +292,8 @@ export default function Profile() {
console.error("Profile save error:", error);
toastError({
title: "Update Failed",
- description: error.message || "Failed to update profile. Please try again.",
+ description:
+ error.message || "Failed to update profile. Please try again.",
});
} finally {
setIsSaving(false);
@@ -280,14 +302,14 @@ export default function Profile() {
const getStatusIcon = (status: string) => {
switch (status) {
- case 'online':
- case 'connected':
+ case "online":
+ case "connected":
return ;
- case 'offline':
- case 'disconnected':
+ case "offline":
+ case "disconnected":
return ;
- case 'maintenance':
- case 'syncing':
+ case "maintenance":
+ case "syncing":
return ;
default:
return ;
@@ -296,17 +318,17 @@ export default function Profile() {
const getStatusColor = (status: string) => {
switch (status) {
- case 'online':
- case 'connected':
- return 'bg-green-500';
- case 'offline':
- case 'disconnected':
- return 'bg-red-500';
- case 'maintenance':
- case 'syncing':
- return 'bg-yellow-500';
+ case "online":
+ case "connected":
+ return "bg-green-500";
+ case "offline":
+ case "disconnected":
+ return "bg-red-500";
+ case "maintenance":
+ case "syncing":
+ return "bg-yellow-500";
default:
- return 'bg-gray-400';
+ return "bg-gray-400";
}
};
@@ -329,14 +351,20 @@ export default function Profile() {
- {profile?.full_name || 'User Profile'}
+ {profile?.full_name || "User Profile"}
{user?.email}
-
+
Current streak: {currentStreak}d
-
+
Longest streak: {longestStreak}d
@@ -346,19 +374,31 @@ export default function Profile() {
-
+
Profile
-
+
Settings
-
+
Overseer
-
+
Network
@@ -379,48 +419,80 @@ export default function Profile() {
- Display Name
+
+ Display Name
+
setProfileData({ ...profileData, displayName: e.target.value })}
+ onChange={(e) =>
+ setProfileData({
+ ...profileData,
+ displayName: e.target.value,
+ })
+ }
className="bg-slate-900/50 border-slate-600 text-white"
/>
- Company
+
+ Company
+
setProfileData({ ...profileData, company: e.target.value })}
+ onChange={(e) =>
+ setProfileData({
+ ...profileData,
+ company: e.target.value,
+ })
+ }
className="bg-slate-900/50 border-slate-600 text-white"
/>
- Location
+
+ Location
+
setProfileData({ ...profileData, location: e.target.value })}
+ onChange={(e) =>
+ setProfileData({
+ ...profileData,
+ location: e.target.value,
+ })
+ }
className="bg-slate-900/50 border-slate-600 text-white"
/>
- Website
+
+ Website
+
setProfileData({ ...profileData, website: e.target.value })}
+ onChange={(e) =>
+ setProfileData({
+ ...profileData,
+ website: e.target.value,
+ })
+ }
className="bg-slate-900/50 border-slate-600 text-white"
/>