Prettier format pending files

This commit is contained in:
Builder.io 2025-10-19 03:02:40 +00:00
parent 97f60000d1
commit 9865e97baf
5 changed files with 204 additions and 68 deletions

View file

@ -1,4 +1,10 @@
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} 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 { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
@ -15,17 +21,26 @@ export default function ShowcaseCard({ p }: { p: ShowcaseProject }) {
<Card className="bg-card/60 border-border/40 backdrop-blur overflow-hidden group"> <Card className="bg-card/60 border-border/40 backdrop-blur overflow-hidden group">
{p.image && ( {p.image && (
<div className="relative h-44 w-full"> <div className="relative h-44 w-full">
<img src={p.image} alt={p.title} className="h-full w-full object-cover" loading="lazy" /> <img
src={p.image}
alt={p.title}
className="h-full w-full object-cover"
loading="lazy"
/>
</div> </div>
)} )}
<CardHeader className="pb-2"> <CardHeader className="pb-2">
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{p.orgUnit && ( {p.orgUnit && (
<Badge className="bg-gradient-to-r from-aethex-500 to-neon-blue">{p.orgUnit}</Badge> <Badge className="bg-gradient-to-r from-aethex-500 to-neon-blue">
{p.orgUnit}
</Badge>
)} )}
{p.timeframe && ( {p.timeframe && (
<span className="text-xs text-muted-foreground">{p.timeframe}</span> <span className="text-xs text-muted-foreground">
{p.timeframe}
</span>
)} )}
</div> </div>
</div> </div>
@ -36,7 +51,9 @@ export default function ShowcaseCard({ p }: { p: ShowcaseProject }) {
{p.tags && p.tags.length > 0 && ( {p.tags && p.tags.length > 0 && (
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{p.tags.map((t) => ( {p.tags.map((t) => (
<Badge key={t} variant="outline" className="text-xs">{t}</Badge> <Badge key={t} variant="outline" className="text-xs">
{t}
</Badge>
))} ))}
</div> </div>
)} )}
@ -61,7 +78,9 @@ export default function ShowcaseCard({ p }: { p: ShowcaseProject }) {
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{p.links.map((l) => ( {p.links.map((l) => (
<Button key={l.href} asChild size="sm" variant="outline"> <Button key={l.href} asChild size="sm" variant="outline">
<a href={l.href} target="_blank" rel="noreferrer noopener">{l.label}</a> <a href={l.href} target="_blank" rel="noreferrer noopener">
{l.label}
</a>
</Button> </Button>
))} ))}
</div> </div>

View file

@ -45,9 +45,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"Marketplace for buying, selling, and trading digital goods and services across the AeThex ecosystem.", "Marketplace for buying, selling, and trading digital goods and services across the AeThex ecosystem.",
tags: ["Platform", "Marketplace", "Commerce"], tags: ["Platform", "Marketplace", "Commerce"],
contributors: [ contributors: [{ name: "AeThex Commerce", avatar: "/placeholder.svg" }],
{ name: "AeThex Commerce", avatar: "/placeholder.svg" },
],
}, },
{ {
id: "rodeo-roundup", id: "rodeo-roundup",
@ -58,9 +56,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"App for discovering and tracking rodeos nearby with structured event data, maps, and alerts.", "App for discovering and tracking rodeos nearby with structured event data, maps, and alerts.",
tags: ["Studio", "Mobile", "Events", "Maps"], tags: ["Studio", "Mobile", "Events", "Maps"],
contributors: [ contributors: [{ name: "Studio Build Team", avatar: "/placeholder.svg" }],
{ name: "Studio Build Team", avatar: "/placeholder.svg" },
],
}, },
{ {
id: "hells-highway", id: "hells-highway",
@ -85,9 +81,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"Internal game development toolkit and build pipeline utilities for rapid prototyping and shipping.", "Internal game development toolkit and build pipeline utilities for rapid prototyping and shipping.",
tags: ["Labs", "Toolkit", "DevTools"], tags: ["Labs", "Toolkit", "DevTools"],
contributors: [ contributors: [{ name: "Labs Automation", avatar: "/placeholder.svg" }],
{ name: "Labs Automation", avatar: "/placeholder.svg" },
],
}, },
{ {
id: "lone-star-bar", id: "lone-star-bar",
@ -98,9 +92,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"17+ social game on Roblox focusing on immersive spaces and social mechanics.", "17+ social game on Roblox focusing on immersive spaces and social mechanics.",
tags: ["Studio", "Roblox", "Social", "Game"], tags: ["Studio", "Roblox", "Social", "Game"],
contributors: [ contributors: [{ name: "Social Experiences", avatar: "/placeholder.svg" }],
{ name: "Social Experiences", avatar: "/placeholder.svg" },
],
links: [ links: [
{ {
label: "Roblox", label: "Roblox",
@ -116,9 +108,7 @@ export const SHOWCASE: ShowcaseProject[] = [
timeframe: "Nov 2022 Present", timeframe: "Nov 2022 Present",
description: "Narrativedriven initiative produced by AeThex Studio.", description: "Narrativedriven initiative produced by AeThex Studio.",
tags: ["Studio", "Narrative", "Production"], tags: ["Studio", "Narrative", "Production"],
contributors: [ contributors: [{ name: "Story Group", avatar: "/placeholder.svg" }],
{ name: "Story Group", avatar: "/placeholder.svg" },
],
links: [ links: [
{ label: "Show project", href: "https://aethex.co/crooked-are-we" }, { label: "Show project", href: "https://aethex.co/crooked-are-we" },
], ],
@ -132,9 +122,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"Studio directed and shipped a polished prototype game in 86 hours for INSPIRE 2025.", "Studio directed and shipped a polished prototype game in 86 hours for INSPIRE 2025.",
tags: ["Studio", "Game Jam", "Leadership", "Prototype"], tags: ["Studio", "Game Jam", "Leadership", "Prototype"],
contributors: [ contributors: [{ name: "Strike Team", avatar: "/placeholder.svg" }],
{ name: "Strike Team", avatar: "/placeholder.svg" },
],
}, },
{ {
id: "the-prototypes-control", id: "the-prototypes-control",
@ -145,9 +133,7 @@ export const SHOWCASE: ShowcaseProject[] = [
description: description:
"Roblox DevRel Challenge 2025 entry focused on rapid prototyping and control schemes.", "Roblox DevRel Challenge 2025 entry focused on rapid prototyping and control schemes.",
tags: ["Studio", "Roblox", "Prototype", "Challenge"], tags: ["Studio", "Roblox", "Prototype", "Challenge"],
contributors: [ contributors: [{ name: "Prototype Unit", avatar: "/placeholder.svg" }],
{ name: "Prototype Unit", avatar: "/placeholder.svg" },
],
links: [ links: [
{ {
label: "Roblox", label: "Roblox",

View file

@ -1,6 +1,12 @@
import Layout from "@/components/Layout"; import Layout from "@/components/Layout";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
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 ShowcaseCard from "@/components/showcase/ShowcaseCard"; import ShowcaseCard from "@/components/showcase/ShowcaseCard";
import { SHOWCASE, type ShowcaseProject } from "@/data/showcase"; import { SHOWCASE, type ShowcaseProject } from "@/data/showcase";
@ -22,7 +28,7 @@ export default function Projects() {
supabase supabase
.from<any>("showcase_projects" as any) .from<any>("showcase_projects" as any)
.select( .select(
"id,title,org_unit,role,timeframe,description,tags,image, links:showcase_project_links(label,href), contributors:showcase_contributors(name,title,avatar)" "id,title,org_unit,role,timeframe,description,tags,image, links:showcase_project_links(label,href), contributors:showcase_contributors(name,title,avatar)",
) )
.order("created_at", { ascending: false }) .order("created_at", { ascending: false })
.then(({ data, error }) => { .then(({ data, error }) => {
@ -70,7 +76,15 @@ export default function Projects() {
.finally(() => setLoading(false)); .finally(() => setLoading(false));
}, [dbItems]); }, [dbItems]);
const items = useMemo(() => (dbItems && dbItems.length ? dbItems : cmsItems && cmsItems.length ? cmsItems : SHOWCASE), [dbItems, cmsItems]); const items = useMemo(
() =>
dbItems && dbItems.length
? dbItems
: cmsItems && cmsItems.length
? cmsItems
: SHOWCASE,
[dbItems, cmsItems],
);
const hasProjects = Array.isArray(items) && items.length > 0; const hasProjects = Array.isArray(items) && items.length > 0;
return ( return (
@ -79,13 +93,33 @@ export default function Projects() {
<section className="container mx-auto max-w-6xl px-4"> <section className="container mx-auto max-w-6xl px-4">
<div className="flex items-center justify-between gap-3 flex-wrap"> <div className="flex items-center justify-between gap-3 flex-wrap">
<div> <div>
<Badge variant="outline" className="border-aethex-400/50 text-aethex-300">Showcase</Badge> <Badge
<h1 className="mt-2 text-4xl font-extrabold text-gradient">Projects & Testimonials</h1> variant="outline"
<p className="text-muted-foreground max-w-2xl mt-1">Studio initiatives across AeThex Platform, Labs, and Studio.</p> className="border-aethex-400/50 text-aethex-300"
>
Showcase
</Badge>
<h1 className="mt-2 text-4xl font-extrabold text-gradient">
Projects & Testimonials
</h1>
<p className="text-muted-foreground max-w-2xl mt-1">
Studio initiatives across AeThex Platform, Labs, and Studio.
</p>
</div> </div>
{isOwner && ( {isOwner && (
<Button asChild variant="outline" size="sm" title="Edit in Builder CMS"> <Button
<a href="https://builder.io/content" target="_blank" rel="noreferrer noopener">Open CMS</a> asChild
variant="outline"
size="sm"
title="Edit in Builder CMS"
>
<a
href="https://builder.io/content"
target="_blank"
rel="noreferrer noopener"
>
Open CMS
</a>
</Button> </Button>
)} )}
</div> </div>
@ -103,7 +137,8 @@ export default function Projects() {
<CardHeader> <CardHeader>
<CardTitle>No projects yet</CardTitle> <CardTitle>No projects yet</CardTitle>
<CardDescription> <CardDescription>
Add entries in <code>code/client/data/showcase.ts</code> or manage them via CMS. Add entries in <code>code/client/data/showcase.ts</code> or
manage them via CMS.
</CardDescription> </CardDescription>
</CardHeader> </CardHeader>
<CardContent className="flex items-center gap-2 pt-0 pb-6"> <CardContent className="flex items-center gap-2 pt-0 pb-6">

View file

@ -1,22 +1,41 @@
import Layout from "@/components/Layout"; import Layout from "@/components/Layout";
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 { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { supabase } from "@/lib/supabase"; import { supabase } from "@/lib/supabase";
import { useAuth } from "@/contexts/AuthContext"; import { useAuth } from "@/contexts/AuthContext";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
interface Link { label: string; href: string } interface Link {
interface Contributor { name: string; title?: string; avatar?: string } label: string;
href: string;
}
interface Contributor {
name: string;
title?: string;
avatar?: string;
}
export default function ProjectsAdmin() { export default function ProjectsAdmin() {
const { user } = useAuth(); const { user } = useAuth();
const isOwner = user?.email?.toLowerCase() === "mrpiglr@gmail.com"; const isOwner = user?.email?.toLowerCase() === "mrpiglr@gmail.com";
const [list, setList] = useState<any[]>([]); const [list, setList] = useState<any[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [draft, setDraft] = useState<any>({ title: "", org_unit: "Studio", timeframe: "", description: "", tags: "" }); const [draft, setDraft] = useState<any>({
title: "",
org_unit: "Studio",
timeframe: "",
description: "",
tags: "",
});
useEffect(() => { useEffect(() => {
if (!isOwner) return; if (!isOwner) return;
@ -30,18 +49,32 @@ export default function ProjectsAdmin() {
}, [isOwner]); }, [isOwner]);
const create = async () => { const create = async () => {
const tags = (draft.tags || "").split(",").map((s: string) => s.trim()).filter(Boolean); const tags = (draft.tags || "")
const { error } = await supabase.from<any>("showcase_projects" as any).insert({ .split(",")
title: draft.title, .map((s: string) => s.trim())
org_unit: draft.org_unit, .filter(Boolean);
role: "AeThex", const { error } = await supabase
timeframe: draft.timeframe || null, .from<any>("showcase_projects" as any)
description: draft.description || null, .insert({
tags, title: draft.title,
}); org_unit: draft.org_unit,
role: "AeThex",
timeframe: draft.timeframe || null,
description: draft.description || null,
tags,
});
if (!error) { if (!error) {
setDraft({ title: "", org_unit: "Studio", timeframe: "", description: "", tags: "" }); setDraft({
const { data } = await supabase.from<any>("showcase_projects" as any).select("id,title,org_unit,role,timeframe,description,tags").order("created_at", { ascending: false }); title: "",
org_unit: "Studio",
timeframe: "",
description: "",
tags: "",
});
const { data } = await supabase
.from<any>("showcase_projects" as any)
.select("id,title,org_unit,role,timeframe,description,tags")
.order("created_at", { ascending: false });
setList(data || []); setList(data || []);
} }
}; };
@ -69,11 +102,22 @@ export default function ProjectsAdmin() {
<section className="container mx-auto max-w-6xl px-4"> <section className="container mx-auto max-w-6xl px-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div> <div>
<Badge variant="outline" className="border-aethex-400/50 text-aethex-300">Admin</Badge> <Badge
<h1 className="mt-2 text-3xl font-extrabold text-gradient">Projects Admin</h1> variant="outline"
<p className="text-muted-foreground">Create and manage showcase entries (Supabase)</p> className="border-aethex-400/50 text-aethex-300"
>
Admin
</Badge>
<h1 className="mt-2 text-3xl font-extrabold text-gradient">
Projects Admin
</h1>
<p className="text-muted-foreground">
Create and manage showcase entries (Supabase)
</p>
</div> </div>
<Button asChild variant="outline" size="sm"><a href="/projects">View page</a></Button> <Button asChild variant="outline" size="sm">
<a href="/projects">View page</a>
</Button>
</div> </div>
</section> </section>
@ -84,20 +128,48 @@ export default function ProjectsAdmin() {
<CardDescription>Title and basics</CardDescription> <CardDescription>Title and basics</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-3"> <CardContent className="space-y-3">
<Input placeholder="Title" value={draft.title} onChange={(e) => setDraft({ ...draft, title: e.target.value })} /> <Input
placeholder="Title"
value={draft.title}
onChange={(e) => setDraft({ ...draft, title: e.target.value })}
/>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<select className="rounded border border-border/40 bg-background/70 px-3 py-2" value={draft.org_unit} onChange={(e) => setDraft({ ...draft, org_unit: e.target.value })}> <select
className="rounded border border-border/40 bg-background/70 px-3 py-2"
value={draft.org_unit}
onChange={(e) =>
setDraft({ ...draft, org_unit: e.target.value })
}
>
<option>Studio</option> <option>Studio</option>
<option>Labs</option> <option>Labs</option>
<option>Platform</option> <option>Platform</option>
<option>Community</option> <option>Community</option>
</select> </select>
<Input placeholder="Timeframe (e.g., Jan 2025 Present)" value={draft.timeframe} onChange={(e) => setDraft({ ...draft, timeframe: e.target.value })} /> <Input
placeholder="Timeframe (e.g., Jan 2025 Present)"
value={draft.timeframe}
onChange={(e) =>
setDraft({ ...draft, timeframe: e.target.value })
}
/>
</div> </div>
<Textarea placeholder="Description" value={draft.description} onChange={(e) => setDraft({ ...draft, description: e.target.value })} /> <Textarea
<Input placeholder="Tags (comma separated)" value={draft.tags} onChange={(e) => setDraft({ ...draft, tags: e.target.value })} /> placeholder="Description"
value={draft.description}
onChange={(e) =>
setDraft({ ...draft, description: e.target.value })
}
/>
<Input
placeholder="Tags (comma separated)"
value={draft.tags}
onChange={(e) => setDraft({ ...draft, tags: e.target.value })}
/>
<div className="flex justify-end"> <div className="flex justify-end">
<Button onClick={create} disabled={!draft.title}>Create</Button> <Button onClick={create} disabled={!draft.title}>
Create
</Button>
</div> </div>
</CardContent> </CardContent>
</Card> </Card>
@ -105,18 +177,39 @@ export default function ProjectsAdmin() {
<Card className="bg-card/60 border-border/40 backdrop-blur"> <Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader> <CardHeader>
<CardTitle>Existing</CardTitle> <CardTitle>Existing</CardTitle>
<CardDescription>{loading ? "Loading..." : `${list.length} items`}</CardDescription> <CardDescription>
{loading ? "Loading..." : `${list.length} items`}
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-2"> <CardContent className="space-y-2">
{list.map((p) => ( {list.map((p) => (
<div key={p.id} className="flex items-center justify-between rounded border border-border/40 p-2 text-sm"> <div
key={p.id}
className="flex items-center justify-between rounded border border-border/40 p-2 text-sm"
>
<div className="min-w-0"> <div className="min-w-0">
<div className="font-medium truncate">{p.title}</div> <div className="font-medium truncate">{p.title}</div>
<div className="text-xs text-muted-foreground">{p.org_unit} {p.timeframe || ""}</div> <div className="text-xs text-muted-foreground">
{p.org_unit} {p.timeframe || ""}
</div>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button asChild size="sm" variant="outline"><a href={`/projects/${p.id}`}>View</a></Button> <Button asChild size="sm" variant="outline">
<Button size="sm" variant="destructive" onClick={async () => { await supabase.from<any>("showcase_projects" as any).delete().eq("id", p.id); setList(list.filter((x) => x.id !== p.id)); }}>Delete</Button> <a href={`/projects/${p.id}`}>View</a>
</Button>
<Button
size="sm"
variant="destructive"
onClick={async () => {
await supabase
.from<any>("showcase_projects" as any)
.delete()
.eq("id", p.id);
setList(list.filter((x) => x.id !== p.id));
}}
>
Delete
</Button>
</div> </div>
</div> </div>
))} ))}

View file

@ -216,7 +216,8 @@ export default function Roadmap() {
try { try {
aethexToast.info({ aethexToast.info({
title: "Sign in required", title: "Sign in required",
description: "Create an account to unlock Dev Drops and save progress.", description:
"Create an account to unlock Dev Drops and save progress.",
}); });
} catch {} } catch {}
return; return;
@ -469,7 +470,9 @@ export default function Roadmap() {
variant="outline" variant="outline"
onClick={() => toggleUnlock(p.id)} onClick={() => toggleUnlock(p.id)}
disabled={!user} disabled={!user}
className={!user ? "cursor-not-allowed opacity-60" : undefined} className={
!user ? "cursor-not-allowed opacity-60" : undefined
}
title={!user ? "Sign in to unlock" : undefined} title={!user ? "Sign in to unlock" : undefined}
> >
{unlocked[p.id] ? ( {unlocked[p.id] ? (