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 { Button } from "@/components/ui/button";
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">
{p.image && (
<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>
)}
<CardHeader className="pb-2">
<div className="flex items-center justify-between gap-3">
<div className="flex items-center gap-2">
{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 && (
<span className="text-xs text-muted-foreground">{p.timeframe}</span>
<span className="text-xs text-muted-foreground">
{p.timeframe}
</span>
)}
</div>
</div>
@ -36,7 +51,9 @@ export default function ShowcaseCard({ p }: { p: ShowcaseProject }) {
{p.tags && p.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{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>
)}
@ -61,7 +78,9 @@ export default function ShowcaseCard({ p }: { p: ShowcaseProject }) {
<div className="flex flex-wrap gap-2">
{p.links.map((l) => (
<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>
))}
</div>

View file

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

View file

@ -1,6 +1,12 @@
import Layout from "@/components/Layout";
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 ShowcaseCard from "@/components/showcase/ShowcaseCard";
import { SHOWCASE, type ShowcaseProject } from "@/data/showcase";
@ -22,7 +28,7 @@ export default function Projects() {
supabase
.from<any>("showcase_projects" as any)
.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 })
.then(({ data, error }) => {
@ -70,7 +76,15 @@ export default function Projects() {
.finally(() => setLoading(false));
}, [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;
return (
@ -79,13 +93,33 @@ export default function Projects() {
<section className="container mx-auto max-w-6xl px-4">
<div className="flex items-center justify-between gap-3 flex-wrap">
<div>
<Badge variant="outline" 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>
<Badge
variant="outline"
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>
{isOwner && (
<Button 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
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>
)}
</div>
@ -103,7 +137,8 @@ export default function Projects() {
<CardHeader>
<CardTitle>No projects yet</CardTitle>
<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>
</CardHeader>
<CardContent className="flex items-center gap-2 pt-0 pb-6">

View file

@ -1,22 +1,41 @@
import Layout from "@/components/Layout";
import { Badge } from "@/components/ui/badge";
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 { Textarea } from "@/components/ui/textarea";
import { supabase } from "@/lib/supabase";
import { useAuth } from "@/contexts/AuthContext";
import { useEffect, useState } from "react";
interface Link { label: string; href: string }
interface Contributor { name: string; title?: string; avatar?: string }
interface Link {
label: string;
href: string;
}
interface Contributor {
name: string;
title?: string;
avatar?: string;
}
export default function ProjectsAdmin() {
const { user } = useAuth();
const isOwner = user?.email?.toLowerCase() === "mrpiglr@gmail.com";
const [list, setList] = useState<any[]>([]);
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(() => {
if (!isOwner) return;
@ -30,18 +49,32 @@ export default function ProjectsAdmin() {
}, [isOwner]);
const create = async () => {
const tags = (draft.tags || "").split(",").map((s: string) => s.trim()).filter(Boolean);
const { error } = await supabase.from<any>("showcase_projects" as any).insert({
title: draft.title,
org_unit: draft.org_unit,
role: "AeThex",
timeframe: draft.timeframe || null,
description: draft.description || null,
tags,
});
const tags = (draft.tags || "")
.split(",")
.map((s: string) => s.trim())
.filter(Boolean);
const { error } = await supabase
.from<any>("showcase_projects" as any)
.insert({
title: draft.title,
org_unit: draft.org_unit,
role: "AeThex",
timeframe: draft.timeframe || null,
description: draft.description || null,
tags,
});
if (!error) {
setDraft({ 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 });
setDraft({
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 || []);
}
};
@ -69,11 +102,22 @@ export default function ProjectsAdmin() {
<section className="container mx-auto max-w-6xl px-4">
<div className="flex items-center justify-between">
<div>
<Badge variant="outline" 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>
<Badge
variant="outline"
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>
<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>
</section>
@ -84,20 +128,48 @@ export default function ProjectsAdmin() {
<CardDescription>Title and basics</CardDescription>
</CardHeader>
<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">
<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>Labs</option>
<option>Platform</option>
<option>Community</option>
</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>
<Textarea 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 })} />
<Textarea
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">
<Button onClick={create} disabled={!draft.title}>Create</Button>
<Button onClick={create} disabled={!draft.title}>
Create
</Button>
</div>
</CardContent>
</Card>
@ -105,18 +177,39 @@ export default function ProjectsAdmin() {
<Card className="bg-card/60 border-border/40 backdrop-blur">
<CardHeader>
<CardTitle>Existing</CardTitle>
<CardDescription>{loading ? "Loading..." : `${list.length} items`}</CardDescription>
<CardDescription>
{loading ? "Loading..." : `${list.length} items`}
</CardDescription>
</CardHeader>
<CardContent className="space-y-2">
{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="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 className="flex items-center gap-2">
<Button asChild size="sm" variant="outline"><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>
<Button asChild size="sm" variant="outline">
<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>
))}

View file

@ -216,7 +216,8 @@ export default function Roadmap() {
try {
aethexToast.info({
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 {}
return;
@ -469,7 +470,9 @@ export default function Roadmap() {
variant="outline"
onClick={() => toggleUnlock(p.id)}
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}
>
{unlocked[p.id] ? (