mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-30 03:00:41 -04:00
Added deleting submissions and viewing the popout
This commit is contained in:
@@ -275,7 +275,10 @@ async def get_store_submissions(
|
||||
# Convert to response models
|
||||
submission_models = [
|
||||
backend.server.v2.store.model.StoreSubmission(
|
||||
agent_id=sub.agent_id,
|
||||
agent_version=sub.agent_version,
|
||||
name=sub.name,
|
||||
sub_heading=sub.sub_heading,
|
||||
slug=sub.slug,
|
||||
description=sub.description,
|
||||
image_urls=sub.image_urls or [],
|
||||
@@ -312,6 +315,49 @@ async def get_store_submissions(
|
||||
)
|
||||
|
||||
|
||||
async def delete_store_submission(
|
||||
user_id: str,
|
||||
submission_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
Delete a store listing submission.
|
||||
|
||||
Args:
|
||||
user_id: ID of the authenticated user
|
||||
submission_id: ID of the submission to be deleted
|
||||
|
||||
Returns:
|
||||
bool: True if the submission was successfully deleted, False otherwise
|
||||
"""
|
||||
logger.debug(f"Deleting store submission {submission_id} for user {user_id}")
|
||||
|
||||
try:
|
||||
# Verify the submission belongs to this user
|
||||
submission = await prisma.models.StoreListing.prisma().find_first(
|
||||
where={"agentId": submission_id, "owningUserId": user_id}
|
||||
)
|
||||
|
||||
if not submission:
|
||||
logger.warning(f"Submission not found for user {user_id}: {submission_id}")
|
||||
raise backend.server.v2.store.exceptions.SubmissionNotFoundError(
|
||||
f"Submission not found for this user. User ID: {user_id}, Submission ID: {submission_id}"
|
||||
)
|
||||
|
||||
# Delete the submission
|
||||
await prisma.models.StoreListing.prisma().delete(
|
||||
where=prisma.types.StoreListingWhereUniqueInput(id=submission.id)
|
||||
)
|
||||
|
||||
logger.debug(
|
||||
f"Successfully deleted submission {submission_id} for user {user_id}"
|
||||
)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error deleting store submission: {str(e)}")
|
||||
return False
|
||||
|
||||
|
||||
async def create_store_submission(
|
||||
user_id: str,
|
||||
agent_id: str,
|
||||
@@ -398,8 +444,11 @@ async def create_store_submission(
|
||||
logger.debug(f"Created store listing for agent {agent_id}")
|
||||
# Return submission details
|
||||
return backend.server.v2.store.model.StoreSubmission(
|
||||
agent_id=agent_id,
|
||||
agent_version=agent_version,
|
||||
name=name,
|
||||
slug=slug,
|
||||
sub_heading=sub_heading,
|
||||
description=description,
|
||||
image_urls=image_urls,
|
||||
date_submitted=listing.createdAt,
|
||||
|
||||
@@ -68,3 +68,9 @@ class ProfileNotFoundError(StoreError):
|
||||
"""Raised when a profile is not found"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class SubmissionNotFoundError(StoreError):
|
||||
"""Raised when a submission is not found"""
|
||||
|
||||
pass
|
||||
|
||||
@@ -100,7 +100,10 @@ class Profile(pydantic.BaseModel):
|
||||
|
||||
|
||||
class StoreSubmission(pydantic.BaseModel):
|
||||
agent_id: str
|
||||
agent_version: int
|
||||
name: str
|
||||
sub_heading: str
|
||||
slug: str
|
||||
description: str
|
||||
image_urls: list[str]
|
||||
|
||||
@@ -243,6 +243,38 @@ async def get_my_agents(
|
||||
raise
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/submissions/{submission_id}",
|
||||
tags=["store", "private"],
|
||||
dependencies=[fastapi.Depends(autogpt_libs.auth.middleware.auth_middleware)],
|
||||
)
|
||||
async def delete_submission(
|
||||
user_id: typing.Annotated[
|
||||
str, fastapi.Depends(autogpt_libs.auth.depends.get_user_id)
|
||||
],
|
||||
submission_id: str,
|
||||
) -> bool:
|
||||
"""
|
||||
Delete a store listing submission.
|
||||
|
||||
Args:
|
||||
user_id (str): ID of the authenticated user
|
||||
submission_id (str): ID of the submission to be deleted
|
||||
|
||||
Returns:
|
||||
bool: True if the submission was successfully deleted, False otherwise
|
||||
"""
|
||||
try:
|
||||
result = await backend.server.v2.store.db.delete_store_submission(
|
||||
user_id=user_id,
|
||||
submission_id=submission_id,
|
||||
)
|
||||
return result
|
||||
except Exception:
|
||||
logger.exception("Exception occurred whilst deleting store submission")
|
||||
raise
|
||||
|
||||
|
||||
@router.get(
|
||||
"/submissions",
|
||||
tags=["store", "private"],
|
||||
|
||||
@@ -91,8 +91,11 @@ CREATE VIEW "StoreSubmission" AS
|
||||
SELECT
|
||||
sl.id as listing_id,
|
||||
sl."owningUserId" as user_id,
|
||||
slv."agentId" as agent_id,
|
||||
slv."version" as agent_version,
|
||||
slv.slug,
|
||||
slv.name,
|
||||
slv."subHeading" as sub_heading,
|
||||
slv.description,
|
||||
slv."imageUrls" as image_urls,
|
||||
slv."createdAt" as date_submitted,
|
||||
@@ -107,9 +110,9 @@ LEFT JOIN (
|
||||
SELECT "agentGraphId", COUNT(*) as run_count
|
||||
FROM "AgentGraphExecution"
|
||||
GROUP BY "agentGraphId"
|
||||
) ar ON ar."agentGraphId" = sl."agentId"
|
||||
) ar ON ar."agentGraphId" = slv."agentId"
|
||||
WHERE sl."isDeleted" = FALSE
|
||||
GROUP BY sl.id, sl."owningUserId", slv."createdAt", slv.slug, slv.name, slv.description, slv."imageUrls",
|
||||
sls."createdAt", sls."Status", ar.run_count;
|
||||
GROUP BY sl.id, sl."owningUserId", slv."agentId", slv."version", slv.slug, slv.name, slv."subHeading",
|
||||
slv.description, slv."imageUrls", slv."createdAt", sls."Status", ar.run_count;
|
||||
|
||||
COMMIT;
|
||||
@@ -479,12 +479,15 @@ view StoreSubmission {
|
||||
user_id String
|
||||
slug String
|
||||
name String
|
||||
sub_heading String
|
||||
description String
|
||||
image_urls String[]
|
||||
date_submitted DateTime
|
||||
status SubmissionStatus
|
||||
runs Int
|
||||
rating Float
|
||||
agent_id String
|
||||
agent_version Int
|
||||
|
||||
@@index([user_id])
|
||||
}
|
||||
|
||||
@@ -1,15 +1,26 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import { AgentTable } from "@/components/agptui/AgentTable";
|
||||
import { AgentTableRowProps } from "@/components/agptui/AgentTableRow";
|
||||
import { Button } from "@/components/agptui/Button";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import AutoGPTServerAPIServerSide from "@/lib/autogpt-server-api";
|
||||
import { createServerClient } from "@/lib/supabase/server";
|
||||
import AutoGPTServerAPI from "@/lib/autogpt-server-api";
|
||||
import { createClient } from "@/lib/supabase/client";
|
||||
import { StatusType } from "@/components/agptui/Status";
|
||||
import { PublishAgentPopout } from "@/components/agptui/composite/PublishAgentPopout";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
StoreSubmissionsResponse,
|
||||
StoreSubmissionRequest,
|
||||
} from "@/lib/autogpt-server-api/types";
|
||||
|
||||
async function getDashboardData() {
|
||||
// Get the supabase client first
|
||||
const supabase = createServerClient();
|
||||
const supabase = createClient();
|
||||
if (!supabase) {
|
||||
return { submissions: [] };
|
||||
}
|
||||
|
||||
const {
|
||||
data: { session },
|
||||
} = await supabase.auth.getSession();
|
||||
@@ -19,11 +30,10 @@ async function getDashboardData() {
|
||||
return { profile: null };
|
||||
}
|
||||
|
||||
// Create API client with the same supabase instance
|
||||
const api = new AutoGPTServerAPIServerSide(
|
||||
const api = new AutoGPTServerAPI(
|
||||
process.env.NEXT_PUBLIC_AGPT_SERVER_URL,
|
||||
process.env.NEXT_PUBLIC_AGPT_WS_SERVER_URL,
|
||||
supabase, // Pass the supabase client instance
|
||||
supabase,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -39,12 +49,57 @@ async function getDashboardData() {
|
||||
}
|
||||
}
|
||||
|
||||
export default async function Page({
|
||||
export default function Page({
|
||||
params: { lang },
|
||||
}: {
|
||||
params: { lang: string };
|
||||
}) {
|
||||
const { submissions } = await getDashboardData();
|
||||
const [submissions, setSubmissions] = useState<StoreSubmissionsResponse>();
|
||||
const [openPopout, setOpenPopout] = useState<boolean>(false);
|
||||
const [submissionData, setSubmissionData] =
|
||||
useState<StoreSubmissionRequest>();
|
||||
const [popoutStep, setPopoutStep] = useState<"select" | "info" | "review">(
|
||||
"info",
|
||||
);
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
const { submissions } = await getDashboardData();
|
||||
if (submissions) {
|
||||
setSubmissions(submissions as StoreSubmissionsResponse);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
|
||||
const onEditSubmission = useCallback((submission: StoreSubmissionRequest) => {
|
||||
setSubmissionData(submission);
|
||||
setPopoutStep("review");
|
||||
setOpenPopout(true);
|
||||
}, []);
|
||||
|
||||
const onDeleteSubmission = useCallback(
|
||||
(submission_id: string) => {
|
||||
const supabase = createClient();
|
||||
if (!supabase) {
|
||||
return;
|
||||
}
|
||||
const api = new AutoGPTServerAPI(
|
||||
process.env.NEXT_PUBLIC_AGPT_SERVER_URL,
|
||||
process.env.NEXT_PUBLIC_AGPT_WS_SERVER_URL,
|
||||
supabase,
|
||||
);
|
||||
api.deleteStoreSubmission(submission_id);
|
||||
fetchData();
|
||||
},
|
||||
[fetchData],
|
||||
);
|
||||
|
||||
const onOpenPopout = useCallback(() => {
|
||||
setPopoutStep("select");
|
||||
setOpenPopout(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<main className="flex-1 px-6 py-8 md:px-10">
|
||||
@@ -61,10 +116,13 @@ export default async function Page({
|
||||
</div>
|
||||
<PublishAgentPopout
|
||||
trigger={
|
||||
<Button variant="default" size="lg">
|
||||
<Button variant="default" size="lg" onClick={onOpenPopout}>
|
||||
Create New Agent
|
||||
</Button>
|
||||
}
|
||||
openPopout={openPopout}
|
||||
inputStep={popoutStep}
|
||||
submissionData={submissionData}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -77,17 +135,25 @@ export default async function Page({
|
||||
</h2>
|
||||
<AgentTable
|
||||
agents={
|
||||
submissions?.submissions.map((submission, index) => ({
|
||||
(submissions?.submissions.map((submission, index) => ({
|
||||
id: index,
|
||||
agent_id: submission.agent_id,
|
||||
agent_version: submission.agent_version,
|
||||
sub_heading: submission.sub_heading,
|
||||
date_submitted: submission.date_submitted,
|
||||
agentName: submission.name,
|
||||
description: submission.description,
|
||||
imageSrc: submission.image_urls[0] || "",
|
||||
imageSrc: submission.image_urls || [""],
|
||||
dateSubmitted: new Date(
|
||||
submission.date_submitted,
|
||||
).toLocaleDateString(),
|
||||
status: submission.status.toLowerCase() as StatusType,
|
||||
})) || []
|
||||
runs: submission.runs,
|
||||
rating: submission.rating,
|
||||
})) as AgentTableRowProps[]) || []
|
||||
}
|
||||
onEditSubmission={onEditSubmission}
|
||||
onDeleteSubmission={onDeleteSubmission}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -87,7 +87,7 @@ function SearchResults({
|
||||
if (sortValue === "runs") {
|
||||
sortBy = "runs";
|
||||
} else if (sortValue === "rating") {
|
||||
sortBy = "rating";
|
||||
sortBy = "rating";
|
||||
}
|
||||
|
||||
const sortedAgents = [...agents].sort((a, b) => {
|
||||
@@ -96,7 +96,9 @@ function SearchResults({
|
||||
} else if (sortBy === "rating") {
|
||||
return b.rating - a.rating;
|
||||
} else {
|
||||
return new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime();
|
||||
return (
|
||||
new Date(b.updated_at).getTime() - new Date(a.updated_at).getTime()
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -3,12 +3,36 @@
|
||||
import * as React from "react";
|
||||
import { AgentTableRow, AgentTableRowProps } from "./AgentTableRow";
|
||||
import { AgentTableCard } from "./AgentTableCard";
|
||||
import { StoreSubmissionRequest } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
export interface AgentTableProps {
|
||||
agents: AgentTableRowProps[];
|
||||
onEditSubmission: (submission: StoreSubmissionRequest) => void;
|
||||
onDeleteSubmission: (submission_id: string) => void;
|
||||
}
|
||||
|
||||
export const AgentTable: React.FC<AgentTableProps> = ({ agents }) => {
|
||||
export const AgentTable: React.FC<AgentTableProps> = ({
|
||||
agents,
|
||||
onEditSubmission,
|
||||
onDeleteSubmission,
|
||||
}) => {
|
||||
// Use state to track selected agents
|
||||
const [selectedAgents, setSelectedAgents] = React.useState<Set<string>>(
|
||||
new Set(),
|
||||
);
|
||||
|
||||
// Handle select all checkbox
|
||||
const handleSelectAll = React.useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
if (e.target.checked) {
|
||||
setSelectedAgents(new Set(agents.map((agent) => agent.id.toString())));
|
||||
} else {
|
||||
setSelectedAgents(new Set());
|
||||
}
|
||||
},
|
||||
[agents],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
{/* Table header - Hide on mobile */}
|
||||
@@ -22,6 +46,10 @@ export const AgentTable: React.FC<AgentTableProps> = ({ agents }) => {
|
||||
id="selectAllAgents"
|
||||
aria-label="Select all agents"
|
||||
className="mr-4 h-5 w-5 rounded border-2 border-neutral-400 dark:border-neutral-600"
|
||||
checked={
|
||||
selectedAgents.size === agents.length && agents.length > 0
|
||||
}
|
||||
onChange={handleSelectAll}
|
||||
/>
|
||||
<label
|
||||
htmlFor="selectAllAgents"
|
||||
@@ -57,8 +85,12 @@ export const AgentTable: React.FC<AgentTableProps> = ({ agents }) => {
|
||||
{agents.length > 0 ? (
|
||||
<div className="flex flex-col">
|
||||
{agents.map((agent, index) => (
|
||||
<div key={index} className="md:block">
|
||||
<AgentTableRow {...agent} />
|
||||
<div key={agent.id} className="md:block">
|
||||
<AgentTableRow
|
||||
{...agent}
|
||||
onEditSubmission={onEditSubmission}
|
||||
onDeleteSubmission={onDeleteSubmission}
|
||||
/>
|
||||
<div className="block md:hidden">
|
||||
<AgentTableCard {...agent} />
|
||||
</div>
|
||||
|
||||
@@ -6,26 +6,46 @@ import { IconStarFilled, IconMore } from "@/components/ui/icons";
|
||||
import { Status, StatusType } from "./Status";
|
||||
|
||||
export interface AgentTableCardProps {
|
||||
agent_id: string;
|
||||
agent_version: number;
|
||||
agentName: string;
|
||||
sub_heading: string;
|
||||
description: string;
|
||||
imageSrc: string;
|
||||
imageSrc: string[];
|
||||
dateSubmitted: string;
|
||||
status: StatusType;
|
||||
runs?: number;
|
||||
rating?: number;
|
||||
runs: number;
|
||||
rating: number;
|
||||
id: number;
|
||||
onEditSubmission: (submission: StoreSubmissionRequest) => void;
|
||||
}
|
||||
|
||||
export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
agent_id,
|
||||
agent_version,
|
||||
agentName,
|
||||
sub_heading,
|
||||
description,
|
||||
imageSrc,
|
||||
dateSubmitted,
|
||||
status,
|
||||
runs,
|
||||
rating,
|
||||
id,
|
||||
onEditSubmission,
|
||||
}) => {
|
||||
const onEdit = () => {
|
||||
console.log("Edit agent", agentName);
|
||||
onEditSubmission({
|
||||
agent_id,
|
||||
agent_version,
|
||||
slug: "",
|
||||
name: agentName,
|
||||
sub_heading,
|
||||
description,
|
||||
image_urls: imageSrc,
|
||||
categories: [],
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -33,7 +53,7 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
<div className="flex gap-4">
|
||||
<div className="relative h-[56px] w-[100px] overflow-hidden rounded-lg bg-[#d9d9d9] dark:bg-neutral-800">
|
||||
<Image
|
||||
src={imageSrc}
|
||||
src={imageSrc?.[0] ?? "/nada.png"}
|
||||
alt={agentName}
|
||||
fill
|
||||
style={{ objectFit: "cover" }}
|
||||
@@ -60,19 +80,15 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{dateSubmitted}
|
||||
</div>
|
||||
{runs && (
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{runs.toLocaleString()} runs
|
||||
</div>
|
||||
)}
|
||||
{rating && (
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
{rating.toFixed(1)}
|
||||
</span>
|
||||
<IconStarFilled className="h-4 w-4 text-neutral-800 dark:text-neutral-200" />
|
||||
</div>
|
||||
)}
|
||||
<div className="text-sm text-neutral-600 dark:text-neutral-400">
|
||||
{runs.toLocaleString()} runs
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="text-sm font-medium text-neutral-800 dark:text-neutral-200">
|
||||
{rating.toFixed(1)}
|
||||
</span>
|
||||
<IconStarFilled className="h-4 w-4 text-neutral-800 dark:text-neutral-200" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,23 +2,34 @@
|
||||
|
||||
import * as React from "react";
|
||||
import Image from "next/image";
|
||||
import { Button } from "./Button";
|
||||
import { IconStarFilled, IconEdit, IconMore } from "@/components/ui/icons";
|
||||
import { IconStarFilled, IconMore, IconEdit } from "@/components/ui/icons";
|
||||
import { Status, StatusType } from "./Status";
|
||||
import * as ContextMenu from "@radix-ui/react-context-menu";
|
||||
import { TrashIcon } from "@radix-ui/react-icons";
|
||||
import { StoreSubmissionRequest } from "@/lib/autogpt-server-api/types";
|
||||
|
||||
export interface AgentTableRowProps {
|
||||
agent_id: string;
|
||||
agent_version: number;
|
||||
agentName: string;
|
||||
sub_heading: string;
|
||||
description: string;
|
||||
imageSrc: string;
|
||||
dateSubmitted: string;
|
||||
imageSrc: string[];
|
||||
date_submitted: string;
|
||||
status: StatusType;
|
||||
runs?: number;
|
||||
rating?: number;
|
||||
runs: number;
|
||||
rating: number;
|
||||
dateSubmitted: string;
|
||||
id: number;
|
||||
onEditSubmission: (submission: StoreSubmissionRequest) => void;
|
||||
onDeleteSubmission: (submission_id: string) => void;
|
||||
}
|
||||
|
||||
export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
agent_id,
|
||||
agent_version,
|
||||
agentName,
|
||||
sub_heading,
|
||||
description,
|
||||
imageSrc,
|
||||
dateSubmitted,
|
||||
@@ -26,13 +37,36 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
runs,
|
||||
rating,
|
||||
id,
|
||||
onEditSubmission,
|
||||
onDeleteSubmission,
|
||||
}) => {
|
||||
// Create a unique ID for the checkbox
|
||||
const checkboxId = `agent-${id}-checkbox`;
|
||||
|
||||
const onEdit = () => {
|
||||
console.log("Edit agent", id);
|
||||
};
|
||||
const handleEdit = React.useCallback(() => {
|
||||
onEditSubmission({
|
||||
agent_id,
|
||||
agent_version,
|
||||
slug: "",
|
||||
name: agentName,
|
||||
sub_heading,
|
||||
description,
|
||||
image_urls: imageSrc,
|
||||
categories: [],
|
||||
} as StoreSubmissionRequest);
|
||||
}, [
|
||||
agent_id,
|
||||
agent_version,
|
||||
agentName,
|
||||
sub_heading,
|
||||
description,
|
||||
imageSrc,
|
||||
onEditSubmission,
|
||||
]);
|
||||
|
||||
const handleDelete = React.useCallback(() => {
|
||||
onDeleteSubmission(agent_id);
|
||||
}, [agent_id, onDeleteSubmission]);
|
||||
|
||||
return (
|
||||
<div className="hidden items-center border-b border-neutral-300 px-4 py-4 hover:bg-neutral-50 dark:border-neutral-700 dark:hover:bg-neutral-800 md:flex">
|
||||
@@ -56,7 +90,7 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="relative h-[70px] w-[125px] overflow-hidden rounded-[10px] bg-[#d9d9d9] dark:bg-neutral-700">
|
||||
<Image
|
||||
src={imageSrc}
|
||||
src={imageSrc?.[0] ?? "/nada.png"}
|
||||
alt={agentName}
|
||||
fill
|
||||
style={{ objectFit: "cover" }}
|
||||
@@ -105,12 +139,30 @@ export const AgentTableRow: React.FC<AgentTableRowProps> = ({
|
||||
|
||||
{/* Actions - Three dots menu */}
|
||||
<div className="flex justify-end">
|
||||
<button
|
||||
onClick={onEdit}
|
||||
className="rounded-full p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700"
|
||||
>
|
||||
<IconMore className="h-5 w-5 text-neutral-800 dark:text-neutral-200" />
|
||||
</button>
|
||||
<ContextMenu.Root>
|
||||
<ContextMenu.Trigger>
|
||||
<button className="rounded-full p-1 hover:bg-neutral-100 dark:hover:bg-neutral-700">
|
||||
<IconMore className="h-5 w-5 text-neutral-800 dark:text-neutral-200" />
|
||||
</button>
|
||||
</ContextMenu.Trigger>
|
||||
<ContextMenu.Content className="z-10 rounded-xl border bg-white p-1 shadow-md dark:bg-gray-800">
|
||||
<ContextMenu.Item
|
||||
onSelect={handleEdit}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<IconEdit className="mr-2 h-5 w-5 dark:text-gray-100" />
|
||||
<span className="dark:text-gray-100">Edit</span>
|
||||
</ContextMenu.Item>
|
||||
<ContextMenu.Separator className="my-1 h-px bg-gray-300 dark:bg-gray-600" />
|
||||
<ContextMenu.Item
|
||||
onSelect={handleDelete}
|
||||
className="flex cursor-pointer items-center rounded-md px-3 py-2 text-red-500 hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
>
|
||||
<TrashIcon className="mr-2 h-5 w-5 text-red-500 dark:text-red-400" />
|
||||
<span className="dark:text-red-400">Delete</span>
|
||||
</ContextMenu.Item>
|
||||
</ContextMenu.Content>
|
||||
</ContextMenu.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,12 +26,12 @@ export const BecomeACreator: React.FC<BecomeACreatorProps> = ({
|
||||
<div className="left-0 top-0 h-px w-full bg-gray-200 dark:bg-gray-700" />
|
||||
|
||||
{/* Title */}
|
||||
<div className="pt-4 mb-4 left-4 top-[26px] font-['Poppins'] text-base font-semibold leading-7 text-neutral-800 dark:text-neutral-200 md:left-6 md:text-lg lg:left-8">
|
||||
<div className="left-4 top-[26px] mb-4 pt-4 font-['Poppins'] text-base font-semibold leading-7 text-neutral-800 dark:text-neutral-200 md:left-6 md:text-lg lg:left-8">
|
||||
{title}
|
||||
</div>
|
||||
|
||||
{/* Content Container */}
|
||||
<div className="absolute left-1/2 top-1/2 w-full max-w-[900px] -translate-x-1/2 -translate-y-1/2 px-4 pt-16 md:pt-10 text-center md:px-6 lg:px-0">
|
||||
<div className="absolute left-1/2 top-1/2 w-full max-w-[900px] -translate-x-1/2 -translate-y-1/2 px-4 pt-16 text-center md:px-6 md:pt-10 lg:px-0">
|
||||
<h2 className="font-poppins mb-6 text-3xl font-semibold leading-tight text-neutral-950 dark:text-neutral-50 md:mb-8 md:text-4xl md:leading-[1.2] lg:mb-12 lg:text-5xl lg:leading-[54px]">
|
||||
Build AI agents and share
|
||||
<br />
|
||||
|
||||
@@ -14,7 +14,6 @@ import { CreatorDetails, ProfileDetails } from "@/lib/autogpt-server-api/types";
|
||||
import { createClient } from "@/lib/supabase/client";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
|
||||
|
||||
export const ProfileInfoForm = ({ profile }: { profile: CreatorDetails }) => {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [profileData, setProfileData] = useState(profile);
|
||||
@@ -252,7 +251,7 @@ export const ProfileInfoForm = ({ profile }: { profile: CreatorDetails }) => {
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="py-8 flex h-[50px] items-center justify-end gap-3">
|
||||
<div className="flex h-[50px] items-center justify-end gap-3 py-8">
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
|
||||
@@ -149,6 +149,9 @@ export const ProfilePopoutMenu: React.FC<ProfilePopoutMenuProps> = ({
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
inputStep="select"
|
||||
submissionData={null}
|
||||
openPopout={false}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
||||
@@ -102,7 +102,9 @@ export const SettingsInputForm = ({
|
||||
/>
|
||||
</div>
|
||||
{!passwordsMatch && (
|
||||
<p className="text-red-500 text-sm mt-1">Passwords do not match</p>
|
||||
<p className="mt-1 text-sm text-red-500">
|
||||
Passwords do not match
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -20,17 +20,16 @@ import AutoGPTServerAPI from "@/lib/autogpt-server-api/client";
|
||||
import { useRouter } from "next/navigation";
|
||||
interface PublishAgentPopoutProps {
|
||||
trigger?: React.ReactNode;
|
||||
openPopout?: boolean;
|
||||
inputStep?: "select" | "info" | "review";
|
||||
submissionData?: StoreSubmissionRequest;
|
||||
}
|
||||
|
||||
export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
trigger,
|
||||
}) => {
|
||||
const [step, setStep] = React.useState<"select" | "info" | "review">(
|
||||
"select",
|
||||
);
|
||||
const [myAgents, setMyAgents] = React.useState<MyAgentsResponse | null>(null);
|
||||
const [selectedAgent, setSelectedAgent] = React.useState<string | null>(null);
|
||||
const [publishData, setPublishData] = React.useState<StoreSubmissionRequest>({
|
||||
openPopout = false,
|
||||
inputStep = "select",
|
||||
submissionData = {
|
||||
name: "",
|
||||
sub_heading: "",
|
||||
slug: "",
|
||||
@@ -39,7 +38,15 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
agent_id: "",
|
||||
agent_version: 0,
|
||||
categories: [],
|
||||
});
|
||||
},
|
||||
}) => {
|
||||
const [step, setStep] = React.useState<"select" | "info" | "review">(
|
||||
inputStep,
|
||||
);
|
||||
const [myAgents, setMyAgents] = React.useState<MyAgentsResponse | null>(null);
|
||||
const [selectedAgent, setSelectedAgent] = React.useState<string | null>(null);
|
||||
const [publishData, setPublishData] =
|
||||
React.useState<StoreSubmissionRequest>(submissionData);
|
||||
const [selectedAgentId, setSelectedAgentId] = React.useState<string | null>(
|
||||
null,
|
||||
);
|
||||
@@ -51,15 +58,27 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
const popupId = React.useId();
|
||||
const router = useRouter();
|
||||
|
||||
const supabase = createClient();
|
||||
const supabase = React.useMemo(() => createClient(), []);
|
||||
|
||||
const api = new AutoGPTServerAPI(
|
||||
process.env.NEXT_PUBLIC_AGPT_SERVER_URL,
|
||||
process.env.NEXT_PUBLIC_AGPT_WS_SERVER_URL,
|
||||
supabase,
|
||||
const api = React.useMemo(
|
||||
() =>
|
||||
new AutoGPTServerAPI(
|
||||
process.env.NEXT_PUBLIC_AGPT_SERVER_URL,
|
||||
process.env.NEXT_PUBLIC_AGPT_WS_SERVER_URL,
|
||||
supabase,
|
||||
),
|
||||
[supabase],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("PublishAgentPopout Effect");
|
||||
setOpen(openPopout);
|
||||
setStep(inputStep);
|
||||
setPublishData(submissionData);
|
||||
}, [openPopout]);
|
||||
|
||||
React.useEffect(() => {
|
||||
console.log("LoadMyAgents Effect");
|
||||
if (open) {
|
||||
const loadMyAgents = async () => {
|
||||
try {
|
||||
@@ -213,7 +232,10 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
thumbnailSrc={publishData.image_urls[0]}
|
||||
onClose={handleClose}
|
||||
onDone={handleClose}
|
||||
onViewProgress={() => router.push("/store/dashboard")}
|
||||
onViewProgress={() => {
|
||||
router.push("/store/dashboard");
|
||||
handleClose();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -223,7 +245,14 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={(isOpen) => {
|
||||
if (isOpen !== open) {
|
||||
setOpen(isOpen);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
{trigger || <Button variant="default">Publish Agent</Button>}
|
||||
</PopoverTrigger>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/* flow.css or index.css */
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
|
||||
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
|
||||
sans-serif;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
}
|
||||
|
||||
code {
|
||||
|
||||
@@ -323,6 +323,10 @@ export default class BaseAutoGPTServerAPI {
|
||||
return this._request("POST", "/store/submissions", submission);
|
||||
}
|
||||
|
||||
deleteStoreSubmission(submission_id: string): Promise<boolean> {
|
||||
return this._request("DELETE", `/store/submissions/${submission_id}`);
|
||||
}
|
||||
|
||||
uploadStoreSubmissionMedia(file: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append("file", file);
|
||||
|
||||
@@ -417,7 +417,10 @@ export type CreatorDetails = {
|
||||
};
|
||||
|
||||
export type StoreSubmission = {
|
||||
agent_id: string;
|
||||
agent_version: number;
|
||||
name: string;
|
||||
sub_heading: string;
|
||||
description: string;
|
||||
image_urls: string[];
|
||||
date_submitted: string;
|
||||
|
||||
Reference in New Issue
Block a user