refactor(platform): simplify waitlist code and remove type duplication

- Backend: Extract _waitlist_to_store_entry helper to reduce duplication
- Backend: Use dict comprehension in update_waitlist_admin for cleaner code
- Frontend: Import types directly from shared types file instead of re-exporting
- Frontend: Remove redundant isMember check in WaitlistCard handleJoinClick

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Nicholas Tindle
2026-01-09 16:25:27 -07:00
parent 8c45a5ee98
commit b3999669f2
6 changed files with 51 additions and 122 deletions

View File

@@ -1982,6 +1982,23 @@ async def get_agent_as_admin(
return graph
def _waitlist_to_store_entry(
waitlist: prisma.models.WaitlistEntry,
) -> store_model.StoreWaitlistEntry:
"""Convert a WaitlistEntry to StoreWaitlistEntry for public display."""
return store_model.StoreWaitlistEntry(
waitlist_id=waitlist.id,
slug=waitlist.slug,
name=waitlist.name,
subHeading=waitlist.subHeading,
videoUrl=waitlist.videoUrl,
agentOutputDemoUrl=waitlist.agentOutputDemoUrl,
imageUrls=waitlist.imageUrls or [],
description=waitlist.description,
categories=waitlist.categories,
)
async def get_waitlist() -> list[store_model.StoreWaitlistEntry]:
"""Get all active waitlists for public display."""
try:
@@ -1997,20 +2014,7 @@ async def get_waitlist() -> list[store_model.StoreWaitlistEntry]:
active_waitlists = [w for w in waitlists if w.status not in excluded_statuses]
sorted_list = sorted(active_waitlists, key=lambda x: x.votes, reverse=True)
return [
store_model.StoreWaitlistEntry(
waitlist_id=waitlist.id,
slug=waitlist.slug,
name=waitlist.name,
subHeading=waitlist.subHeading,
videoUrl=waitlist.videoUrl,
agentOutputDemoUrl=waitlist.agentOutputDemoUrl,
imageUrls=waitlist.imageUrls or [],
description=waitlist.description,
categories=waitlist.categories,
)
for waitlist in sorted_list
]
return [_waitlist_to_store_entry(w) for w in sorted_list]
except Exception as e:
logger.error(f"Error fetching waitlists: {e}")
raise DatabaseError("Failed to fetch waitlists") from e
@@ -2100,17 +2104,7 @@ async def add_user_to_waitlist(
else:
logger.debug(f"Email {email} already on waitlist {waitlist_id}")
return store_model.StoreWaitlistEntry(
waitlist_id=waitlist.id,
slug=waitlist.slug,
name=waitlist.name,
subHeading=waitlist.subHeading,
videoUrl=waitlist.videoUrl,
agentOutputDemoUrl=waitlist.agentOutputDemoUrl,
imageUrls=waitlist.imageUrls or [],
description=waitlist.description,
categories=waitlist.categories,
)
return _waitlist_to_store_entry(waitlist)
except ValueError:
raise
@@ -2232,28 +2226,24 @@ async def update_waitlist_admin(
try:
# Build update data from non-None fields
update_data: dict[str, typing.Any] = {}
field_mappings = {
"name": data.name,
"slug": data.slug,
"subHeading": data.subHeading,
"description": data.description,
"categories": data.categories,
"imageUrls": data.imageUrls,
"videoUrl": data.videoUrl,
"agentOutputDemoUrl": data.agentOutputDemoUrl,
"storeListingId": data.storeListingId,
}
update_data: dict[str, typing.Any] = {
k: v for k, v in field_mappings.items() if v is not None
}
if data.name is not None:
update_data["name"] = data.name
if data.slug is not None:
update_data["slug"] = data.slug
if data.subHeading is not None:
update_data["subHeading"] = data.subHeading
if data.description is not None:
update_data["description"] = data.description
if data.categories is not None:
update_data["categories"] = data.categories
if data.imageUrls is not None:
update_data["imageUrls"] = data.imageUrls
if data.videoUrl is not None:
update_data["videoUrl"] = data.videoUrl
if data.agentOutputDemoUrl is not None:
update_data["agentOutputDemoUrl"] = data.agentOutputDemoUrl
# Handle status separately due to enum conversion
if data.status is not None:
update_data["status"] = prisma.enums.WaitlistExternalStatus(data.status)
if data.storeListingId is not None:
update_data["storeListingId"] = data.storeListingId
if not update_data:
# No updates, just return current data

View File

@@ -2,67 +2,13 @@
import { revalidatePath } from "next/cache";
import BackendAPI from "@/lib/autogpt-server-api";
export type WaitlistAdminResponse = {
id: string;
createdAt: string;
updatedAt: string;
slug: string;
name: string;
subHeading: string;
description: string;
categories: string[];
imageUrls: string[];
videoUrl: string | null;
agentOutputDemoUrl: string | null;
status: string;
votes: number;
signupCount: number;
storeListingId: string | null;
owningUserId: string;
};
export type WaitlistAdminListResponse = {
waitlists: WaitlistAdminResponse[];
totalCount: number;
};
export type WaitlistSignup = {
type: "user" | "email";
userId: string | null;
email: string | null;
username: string | null;
};
export type WaitlistSignupListResponse = {
waitlistId: string;
signups: WaitlistSignup[];
totalCount: number;
};
export type WaitlistCreateRequest = {
name: string;
slug: string;
subHeading: string;
description: string;
categories?: string[];
imageUrls?: string[];
videoUrl?: string | null;
agentOutputDemoUrl?: string | null;
};
export type WaitlistUpdateRequest = {
name?: string;
slug?: string;
subHeading?: string;
description?: string;
categories?: string[];
imageUrls?: string[];
videoUrl?: string | null;
agentOutputDemoUrl?: string | null;
status?: string;
storeListingId?: string | null;
};
import type {
WaitlistAdminResponse,
WaitlistAdminListResponse,
WaitlistSignupListResponse,
WaitlistCreateRequest,
WaitlistUpdateRequest,
} from "@/lib/autogpt-server-api/types";
export async function getWaitlistsAdmin(): Promise<WaitlistAdminListResponse> {
const api = new BackendAPI();

View File

@@ -20,11 +20,11 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/__legacy__/ui/select";
import {
updateWaitlist,
type WaitlistAdminResponse,
type WaitlistUpdateRequest,
} from "../actions";
import { updateWaitlist } from "../actions";
import type {
WaitlistAdminResponse,
WaitlistUpdateRequest,
} from "@/lib/autogpt-server-api/types";
import { useToast } from "@/components/molecules/Toast/use-toast";
type EditWaitlistDialogProps = {

View File

@@ -17,10 +17,8 @@ import {
TableHeader,
TableRow,
} from "@/components/__legacy__/ui/table";
import {
getWaitlistSignups,
type WaitlistSignupListResponse,
} from "../actions";
import { getWaitlistSignups } from "../actions";
import type { WaitlistSignupListResponse } from "@/lib/autogpt-server-api/types";
import { useToast } from "@/components/molecules/Toast/use-toast";
import { User, Mail, Download } from "lucide-react";

View File

@@ -10,11 +10,8 @@ import {
TableRow,
} from "@/components/__legacy__/ui/table";
import { Button } from "@/components/atoms/Button/Button";
import {
getWaitlistsAdmin,
deleteWaitlist,
type WaitlistAdminResponse,
} from "../actions";
import { getWaitlistsAdmin, deleteWaitlist } from "../actions";
import type { WaitlistAdminResponse } from "@/lib/autogpt-server-api/types";
import { EditWaitlistDialog } from "./EditWaitlistDialog";
import { WaitlistSignupsDialog } from "./WaitlistSignupsDialog";
import { Trash2, Edit, Users, Link } from "lucide-react";

View File

@@ -25,9 +25,7 @@ export function WaitlistCard({
}: WaitlistCardProps) {
function handleJoinClick(e: React.MouseEvent) {
e.stopPropagation();
if (!isMember) {
onJoinClick(e);
}
onJoinClick(e);
}
return (