Prettier format pending files

This commit is contained in:
Builder.io 2025-10-05 00:11:30 +00:00
parent c00bdfcc2d
commit 9e8163d005
9 changed files with 773 additions and 336 deletions

View file

@ -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)) {

View file

@ -67,12 +67,18 @@ const App = () => (
<Route path="/profile/me" element={<Profile />} />
<Route path="/developers" element={<DevelopersDirectory />} />
<Route path="/developers/me" element={<LegacyPassportRedirect />} />
<Route
path="/developers/me"
element={<LegacyPassportRedirect />}
/>
<Route
path="/developers/:id"
element={<LegacyPassportRedirect />}
/>
<Route path="/profiles" element={<Navigate to="/developers" replace />} />
<Route
path="/profiles"
element={<Navigate to="/developers" replace />}
/>
<Route path="/profiles/me" element={<LegacyPassportRedirect />} />
<Route
path="/profiles/:id"

View file

@ -150,10 +150,14 @@ const PassportSummary = ({
<div className="w-full max-w-xs space-y-3 rounded-xl bg-slate-900/60 p-4 border border-slate-800">
<div className="flex items-center justify-between text-slate-200">
<span className="text-sm font-medium uppercase tracking-wider">
{isLegendary ? "Legendary Status" : `Progress to Level ${level + 1}`}
{isLegendary
? "Legendary Status"
: `Progress to Level ${level + 1}`}
</span>
<span className="text-sm text-slate-300">
{isLegendary ? "MAX" : `${(progressToNextLevel || 0).toFixed(0)}%`}
{isLegendary
? "MAX"
: `${(progressToNextLevel || 0).toFixed(0)}%`}
</span>
</div>
<Progress value={progressToNextLevel} className="h-2" />

View file

@ -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<AethexUserProfile | null> {
async getProfileByUsername(
username: string,
): Promise<AethexUserProfile | null> {
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<void> {
async updateUserXPAndLevel(
userId: string,
xpGained: number | null = null,
): Promise<void> {
ensureSupabase();
const { data: profile, error } = await supabase
@ -719,7 +718,8 @@ export const aethexAchievementService = {
const updates: Record<string, number> = {};
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) {

View file

@ -36,7 +36,8 @@ const fetchMock = fetch as unknown as Mock;
vi.mock("@/lib/supabase", () => {
const userProfiles = new Map<string, any>();
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: () => ({}),

File diff suppressed because it is too large Load diff

View file

@ -58,7 +58,9 @@ const DeveloperCard = ({ profile }: DeveloperCardProps) => {
<CardHeader className="space-y-3">
<div className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2">
<Badge className={cn("text-xs uppercase tracking-wider", realmStyle)}>
<Badge
className={cn("text-xs uppercase tracking-wider", realmStyle)}
>
{profile.user_type.replace("_", " ")}
</Badge>
{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"
>
<Link to={passportHref} className="flex items-center justify-center gap-2">
<Link
to={passportHref}
className="flex items-center justify-center gap-2"
>
<UserRound className="h-4 w-4" />
View Passport
</Link>
@ -193,8 +198,8 @@ const DevelopersDirectory = () => {
Discover AeThex developers
</h1>
<p className="text-slate-300">
Browse verified builders, clients, and community members across
every AeThex realm.
Browse verified builders, clients, and community members
across every AeThex realm.
</p>
</div>
<div className="flex gap-2">

View file

@ -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<CrossSiteCommunication[]>([
{
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<SiteStatus[]>([
{
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 <CheckCircle className="h-4 w-4 text-green-500" />;
case 'offline':
case 'disconnected':
case "offline":
case "disconnected":
return <XCircle className="h-4 w-4 text-red-500" />;
case 'maintenance':
case 'syncing':
case "maintenance":
case "syncing":
return <RefreshCw className="h-4 w-4 text-yellow-500 animate-spin" />;
default:
return <Circle className="h-4 w-4 text-gray-400" />;
@ -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() {
</Avatar>
<div>
<h1 className="text-3xl font-bold text-white">
{profile?.full_name || 'User Profile'}
{profile?.full_name || "User Profile"}
</h1>
<p className="text-gray-300">{user?.email}</p>
<div className="mt-3 flex flex-wrap gap-2">
<Badge variant="outline" className="border-purple-400/40 text-purple-200">
<Badge
variant="outline"
className="border-purple-400/40 text-purple-200"
>
Current streak: {currentStreak}d
</Badge>
<Badge variant="outline" className="border-purple-400/40 text-purple-200">
<Badge
variant="outline"
className="border-purple-400/40 text-purple-200"
>
Longest streak: {longestStreak}d
</Badge>
</div>
@ -346,19 +374,31 @@ export default function Profile() {
<Tabs defaultValue="profile" className="space-y-6">
<TabsList className="grid w-full grid-cols-4 bg-slate-800/50 border border-slate-700">
<TabsTrigger value="profile" className="text-white data-[state=active]:bg-purple-600">
<TabsTrigger
value="profile"
className="text-white data-[state=active]:bg-purple-600"
>
<User className="h-4 w-4 mr-2" />
Profile
</TabsTrigger>
<TabsTrigger value="settings" className="text-white data-[state=active]:bg-purple-600">
<TabsTrigger
value="settings"
className="text-white data-[state=active]:bg-purple-600"
>
<Settings className="h-4 w-4 mr-2" />
Settings
</TabsTrigger>
<TabsTrigger value="overseer" className="text-white data-[state=active]:bg-purple-600">
<TabsTrigger
value="overseer"
className="text-white data-[state=active]:bg-purple-600"
>
<Monitor className="h-4 w-4 mr-2" />
Overseer
</TabsTrigger>
<TabsTrigger value="network" className="text-white data-[state=active]:bg-purple-600">
<TabsTrigger
value="network"
className="text-white data-[state=active]:bg-purple-600"
>
<Network className="h-4 w-4 mr-2" />
Network
</TabsTrigger>
@ -379,48 +419,80 @@ export default function Profile() {
<CardContent className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="displayName" className="text-white">Display Name</Label>
<Label htmlFor="displayName" className="text-white">
Display Name
</Label>
<Input
id="displayName"
value={profileData.displayName}
onChange={(e) => setProfileData({ ...profileData, displayName: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
displayName: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
<div>
<Label htmlFor="company" className="text-white">Company</Label>
<Label htmlFor="company" className="text-white">
Company
</Label>
<Input
id="company"
value={profileData.company}
onChange={(e) => setProfileData({ ...profileData, company: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
company: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
<div>
<Label htmlFor="location" className="text-white">Location</Label>
<Label htmlFor="location" className="text-white">
Location
</Label>
<Input
id="location"
value={profileData.location}
onChange={(e) => setProfileData({ ...profileData, location: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
location: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
<div>
<Label htmlFor="website" className="text-white">Website</Label>
<Label htmlFor="website" className="text-white">
Website
</Label>
<Input
id="website"
value={profileData.website}
onChange={(e) => setProfileData({ ...profileData, website: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
website: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
</div>
<div>
<Label htmlFor="bio" className="text-white">Bio</Label>
<Label htmlFor="bio" className="text-white">
Bio
</Label>
<Textarea
id="bio"
value={profileData.bio}
onChange={(e) => setProfileData({ ...profileData, bio: e.target.value })}
onChange={(e) =>
setProfileData({ ...profileData, bio: e.target.value })
}
className="bg-slate-900/50 border-slate-600 text-white"
rows={3}
/>
@ -428,35 +500,60 @@ export default function Profile() {
<Separator className="bg-slate-600" />
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<Label htmlFor="github" className="text-white">GitHub Username</Label>
<Label htmlFor="github" className="text-white">
GitHub Username
</Label>
<Input
id="github"
value={profileData.githubUsername}
onChange={(e) => setProfileData({ ...profileData, githubUsername: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
githubUsername: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
<div>
<Label htmlFor="twitter" className="text-white">Twitter Username</Label>
<Label htmlFor="twitter" className="text-white">
Twitter Username
</Label>
<Input
id="twitter"
value={profileData.twitterUsername}
onChange={(e) => setProfileData({ ...profileData, twitterUsername: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
twitterUsername: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
<div>
<Label htmlFor="linkedin" className="text-white">LinkedIn URL</Label>
<Label htmlFor="linkedin" className="text-white">
LinkedIn URL
</Label>
<Input
id="linkedin"
value={profileData.linkedinUrl}
onChange={(e) => setProfileData({ ...profileData, linkedinUrl: e.target.value })}
onChange={(e) =>
setProfileData({
...profileData,
linkedinUrl: e.target.value,
})
}
className="bg-slate-900/50 border-slate-600 text-white"
/>
</div>
</div>
<Button onClick={handleProfileSave} disabled={isSaving} className="bg-purple-600 hover:bg-purple-700">
{isSaving ? 'Saving...' : 'Save Profile'}
<Button
onClick={handleProfileSave}
disabled={isSaving}
className="bg-purple-600 hover:bg-purple-700"
>
{isSaving ? "Saving..." : "Save Profile"}
</Button>
</CardContent>
</Card>
@ -474,14 +571,20 @@ export default function Profile() {
</CardHeader>
<CardContent className="space-y-4">
{Object.entries(notifications).map(([key, value]) => (
<div key={key} className="flex items-center justify-between">
<div
key={key}
className="flex items-center justify-between"
>
<Label className="text-white capitalize">
{key.replace(/([A-Z])/g, ' $1').trim()}
{key.replace(/([A-Z])/g, " $1").trim()}
</Label>
<Switch
checked={value}
onCheckedChange={(checked) =>
setNotifications({ ...notifications, [key]: checked })
onCheckedChange={(checked) =>
setNotifications({
...notifications,
[key]: checked,
})
}
/>
</div>
@ -498,16 +601,28 @@ export default function Profile() {
</CardHeader>
<CardContent className="space-y-4">
{Object.entries(privacy).map(([key, value]) => (
<div key={key} className="flex items-center justify-between">
<div
key={key}
className="flex items-center justify-between"
>
<Label className="text-white capitalize">
{key.replace(/([A-Z])/g, ' $1').trim()}
{key.replace(/([A-Z])/g, " $1").trim()}
</Label>
<Switch
checked={typeof value === 'boolean' ? value : value === 'public'}
onCheckedChange={(checked) =>
setPrivacy({
...privacy,
[key]: typeof value === 'boolean' ? checked : (checked ? 'public' : 'private')
checked={
typeof value === "boolean"
? value
: value === "public"
}
onCheckedChange={(checked) =>
setPrivacy({
...privacy,
[key]:
typeof value === "boolean"
? checked
: checked
? "public"
: "private",
})
}
/>
@ -526,7 +641,9 @@ export default function Profile() {
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-400">System Health</p>
<p className="text-2xl font-bold text-green-400">{overseerData.systemHealth}%</p>
<p className="text-2xl font-bold text-green-400">
{overseerData.systemHealth}%
</p>
</div>
<Activity className="h-8 w-8 text-green-400" />
</div>
@ -538,7 +655,9 @@ export default function Profile() {
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-400">Active Users</p>
<p className="text-2xl font-bold text-blue-400">{overseerData.activeUsers}</p>
<p className="text-2xl font-bold text-blue-400">
{overseerData.activeUsers}
</p>
</div>
<Users className="h-8 w-8 text-blue-400" />
</div>
@ -550,7 +669,9 @@ export default function Profile() {
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-400">Server Load</p>
<p className="text-2xl font-bold text-yellow-400">{overseerData.serverLoad}%</p>
<p className="text-2xl font-bold text-yellow-400">
{overseerData.serverLoad}%
</p>
</div>
<Server className="h-8 w-8 text-yellow-400" />
</div>
@ -562,7 +683,9 @@ export default function Profile() {
<div className="flex items-center justify-between">
<div>
<p className="text-sm text-gray-400">Error Rate</p>
<p className="text-2xl font-bold text-red-400">{overseerData.errorRate}%</p>
<p className="text-2xl font-bold text-red-400">
{overseerData.errorRate}%
</p>
</div>
<AlertTriangle className="h-8 w-8 text-red-400" />
</div>
@ -583,19 +706,29 @@ export default function Profile() {
<CardContent>
<div className="space-y-4">
{siteStatuses.map((site, index) => (
<div key={index} className="flex items-center justify-between p-4 bg-slate-900/50 rounded-lg border border-slate-600">
<div
key={index}
className="flex items-center justify-between p-4 bg-slate-900/50 rounded-lg border border-slate-600"
>
<div className="flex items-center space-x-4">
{getStatusIcon(site.status)}
<div>
<h4 className="text-white font-medium">{site.name}</h4>
<h4 className="text-white font-medium">
{site.name}
</h4>
<p className="text-sm text-gray-400">{site.url}</p>
</div>
</div>
<div className="text-right">
<Badge variant="outline" className={`${getStatusColor(site.status)} text-white border-0`}>
<Badge
variant="outline"
className={`${getStatusColor(site.status)} text-white border-0`}
>
{site.status}
</Badge>
<p className="text-sm text-gray-400 mt-1">{site.responseTime}ms</p>
<p className="text-sm text-gray-400 mt-1">
{site.responseTime}ms
</p>
</div>
</div>
))}
@ -619,17 +752,29 @@ export default function Profile() {
<CardContent>
<div className="space-y-4">
{crossSiteData.map((site, index) => (
<div key={index} className="flex items-center justify-between p-4 bg-slate-900/50 rounded-lg border border-slate-600">
<div
key={index}
className="flex items-center justify-between p-4 bg-slate-900/50 rounded-lg border border-slate-600"
>
<div className="flex items-center space-x-4">
{getStatusIcon(site.status)}
<div>
<h4 className="text-white font-medium">{site.siteName}</h4>
<p className="text-sm text-gray-400">Last sync: {site.lastSync}</p>
<h4 className="text-white font-medium">
{site.siteName}
</h4>
<p className="text-sm text-gray-400">
Last sync: {site.lastSync}
</p>
</div>
</div>
<div className="text-right">
<p className="text-white font-medium">{site.dataExchanged} MB</p>
<Badge variant="outline" className={`${getStatusColor(site.status)} text-white border-0 mt-1`}>
<p className="text-white font-medium">
{site.dataExchanged} MB
</p>
<Badge
variant="outline"
className={`${getStatusColor(site.status)} text-white border-0 mt-1`}
>
{site.status}
</Badge>
</div>
@ -653,28 +798,42 @@ export default function Profile() {
<h4 className="text-white font-medium">Auto-Sync</h4>
<Switch defaultChecked />
</div>
<p className="text-sm text-gray-400">Automatically sync data between sites</p>
<p className="text-sm text-gray-400">
Automatically sync data between sites
</p>
</div>
<div className="p-4 bg-slate-900/50 rounded-lg border border-slate-600">
<div className="flex items-center justify-between mb-2">
<h4 className="text-white font-medium">Real-time Updates</h4>
<h4 className="text-white font-medium">
Real-time Updates
</h4>
<Switch defaultChecked />
</div>
<p className="text-sm text-gray-400">Enable real-time cross-site communication</p>
<p className="text-sm text-gray-400">
Enable real-time cross-site communication
</p>
</div>
<div className="p-4 bg-slate-900/50 rounded-lg border border-slate-600">
<div className="flex items-center justify-between mb-2">
<h4 className="text-white font-medium">Data Encryption</h4>
<h4 className="text-white font-medium">
Data Encryption
</h4>
<Switch defaultChecked />
</div>
<p className="text-sm text-gray-400">Encrypt data during transmission</p>
<p className="text-sm text-gray-400">
Encrypt data during transmission
</p>
</div>
<div className="p-4 bg-slate-900/50 rounded-lg border border-slate-600">
<div className="flex items-center justify-between mb-2">
<h4 className="text-white font-medium">Monitoring Alerts</h4>
<h4 className="text-white font-medium">
Monitoring Alerts
</h4>
<Switch defaultChecked />
</div>
<p className="text-sm text-gray-400">Get notified of connection issues</p>
<p className="text-sm text-gray-400">
Get notified of connection issues
</p>
</div>
</div>
</CardContent>

View file

@ -75,8 +75,9 @@ const ProfilePassport = () => {
const loadPassport = async () => {
setLoading(true);
try {
let resolvedProfile: (AethexUserProfile & { email?: string | null }) | null =
null;
let resolvedProfile:
| (AethexUserProfile & { email?: string | null })
| null = null;
let resolvedId: string | null = null;
if (isSelfRoute) {
@ -89,7 +90,8 @@ const ProfilePassport = () => {
}
if (
currentUser.username.toLowerCase() === requestedUsername.toLowerCase() &&
currentUser.username.toLowerCase() ===
requestedUsername.toLowerCase() &&
currentUser.username !== requestedUsername
) {
navigate(`/passport/${currentUser.username}`, { replace: true });
@ -215,7 +217,7 @@ const ProfilePassport = () => {
...resolvedProfile,
email:
resolvedProfile.email ??
(viewingSelf ? user?.email ?? authProfile?.email ?? null : null),
(viewingSelf ? (user?.email ?? authProfile?.email ?? null) : null),
});
setAchievements(achievementList ?? []);
setInterests(interestList ?? []);
@ -261,7 +263,8 @@ const ProfilePassport = () => {
((user?.id && profile.id && user.id === profile.id) ||
(authProfile?.username &&
profile.username &&
authProfile.username.toLowerCase() === profile.username.toLowerCase())),
authProfile.username.toLowerCase() ===
profile.username.toLowerCase())),
);
if (loading) {