Prettier format pending files
This commit is contained in:
parent
d28c0e3937
commit
ef56ff44fd
8 changed files with 333 additions and 98 deletions
|
|
@ -52,7 +52,10 @@ const App = () => (
|
||||||
<Route path="/dashboard" element={<Dashboard />} />
|
<Route path="/dashboard" element={<Dashboard />} />
|
||||||
<Route path="/admin" element={<Admin />} />
|
<Route path="/admin" element={<Admin />} />
|
||||||
<Route path="/network" element={<Network />} />
|
<Route path="/network" element={<Network />} />
|
||||||
<Route path="/profile" element={<Navigate to="/network" replace />} />
|
<Route
|
||||||
|
path="/profile"
|
||||||
|
element={<Navigate to="/network" replace />}
|
||||||
|
/>
|
||||||
<Route path="/login" element={<Login />} />
|
<Route path="/login" element={<Login />} />
|
||||||
|
|
||||||
{/* Service routes */}
|
{/* Service routes */}
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||||
{(() => {
|
{(() => {
|
||||||
const isOwner = Array.isArray(roles) && roles.includes("owner");
|
const isOwner = Array.isArray(roles) && roles.includes("owner");
|
||||||
const navItems = user
|
const navItems = user
|
||||||
? (isOwner ? [{ name: "Admin", href: "/admin" }, ...userNavigation] : userNavigation)
|
? isOwner
|
||||||
|
? [{ name: "Admin", href: "/admin" }, ...userNavigation]
|
||||||
|
: userNavigation
|
||||||
: navigation;
|
: navigation;
|
||||||
return navItems.map((item, index) => (
|
return navItems.map((item, index) => (
|
||||||
<Link
|
<Link
|
||||||
|
|
@ -93,7 +95,12 @@ export default function Layout({ children }: LayoutProps) {
|
||||||
{user ? (
|
{user ? (
|
||||||
// Logged in - always show Dashboard button; show avatar menu if profile exists
|
// Logged in - always show Dashboard button; show avatar menu if profile exists
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<Button asChild variant="outline" size="sm" className="hover-lift">
|
<Button
|
||||||
|
asChild
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
className="hover-lift"
|
||||||
|
>
|
||||||
<Link to="/dashboard">Dashboard</Link>
|
<Link to="/dashboard">Dashboard</Link>
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" className="hover-lift">
|
<Button variant="ghost" size="sm" className="hover-lift">
|
||||||
|
|
@ -141,9 +148,9 @@ export default function Layout({ children }: LayoutProps) {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem asChild>
|
<DropdownMenuItem asChild>
|
||||||
<Link to="/dashboard" className="cursor-pointer">
|
<Link to="/dashboard" className="cursor-pointer">
|
||||||
<Settings className="mr-2 h-4 w-4" />
|
<Settings className="mr-2 h-4 w-4" />
|
||||||
Settings
|
Settings
|
||||||
</Link>
|
</Link>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,9 @@ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({
|
||||||
// Auto-seed owner roles if logging in as site owner
|
// Auto-seed owner roles if logging in as site owner
|
||||||
const ownerEmail = userProfile?.email?.toLowerCase();
|
const ownerEmail = userProfile?.email?.toLowerCase();
|
||||||
if (ownerEmail === "mrpiglr@gmail.com" && !r.includes("owner")) {
|
if (ownerEmail === "mrpiglr@gmail.com" && !r.includes("owner")) {
|
||||||
const seeded = Array.from(new Set(["owner", "admin", "founder", ...r]));
|
const seeded = Array.from(
|
||||||
|
new Set(["owner", "admin", "founder", ...r]),
|
||||||
|
);
|
||||||
await aethexRoleService.setUserRoles(userId, seeded);
|
await aethexRoleService.setUserRoles(userId, seeded);
|
||||||
r = seeded;
|
r = seeded;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ function isTableMissing(err: any): boolean {
|
||||||
const msg = String(err?.message || err?.hint || err?.details || "");
|
const msg = String(err?.message || err?.hint || err?.details || "");
|
||||||
return (
|
return (
|
||||||
err?.code === "42P01" || // undefined_table
|
err?.code === "42P01" || // undefined_table
|
||||||
msg.includes("relation \"") ||
|
msg.includes('relation "') ||
|
||||||
msg.includes("does not exist") ||
|
msg.includes("does not exist") ||
|
||||||
msg.includes("table")
|
msg.includes("table")
|
||||||
);
|
);
|
||||||
|
|
@ -86,12 +86,15 @@ export const aethexUserService = {
|
||||||
email: user.email,
|
email: user.email,
|
||||||
} as AethexUserProfile;
|
} as AethexUserProfile;
|
||||||
}
|
}
|
||||||
const created = await mockAuth.updateProfile(user.id as any, {
|
const created = await mockAuth.updateProfile(
|
||||||
username: user.email?.split("@")[0] || "user",
|
user.id as any,
|
||||||
email: user.email || "",
|
{
|
||||||
role: "member",
|
username: user.email?.split("@")[0] || "user",
|
||||||
onboarded: true,
|
email: user.email || "",
|
||||||
} as any);
|
role: "member",
|
||||||
|
onboarded: true,
|
||||||
|
} as any,
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
...(created as any),
|
...(created as any),
|
||||||
email: user.email,
|
email: user.email,
|
||||||
|
|
@ -123,7 +126,10 @@ export const aethexUserService = {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.warn("Error updating profile, attempting mock fallback:", error);
|
console.warn("Error updating profile, attempting mock fallback:", error);
|
||||||
if (isTableMissing(error)) {
|
if (isTableMissing(error)) {
|
||||||
const mock = await mockAuth.updateProfile(userId as any, updates as any);
|
const mock = await mockAuth.updateProfile(
|
||||||
|
userId as any,
|
||||||
|
updates as any,
|
||||||
|
);
|
||||||
return mock as unknown as AethexUserProfile;
|
return mock as unknown as AethexUserProfile;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
|
|
@ -162,17 +168,20 @@ export const aethexUserService = {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.warn("Error creating profile, attempting mock fallback:", error);
|
console.warn("Error creating profile, attempting mock fallback:", error);
|
||||||
if (isTableMissing(error)) {
|
if (isTableMissing(error)) {
|
||||||
const mock = await mockAuth.updateProfile(userId as any, {
|
const mock = await mockAuth.updateProfile(
|
||||||
username: profileData.username || `user_${Date.now()}`,
|
userId as any,
|
||||||
full_name: profileData.full_name,
|
{
|
||||||
bio: profileData.bio,
|
username: profileData.username || `user_${Date.now()}`,
|
||||||
location: profileData.location,
|
full_name: profileData.full_name,
|
||||||
linkedin_url: profileData.linkedin_url as any,
|
bio: profileData.bio,
|
||||||
github_url: profileData.github_url as any,
|
location: profileData.location,
|
||||||
twitter_url: profileData.twitter_url as any,
|
linkedin_url: profileData.linkedin_url as any,
|
||||||
level: 1,
|
github_url: profileData.github_url as any,
|
||||||
total_xp: 0,
|
twitter_url: profileData.twitter_url as any,
|
||||||
} as any);
|
level: 1,
|
||||||
|
total_xp: 0,
|
||||||
|
} as any,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...(mock as any),
|
...(mock as any),
|
||||||
|
|
@ -206,7 +215,9 @@ export const aethexUserService = {
|
||||||
interest,
|
interest,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { error } = await supabase.from("user_interests").insert(interestRows);
|
const { error } = await supabase
|
||||||
|
.from("user_interests")
|
||||||
|
.insert(interestRows);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
if (isTableMissing(error)) return;
|
if (isTableMissing(error)) return;
|
||||||
|
|
@ -283,7 +294,10 @@ export const aethexProjectService = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteProject(projectId: string): Promise<boolean> {
|
async deleteProject(projectId: string): Promise<boolean> {
|
||||||
const { error } = await supabase.from("projects").delete().eq("id", projectId);
|
const { error } = await supabase
|
||||||
|
.from("projects")
|
||||||
|
.delete()
|
||||||
|
.eq("id", projectId);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.warn("Error deleting project:", error);
|
console.warn("Error deleting project:", error);
|
||||||
|
|
@ -410,7 +424,8 @@ export const aethexAchievementService = {
|
||||||
const updates: any = {};
|
const updates: any = {};
|
||||||
if ("total_xp" in (profile as any)) updates.total_xp = newTotalXP;
|
if ("total_xp" in (profile as any)) updates.total_xp = newTotalXP;
|
||||||
if ("level" in (profile as any)) updates.level = newLevel;
|
if ("level" in (profile as any)) updates.level = newLevel;
|
||||||
if ("loyalty_points" in (profile as any)) updates.loyalty_points = newLoyaltyPoints;
|
if ("loyalty_points" in (profile as any))
|
||||||
|
updates.loyalty_points = newLoyaltyPoints;
|
||||||
|
|
||||||
if (Object.keys(updates).length > 0) {
|
if (Object.keys(updates).length > 0) {
|
||||||
await supabase.from("user_profiles").update(updates).eq("id", userId);
|
await supabase.from("user_profiles").update(updates).eq("id", userId);
|
||||||
|
|
@ -426,7 +441,10 @@ export const aethexAchievementService = {
|
||||||
.single();
|
.single();
|
||||||
|
|
||||||
if (levelUpAchievement.data) {
|
if (levelUpAchievement.data) {
|
||||||
await this.awardAchievement(userId, (levelUpAchievement.data as any).id);
|
await this.awardAchievement(
|
||||||
|
userId,
|
||||||
|
(levelUpAchievement.data as any).id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -588,9 +606,12 @@ export const aethexRoleService = {
|
||||||
async setUserRoles(userId: string, roles: string[]): Promise<void> {
|
async setUserRoles(userId: string, roles: string[]): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const rows = roles.map((role) => ({ user_id: userId, role }));
|
const rows = roles.map((role) => ({ user_id: userId, role }));
|
||||||
const { error } = await supabase.from("user_roles").upsert(rows as any, {
|
const { error } = await supabase.from("user_roles").upsert(
|
||||||
onConflict: "user_id,role",
|
rows as any,
|
||||||
} as any);
|
{
|
||||||
|
onConflict: "user_id,role",
|
||||||
|
} as any,
|
||||||
|
);
|
||||||
if (!error) return;
|
if (!error) return;
|
||||||
} catch {}
|
} catch {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,8 @@ export const aethexSocialService = {
|
||||||
.from("user_follows")
|
.from("user_follows")
|
||||||
.select("following_id")
|
.select("following_id")
|
||||||
.eq("follower_id", userId);
|
.eq("follower_id", userId);
|
||||||
if (!error && data) return (data as any[]).map((r: any) => r.following_id);
|
if (!error && data)
|
||||||
|
return (data as any[]).map((r: any) => r.following_id);
|
||||||
} catch {}
|
} catch {}
|
||||||
try {
|
try {
|
||||||
const raw = localStorage.getItem("mock_follows");
|
const raw = localStorage.getItem("mock_follows");
|
||||||
|
|
@ -40,7 +41,9 @@ export const aethexSocialService = {
|
||||||
} catch {}
|
} catch {}
|
||||||
const raw = localStorage.getItem("mock_follows");
|
const raw = localStorage.getItem("mock_follows");
|
||||||
const map = raw ? JSON.parse(raw) : {};
|
const map = raw ? JSON.parse(raw) : {};
|
||||||
const set: string[] = Array.from(new Set([...(map[followerId] || []), followingId]));
|
const set: string[] = Array.from(
|
||||||
|
new Set([...(map[followerId] || []), followingId]),
|
||||||
|
);
|
||||||
map[followerId] = set;
|
map[followerId] = set;
|
||||||
localStorage.setItem("mock_follows", JSON.stringify(map));
|
localStorage.setItem("mock_follows", JSON.stringify(map));
|
||||||
},
|
},
|
||||||
|
|
@ -56,7 +59,9 @@ export const aethexSocialService = {
|
||||||
} catch {}
|
} catch {}
|
||||||
const raw = localStorage.getItem("mock_follows");
|
const raw = localStorage.getItem("mock_follows");
|
||||||
const map = raw ? JSON.parse(raw) : {};
|
const map = raw ? JSON.parse(raw) : {};
|
||||||
const list: string[] = (map[followerId] || []).filter((id: string) => id !== followingId);
|
const list: string[] = (map[followerId] || []).filter(
|
||||||
|
(id: string) => id !== followingId,
|
||||||
|
);
|
||||||
map[followerId] = list;
|
map[followerId] = list;
|
||||||
localStorage.setItem("mock_follows", JSON.stringify(map));
|
localStorage.setItem("mock_follows", JSON.stringify(map));
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,23 @@ import LoadingScreen from "@/components/LoadingScreen";
|
||||||
import { useAuth } from "@/contexts/AuthContext";
|
import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Shield, UserCog, Rocket, Settings, Users, Activity } from "lucide-react";
|
import {
|
||||||
|
Shield,
|
||||||
|
UserCog,
|
||||||
|
Rocket,
|
||||||
|
Settings,
|
||||||
|
Users,
|
||||||
|
Activity,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
export default function Admin() {
|
export default function Admin() {
|
||||||
const { user, loading, roles } = useAuth();
|
const { user, loading, roles } = useAuth();
|
||||||
|
|
@ -23,7 +36,11 @@ export default function Admin() {
|
||||||
|
|
||||||
if (loading || !user) {
|
if (loading || !user) {
|
||||||
return (
|
return (
|
||||||
<LoadingScreen message="Verifying admin access..." showProgress duration={1000} />
|
<LoadingScreen
|
||||||
|
message="Verifying admin access..."
|
||||||
|
showProgress
|
||||||
|
duration={1000}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,10 +52,14 @@ export default function Admin() {
|
||||||
<Card className="bg-red-500/10 border-red-500/30">
|
<Card className="bg-red-500/10 border-red-500/30">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle className="text-red-400">Access Denied</CardTitle>
|
<CardTitle className="text-red-400">Access Denied</CardTitle>
|
||||||
<CardDescription>You dont have permission to access the admin panel.</CardDescription>
|
<CardDescription>
|
||||||
|
You dont have permission to access the admin panel.
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button onClick={() => navigate("/dashboard")}>Go to Dashboard</Button>
|
<Button onClick={() => navigate("/dashboard")}>
|
||||||
|
Go to Dashboard
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -54,16 +75,37 @@ export default function Admin() {
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gradient">Admin Panel</h1>
|
<h1 className="text-3xl font-bold text-gradient">Admin Panel</h1>
|
||||||
<p className="text-muted-foreground">Site Owner • Admin • Founder</p>
|
<p className="text-muted-foreground">
|
||||||
|
Site Owner • Admin • Founder
|
||||||
|
</p>
|
||||||
<div className="flex gap-2 mt-2">
|
<div className="flex gap-2 mt-2">
|
||||||
<Badge variant="outline" className="border-green-500/50 text-green-400">Site Owner</Badge>
|
<Badge
|
||||||
<Badge variant="outline" className="border-blue-500/50 text-blue-400">Admin</Badge>
|
variant="outline"
|
||||||
<Badge variant="outline" className="border-purple-500/50 text-purple-400">Founder</Badge>
|
className="border-green-500/50 text-green-400"
|
||||||
|
>
|
||||||
|
Site Owner
|
||||||
|
</Badge>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="border-blue-500/50 text-blue-400"
|
||||||
|
>
|
||||||
|
Admin
|
||||||
|
</Badge>
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="border-purple-500/50 text-purple-400"
|
||||||
|
>
|
||||||
|
Founder
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="outline" onClick={() => navigate("/dashboard")}>Dashboard</Button>
|
<Button variant="outline" onClick={() => navigate("/dashboard")}>
|
||||||
<Button variant="outline" onClick={() => navigate("/profile")}>Profile</Button>
|
Dashboard
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" onClick={() => navigate("/profile")}>
|
||||||
|
Profile
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -74,12 +116,15 @@ export default function Admin() {
|
||||||
<Shield className="h-5 w-5 text-green-400" />
|
<Shield className="h-5 w-5 text-green-400" />
|
||||||
<CardTitle className="text-lg">Access Control</CardTitle>
|
<CardTitle className="text-lg">Access Control</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>Owner-only access is enforced by email</CardDescription>
|
<CardDescription>
|
||||||
|
Owner-only access is enforced by email
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<ul className="text-sm space-y-1 text-muted-foreground">
|
<ul className="text-sm space-y-1 text-muted-foreground">
|
||||||
<li>
|
<li>
|
||||||
Owner: <span className="text-foreground">mrpiglr@gmail.com</span>
|
Owner:{" "}
|
||||||
|
<span className="text-foreground">mrpiglr@gmail.com</span>
|
||||||
</li>
|
</li>
|
||||||
<li>All other users are denied access</li>
|
<li>All other users are denied access</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -92,7 +137,9 @@ export default function Admin() {
|
||||||
<Users className="h-5 w-5 text-blue-400" />
|
<Users className="h-5 w-5 text-blue-400" />
|
||||||
<CardTitle className="text-lg">Users & Roles</CardTitle>
|
<CardTitle className="text-lg">Users & Roles</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
<CardDescription>Future: manage roles, invitations, and status</CardDescription>
|
<CardDescription>
|
||||||
|
Future: manage roles, invitations, and status
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<p className="text-sm text-muted-foreground">Coming soon</p>
|
<p className="text-sm text-muted-foreground">Coming soon</p>
|
||||||
|
|
@ -108,7 +155,12 @@ export default function Admin() {
|
||||||
<CardDescription>Branding, legal, integrations</CardDescription>
|
<CardDescription>Branding, legal, integrations</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button variant="outline" onClick={() => navigate("/get-started")}>Open Settings</Button>
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => navigate("/get-started")}
|
||||||
|
>
|
||||||
|
Open Settings
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
@ -138,8 +190,16 @@ export default function Admin() {
|
||||||
<CardDescription>Common admin operations</CardDescription>
|
<CardDescription>Common admin operations</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="flex flex-wrap gap-2">
|
<CardContent className="flex flex-wrap gap-2">
|
||||||
<Button size="sm" onClick={() => navigate("/dashboard")}>View Dashboard</Button>
|
<Button size="sm" onClick={() => navigate("/dashboard")}>
|
||||||
<Button size="sm" variant="outline" onClick={() => navigate("/onboarding")}>Run Onboarding</Button>
|
View Dashboard
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => navigate("/onboarding")}
|
||||||
|
>
|
||||||
|
Run Onboarding
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -141,9 +141,9 @@ export default function Index() {
|
||||||
Crafting Digital Realities
|
Crafting Digital Realities
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-lg text-muted-foreground max-w-2xl mx-auto animate-slide-up">
|
<p className="text-lg text-muted-foreground max-w-2xl mx-auto animate-slide-up">
|
||||||
Where vision meets execution. We craft experiences through
|
Where vision meets execution. We craft experiences through
|
||||||
design, development, and community.
|
design, development, and community.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -247,8 +247,12 @@ export default function Index() {
|
||||||
<section className="py-20">
|
<section className="py-20">
|
||||||
<div className="container mx-auto px-4">
|
<div className="container mx-auto px-4">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12">
|
||||||
<h2 className="text-3xl lg:text-4xl font-bold text-gradient">Everything We Offer</h2>
|
<h2 className="text-3xl lg:text-4xl font-bold text-gradient">
|
||||||
<p className="text-muted-foreground mt-2">Explore services, programs, resources, and community</p>
|
Everything We Offer
|
||||||
|
</h2>
|
||||||
|
<p className="text-muted-foreground mt-2">
|
||||||
|
Explore services, programs, resources, and community
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
|
@ -258,7 +262,9 @@ export default function Index() {
|
||||||
<CardDescription>Studios and indie support</CardDescription>
|
<CardDescription>Studios and indie support</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button asChild className="w-full"><Link to="/game-development">Learn More</Link></Button>
|
<Button asChild className="w-full">
|
||||||
|
<Link to="/game-development">Learn More</Link>
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
@ -268,7 +274,9 @@ export default function Index() {
|
||||||
<CardDescription>Architecture & delivery</CardDescription>
|
<CardDescription>Architecture & delivery</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button asChild className="w-full"><Link to="/consulting">Learn More</Link></Button>
|
<Button asChild className="w-full">
|
||||||
|
<Link to="/consulting">Learn More</Link>
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
@ -278,7 +286,9 @@ export default function Index() {
|
||||||
<CardDescription>Programs and guidance</CardDescription>
|
<CardDescription>Programs and guidance</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button asChild className="w-full"><Link to="/mentorship">Learn More</Link></Button>
|
<Button asChild className="w-full">
|
||||||
|
<Link to="/mentorship">Learn More</Link>
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
|
@ -288,7 +298,9 @@ export default function Index() {
|
||||||
<CardDescription>Innovation and R&D</CardDescription>
|
<CardDescription>Innovation and R&D</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button asChild className="w-full"><Link to="/research">Learn More</Link></Button>
|
<Button asChild className="w-full">
|
||||||
|
<Link to="/research">Learn More</Link>
|
||||||
|
</Button>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -301,8 +313,12 @@ export default function Index() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button asChild variant="outline"><Link to="/docs">Docs</Link></Button>
|
<Button asChild variant="outline">
|
||||||
<Button asChild variant="outline"><Link to="/docs/tutorials">Tutorials</Link></Button>
|
<Link to="/docs">Docs</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild variant="outline">
|
||||||
|
<Link to="/docs/tutorials">Tutorials</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -314,8 +330,12 @@ export default function Index() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button asChild variant="outline"><Link to="/community">Community</Link></Button>
|
<Button asChild variant="outline">
|
||||||
<Button asChild variant="outline"><Link to="/blog">Blog</Link></Button>
|
<Link to="/community">Community</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild variant="outline">
|
||||||
|
<Link to="/blog">Blog</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -327,8 +347,12 @@ export default function Index() {
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button asChild variant="outline"><Link to="/about">About</Link></Button>
|
<Button asChild variant="outline">
|
||||||
<Button asChild variant="outline"><Link to="/contact">Contact</Link></Button>
|
<Link to="/about">About</Link>
|
||||||
|
</Button>
|
||||||
|
<Button asChild variant="outline">
|
||||||
|
<Link to="/contact">Contact</Link>
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -355,12 +379,36 @@ export default function Index() {
|
||||||
{/* Interactive Technology Grid */}
|
{/* Interactive Technology Grid */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 animate-fade-in">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 animate-fade-in">
|
||||||
{[
|
{[
|
||||||
{ name: "Game Studios", status: "Active", color: "from-purple-500 to-blue-600" },
|
{
|
||||||
{ name: "Design Systems", status: "Evolving", color: "from-blue-500 to-green-600" },
|
name: "Game Studios",
|
||||||
{ name: "Creator Tools", status: "Live", color: "from-green-500 to-yellow-600" },
|
status: "Active",
|
||||||
{ name: "Launch Ops", status: "Scaling", color: "from-yellow-500 to-red-600" },
|
color: "from-purple-500 to-blue-600",
|
||||||
{ name: "Content Pipeline", status: "In Progress", color: "from-red-500 to-purple-600" },
|
},
|
||||||
{ name: "Edge Experiences", status: "Deployed", color: "from-purple-500 to-pink-600" },
|
{
|
||||||
|
name: "Design Systems",
|
||||||
|
status: "Evolving",
|
||||||
|
color: "from-blue-500 to-green-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Creator Tools",
|
||||||
|
status: "Live",
|
||||||
|
color: "from-green-500 to-yellow-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Launch Ops",
|
||||||
|
status: "Scaling",
|
||||||
|
color: "from-yellow-500 to-red-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Content Pipeline",
|
||||||
|
status: "In Progress",
|
||||||
|
color: "from-red-500 to-purple-600",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Edge Experiences",
|
||||||
|
status: "Deployed",
|
||||||
|
color: "from-purple-500 to-pink-600",
|
||||||
|
},
|
||||||
].map((tech, index) => (
|
].map((tech, index) => (
|
||||||
<Card
|
<Card
|
||||||
key={index}
|
key={index}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,13 @@ import { useAuth } from "@/contexts/AuthContext";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import LoadingScreen from "@/components/LoadingScreen";
|
import LoadingScreen from "@/components/LoadingScreen";
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
@ -52,7 +58,13 @@ export default function Network() {
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading || isLoading) {
|
if (loading || isLoading) {
|
||||||
return <LoadingScreen message="Loading your network..." showProgress duration={1000} />;
|
return (
|
||||||
|
<LoadingScreen
|
||||||
|
message="Loading your network..."
|
||||||
|
showProgress
|
||||||
|
duration={1000}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) return null;
|
if (!user) return null;
|
||||||
|
|
@ -68,14 +80,28 @@ export default function Network() {
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
<Avatar className="h-16 w-16">
|
<Avatar className="h-16 w-16">
|
||||||
<AvatarImage src={profile?.avatar_url} />
|
<AvatarImage src={profile?.avatar_url} />
|
||||||
<AvatarFallback>{profile?.full_name?.[0] || user.email?.[0]?.toUpperCase()}</AvatarFallback>
|
<AvatarFallback>
|
||||||
|
{profile?.full_name?.[0] ||
|
||||||
|
user.email?.[0]?.toUpperCase()}
|
||||||
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<h2 className="text-xl font-semibold">{profile?.full_name || user.email?.split("@")[0]}</h2>
|
<h2 className="text-xl font-semibold">
|
||||||
<p className="text-sm text-muted-foreground">{profile?.role || "Member"}</p>
|
{profile?.full_name || user.email?.split("@")[0]}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
{profile?.role || "Member"}
|
||||||
|
</p>
|
||||||
<div className="mt-2 flex gap-2">
|
<div className="mt-2 flex gap-2">
|
||||||
<Badge variant="outline" className="border-aethex-400/50 text-aethex-400">Level {profile?.level || 1}</Badge>
|
<Badge
|
||||||
<Badge variant="outline">{(profile as any)?.experience_level || "beginner"}</Badge>
|
variant="outline"
|
||||||
|
className="border-aethex-400/50 text-aethex-400"
|
||||||
|
>
|
||||||
|
Level {profile?.level || 1}
|
||||||
|
</Badge>
|
||||||
|
<Badge variant="outline">
|
||||||
|
{(profile as any)?.experience_level || "beginner"}
|
||||||
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -83,8 +109,18 @@ export default function Network() {
|
||||||
<p className="text-sm text-muted-foreground">{profile.bio}</p>
|
<p className="text-sm text-muted-foreground">{profile.bio}</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="outline" onClick={() => navigate("/dashboard")}>Edit Profile</Button>
|
<Button
|
||||||
<Button variant="outline" onClick={() => navigate("/onboarding")}>Improve Profile</Button>
|
variant="outline"
|
||||||
|
onClick={() => navigate("/dashboard")}
|
||||||
|
>
|
||||||
|
Edit Profile
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => navigate("/onboarding")}
|
||||||
|
>
|
||||||
|
Improve Profile
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -92,25 +128,50 @@ export default function Network() {
|
||||||
<Card className="bg-card/50 border-border/50">
|
<Card className="bg-card/50 border-border/50">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Recommendations</CardTitle>
|
<CardTitle>Recommendations</CardTitle>
|
||||||
<CardDescription>People who align with your interests</CardDescription>
|
<CardDescription>
|
||||||
|
People who align with your interests
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3">
|
<CardContent className="space-y-3">
|
||||||
{recommended.slice(0, 3).map((r) => (
|
{recommended.slice(0, 3).map((r) => (
|
||||||
<div key={r.id} className="flex items-center justify-between">
|
<div key={r.id} className="flex items-center justify-between">
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Avatar className="h-10 w-10"><AvatarImage src={r.avatar_url} /><AvatarFallback>{(r.full_name || r.username || "U")[0]}</AvatarFallback></Avatar>
|
<Avatar className="h-10 w-10">
|
||||||
|
<AvatarImage src={r.avatar_url} />
|
||||||
|
<AvatarFallback>
|
||||||
|
{(r.full_name || r.username || "U")[0]}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-medium">{r.full_name || r.username}</div>
|
<div className="font-medium">
|
||||||
<div className="text-xs text-muted-foreground">{r.bio?.slice(0, 40) || "Member"}</div>
|
{r.full_name || r.username}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
{r.bio?.slice(0, 40) || "Member"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button size="sm" variant={isFollowing(r.id) ? "outline" : "default"} onClick={() => toggleFollow(r.id)}>
|
<Button
|
||||||
{isFollowing(r.id) ? (<span className="flex items-center gap-1"><UserCheck className="h-4 w-4" /> Following</span>) : (<span className="flex items-center gap-1"><UserPlus className="h-4 w-4" /> Follow</span>)}
|
size="sm"
|
||||||
|
variant={isFollowing(r.id) ? "outline" : "default"}
|
||||||
|
onClick={() => toggleFollow(r.id)}
|
||||||
|
>
|
||||||
|
{isFollowing(r.id) ? (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<UserCheck className="h-4 w-4" /> Following
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<UserPlus className="h-4 w-4" /> Follow
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{recommended.length === 0 && (
|
{recommended.length === 0 && (
|
||||||
<div className="text-sm text-muted-foreground">No recommendations yet.</div>
|
<div className="text-sm text-muted-foreground">
|
||||||
|
No recommendations yet.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
@ -121,25 +182,53 @@ export default function Network() {
|
||||||
<Card className="bg-card/50 border-border/50">
|
<Card className="bg-card/50 border-border/50">
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>Discover People</CardTitle>
|
<CardTitle>Discover People</CardTitle>
|
||||||
<CardDescription>Connect with creators, clients, and members</CardDescription>
|
<CardDescription>
|
||||||
|
Connect with creators, clients, and members
|
||||||
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
{recommended.map((r) => (
|
{recommended.map((r) => (
|
||||||
<div key={r.id} className="flex items-center justify-between p-3 rounded-lg border border-border/50 hover:border-aethex-400/50 transition-all">
|
<div
|
||||||
|
key={r.id}
|
||||||
|
className="flex items-center justify-between p-3 rounded-lg border border-border/50 hover:border-aethex-400/50 transition-all"
|
||||||
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Avatar className="h-12 w-12"><AvatarImage src={r.avatar_url} /><AvatarFallback>{(r.full_name || r.username || "U")[0]}</AvatarFallback></Avatar>
|
<Avatar className="h-12 w-12">
|
||||||
|
<AvatarImage src={r.avatar_url} />
|
||||||
|
<AvatarFallback>
|
||||||
|
{(r.full_name || r.username || "U")[0]}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
<div>
|
<div>
|
||||||
<div className="font-semibold">{r.full_name || r.username}</div>
|
<div className="font-semibold">
|
||||||
<div className="text-xs text-muted-foreground">{r.bio?.slice(0, 80) || "Member"}</div>
|
{r.full_name || r.username}
|
||||||
|
</div>
|
||||||
|
<div className="text-xs text-muted-foreground">
|
||||||
|
{r.bio?.slice(0, 80) || "Member"}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button size="sm" variant={isFollowing(r.id) ? "outline" : "default"} onClick={() => toggleFollow(r.id)}>
|
<Button
|
||||||
{isFollowing(r.id) ? (<span className="flex items-center gap-1"><UserCheck className="h-4 w-4" /> Following</span>) : (<span className="flex items-center gap-1"><UserPlus className="h-4 w-4" /> Follow</span>)}
|
size="sm"
|
||||||
|
variant={isFollowing(r.id) ? "outline" : "default"}
|
||||||
|
onClick={() => toggleFollow(r.id)}
|
||||||
|
>
|
||||||
|
{isFollowing(r.id) ? (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<UserCheck className="h-4 w-4" /> Following
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-1">
|
||||||
|
<UserPlus className="h-4 w-4" /> Follow
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
{recommended.length === 0 && (
|
{recommended.length === 0 && (
|
||||||
<div className="text-sm text-muted-foreground">No people found yet.</div>
|
<div className="text-sm text-muted-foreground">
|
||||||
|
No people found yet.
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue