From 05a21c53f9a25fe17c0000798441f0f55d0793d6 Mon Sep 17 00:00:00 2001 From: Biwas Bhandari Date: Sat, 9 Nov 2024 13:18:19 +0545 Subject: [PATCH 01/31] feat(sidebar): sidebar for dashboard --- package-lock.json | 125 ++++ package.json | 1 + src/app/dashboard/page.tsx | 5 +- src/app/globals.css | 4 +- src/components/crews/CrewManagement.tsx | 173 ++--- src/components/dashboard/Dashboard.tsx | 87 ++- src/components/dashboard/DashboardChat.tsx | 2 +- src/components/ui/button.tsx | 2 +- src/components/ui/input.tsx | 41 +- src/components/ui/sidebar.tsx | 763 +++++++++++++++++++++ src/components/ui/tooltip.tsx | 30 + src/hooks/use-mobile.tsx | 19 + tailwind.config.ts | 10 + 13 files changed, 1079 insertions(+), 183 deletions(-) create mode 100644 src/components/ui/sidebar.tsx create mode 100644 src/components/ui/tooltip.tsx create mode 100644 src/hooks/use-mobile.tsx diff --git a/package-lock.json b/package-lock.json index 6c56d364..e51e8389 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,7 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-tooltip": "^1.1.3", "@stacks/connect": "^7.7.1", "@stacks/connect-react": "^22.4.2", "@stacks/connect-ui": "^6.4.1", @@ -1857,6 +1858,130 @@ } } }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.3.tgz", + "integrity": "sha512-Z4w1FIS0BqVFI2c1jZvb/uDVJijJjJ2ZMuPV81oVgTZ7g3BZxobplnMVvXtFWgtozdvYJ+MFWtwkM5S2HnAong==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", + "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", + "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", + "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", diff --git a/package.json b/package.json index 71141f28..8e67f242 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-tooltip": "^1.1.3", "@stacks/connect": "^7.7.1", "@stacks/connect-react": "^22.4.2", "@stacks/connect-ui": "^6.4.1", diff --git a/src/app/dashboard/page.tsx b/src/app/dashboard/page.tsx index 9cde4e2f..135c1aba 100644 --- a/src/app/dashboard/page.tsx +++ b/src/app/dashboard/page.tsx @@ -1,10 +1,13 @@ import React from "react"; import Dashboard from "@/components/dashboard/Dashboard"; +import { SidebarProvider } from "@/components/ui/sidebar"; const page = () => { return (
- + + +
); }; diff --git a/src/app/globals.css b/src/app/globals.css index 4670bf2b..1604c056 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -54,7 +54,7 @@ body { --chart-2: 231 95% 41%; /* aibtc.blue.500 */ --chart-3: 220 2% 35%; /* aibtc.gray.500 */ --chart-4: 21 100% 70%; /* aibtc.orange.300 */ - --chart-5: 231 95% 60%; /* aibtc.blue.300 */ + --chart-5: 231 95% 60%; /* aibtc.blue.300 */ --sidebar-background: 0 0% 98%; --sidebar-foreground: 240 5.3% 26.1%; --sidebar-primary: 240 5.9% 10%; --sidebar-primary-foreground: 0 0% 98%; --sidebar-accent: 240 4.8% 95.9%; --sidebar-accent-foreground: 240 5.9% 10%; --sidebar-border: 220 13% 91%; --sidebar-ring: 217.2 91.2% 59.8%; } .dark { @@ -92,7 +92,7 @@ body { --chart-2: 231 95% 45%; /* darker blue */ --chart-3: 220 2% 25%; /* darker gray */ --chart-4: 21 100% 65%; /* lighter orange */ - --chart-5: 231 95% 65%; /* lighter blue */ + --chart-5: 231 95% 65%; /* lighter blue */ --sidebar-background: 240 5.9% 10%; --sidebar-foreground: 240 4.8% 95.9%; --sidebar-primary: 224.3 76.3% 48%; --sidebar-primary-foreground: 0 0% 100%; --sidebar-accent: 240 3.7% 15.9%; --sidebar-accent-foreground: 240 4.8% 95.9%; --sidebar-border: 240 3.7% 15.9%; --sidebar-ring: 217.2 91.2% 59.8%; } } diff --git a/src/components/crews/CrewManagement.tsx b/src/components/crews/CrewManagement.tsx index 9c7d47f4..b67da902 100644 --- a/src/components/crews/CrewManagement.tsx +++ b/src/components/crews/CrewManagement.tsx @@ -12,14 +12,6 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; import { PlusIcon, Trash2Icon, @@ -30,6 +22,7 @@ import { } from "lucide-react"; import CrewForm from "./CrewForm"; import { Crew, CrewManagementProps } from "@/types/supabase"; +import { ScrollArea } from "@/components/ui/scroll-area"; export function CrewManagement({ initialCrews, @@ -46,35 +39,13 @@ export function CrewManagement({ const handleDelete = async (id: number) => { setLoading(true); try { - // First delete all tasks associated with the crew - const { error: tasksError } = await supabase - .from("tasks") - .delete() - .eq("crew_id", id); - - if (tasksError) throw tasksError; - - // Then delete all agents associated with the crew - const { error: agentsError } = await supabase - .from("agents") - .delete() - .eq("crew_id", id); - - if (agentsError) throw agentsError; + await supabase.from("tasks").delete().eq("crew_id", id); + await supabase.from("agents").delete().eq("crew_id", id); + await supabase.from("crews").delete().eq("id", id); - // Finally delete the crew itself - const { error: crewError } = await supabase - .from("crews") - .delete() - .eq("id", id); - - if (crewError) throw crewError; - - // Update the local state to remove the deleted crew const updatedCrews = crews.filter((crew) => crew.id !== id); setCrews(updatedCrews); - // Clear selected crew if we're deleting it if (selectedCrew?.id === id) { onCrewSelect(null); } @@ -104,13 +75,13 @@ export function CrewManagement({ }; return ( -
-
-

Crews

+
+
+

Your Crews

- @@ -125,78 +96,60 @@ export function CrewManagement({
-
- - - - Name - Description - Created - Actions - - - - {crews.map((crew) => ( - - -
- - {crew.name} -
-
- - {crew.description || "No description"} - - - {new Date(crew.created_at).toLocaleDateString()} - - -
- - - -
-
-
- ))} -
-
-
+ +
+ {crews.map((crew) => ( +
+
+
+ + {crew.name} +
+ +
+

+ {crew.description || "No description"} +

+
+ + Created: {new Date(crew.created_at).toLocaleDateString()} + +
+ + +
+
+
+ ))} +
+
); } diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index c6647d1a..04770e92 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -4,17 +4,17 @@ import { useState, useEffect, useCallback } from "react"; import { supabase } from "@/utils/supabase/client"; import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; -import { - Card, - CardHeader, - CardTitle, - CardContent, - CardFooter, -} from "@/components/ui/card"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { AlertCircle } from "lucide-react"; +import { AlertCircle, Menu } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Button } from "@/components/ui/button"; +import { + Sidebar, + SidebarContent, + SidebarHeader, + SidebarTrigger, +} from "@/components/ui/sidebar"; export default function Dashboard() { const [crews, setCrews] = useState([]); @@ -31,10 +31,7 @@ export default function Dashboard() { .select("id, name, description, created_at") .order("created_at", { ascending: false }); - if (error) { - throw error; - } - + if (error) throw error; setCrews(data || []); } catch (err) { console.error("Error fetching crews:", err); @@ -62,10 +59,7 @@ export default function Dashboard() { .eq("profile_id", user.id) .eq("name", "Trading Analyzer"); - if (error) { - throw error; - } - + if (error) throw error; setHasClonedAnalyzer(data && data.length > 0); } catch (err) { console.error("Error checking for cloned analyzer:", err); @@ -77,7 +71,6 @@ export default function Dashboard() { const initializeDashboard = async () => { await Promise.all([fetchCrews(), checkClonedAnalyzer()]); }; - initializeDashboard(); }, [fetchCrews, checkClonedAnalyzer]); @@ -102,31 +95,12 @@ export default function Dashboard() { ); return ( -
-

Dashboard

- - {error && ( - - - Error - {error} - - )} - - - - Chat with your Crews - - - - - - - - - Manage Crews - - +
+ + +

Manage Crews

+
+ {isLoading ? (

Loading crews...

) : ( @@ -137,16 +111,37 @@ export default function Dashboard() { selectedCrew={selectedCrew} /> )} - - {!isLoading && !hasClonedAnalyzer && ( )} - - +
+
+
+
+
+ + + +

Dashboard

+
+
+
+ {error && ( + + + Error + {error} + + )} + +
+
); } diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index 7025ed5c..211eea3b 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -211,7 +211,7 @@ export default function DashboardChat({ selectedCrew }: DashboardChatProps) { return ( -
+
{messages.map((message, index) => (
{} +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => { + return ( + + ) + } +) +Input.displayName = "Input" -const Input = React.forwardRef< - HTMLInputElement, - React.InputHTMLAttributes ->(({ className, type, ...props }, ref) => { - return ( - - ); -}); -Input.displayName = "Input"; - -export { Input }; +export { Input } diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx new file mode 100644 index 00000000..b2cd66d3 --- /dev/null +++ b/src/components/ui/sidebar.tsx @@ -0,0 +1,763 @@ +"use client"; + +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { VariantProps, cva } from "class-variance-authority"; +import { PanelLeft } from "lucide-react"; + +import { useIsMobile } from "@/hooks/use-mobile"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Separator } from "@/components/ui/separator"; +import { Sheet, SheetContent } from "@/components/ui/sheet"; +import { Skeleton } from "@/components/ui/skeleton"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + +const SIDEBAR_COOKIE_NAME = "sidebar:state"; +const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +const SIDEBAR_WIDTH = "16rem"; +const SIDEBAR_WIDTH_MOBILE = "18rem"; +const SIDEBAR_WIDTH_ICON = "3rem"; +const SIDEBAR_KEYBOARD_SHORTCUT = "b"; + +type SidebarContext = { + state: "expanded" | "collapsed"; + open: boolean; + setOpen: (open: boolean) => void; + openMobile: boolean; + setOpenMobile: (open: boolean) => void; + isMobile: boolean; + toggleSidebar: () => void; +}; + +const SidebarContext = React.createContext(null); + +function useSidebar() { + const context = React.useContext(SidebarContext); + if (!context) { + throw new Error("useSidebar must be used within a SidebarProvider."); + } + + return context; +} + +const SidebarProvider = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; + } +>( + ( + { + defaultOpen = true, + open: openProp, + onOpenChange: setOpenProp, + className, + style, + children, + ...props + }, + ref + ) => { + const isMobile = useIsMobile(); + const [openMobile, setOpenMobile] = React.useState(false); + + // This is the internal state of the sidebar. + // We use openProp and setOpenProp for control from outside the component. + const [_open, _setOpen] = React.useState(defaultOpen); + const open = openProp ?? _open; + const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === "function" ? value(open) : value; + if (setOpenProp) { + setOpenProp(openState); + } else { + _setOpen(openState); + } + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + }, + [setOpenProp, open] + ); + + // Helper to toggle the sidebar. + const toggleSidebar = React.useCallback(() => { + return isMobile + ? setOpenMobile((open) => !open) + : setOpen((open) => !open); + }, [isMobile, setOpen, setOpenMobile]); + + // Adds a keyboard shortcut to toggle the sidebar. + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ( + event.key === SIDEBAR_KEYBOARD_SHORTCUT && + (event.metaKey || event.ctrlKey) + ) { + event.preventDefault(); + toggleSidebar(); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [toggleSidebar]); + + // We add a state so that we can do data-state="expanded" or "collapsed". + // This makes it easier to style the sidebar with Tailwind classes. + const state = open ? "expanded" : "collapsed"; + + const contextValue = React.useMemo( + () => ({ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + }), + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] + ); + + return ( + + +
+ {children} +
+
+
+ ); + } +); +SidebarProvider.displayName = "SidebarProvider"; + +const Sidebar = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + side?: "left" | "right"; + variant?: "sidebar" | "floating" | "inset"; + collapsible?: "offcanvas" | "icon" | "none"; + } +>( + ( + { + side = "left", + variant = "sidebar", + collapsible = "offcanvas", + className, + children, + ...props + }, + ref + ) => { + const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); + + if (collapsible === "none") { + return ( +
+ {children} +
+ ); + } + + if (isMobile) { + return ( + + +
{children}
+
+
+ ); + } + + return ( +
+ {/* This is what handles the sidebar gap on desktop */} +
+ +
+ ); + } +); +Sidebar.displayName = "Sidebar"; + +const SidebarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentProps +>(({ className, onClick, ...props }, ref) => { + const { toggleSidebar } = useSidebar(); + + return ( + + ); +}); +SidebarTrigger.displayName = "SidebarTrigger"; + +const SidebarRail = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<"button"> +>(({ className, ...props }, ref) => { + const { toggleSidebar } = useSidebar(); + + return ( + + {({ state }) => ( + + )}

Dashboard

From 37c15bec57bc8abec0bd8d68b7edf954bae7da9e Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 9 Nov 2024 23:35:25 -0700 Subject: [PATCH 05/31] refactor: Remove defaultOpen prop from Sidebar component --- src/components/dashboard/Dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index e6dac21c..c2a42aba 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -96,7 +96,7 @@ export default function Dashboard() { return (
- +

Manage Crews

From b95a49bb35dcd24da98223931292abac792bdbcf Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:35:28 -0700 Subject: [PATCH 06/31] fix: Resolve TypeScript error in SidebarTrigger component --- src/components/dashboard/Dashboard.tsx | 13 +++++-------- src/components/ui/sidebar.tsx | 10 ++++++---- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index c2a42aba..d54b2568 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -124,14 +124,11 @@ export default function Dashboard() {
{({ state }) => ( - + state === "expanded" ? ( + + ) : ( + + ) )}

Dashboard

diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index ebbc300a..d0498950 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -261,9 +261,11 @@ Sidebar.displayName = "Sidebar"; const SidebarTrigger = React.forwardRef< React.ElementRef, - React.ComponentProps ->(({ className, onClick, ...props }, ref) => { - const { toggleSidebar } = useSidebar(); + React.ComponentProps & { + children?: React.ReactNode | ((props: { state: "expanded" | "collapsed" }) => React.ReactNode); + } +>(({ className, onClick, children, ...props }, ref) => { + const { toggleSidebar, state } = useSidebar(); return ( ); From a442b609f6d4b43b3e1b772e4359a1de93fb2f94 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:36:40 -0700 Subject: [PATCH 07/31] fix: Resolve TypeScript type inference for SidebarTrigger children prop --- src/components/dashboard/Dashboard.tsx | 4 ++-- src/components/ui/sidebar.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index d54b2568..4feee050 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -123,13 +123,13 @@ export default function Dashboard() {
- {({ state }) => ( + {({ state }) => state === "expanded" ? ( ) : ( ) - )} + }

Dashboard

diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index d0498950..ab80aaa9 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -262,7 +262,7 @@ Sidebar.displayName = "Sidebar"; const SidebarTrigger = React.forwardRef< React.ElementRef, React.ComponentProps & { - children?: React.ReactNode | ((props: { state: "expanded" | "collapsed" }) => React.ReactNode); + children?: React.ReactNode | ((props: { state: "expanded" | "collapsed" }) => JSX.Element); } >(({ className, onClick, children, ...props }, ref) => { const { toggleSidebar, state } = useSidebar(); From c11eee4d1c7e207fed04e17fd09701889fd9194a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 9 Nov 2024 23:38:50 -0700 Subject: [PATCH 08/31] refactor: Simplify sidebar trigger rendering in Dashboard component --- src/components/dashboard/Dashboard.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 4feee050..c8f9381f 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -8,7 +8,6 @@ import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; import { AlertCircle, ChevronLeft, ChevronRight } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; import { Sidebar, SidebarContent, @@ -123,7 +122,7 @@ export default function Dashboard() {
- {({ state }) => + {({ state }) => state === "expanded" ? ( ) : ( From 273d587acd92cb86b2d8369571d015738995fd02 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:38:52 -0700 Subject: [PATCH 09/31] refactor: Simplify sidebar icon rendering with helper function --- src/components/dashboard/Dashboard.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index c8f9381f..9633417c 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -8,6 +8,12 @@ import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; import { AlertCircle, ChevronLeft, ChevronRight } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; + +const getChevronIcon = (state: "expanded" | "collapsed") => { + return state === "expanded" + ? + : ; +}; import { Sidebar, SidebarContent, @@ -122,13 +128,7 @@ export default function Dashboard() {
- {({ state }) => - state === "expanded" ? ( - - ) : ( - - ) - } + {({ state }) => getChevronIcon(state)}

Dashboard

From d71cc8345e546d791cd7f046f0b287850c89530a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 9 Nov 2024 23:42:09 -0700 Subject: [PATCH 10/31] refactor: Improve chevron icon rendering with explicit parentheses --- src/components/dashboard/Dashboard.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 9633417c..3ecb5f6f 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -10,9 +10,11 @@ import { AlertCircle, ChevronLeft, ChevronRight } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; const getChevronIcon = (state: "expanded" | "collapsed") => { - return state === "expanded" - ? - : ; + return state === "expanded" ? ( + + ) : ( + + ); }; import { Sidebar, From 157401fbf9218034d70cd0e6924b1c759846b66f Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:42:11 -0700 Subject: [PATCH 11/31] refactor: Move sidebar toggle to sidebar edge for better positioning --- src/components/dashboard/Dashboard.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 3ecb5f6f..163b5c0d 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -103,10 +103,11 @@ export default function Dashboard() { return (
- - -

Manage Crews

-
+
+ + +

Manage Crews

+
{isLoading ? (

Loading crews...

@@ -125,15 +126,14 @@ export default function Dashboard() { /> )}
+ + {({ state }) => getChevronIcon(state)} +
+
-
- - {({ state }) => getChevronIcon(state)} - -

Dashboard

-
+

Dashboard

{error && ( From 864e7d4012e4dcfbca768895f735613c87b893d7 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:42:37 -0700 Subject: [PATCH 12/31] feat: Double sidebar width in Dashboard and sidebar components --- src/components/dashboard/Dashboard.tsx | 2 +- src/components/ui/sidebar.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 163b5c0d..77e75448 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -104,7 +104,7 @@ export default function Dashboard() { return (
- +

Manage Crews

diff --git a/src/components/ui/sidebar.tsx b/src/components/ui/sidebar.tsx index ab80aaa9..ce4e0f54 100644 --- a/src/components/ui/sidebar.tsx +++ b/src/components/ui/sidebar.tsx @@ -21,7 +21,7 @@ import { const SIDEBAR_COOKIE_NAME = "sidebar:state"; const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; -const SIDEBAR_WIDTH = "16rem"; +const SIDEBAR_WIDTH = "32rem"; const SIDEBAR_WIDTH_MOBILE = "18rem"; const SIDEBAR_WIDTH_ICON = "3rem"; const SIDEBAR_KEYBOARD_SHORTCUT = "b"; From d79284e2c077b2c19617cde08c897508589c0481 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:43:53 -0700 Subject: [PATCH 13/31] fix: Adjust sidebar trigger positioning for better visibility --- src/components/dashboard/Dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 77e75448..4d22767d 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -126,7 +126,7 @@ export default function Dashboard() { /> )} - + {({ state }) => getChevronIcon(state)}
From f8a1682d8c3b3ab93f010a092666ee1c8a2ca4bf Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 9 Nov 2024 23:47:59 -0700 Subject: [PATCH 14/31] refactor: Adjust sidebar styling and positioning --- src/components/dashboard/Dashboard.tsx | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 4d22767d..9bf87deb 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -108,28 +108,28 @@ export default function Dashboard() {

Manage Crews

- - {isLoading ? ( -

Loading crews...

- ) : ( - - )} - {!isLoading && !hasClonedAnalyzer && ( - - )} -
- - {({ state }) => getChevronIcon(state)} - -
+ + {isLoading ? ( +

Loading crews...

+ ) : ( + + )} + {!isLoading && !hasClonedAnalyzer && ( + + )} +
+ + {({ state }) => getChevronIcon(state)} + +
From 3c1ead6a4bb873df4621a00b56c647b0ad2ac84d Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:48:01 -0700 Subject: [PATCH 15/31] feat: Add mobile sheet sidebar for responsive dashboard layout --- src/components/dashboard/Dashboard.tsx | 45 +++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 9bf87deb..1e10ada3 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,8 +6,10 @@ import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { AlertCircle, ChevronLeft, ChevronRight } from "lucide-react"; +import { AlertCircle, ChevronLeft, ChevronRight, Menu } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; const getChevronIcon = (state: "expanded" | "collapsed") => { return state === "expanded" ? ( @@ -103,7 +105,42 @@ export default function Dashboard() { return (
-
+ {/* Mobile Menu */} + + + + + +
+ +

Manage Crews

+
+ + {isLoading ? ( +

Loading crews...

+ ) : ( + + )} + {!isLoading && !hasClonedAnalyzer && ( + + )} +
+
+
+
+ + {/* Desktop Sidebar */} +

Manage Crews

@@ -126,14 +163,14 @@ export default function Dashboard() { /> )} - + {({ state }) => getChevronIcon(state)}
-

Dashboard

+

Dashboard

{error && ( From dcd43a6620bf8e83f12c194193bb28b0cc142ddf Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:48:53 -0700 Subject: [PATCH 16/31] refactor: Simplify dashboard sidebar to use Sheet component across all screen sizes --- src/components/dashboard/Dashboard.tsx | 42 +------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 1e10ada3..4095b0ae 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -11,18 +11,9 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; -const getChevronIcon = (state: "expanded" | "collapsed") => { - return state === "expanded" ? ( - - ) : ( - - ); -}; import { - Sidebar, SidebarContent, SidebarHeader, - SidebarTrigger, } from "@/components/ui/sidebar"; export default function Dashboard() { @@ -105,10 +96,9 @@ export default function Dashboard() { return (
- {/* Mobile Menu */} - @@ -138,36 +128,6 @@ export default function Dashboard() {
- - {/* Desktop Sidebar */} -
- - -

Manage Crews

-
- - {isLoading ? ( -

Loading crews...

- ) : ( - - )} - {!isLoading && !hasClonedAnalyzer && ( - - )} -
- - {({ state }) => getChevronIcon(state)} - -
-

Dashboard

From 84e40de345e2e2bdfd4b7d5b5d2bf38b2edb975e Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:48:59 -0700 Subject: [PATCH 17/31] refactor: Remove unused ChevronLeft and ChevronRight icons from import --- src/components/dashboard/Dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 4095b0ae..d28ed110 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,7 +6,7 @@ import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { AlertCircle, ChevronLeft, ChevronRight, Menu } from "lucide-react"; +import { AlertCircle, Menu } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; From f93d393886441b5d16fb7765f108f885c5c02b11 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sat, 9 Nov 2024 23:58:22 -0700 Subject: [PATCH 18/31] refactor: Adjust sidebar width and clean up imports in Dashboard component --- src/components/dashboard/Dashboard.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index d28ed110..8d41ac13 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -11,10 +11,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; -import { - SidebarContent, - SidebarHeader, -} from "@/components/ui/sidebar"; +import { SidebarContent, SidebarHeader } from "@/components/ui/sidebar"; export default function Dashboard() { const [crews, setCrews] = useState([]); @@ -98,11 +95,18 @@ export default function Dashboard() {
- - +

Manage Crews

From df9e9c9691033a156057b8f047e42397a2b0f3f1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:58:25 -0700 Subject: [PATCH 19/31] feat: Add crew management triggers to nav and dashboard --- src/components/dashboard/Dashboard.tsx | 28 +++++++++++++++----------- src/components/reusables/Navbar.tsx | 9 ++++++++- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 8d41ac13..de10f3b3 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,7 +6,7 @@ import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { AlertCircle, Menu } from "lucide-react"; +import { AlertCircle, Settings } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; @@ -93,16 +93,7 @@ export default function Dashboard() { return (
- - - - +

Dashboard

-
+
{error && ( @@ -144,6 +135,19 @@ export default function Dashboard() { {error} )} +
+ + + {selectedCrew ? `Current: ${selectedCrew.name}` : 'Select a crew'} + +
diff --git a/src/components/reusables/Navbar.tsx b/src/components/reusables/Navbar.tsx index b3507d74..ebd7efb9 100644 --- a/src/components/reusables/Navbar.tsx +++ b/src/components/reusables/Navbar.tsx @@ -17,7 +17,11 @@ import { ChevronDown, Wallet } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { useUserData } from "@/hooks/useUserData"; -export function Nav() { +interface NavProps { + openCrewSidebar?: () => void; +} + +export function Nav({ openCrewSidebar }: NavProps) { const { data: userData, isLoading, error } = useUserData(); const displayAddress = React.useMemo(() => { @@ -135,6 +139,9 @@ export function Nav() { + From e847154a62be2e42d4a3aca327a0c28480fde05c Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sat, 9 Nov 2024 23:58:49 -0700 Subject: [PATCH 20/31] feat: Add sidebarOpen state to Dashboard component --- src/components/dashboard/Dashboard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index de10f3b3..63539147 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -19,6 +19,7 @@ export default function Dashboard() { const [error, setError] = useState(null); const [isLoading, setIsLoading] = useState(true); const [selectedCrew, setSelectedCrew] = useState(null); + const [sidebarOpen, setSidebarOpen] = useState(false); const fetchCrews = useCallback(async () => { setIsLoading(true); From 8b0875ff6d5702b0f16b812fe448363fb218509a Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sun, 10 Nov 2024 00:03:01 -0700 Subject: [PATCH 21/31] refactor: Simplify dashboard chat messages and input placeholder --- src/components/dashboard/Dashboard.tsx | 4 ++-- src/components/dashboard/DashboardChat.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 63539147..a8b8e9ee 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -8,7 +8,7 @@ import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; import { AlertCircle, Settings } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; +import { Sheet, SheetContent } from "@/components/ui/sheet"; import { Button } from "@/components/ui/button"; import { SidebarContent, SidebarHeader } from "@/components/ui/sidebar"; @@ -146,7 +146,7 @@ export default function Dashboard() { - {selectedCrew ? `Current: ${selectedCrew.name}` : 'Select a crew'} + {selectedCrew ? `Current: ${selectedCrew.name}` : "Select a crew"}
diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index 211eea3b..636f7b71 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -61,7 +61,7 @@ export default function DashboardChat({ selectedCrew }: DashboardChatProps) { const initialMessage: Message = { role: "assistant", content: selectedCrew - ? `# Selected: ${selectedCrew.name}\n\n${selectedCrew.description}\n\nHow can I help you today?` + ? `# Selected: ${selectedCrew.name}\n\n${selectedCrew.description}` : "Please select a crew to start chatting.", timestamp: new Date(), }; @@ -279,7 +279,7 @@ export default function DashboardChat({ selectedCrew }: DashboardChatProps) { placeholder={ selectedCrew ? "Type your message..." - : "Select a crew to start chatting" + : "Click the gear to select a crew." } disabled={isLoading || !selectedCrew} className="flex-1" From 3e7f2f0823511b80f17e660ffee74956acd667d6 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sun, 10 Nov 2024 00:03:04 -0700 Subject: [PATCH 22/31] feat: Move crew management button next to chat input --- src/components/dashboard/Dashboard.tsx | 18 ++++-------------- src/components/dashboard/DashboardChat.tsx | 14 ++++++++++++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index a8b8e9ee..a9369d98 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -136,20 +136,10 @@ export default function Dashboard() { {error} )} -
- - - {selectedCrew ? `Current: ${selectedCrew.name}` : "Select a crew"} - -
- + setSidebarOpen(true)} + />
diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index 636f7b71..fe2cd197 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -14,6 +14,7 @@ import { Crew } from "@/types/supabase"; interface DashboardChatProps { selectedCrew: Crew | null; + onOpenCrewManager: () => void; } interface StreamMessage { @@ -34,7 +35,7 @@ interface Message { streamMessages?: StreamMessage[]; } -export default function DashboardChat({ selectedCrew }: DashboardChatProps) { +export default function DashboardChat({ selectedCrew, onOpenCrewManager }: DashboardChatProps) { const { toast } = useToast(); const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); @@ -273,13 +274,22 @@ export default function DashboardChat({ selectedCrew }: DashboardChatProps) {
+ setInput(e.target.value)} placeholder={ selectedCrew ? "Type your message..." - : "Click the gear to select a crew." + : "Select a crew to start chatting" } disabled={isLoading || !selectedCrew} className="flex-1" From 9791e2bcab743b62afe6e287369f9da8efc7580a Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sun, 10 Nov 2024 00:03:33 -0700 Subject: [PATCH 23/31] feat: Import Settings icon from lucide-react --- src/components/dashboard/DashboardChat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index fe2cd197..9909d5cd 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -9,7 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { useToast } from "@/hooks/use-toast"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; -import { Loader2 } from "lucide-react"; +import { Loader2, Settings } from "lucide-react"; import { Crew } from "@/types/supabase"; interface DashboardChatProps { From 517d35cae00d1dbb379cf307ff360e7e2295a176 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sun, 10 Nov 2024 00:11:09 -0700 Subject: [PATCH 24/31] chore: cleanup the mess Went through a few rounds of ideas in the commits, ended up with a mobile friendly version and a button to activate it down by the chat box. Can modify further if needed but this looks like a good starting point. --- src/components/dashboard/Dashboard.tsx | 5 ++-- src/components/dashboard/DashboardChat.tsx | 27 ++++++++++++---------- src/components/reusables/Navbar.tsx | 9 +------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index a9369d98..9d06dedf 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,10 +6,9 @@ import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { AlertCircle, Settings } from "lucide-react"; +import { AlertCircle } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent } from "@/components/ui/sheet"; -import { Button } from "@/components/ui/button"; import { SidebarContent, SidebarHeader } from "@/components/ui/sidebar"; @@ -136,7 +135,7 @@ export default function Dashboard() { {error} )} - setSidebarOpen(true)} /> diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index 9909d5cd..1bc44e60 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -35,7 +35,10 @@ interface Message { streamMessages?: StreamMessage[]; } -export default function DashboardChat({ selectedCrew, onOpenCrewManager }: DashboardChatProps) { +export default function DashboardChat({ + selectedCrew, + onOpenCrewManager, +}: DashboardChatProps) { const { toast } = useToast(); const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); @@ -63,7 +66,7 @@ export default function DashboardChat({ selectedCrew, onOpenCrewManager }: Dashb role: "assistant", content: selectedCrew ? `# Selected: ${selectedCrew.name}\n\n${selectedCrew.description}` - : "Please select a crew to start chatting.", + : "# Select a Crew\n\nClick the gear icon to select a crew.", timestamp: new Date(), }; setMessages([initialMessage]); @@ -274,26 +277,26 @@ export default function DashboardChat({ selectedCrew, onOpenCrewManager }: Dashb
- setInput(e.target.value)} placeholder={ selectedCrew ? "Type your message..." - : "Select a crew to start chatting" + : "Click to select a crew -->" } disabled={isLoading || !selectedCrew} className="flex-1" /> + - From f9bf19c5efb71f876a6fd289bfaba7f5720285e1 Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sun, 10 Nov 2024 00:15:49 -0700 Subject: [PATCH 25/31] feat: Add visual selection indicators for crew selection buttons --- src/components/crews/CrewManagement.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/crews/CrewManagement.tsx b/src/components/crews/CrewManagement.tsx index b67da902..a318bc39 100644 --- a/src/components/crews/CrewManagement.tsx +++ b/src/components/crews/CrewManagement.tsx @@ -4,6 +4,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { supabase } from "@/utils/supabase/client"; import { Button } from "@/components/ui/button"; +import { Circle, CheckCircle } from "lucide-react"; import { useToast } from "@/hooks/use-toast"; import { Dialog, From 72866cc3d54ecf7bb557c4f2d0e3939595bf71ef Mon Sep 17 00:00:00 2001 From: "Jason Schrader (aider)" Date: Sun, 10 Nov 2024 00:16:09 -0700 Subject: [PATCH 26/31] refactor: Update crew selection icons to CheckCircle and Circle --- src/components/crews/CrewManagement.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/crews/CrewManagement.tsx b/src/components/crews/CrewManagement.tsx index a318bc39..37b099e8 100644 --- a/src/components/crews/CrewManagement.tsx +++ b/src/components/crews/CrewManagement.tsx @@ -115,9 +115,9 @@ export function CrewManagement({ onClick={() => onCrewSelect(crew)} > {selectedCrew?.id === crew.id ? ( - + ) : ( - + )}
From e33b60519e5da16f61508abd1439f92ffdb8e677 Mon Sep 17 00:00:00 2001 From: Jason Schrader Date: Sun, 10 Nov 2024 00:19:32 -0700 Subject: [PATCH 27/31] fix: build errors Learn to follow my own advice! --- src/components/crews/CrewManagement.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/components/crews/CrewManagement.tsx b/src/components/crews/CrewManagement.tsx index 37b099e8..e058deb5 100644 --- a/src/components/crews/CrewManagement.tsx +++ b/src/components/crews/CrewManagement.tsx @@ -13,14 +13,7 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { - PlusIcon, - Trash2Icon, - UserIcon, - Settings, - Check, - CheckCircle2, -} from "lucide-react"; +import { PlusIcon, Trash2Icon, UserIcon, Settings } from "lucide-react"; import CrewForm from "./CrewForm"; import { Crew, CrewManagementProps } from "@/types/supabase"; import { ScrollArea } from "@/components/ui/scroll-area"; From 1e0700f652f53005fb40d2a91e2f32825db8b631 Mon Sep 17 00:00:00 2001 From: Biwas Bhandari Date: Sun, 10 Nov 2024 12:33:28 +0545 Subject: [PATCH 28/31] feat(crew): toggle public and private --- package-lock.json | 45 +++++++++++++++++ package.json | 1 + src/components/crews/CrewForm.tsx | 13 +++++ src/components/crews/CrewManagement.tsx | 64 ++++++++++++++++++++++++- src/components/ui/switch.tsx | 29 +++++++++++ src/types/supabase.ts | 1 + 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/components/ui/switch.tsx diff --git a/package-lock.json b/package-lock.json index e51e8389..9a350979 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@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.1", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", "@radix-ui/react-tooltip": "^1.1.3", @@ -1794,6 +1795,50 @@ } } }, + "node_modules/@radix-ui/react-switch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.1.1.tgz", + "integrity": "sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-tabs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz", diff --git a/package.json b/package.json index 8e67f242..ac705a9f 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@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.1", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", "@radix-ui/react-tooltip": "^1.1.3", diff --git a/src/components/crews/CrewForm.tsx b/src/components/crews/CrewForm.tsx index e8405005..6b8edde6 100644 --- a/src/components/crews/CrewForm.tsx +++ b/src/components/crews/CrewForm.tsx @@ -6,12 +6,14 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; +import { Switch } from "@/components/ui/switch"; import { toast } from "@/hooks/use-toast"; import { CrewFormProps, Crew } from "@/types/supabase"; export default function CrewForm({ onCrewCreated, onClose }: CrewFormProps) { const [crewName, setCrewName] = useState(""); const [crewDescription, setCrewDescription] = useState(""); + const [isPublic, setIsPublic] = useState(false); const [loading, setLoading] = useState(false); const handleSubmit = async (e: React.FormEvent) => { @@ -35,6 +37,7 @@ export default function CrewForm({ onCrewCreated, onClose }: CrewFormProps) { name: crewName, description: crewDescription, profile_id: user.id, + is_public: isPublic, }) .select() .single(); @@ -46,10 +49,12 @@ export default function CrewForm({ onCrewCreated, onClose }: CrewFormProps) { name: data.name, description: data.description, created_at: data.created_at, + is_public: data.is_public, }; setCrewName(""); setCrewDescription(""); + setIsPublic(false); onClose(); onCrewCreated(newCrew); toast({ @@ -90,6 +95,14 @@ export default function CrewForm({ onCrewCreated, onClose }: CrewFormProps) { rows={3} />
+
+ + +
diff --git a/src/components/crews/CrewManagement.tsx b/src/components/crews/CrewManagement.tsx index e058deb5..c1899a79 100644 --- a/src/components/crews/CrewManagement.tsx +++ b/src/components/crews/CrewManagement.tsx @@ -13,7 +13,17 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { PlusIcon, Trash2Icon, UserIcon, Settings } from "lucide-react"; +import { + PlusIcon, + Trash2Icon, + UserIcon, + Settings, + Check, + CheckCircle2, + Globe, + Lock, +} from "lucide-react"; +import { Switch } from "@/components/ui/switch"; import CrewForm from "./CrewForm"; import { Crew, CrewManagementProps } from "@/types/supabase"; import { ScrollArea } from "@/components/ui/scroll-area"; @@ -68,6 +78,44 @@ export function CrewManagement({ onCrewUpdate(updatedCrews); }; + const handlePublicToggle = async (crew: Crew) => { + setLoading(true); + try { + const { data, error } = await supabase + .from("crews") + .update({ is_public: !crew.is_public }) + .eq("id", crew.id) + .select() + .single(); + + if (error) throw error; + + const updatedCrews = crews.map((c) => + c.id === crew.id ? { ...c, is_public: data.is_public } : c + ); + setCrews(updatedCrews); + onCrewUpdate(updatedCrews); + + toast({ + title: "Crew updated", + description: `The crew is now ${ + data.is_public + ? "public. Everyone can see your crew and clone it." + : "private. Only you can see it." + }.`, + }); + } catch (error) { + console.error("Error updating crew:", error); + toast({ + title: "Error", + description: "Failed to update the crew. Please try again.", + variant: "destructive", + }); + } finally { + setLoading(false); + } + }; + return (
@@ -121,7 +169,19 @@ export function CrewManagement({ Created: {new Date(crew.created_at).toLocaleDateString()} -
+
+
+ {crew.is_public ? ( + + ) : ( + + )} + handlePublicToggle(crew)} + disabled={loading} + /> +
+ + +
{error && ( diff --git a/src/components/dashboard/DashboardChat.tsx b/src/components/dashboard/DashboardChat.tsx index 1bc44e60..3f8debd0 100644 --- a/src/components/dashboard/DashboardChat.tsx +++ b/src/components/dashboard/DashboardChat.tsx @@ -9,7 +9,7 @@ import { Skeleton } from "@/components/ui/skeleton"; import { useToast } from "@/hooks/use-toast"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; -import { Loader2, Settings } from "lucide-react"; +import { Loader2 } from "lucide-react"; import { Crew } from "@/types/supabase"; interface DashboardChatProps { @@ -35,10 +35,7 @@ interface Message { streamMessages?: StreamMessage[]; } -export default function DashboardChat({ - selectedCrew, - onOpenCrewManager, -}: DashboardChatProps) { +export default function DashboardChat({ selectedCrew }: DashboardChatProps) { const { toast } = useToast(); const [messages, setMessages] = useState([]); const [input, setInput] = useState(""); @@ -281,22 +278,11 @@ export default function DashboardChat({ value={input} onChange={(e) => setInput(e.target.value)} placeholder={ - selectedCrew - ? "Type your message..." - : "Click to select a crew -->" + selectedCrew ? "Type your message..." : "Select a crew to chat.." } disabled={isLoading || !selectedCrew} className="flex-1" /> - +
+ ); +} diff --git a/src/components/public-crew/PublicCrews.tsx b/src/components/public-crew/PublicCrews.tsx new file mode 100644 index 00000000..7cd52bb5 --- /dev/null +++ b/src/components/public-crew/PublicCrews.tsx @@ -0,0 +1,188 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Users, User, CheckSquare, PenTool } from "lucide-react"; +import type { PublicCrew } from "@/types/supabase"; +import { ClonePublicCrew } from "./ClonePublicCrew"; + +export default function PublicCrews() { + const [crews, setCrews] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchCrews = async () => { + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/public-crews` + ); + if (!response.ok) { + throw new Error("Failed to fetch crews"); + } + const data = await response.json(); + setCrews(data); + } catch (error: unknown) { + setError("Failed to load crews. Please try again later."); + console.error("Error fetching crews:", error); + } finally { + setLoading(false); + } + }; + + fetchCrews(); + }, []); + + const handleCloneComplete = () => { + // You can add any additional logic here after a crew has been cloned + console.log("Crew cloned successfully"); + }; + + if (loading) { + return
Loading crews...
; + } + + if (error) { + return
{error}
; + } + + if (crews.length === 0) { + return ( +
+

Public Crews

+ + +

+ No crews available at this time. +

+
+
+
+ ); + } + + return ( +
+

Public Crews

+
+ {crews.map((crew) => ( + + + + + {crew.name} + + {crew.description} + + +

+ Created: {new Date(crew.created_at).toLocaleString()} +

+

+ Creator:{" "} + + {crew.creator_email} + +

+

Agents:

+
    + {crew.agents.map((agent) => ( +
  • + + + + + + + {agent.name} + {agent.role} + + +
    +
    +

    Goal

    +

    {agent.goal}

    +
    +
    +

    Backstory

    +

    {agent.backstory}

    +
    +
    +

    Tools

    +
    + {agent.agent_tools.map((tool, index) => ( + + + {tool} + + ))} +
    +
    +
    +

    Tasks

    +
      + {agent.tasks.map((task) => ( +
    • +

      + + {task.description} +

      +

      + Expected output: {task.expected_output} +

      +
    • + ))} +
    +
    +
    +
    +
    +
    +
  • + ))} +
+
+ +
+
+
+ ))} +
+
+ ); +} diff --git a/src/types/supabase.ts b/src/types/supabase.ts index 8a4bd020..961998f6 100644 --- a/src/types/supabase.ts +++ b/src/types/supabase.ts @@ -4,7 +4,7 @@ export interface Crew { name: string; description: string; created_at: string; - is_public: boolean; + is_public?: boolean; } // Interface for props of the CrewManagement component @@ -85,4 +85,34 @@ export interface CloneAgent { export interface CloneTask { description: string; expected_output: string; -} \ No newline at end of file +} + + +// INTERFACE FOR PUBLIC CREWS + +interface PublicTask { + id: number; + description: string; + expected_output: string; + agent_id: number; + profile_id: string; + } + + interface PublicAgent { + id: number; + name: string; + role: string; + goal: string; + backstory: string; + agent_tools: string[]; + tasks: PublicTask[]; + } + +export interface PublicCrew { + id: number; + name: string; + description: string; + created_at: string; + creator_email: string; + agents: PublicAgent[]; + } \ No newline at end of file From 6038cc92e5a869142838c97e06705265ac196186 Mon Sep 17 00:00:00 2001 From: Biwas Bhandari Date: Sun, 10 Nov 2024 14:43:41 +0545 Subject: [PATCH 30/31] fix: build --- src/components/dashboard/Dashboard.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 7d7b53cb..f3523c0d 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -6,12 +6,7 @@ import { CrewManagement } from "@/components/crews/CrewManagement"; import { CloneTradingAnalyzer } from "@/components/crews/CloneTradingAnalyzer"; import DashboardChat from "./DashboardChat"; import { Crew } from "@/types/supabase"; -import { - AlertCircle, - ArrowBigLeft, - ArrowBigRightDash, - ArrowLeft, -} from "lucide-react"; +import { AlertCircle, ArrowBigRightDash } from "lucide-react"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Sheet, SheetContent } from "@/components/ui/sheet"; import { SidebarContent, SidebarHeader } from "@/components/ui/sidebar"; From 5442a345f4283948a4d53948dd06494ba20fd065 Mon Sep 17 00:00:00 2001 From: Biwas Bhandari Date: Sun, 10 Nov 2024 14:52:20 +0545 Subject: [PATCH 31/31] fix: protect public-crew route --- src/utils/supabase/middleware.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/utils/supabase/middleware.ts b/src/utils/supabase/middleware.ts index ebf993ac..5b260860 100644 --- a/src/utils/supabase/middleware.ts +++ b/src/utils/supabase/middleware.ts @@ -64,6 +64,10 @@ export const updateSession = async (request: NextRequest) => { return NextResponse.redirect(new URL("/", request.url)); } + if (request.nextUrl.pathname.startsWith("/public-crew") && (userError || !user)) { + return NextResponse.redirect(new URL("/", request.url)); + } + if (request.nextUrl.pathname === "/" && !userError) { return NextResponse.redirect(new URL("/dashboard", request.url)); }