From c0afb133a706a4a21df940e66aaa234ef9fa45b6 Mon Sep 17 00:00:00 2001 From: Nicholas Tindle Date: Tue, 20 Aug 2024 05:04:22 -0700 Subject: [PATCH] feat(builder): checkbox for tos on login page and submit agent (#7745) * feat(builder): checkbox for tos on login page * feat(builder): submit agent page DOES NOT WORK * feat(builder): basic upload (not working) * feat(builder): submit page more working but still not * fix(builder): working categories, not dynamic * feat(builder, server): enable submissions (auth error) * fix(lint): linting * feat(builder): submit page terms of service * fix(builder): update lockfile * lint(builder): lint marketplace files --- rnd/autogpt_builder/next.config.mjs | 3 + rnd/autogpt_builder/package.json | 4 +- rnd/autogpt_builder/src/app/login/page.tsx | 44 +- .../src/app/marketplace/page.tsx | 70 ++- .../src/app/marketplace/submit/page.tsx | 410 ++++++++++++++++++ .../src/components/ui/alert.tsx | 60 +++ .../src/components/ui/checkbox.tsx | 30 ++ .../src/components/ui/command.tsx | 155 +++++++ .../src/components/ui/multiselect.tsx | 318 ++++++++++++++ .../src/lib/marketplace-api/client.ts | 14 + rnd/autogpt_builder/yarn.lock | 348 ++++++++++++--- rnd/market/.vscode/launch.json | 3 +- rnd/market/market/app.py | 2 + 13 files changed, 1356 insertions(+), 105 deletions(-) create mode 100644 rnd/autogpt_builder/src/app/marketplace/submit/page.tsx create mode 100644 rnd/autogpt_builder/src/components/ui/alert.tsx create mode 100644 rnd/autogpt_builder/src/components/ui/checkbox.tsx create mode 100644 rnd/autogpt_builder/src/components/ui/command.tsx create mode 100644 rnd/autogpt_builder/src/components/ui/multiselect.tsx diff --git a/rnd/autogpt_builder/next.config.mjs b/rnd/autogpt_builder/next.config.mjs index 1d2f354552..9b18200a15 100644 --- a/rnd/autogpt_builder/next.config.mjs +++ b/rnd/autogpt_builder/next.config.mjs @@ -10,6 +10,9 @@ const nextConfig = { NEXT_PUBLIC_AGPT_MARKETPLACE_URL: process.env.NEXT_PUBLIC_AGPT_MARKETPLACE_URL, }, + images: { + domains: ["images.unsplash.com"], + }, async redirects() { return [ { diff --git a/rnd/autogpt_builder/package.json b/rnd/autogpt_builder/package.json index 60d051f37a..268f5007a9 100644 --- a/rnd/autogpt_builder/package.json +++ b/rnd/autogpt_builder/package.json @@ -12,14 +12,15 @@ "dependencies": { "@hookform/resolvers": "^3.9.0", "@radix-ui/react-avatar": "^1.1.0", + "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-dialog": "^1.1.1", "@radix-ui/react-dropdown-menu": "^2.1.1", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", - "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-scroll-area": "^1.1.0", + "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", @@ -29,6 +30,7 @@ "ajv": "^8.17.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "cmdk": "1.0.0", "date-fns": "^3.6.0", "dotenv": "^16.4.5", "lucide-react": "^0.407.0", diff --git a/rnd/autogpt_builder/src/app/login/page.tsx b/rnd/autogpt_builder/src/app/login/page.tsx index 36b2bf1d1d..8bdef284ed 100644 --- a/rnd/autogpt_builder/src/app/login/page.tsx +++ b/rnd/autogpt_builder/src/app/login/page.tsx @@ -20,10 +20,15 @@ import { FaGoogle, FaGithub, FaDiscord, FaSpinner } from "react-icons/fa"; import { useState } from "react"; import { useSupabase } from "@/components/SupabaseProvider"; import { useRouter } from "next/navigation"; +import Link from "next/link"; +import { Checkbox } from "@/components/ui/checkbox"; const loginFormSchema = z.object({ email: z.string().email().min(2).max(64), password: z.string().min(6).max(64), + agreeToTerms: z.boolean().refine((value) => value === true, { + message: "You must agree to the Terms of Service and Privacy Policy", + }), }); export default function LoginPage() { @@ -38,6 +43,7 @@ export default function LoginPage() { defaultValues: { email: "", password: "", + agreeToTerms: false, }, }); @@ -71,11 +77,6 @@ export default function LoginPage() { redirectTo: process.env.AUTH_CALLBACK_URL ?? `http://localhost:3000/auth/callback`, - // Get Google provider_refresh_token - // queryParams: { - // access_type: 'offline', - // prompt: 'consent', - // }, }, }); @@ -176,6 +177,36 @@ export default function LoginPage() { )} /> + ( + + + + +
+ + I agree to the{" "} + + Terms of Service + {" "} + and{" "} + + Privacy Policy + + + +
+
+ )} + />
diff --git a/rnd/autogpt_builder/src/app/marketplace/page.tsx b/rnd/autogpt_builder/src/app/marketplace/page.tsx index eddc17dae6..44c64f3c51 100644 --- a/rnd/autogpt_builder/src/app/marketplace/page.tsx +++ b/rnd/autogpt_builder/src/app/marketplace/page.tsx @@ -1,6 +1,7 @@ "use client"; import React, { useEffect, useMemo, useState, useCallback } from "react"; import { useRouter } from "next/navigation"; +import Image from "next/image"; import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import MarketplaceAPI, { @@ -8,7 +9,13 @@ import MarketplaceAPI, { AgentListResponse, AgentWithRank, } from "@/lib/marketplace-api"; -import { ChevronLeft, ChevronRight, Search, Star } from "lucide-react"; +import { + ChevronLeft, + ChevronRight, + PlusCircle, + Search, + Star, +} from "lucide-react"; // Utility Functions function debounce any>( @@ -26,29 +33,46 @@ function debounce any>( type Agent = AgentResponse | AgentWithRank; // Components -const HeroSection: React.FC = () => ( -
-
- Marketplace background - +const HeroSection: React.FC = () => { + const router = useRouter(); + + return ( +
+
+ Marketplace background + +
+
+
+

+ AutoGPT Marketplace +

+

+ Discover and share proven AI Agents to supercharge your business. +

+
+ +
-
-

- AutoGPT Marketplace -

-

- Discover and share proven AI Agents to supercharge your business. -

-
-
-); + ); +}; const SearchInput: React.FC<{ value: string; diff --git a/rnd/autogpt_builder/src/app/marketplace/submit/page.tsx b/rnd/autogpt_builder/src/app/marketplace/submit/page.tsx new file mode 100644 index 0000000000..bbe6175240 --- /dev/null +++ b/rnd/autogpt_builder/src/app/marketplace/submit/page.tsx @@ -0,0 +1,410 @@ +"use client"; + +import React, { useState, useEffect, useMemo } from "react"; +import { useRouter } from "next/navigation"; +import { useForm, Controller } from "react-hook-form"; +import ReactFlow, { Background, Controls } from "reactflow"; +import "reactflow/dist/style.css"; +import MarketplaceAPI from "@/lib/marketplace-api"; +import AutoGPTServerAPI from "@/lib/autogpt-server-api"; +import { Card } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + MultiSelector, + MultiSelectorContent, + MultiSelectorInput, + MultiSelectorItem, + MultiSelectorList, + MultiSelectorTrigger, +} from "@/components/ui/multiselect"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +type FormData = { + name: string; + description: string; + author: string; + keywords: string[]; + categories: string[]; + agreeToTerms: boolean; + selectedAgentId: string; +}; + +const SubmitPage: React.FC = () => { + const router = useRouter(); + const { + control, + handleSubmit, + watch, + setValue, + formState: { errors }, + } = useForm({ + defaultValues: { + selectedAgentId: "", // Initialize with an empty string + name: "", + description: "", + author: "", + keywords: [], + categories: [], + agreeToTerms: false, + }, + }); + const [isSubmitting, setIsSubmitting] = useState(false); + const [submitError, setSubmitError] = useState(null); + const [userAgents, setUserAgents] = useState< + Array<{ id: string; name: string; version: number }> + >([]); + const [selectedAgentGraph, setSelectedAgentGraph] = useState(null); + + const selectedAgentId = watch("selectedAgentId"); + + useEffect(() => { + const fetchUserAgents = async () => { + const api = new AutoGPTServerAPI(); + const agents = await api.listGraphs(); + console.log(agents); + setUserAgents( + agents.map((agent) => ({ + id: agent.id, + name: agent.name || `Agent (${agent.id})`, + version: agent.version, + })), + ); + }; + + fetchUserAgents(); + }, []); + + useEffect(() => { + const fetchAgentGraph = async () => { + if (selectedAgentId) { + const api = new AutoGPTServerAPI(); + const graph = await api.getGraph(selectedAgentId); + setSelectedAgentGraph(graph); + setValue("name", graph.name); + setValue("description", graph.description); + } + }; + + fetchAgentGraph(); + }, [selectedAgentId, setValue]); + + const onSubmit = async (data: FormData) => { + setIsSubmitting(true); + setSubmitError(null); + + if (!data.agreeToTerms) { + throw new Error("You must agree to the terms of service"); + } + + try { + if (!selectedAgentGraph) { + throw new Error("Please select an agent"); + } + + const api = new MarketplaceAPI(); + await api.submitAgent( + { + ...selectedAgentGraph, + name: data.name, + description: data.description, + }, + data.author, + data.keywords, + data.categories, + ); + + router.push("/marketplace?submission=success"); + } catch (error) { + console.error("Submission error:", error); + setSubmitError( + error instanceof Error ? error.message : "An unknown error occurred", + ); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+

Submit Your Agent

+ +
+
+ ( +
+ + + {errors.selectedAgentId && ( +

+ {errors.selectedAgentId.message} +

+ )} +
+ )} + /> + + {/* {selectedAgentGraph && ( +
+ + + + +
+ )} */} + + ( +
+ + + {errors.name && ( +

+ {errors.name.message} +

+ )} +
+ )} + /> + + ( +
+ +