mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-10 06:45:28 -05:00
feat(frontend): new navbar design (#10341)
## Changes 🏗️ <img width="900" height="327" alt="Screenshot 2025-07-10 at 20 12 38" src="https://github.com/user-attachments/assets/044f00ed-7e05-46b7-a821-ce1cb0ee9298" /> <br /><br /> Navbar updated to look pretty from the new designs: - the logo is now centred instead of on the left - menu items have been updated to a smaller font-size and less radius - icons have been updated I also generated the API files ( _sorry for the noise_ ). I had to do some border-radius and button updates on the atoms/tokens for it to look good. ## Checklist 📋 ## For code changes: - [x] I have clearly listed my changes in the PR description - [x] I have made a test plan - [x] I have tested my changes according to the test plan: - [x] Login/logout - [x] The new navbar looks good across screens ## For configuration changes No config changes
This commit is contained in:
@@ -1,12 +1,13 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useSearchParams } from "next/navigation";
|
|
||||||
import { GraphID } from "@/lib/autogpt-server-api/types";
|
|
||||||
import FlowEditor from "@/components/Flow";
|
import FlowEditor from "@/components/Flow";
|
||||||
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
|
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
|
||||||
import { useEffect } from "react";
|
import LoadingBox from "@/components/ui/loading";
|
||||||
|
import { GraphID } from "@/lib/autogpt-server-api/types";
|
||||||
|
import { useSearchParams } from "next/navigation";
|
||||||
|
import { Suspense, useEffect } from "react";
|
||||||
|
|
||||||
export default function BuilderPage() {
|
function BuilderContent() {
|
||||||
const query = useSearchParams();
|
const query = useSearchParams();
|
||||||
const { completeStep } = useOnboarding();
|
const { completeStep } = useOnboarding();
|
||||||
|
|
||||||
@@ -15,12 +16,20 @@ export default function BuilderPage() {
|
|||||||
}, [completeStep]);
|
}, [completeStep]);
|
||||||
|
|
||||||
const _graphVersion = query.get("flowVersion");
|
const _graphVersion = query.get("flowVersion");
|
||||||
const graphVersion = _graphVersion ? parseInt(_graphVersion) : undefined
|
const graphVersion = _graphVersion ? parseInt(_graphVersion) : undefined;
|
||||||
return (
|
return (
|
||||||
<FlowEditor
|
<FlowEditor
|
||||||
className="flow-container"
|
className="flow-container"
|
||||||
flowID={query.get("flowID") as GraphID | null ?? undefined}
|
flowID={(query.get("flowID") as GraphID | null) ?? undefined}
|
||||||
flowVersion={graphVersion}
|
flowVersion={graphVersion}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default function BuilderPage() {
|
||||||
|
return (
|
||||||
|
<Suspense fallback={<LoadingBox className="h-[80vh]" />}>
|
||||||
|
<BuilderContent />
|
||||||
|
</Suspense>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,67 +1,10 @@
|
|||||||
|
import { Navbar } from "@/components/layout/Navbar/Navbar";
|
||||||
import { ReactNode } from "react";
|
import { ReactNode } from "react";
|
||||||
import { Navbar } from "@/components/agptui/Navbar";
|
|
||||||
import { IconType } from "@/components/ui/icons";
|
|
||||||
|
|
||||||
export default function PlatformLayout({ children }: { children: ReactNode }) {
|
export default function PlatformLayout({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Navbar
|
<Navbar />
|
||||||
links={[
|
|
||||||
{
|
|
||||||
name: "Marketplace",
|
|
||||||
href: "/marketplace",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Library",
|
|
||||||
href: "/library",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Build",
|
|
||||||
href: "/build",
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
menuItemGroups={[
|
|
||||||
{
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: IconType.Edit,
|
|
||||||
text: "Edit profile",
|
|
||||||
href: "/profile",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: IconType.LayoutDashboard,
|
|
||||||
text: "Creator Dashboard",
|
|
||||||
href: "/profile/dashboard",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
icon: IconType.UploadCloud,
|
|
||||||
text: "Publish an agent",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: IconType.Settings,
|
|
||||||
text: "Settings",
|
|
||||||
href: "/profile/settings",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
icon: IconType.LogOut,
|
|
||||||
text: "Log out",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
<main>{children}</main>
|
<main>{children}</main>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import {
|
import {
|
||||||
ArrowBottomRightIcon,
|
ArrowBottomRightIcon,
|
||||||
QuestionMarkCircledIcon,
|
QuestionMarkCircledIcon,
|
||||||
} from "@radix-ui/react-icons";
|
} from "@radix-ui/react-icons";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
||||||
|
|
||||||
import { LibraryPageStateProvider } from "./components/state-provider";
|
|
||||||
import LibraryActionHeader from "./components/LibraryActionHeader/LibraryActionHeader";
|
import LibraryActionHeader from "./components/LibraryActionHeader/LibraryActionHeader";
|
||||||
import LibraryAgentList from "./components/LibraryAgentList/LibraryAgentList";
|
import LibraryAgentList from "./components/LibraryAgentList/LibraryAgentList";
|
||||||
|
import { LibraryPageStateProvider } from "./components/state-provider";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* LibraryPage Component
|
* LibraryPage Component
|
||||||
@@ -17,7 +17,7 @@ import LibraryAgentList from "./components/LibraryAgentList/LibraryAgentList";
|
|||||||
*/
|
*/
|
||||||
export default function LibraryPage() {
|
export default function LibraryPage() {
|
||||||
return (
|
return (
|
||||||
<main className="container min-h-screen space-y-4 pb-20 sm:px-8 md:px-12">
|
<main className="pt-160 container min-h-screen space-y-4 pb-20 pt-16 sm:px-8 md:px-12">
|
||||||
<LibraryPageStateProvider>
|
<LibraryPageStateProvider>
|
||||||
<LibraryActionHeader />
|
<LibraryActionHeader />
|
||||||
<LibraryAgentList />
|
<LibraryAgentList />
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6,12 +6,12 @@
|
|||||||
* OpenAPI spec version: 0.1
|
* OpenAPI spec version: 0.1
|
||||||
*/
|
*/
|
||||||
import type { CredentialsMetaInputTitle } from "./credentialsMetaInputTitle";
|
import type { CredentialsMetaInputTitle } from "./credentialsMetaInputTitle";
|
||||||
|
import type { ProviderName } from "./providerName";
|
||||||
import type { CredentialsMetaInputType } from "./credentialsMetaInputType";
|
import type { CredentialsMetaInputType } from "./credentialsMetaInputType";
|
||||||
|
|
||||||
export interface CredentialsMetaInput {
|
export interface CredentialsMetaInput {
|
||||||
id: string;
|
id: string;
|
||||||
title?: CredentialsMetaInputTitle;
|
title?: CredentialsMetaInputTitle;
|
||||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
provider: ProviderName;
|
||||||
provider: string;
|
|
||||||
type: CredentialsMetaInputType;
|
type: CredentialsMetaInputType;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,12 @@
|
|||||||
* This server is used to execute agents that are created by the AutoGPT system.
|
* This server is used to execute agents that are created by the AutoGPT system.
|
||||||
* OpenAPI spec version: 0.1
|
* OpenAPI spec version: 0.1
|
||||||
*/
|
*/
|
||||||
|
import type { ProviderName } from "./providerName";
|
||||||
import type { LibraryAgentTriggerInfoConfigSchema } from "./libraryAgentTriggerInfoConfigSchema";
|
import type { LibraryAgentTriggerInfoConfigSchema } from "./libraryAgentTriggerInfoConfigSchema";
|
||||||
import type { LibraryAgentTriggerInfoCredentialsInputName } from "./libraryAgentTriggerInfoCredentialsInputName";
|
import type { LibraryAgentTriggerInfoCredentialsInputName } from "./libraryAgentTriggerInfoCredentialsInputName";
|
||||||
|
|
||||||
export interface LibraryAgentTriggerInfo {
|
export interface LibraryAgentTriggerInfo {
|
||||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
provider: ProviderName;
|
||||||
provider: string;
|
|
||||||
/** Input schema for the trigger block */
|
/** Input schema for the trigger block */
|
||||||
config_schema: LibraryAgentTriggerInfoConfigSchema;
|
config_schema: LibraryAgentTriggerInfoConfigSchema;
|
||||||
credentials_input_name: LibraryAgentTriggerInfoCredentialsInputName;
|
credentials_input_name: LibraryAgentTriggerInfoCredentialsInputName;
|
||||||
|
|||||||
53
autogpt_platform/frontend/src/app/api/__generated__/models/providerName.ts
generated
Normal file
53
autogpt_platform/frontend/src/app/api/__generated__/models/providerName.ts
generated
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
/**
|
||||||
|
* Generated by orval v7.10.0 🍺
|
||||||
|
* Do not edit manually.
|
||||||
|
* AutoGPT Agent Server
|
||||||
|
* This server is used to execute agents that are created by the AutoGPT system.
|
||||||
|
* OpenAPI spec version: 0.1
|
||||||
|
*/
|
||||||
|
|
||||||
|
export type ProviderName = (typeof ProviderName)[keyof typeof ProviderName];
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-redeclare
|
||||||
|
export const ProviderName = {
|
||||||
|
aiml_api: "aiml_api",
|
||||||
|
anthropic: "anthropic",
|
||||||
|
apollo: "apollo",
|
||||||
|
compass: "compass",
|
||||||
|
discord: "discord",
|
||||||
|
d_id: "d_id",
|
||||||
|
e2b: "e2b",
|
||||||
|
exa: "exa",
|
||||||
|
fal: "fal",
|
||||||
|
generic_webhook: "generic_webhook",
|
||||||
|
github: "github",
|
||||||
|
google: "google",
|
||||||
|
google_maps: "google_maps",
|
||||||
|
groq: "groq",
|
||||||
|
http: "http",
|
||||||
|
hubspot: "hubspot",
|
||||||
|
ideogram: "ideogram",
|
||||||
|
jina: "jina",
|
||||||
|
linear: "linear",
|
||||||
|
llama_api: "llama_api",
|
||||||
|
medium: "medium",
|
||||||
|
mem0: "mem0",
|
||||||
|
notion: "notion",
|
||||||
|
nvidia: "nvidia",
|
||||||
|
ollama: "ollama",
|
||||||
|
openai: "openai",
|
||||||
|
openweathermap: "openweathermap",
|
||||||
|
open_router: "open_router",
|
||||||
|
pinecone: "pinecone",
|
||||||
|
reddit: "reddit",
|
||||||
|
replicate: "replicate",
|
||||||
|
revid: "revid",
|
||||||
|
screenshotone: "screenshotone",
|
||||||
|
slant3d: "slant3d",
|
||||||
|
smartlead: "smartlead",
|
||||||
|
smtp: "smtp",
|
||||||
|
twitter: "twitter",
|
||||||
|
todoist: "todoist",
|
||||||
|
unreal_speech: "unreal_speech",
|
||||||
|
zerobounce: "zerobounce",
|
||||||
|
} as const;
|
||||||
@@ -5,13 +5,13 @@
|
|||||||
* This server is used to execute agents that are created by the AutoGPT system.
|
* This server is used to execute agents that are created by the AutoGPT system.
|
||||||
* OpenAPI spec version: 0.1
|
* OpenAPI spec version: 0.1
|
||||||
*/
|
*/
|
||||||
|
import type { ProviderName } from "./providerName";
|
||||||
import type { WebhookConfig } from "./webhookConfig";
|
import type { WebhookConfig } from "./webhookConfig";
|
||||||
|
|
||||||
export interface Webhook {
|
export interface Webhook {
|
||||||
id?: string;
|
id?: string;
|
||||||
user_id: string;
|
user_id: string;
|
||||||
/** Provider name for integrations. Can be any string value, including custom provider names. */
|
provider: ProviderName;
|
||||||
provider: string;
|
|
||||||
credentials_id: string;
|
credentials_id: string;
|
||||||
webhook_type: string;
|
webhook_type: string;
|
||||||
resource: string;
|
resource: string;
|
||||||
|
|||||||
@@ -18,9 +18,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The provider to initiate an OAuth flow for",
|
"title": "The provider to initiate an OAuth flow for"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -65,9 +64,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The target provider for this OAuth exchange",
|
"title": "The target provider for this OAuth exchange"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -135,9 +133,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The provider to list credentials for",
|
"title": "The provider to list credentials for"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -176,9 +173,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The provider to create credentials for",
|
"title": "The provider to create credentials for"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@@ -257,9 +253,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The provider to retrieve credentials for",
|
"title": "The provider to retrieve credentials for"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -320,9 +315,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "The provider to delete credentials for",
|
"title": "The provider to delete credentials for"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -386,9 +380,8 @@
|
|||||||
"in": "path",
|
"in": "path",
|
||||||
"required": true,
|
"required": true,
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/ProviderName",
|
||||||
"title": "Provider where the webhook was registered",
|
"title": "Provider where the webhook was registered"
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -443,86 +436,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/integrations/providers": {
|
|
||||||
"get": {
|
|
||||||
"tags": ["v1", "integrations"],
|
|
||||||
"summary": "List Providers",
|
|
||||||
"description": "Get a list of all available provider names.\n\nReturns both statically defined providers (from ProviderName enum)\nand dynamically registered providers (from SDK decorators).\n\nNote: The complete list of provider names is also available as a constant\nin the generated TypeScript client via PROVIDER_NAMES.",
|
|
||||||
"operationId": "getV1ListProviders",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful Response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"items": { "type": "string" },
|
|
||||||
"type": "array",
|
|
||||||
"title": "Response Getv1Listproviders"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/integrations/providers/names": {
|
|
||||||
"get": {
|
|
||||||
"tags": ["v1", "integrations"],
|
|
||||||
"summary": "Get Provider Names",
|
|
||||||
"description": "Get all provider names in a structured format.\n\nThis endpoint is specifically designed to expose the provider names\nin the OpenAPI schema so that code generators like Orval can create\nappropriate TypeScript constants.",
|
|
||||||
"operationId": "getV1GetProviderNames",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful Response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ProviderNamesResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/integrations/providers/constants": {
|
|
||||||
"get": {
|
|
||||||
"tags": ["v1", "integrations"],
|
|
||||||
"summary": "Get Provider Constants",
|
|
||||||
"description": "Get provider names as constants.\n\nThis endpoint returns a model with provider names as constants,\nspecifically designed for OpenAPI code generation tools to create\nTypeScript constants.",
|
|
||||||
"operationId": "getV1GetProviderConstants",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful Response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": { "$ref": "#/components/schemas/ProviderConstants" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/integrations/providers/enum-example": {
|
|
||||||
"get": {
|
|
||||||
"tags": ["v1", "integrations"],
|
|
||||||
"summary": "Get Provider Enum Example",
|
|
||||||
"description": "Example endpoint that uses the CompleteProviderNames enum.\n\nThis endpoint exists to ensure that the CompleteProviderNames enum is included\nin the OpenAPI schema, which will cause Orval to generate it as a\nTypeScript enum/constant.",
|
|
||||||
"operationId": "getV1GetProviderEnumExample",
|
|
||||||
"responses": {
|
|
||||||
"200": {
|
|
||||||
"description": "Successful Response",
|
|
||||||
"content": {
|
|
||||||
"application/json": {
|
|
||||||
"schema": {
|
|
||||||
"$ref": "#/components/schemas/ProviderEnumResponse"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/analytics/log_raw_metric": {
|
"/api/analytics/log_raw_metric": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": ["v1", "analytics"],
|
"tags": ["v1", "analytics"],
|
||||||
@@ -3373,6 +3286,48 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/library/agents/by-graph/{graph_id}": {
|
||||||
|
"get": {
|
||||||
|
"tags": ["v2", "library", "private"],
|
||||||
|
"summary": "Get Library Agent By Graph Id",
|
||||||
|
"operationId": "getV2GetLibraryAgentByGraphId",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "graph_id",
|
||||||
|
"in": "path",
|
||||||
|
"required": true,
|
||||||
|
"schema": { "type": "string", "title": "Graph Id" }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "version",
|
||||||
|
"in": "query",
|
||||||
|
"required": false,
|
||||||
|
"schema": {
|
||||||
|
"anyOf": [{ "type": "integer" }, { "type": "null" }],
|
||||||
|
"title": "Version"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Successful Response",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/LibraryAgent" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"description": "Validation Error",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/library/agents/marketplace/{store_listing_version_id}": {
|
"/api/library/agents/marketplace/{store_listing_version_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": ["v2", "library", "private", "store, library"],
|
"tags": ["v2", "library", "private", "store, library"],
|
||||||
@@ -4146,11 +4101,7 @@
|
|||||||
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
"anyOf": [{ "type": "string" }, { "type": "null" }],
|
||||||
"title": "Title"
|
"title": "Title"
|
||||||
},
|
},
|
||||||
"provider": {
|
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||||
"type": "string",
|
|
||||||
"title": "Provider",
|
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
},
|
|
||||||
"type": {
|
"type": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["api_key", "oauth2", "user_password", "host_scoped"],
|
"enum": ["api_key", "oauth2", "user_password", "host_scoped"],
|
||||||
@@ -4160,8 +4111,8 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["id", "provider", "type"],
|
"required": ["id", "provider", "type"],
|
||||||
"title": "CredentialsMetaInput",
|
"title": "CredentialsMetaInput",
|
||||||
"credentials_provider": ["string"],
|
"credentials_provider": [],
|
||||||
"credentials_types": ["api_key", "oauth2", "user_password"]
|
"credentials_types": []
|
||||||
},
|
},
|
||||||
"CredentialsMetaResponse": {
|
"CredentialsMetaResponse": {
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -4869,11 +4820,7 @@
|
|||||||
},
|
},
|
||||||
"LibraryAgentTriggerInfo": {
|
"LibraryAgentTriggerInfo": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"provider": {
|
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||||
"type": "string",
|
|
||||||
"title": "Provider",
|
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
},
|
|
||||||
"config_schema": {
|
"config_schema": {
|
||||||
"additionalProperties": true,
|
"additionalProperties": true,
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -5288,6 +5235,7 @@
|
|||||||
"AGENT_INPUT",
|
"AGENT_INPUT",
|
||||||
"CONGRATS",
|
"CONGRATS",
|
||||||
"GET_RESULTS",
|
"GET_RESULTS",
|
||||||
|
"RUN_AGENTS",
|
||||||
"MARKETPLACE_VISIT",
|
"MARKETPLACE_VISIT",
|
||||||
"MARKETPLACE_ADD_AGENT",
|
"MARKETPLACE_ADD_AGENT",
|
||||||
"MARKETPLACE_RUN_AGENT",
|
"MARKETPLACE_RUN_AGENT",
|
||||||
@@ -5675,44 +5623,51 @@
|
|||||||
"required": ["name", "username", "description", "links"],
|
"required": ["name", "username", "description", "links"],
|
||||||
"title": "ProfileDetails"
|
"title": "ProfileDetails"
|
||||||
},
|
},
|
||||||
"ProviderConstants": {
|
"ProviderName": {
|
||||||
"properties": {
|
"type": "string",
|
||||||
"PROVIDER_NAMES": {
|
"enum": [
|
||||||
"additionalProperties": { "type": "string" },
|
"aiml_api",
|
||||||
"type": "object",
|
"anthropic",
|
||||||
"title": "Provider Names",
|
"apollo",
|
||||||
"description": "All available provider names as a constant mapping"
|
"compass",
|
||||||
}
|
"discord",
|
||||||
},
|
"d_id",
|
||||||
"type": "object",
|
"e2b",
|
||||||
"title": "ProviderConstants",
|
"exa",
|
||||||
"description": "Model that exposes all provider names as a constant in the OpenAPI schema.\nThis is designed to be converted by Orval into a TypeScript constant."
|
"fal",
|
||||||
},
|
"generic_webhook",
|
||||||
"ProviderEnumResponse": {
|
"github",
|
||||||
"properties": {
|
"google",
|
||||||
"provider": {
|
"google_maps",
|
||||||
"type": "string",
|
"groq",
|
||||||
"title": "Provider",
|
"http",
|
||||||
"description": "A provider name from the complete list of providers"
|
"hubspot",
|
||||||
}
|
"ideogram",
|
||||||
},
|
"jina",
|
||||||
"type": "object",
|
"linear",
|
||||||
"required": ["provider"],
|
"llama_api",
|
||||||
"title": "ProviderEnumResponse",
|
"medium",
|
||||||
"description": "Response containing a provider from the enum."
|
"mem0",
|
||||||
},
|
"notion",
|
||||||
"ProviderNamesResponse": {
|
"nvidia",
|
||||||
"properties": {
|
"ollama",
|
||||||
"providers": {
|
"openai",
|
||||||
"items": { "type": "string" },
|
"openweathermap",
|
||||||
"type": "array",
|
"open_router",
|
||||||
"title": "Providers",
|
"pinecone",
|
||||||
"description": "List of all available provider names"
|
"reddit",
|
||||||
}
|
"replicate",
|
||||||
},
|
"revid",
|
||||||
"type": "object",
|
"screenshotone",
|
||||||
"title": "ProviderNamesResponse",
|
"slant3d",
|
||||||
"description": "Response containing list of all provider names."
|
"smartlead",
|
||||||
|
"smtp",
|
||||||
|
"twitter",
|
||||||
|
"todoist",
|
||||||
|
"unreal_speech",
|
||||||
|
"zerobounce"
|
||||||
|
],
|
||||||
|
"title": "ProviderName"
|
||||||
},
|
},
|
||||||
"RefundRequest": {
|
"RefundRequest": {
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -6486,11 +6441,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"id": { "type": "string", "title": "Id" },
|
"id": { "type": "string", "title": "Id" },
|
||||||
"user_id": { "type": "string", "title": "User Id" },
|
"user_id": { "type": "string", "title": "User Id" },
|
||||||
"provider": {
|
"provider": { "$ref": "#/components/schemas/ProviderName" },
|
||||||
"type": "string",
|
|
||||||
"title": "Provider",
|
|
||||||
"description": "Provider name for integrations. Can be any string value, including custom provider names."
|
|
||||||
},
|
|
||||||
"credentials_id": { "type": "string", "title": "Credentials Id" },
|
"credentials_id": { "type": "string", "title": "Credentials Id" },
|
||||||
"webhook_type": { "type": "string", "title": "Webhook Type" },
|
"webhook_type": { "type": "string", "title": "Webhook Type" },
|
||||||
"resource": { "type": "string", "title": "Resource" },
|
"resource": { "type": "string", "title": "Resource" },
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
import * as React from "react";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { ProfilePopoutMenu } from "./ProfilePopoutMenu";
|
|
||||||
import { IconType, IconLogIn, IconAutoGPTLogo } from "@/components/ui/icons";
|
|
||||||
import { MobileNavBar } from "./MobileNavBar";
|
|
||||||
import { Button } from "./Button";
|
|
||||||
import Wallet from "./Wallet";
|
|
||||||
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
|
|
||||||
import { NavbarLink } from "./NavbarLink";
|
|
||||||
|
|
||||||
import BackendAPI from "@/lib/autogpt-server-api";
|
|
||||||
import { getServerUser } from "@/lib/supabase/server/getServerUser";
|
|
||||||
|
|
||||||
// Disable theme toggle for now
|
|
||||||
// import { ThemeToggle } from "./ThemeToggle";
|
|
||||||
|
|
||||||
interface NavLink {
|
|
||||||
name: string;
|
|
||||||
href: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NavbarProps {
|
|
||||||
links: NavLink[];
|
|
||||||
menuItemGroups: {
|
|
||||||
groupName?: string;
|
|
||||||
items: {
|
|
||||||
icon: IconType;
|
|
||||||
text: string;
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
}[];
|
|
||||||
}[];
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getProfileData() {
|
|
||||||
const api = new BackendAPI();
|
|
||||||
const profile = await Promise.resolve(api.getStoreProfile());
|
|
||||||
|
|
||||||
return profile;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Navbar = async ({ links, menuItemGroups }: NavbarProps) => {
|
|
||||||
const { user } = await getServerUser();
|
|
||||||
const isLoggedIn = user !== null;
|
|
||||||
let profile: ProfileDetails | null = null;
|
|
||||||
if (isLoggedIn) {
|
|
||||||
profile = await getProfileData();
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<nav className="sticky top-0 z-40 mx-[16px] hidden h-16 items-center justify-between rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 py-3 pl-6 pr-3 backdrop-blur-[26px] dark:border-gray-700 dark:bg-gray-900 md:inline-flex">
|
|
||||||
<div className="flex items-center gap-11">
|
|
||||||
<div className="relative h-10 w-[88.87px]">
|
|
||||||
<IconAutoGPTLogo className="h-full w-full" />
|
|
||||||
</div>
|
|
||||||
{links.map((link) => (
|
|
||||||
<NavbarLink key={link.name} name={link.name} href={link.href} />
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
{/* Profile section */}
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
{isLoggedIn ? (
|
|
||||||
<div className="flex items-center gap-4">
|
|
||||||
{profile && <Wallet />}
|
|
||||||
<ProfilePopoutMenu
|
|
||||||
menuItemGroups={menuItemGroups}
|
|
||||||
userName={profile?.username}
|
|
||||||
userEmail={profile?.name}
|
|
||||||
avatarSrc={profile?.avatar_url}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Link href="/login">
|
|
||||||
<Button
|
|
||||||
size="sm"
|
|
||||||
className="flex items-center justify-end space-x-2"
|
|
||||||
>
|
|
||||||
<IconLogIn className="h-5 h-[48px] w-5" />
|
|
||||||
<span>Log In</span>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
{/* <ThemeToggle /> */}
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
{/* Mobile Navbar - Adjust positioning */}
|
|
||||||
<>
|
|
||||||
{isLoggedIn ? (
|
|
||||||
<div className="fixed right-4 top-4 z-50">
|
|
||||||
<MobileNavBar
|
|
||||||
userName={profile?.username}
|
|
||||||
menuItemGroups={[
|
|
||||||
{
|
|
||||||
groupName: "Navigation",
|
|
||||||
items: links.map((link) => ({
|
|
||||||
icon:
|
|
||||||
link.name === "Marketplace"
|
|
||||||
? IconType.Marketplace
|
|
||||||
: link.name === "Library"
|
|
||||||
? IconType.Library
|
|
||||||
: link.name === "Build"
|
|
||||||
? IconType.Builder
|
|
||||||
: link.name === "Monitor"
|
|
||||||
? IconType.Library
|
|
||||||
: IconType.LayoutDashboard,
|
|
||||||
text: link.name,
|
|
||||||
href: link.href,
|
|
||||||
})),
|
|
||||||
},
|
|
||||||
...menuItemGroups,
|
|
||||||
]}
|
|
||||||
userEmail={profile?.name}
|
|
||||||
avatarSrc={profile?.avatar_url}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<Link
|
|
||||||
href="/login"
|
|
||||||
className="fixed right-4 top-4 z-50 mt-4 inline-flex h-8 items-center justify-end rounded-lg pr-4 md:hidden"
|
|
||||||
>
|
|
||||||
<Button size="sm" className="flex items-center space-x-2">
|
|
||||||
<IconLogIn className="h-5 w-5" />
|
|
||||||
<span>Log In</span>
|
|
||||||
</Button>
|
|
||||||
</Link>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import Link from "next/link";
|
|
||||||
import {
|
|
||||||
IconShoppingCart,
|
|
||||||
IconBoxes,
|
|
||||||
IconLibrary,
|
|
||||||
IconLaptop,
|
|
||||||
} from "@/components/ui/icons";
|
|
||||||
import { usePathname } from "next/navigation";
|
|
||||||
|
|
||||||
interface NavbarLinkProps {
|
|
||||||
name: string;
|
|
||||||
href: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const NavbarLink = ({ name, href }: NavbarLinkProps) => {
|
|
||||||
const pathname = usePathname();
|
|
||||||
const parts = pathname.split("/");
|
|
||||||
const activeLink = "/" + (parts.length > 2 ? parts[2] : parts[1]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Link
|
|
||||||
href={href}
|
|
||||||
data-testid={`navbar-link-${name.toLowerCase()}`}
|
|
||||||
className="font-poppins text-[20px] leading-[28px]"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={`h-[48px] px-5 py-4 ${
|
|
||||||
activeLink === href
|
|
||||||
? "rounded-2xl bg-neutral-800 dark:bg-neutral-200"
|
|
||||||
: ""
|
|
||||||
} flex items-center justify-start gap-3`}
|
|
||||||
>
|
|
||||||
{href === "/marketplace" && (
|
|
||||||
<IconShoppingCart
|
|
||||||
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{href === "/build" && (
|
|
||||||
<IconBoxes
|
|
||||||
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{href === "/monitor" && (
|
|
||||||
<IconLaptop
|
|
||||||
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{href === "/library" && (
|
|
||||||
<IconLibrary
|
|
||||||
className={`h-6 w-6 ${activeLink === href ? "text-white dark:text-black" : ""}`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<div
|
|
||||||
className={`hidden font-poppins text-[20px] font-medium leading-[28px] lg:block ${
|
|
||||||
activeLink === href
|
|
||||||
? "text-neutral-50 dark:text-neutral-900"
|
|
||||||
: "text-neutral-900 dark:text-neutral-50"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -109,7 +109,7 @@ export default function Wallet() {
|
|||||||
<button
|
<button
|
||||||
ref={walletRef}
|
ref={walletRef}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex items-center gap-1 rounded-md bg-zinc-200 px-3 py-2 text-sm transition-colors duration-200 hover:bg-zinc-300",
|
"relative flex items-center gap-1 rounded-md bg-zinc-50 px-3 py-2 text-sm",
|
||||||
)}
|
)}
|
||||||
onClick={onWalletOpen}
|
onClick={onWalletOpen}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const meta: Meta<typeof Button> = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default meta;
|
export default meta;
|
||||||
type Story = StoryObj<typeof meta>;
|
type Story = StoryObj<typeof Button>;
|
||||||
|
|
||||||
// Basic variants
|
// Basic variants
|
||||||
export const Primary: Story = {
|
export const Primary: Story = {
|
||||||
|
|||||||
@@ -1,82 +1,78 @@
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { CircleNotchIcon } from "@phosphor-icons/react/dist/ssr";
|
import { CircleNotchIcon } from "@phosphor-icons/react/dist/ssr";
|
||||||
import { cva, type VariantProps } from "class-variance-authority";
|
import Link, { type LinkProps } from "next/link";
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
import { ButtonProps, extendedButtonVariants } from "./helpers";
|
||||||
|
|
||||||
// Extended button variants based on our design system
|
export function Button(props: ButtonProps) {
|
||||||
const extendedButtonVariants = cva(
|
const {
|
||||||
"inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 font-['Geist'] leading-snug border",
|
className,
|
||||||
{
|
variant,
|
||||||
variants: {
|
size,
|
||||||
variant: {
|
loading = false,
|
||||||
primary:
|
leftIcon,
|
||||||
"bg-zinc-800 border-zinc-800 text-white hover:bg-zinc-900 hover:border-zinc-900 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
|
rightIcon,
|
||||||
secondary:
|
children,
|
||||||
"bg-zinc-100 border-zinc-100 text-black hover:bg-zinc-300 hover:border-zinc-300 rounded-full disabled:text-zinc-300 disabled:bg-zinc-50 disabled:border-zinc-50 disabled:opacity-1",
|
as = "button",
|
||||||
destructive:
|
...restProps
|
||||||
"bg-red-500 border-red-500 text-white hover:bg-red-600 hover:border-red-600 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
|
} = props;
|
||||||
outline:
|
|
||||||
"bg-transparent border-zinc-700 text-black hover:bg-zinc-100 hover:border-zinc-700 rounded-full disabled:border-zinc-200 disabled:text-zinc-200 disabled:opacity-1",
|
|
||||||
ghost:
|
|
||||||
"bg-transparent border-transparent text-black hover:bg-zinc-50 hover:border-zinc-50 rounded-full disabled:text-zinc-200 disabled:opacity-1",
|
|
||||||
icon: "bg-white text-black border border-zinc-600 hover:bg-zinc-100 rounded-[96px] disabled:opacity-1",
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
small: "px-3 py-2 text-sm gap-1.5 h-[2.25rem]",
|
|
||||||
large: "min-w-20 px-4 py-3 text-sm gap-2",
|
|
||||||
icon: "p-3",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
defaultVariants: {
|
|
||||||
variant: "primary",
|
|
||||||
size: "large",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
export interface ButtonProps
|
const disabled = "disabled" in props ? props.disabled : false;
|
||||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
|
||||||
VariantProps<typeof extendedButtonVariants> {
|
|
||||||
loading?: boolean;
|
|
||||||
leftIcon?: React.ReactNode;
|
|
||||||
rightIcon?: React.ReactNode;
|
|
||||||
asChild?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Button({
|
|
||||||
className,
|
|
||||||
variant,
|
|
||||||
size,
|
|
||||||
loading = false,
|
|
||||||
leftIcon,
|
|
||||||
rightIcon,
|
|
||||||
children,
|
|
||||||
disabled,
|
|
||||||
...props
|
|
||||||
}: ButtonProps) {
|
|
||||||
const isDisabled = disabled;
|
const isDisabled = disabled;
|
||||||
|
|
||||||
|
const buttonContent = (
|
||||||
|
<>
|
||||||
|
{loading && (
|
||||||
|
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
||||||
|
)}
|
||||||
|
{!loading && leftIcon}
|
||||||
|
{children}
|
||||||
|
{!loading && rightIcon}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return variant === "ghost" ? (
|
const loadingClassName =
|
||||||
<button
|
variant === "ghost"
|
||||||
|
? cn(
|
||||||
|
extendedButtonVariants({ variant, size, className }),
|
||||||
|
"pointer-events-none",
|
||||||
|
)
|
||||||
|
: cn(
|
||||||
|
extendedButtonVariants({ variant: "primary", size, className }),
|
||||||
|
"pointer-events-none border-zinc-500 bg-zinc-500 text-white",
|
||||||
|
);
|
||||||
|
|
||||||
|
return as === "NextLink" ? (
|
||||||
|
<Link
|
||||||
|
{...(restProps as LinkProps)}
|
||||||
|
className={loadingClassName}
|
||||||
|
aria-disabled="true"
|
||||||
|
>
|
||||||
|
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
) : (
|
||||||
|
<button className={loadingClassName} disabled>
|
||||||
|
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (as === "NextLink") {
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
{...(restProps as LinkProps)}
|
||||||
className={cn(
|
className={cn(
|
||||||
extendedButtonVariants({ variant, size, className }),
|
extendedButtonVariants({ variant, size, className }),
|
||||||
"pointer-events-none",
|
loading && "pointer-events-none",
|
||||||
|
isDisabled && "pointer-events-none opacity-50",
|
||||||
)}
|
)}
|
||||||
|
aria-disabled={isDisabled}
|
||||||
>
|
>
|
||||||
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
{buttonContent}
|
||||||
{children}
|
</Link>
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
|
||||||
className={cn(
|
|
||||||
extendedButtonVariants({ variant: "primary", size, className }),
|
|
||||||
"pointer-events-none border-zinc-500 bg-zinc-500 text-white",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
|
||||||
{children}
|
|
||||||
</button>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,18 +83,9 @@ function Button({
|
|||||||
loading && "pointer-events-none",
|
loading && "pointer-events-none",
|
||||||
)}
|
)}
|
||||||
disabled={isDisabled}
|
disabled={isDisabled}
|
||||||
{...props}
|
{...(restProps as React.ButtonHTMLAttributes<HTMLButtonElement>)}
|
||||||
>
|
>
|
||||||
{loading && (
|
{buttonContent}
|
||||||
<CircleNotchIcon className="h-4 w-4 animate-spin" weight="bold" />
|
|
||||||
)}
|
|
||||||
{!loading && leftIcon}
|
|
||||||
{children}
|
|
||||||
{!loading && rightIcon}
|
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Button.displayName = "Button";
|
|
||||||
|
|
||||||
export { Button, extendedButtonVariants };
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { cva, VariantProps } from "class-variance-authority";
|
||||||
|
import { LinkProps } from "next/link";
|
||||||
|
|
||||||
|
// Extended button variants based on our design system
|
||||||
|
export const extendedButtonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-neutral-950 disabled:pointer-events-none disabled:opacity-50 font-sans leading-snug border",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
primary:
|
||||||
|
"bg-zinc-800 border-zinc-800 text-white hover:bg-zinc-900 hover:border-zinc-900 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
|
||||||
|
secondary:
|
||||||
|
"bg-zinc-100 border-zinc-100 text-black hover:bg-zinc-300 hover:border-zinc-300 rounded-full disabled:text-zinc-300 disabled:bg-zinc-50 disabled:border-zinc-50 disabled:opacity-1",
|
||||||
|
destructive:
|
||||||
|
"bg-red-500 border-red-500 text-white hover:bg-red-600 hover:border-red-600 rounded-full disabled:text-white disabled:bg-zinc-200 disabled:border-zinc-200 disabled:opacity-1",
|
||||||
|
outline:
|
||||||
|
"bg-transparent border-zinc-700 text-black hover:bg-zinc-100 hover:border-zinc-700 rounded-full disabled:border-zinc-200 disabled:text-zinc-200 disabled:opacity-1",
|
||||||
|
ghost:
|
||||||
|
"bg-transparent border-transparent text-black hover:bg-zinc-50 hover:border-zinc-50 rounded-full disabled:text-zinc-200 disabled:opacity-1",
|
||||||
|
icon: "bg-white text-black border border-zinc-600 hover:bg-zinc-100 rounded-[96px] disabled:opacity-1",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
small: "px-3 py-2 text-sm gap-1.5 h-[2.25rem]",
|
||||||
|
large: "min-w-20 px-4 py-3 text-sm gap-2",
|
||||||
|
icon: "p-3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "primary",
|
||||||
|
size: "large",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
type BaseButtonProps = {
|
||||||
|
loading?: boolean;
|
||||||
|
leftIcon?: React.ReactNode;
|
||||||
|
rightIcon?: React.ReactNode;
|
||||||
|
asChild?: boolean;
|
||||||
|
} & VariantProps<typeof extendedButtonVariants>;
|
||||||
|
|
||||||
|
type ButtonAsButton = BaseButtonProps &
|
||||||
|
React.ButtonHTMLAttributes<HTMLButtonElement> & {
|
||||||
|
as?: "button";
|
||||||
|
href?: never;
|
||||||
|
};
|
||||||
|
|
||||||
|
type ButtonAsLink = BaseButtonProps &
|
||||||
|
Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> &
|
||||||
|
LinkProps & {
|
||||||
|
as: "NextLink";
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ButtonProps = ButtonAsButton | ButtonAsLink;
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
import { IconAutoGPTLogo, IconType } from "@/components/ui/icons";
|
||||||
|
import Wallet from "../../agptui/Wallet";
|
||||||
|
import { AccountMenu } from "./components/AccountMenu/AccountMenu";
|
||||||
|
import { LoginButton } from "./components/LoginButton";
|
||||||
|
import { MobileNavBar } from "./components/MobileNavbar/MobileNavBar";
|
||||||
|
import { NavbarLink } from "./components/NavbarLink";
|
||||||
|
import { accountMenuItems, loggedInLinks, loggedOutLinks } from "./helpers";
|
||||||
|
import { getNavbarAccountData } from "./data";
|
||||||
|
|
||||||
|
export async function Navbar() {
|
||||||
|
const { profile, isLoggedIn } = await getNavbarAccountData();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<nav className="sticky top-0 z-40 hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-[#f3f4f6]/20 p-3 backdrop-blur-[26px] md:inline-flex">
|
||||||
|
{/* Left section */}
|
||||||
|
<div className="flex flex-1 items-center gap-6">
|
||||||
|
{isLoggedIn
|
||||||
|
? loggedInLinks.map((link) => (
|
||||||
|
<NavbarLink key={link.name} name={link.name} href={link.href} />
|
||||||
|
))
|
||||||
|
: loggedOutLinks.map((link) => (
|
||||||
|
<NavbarLink key={link.name} name={link.name} href={link.href} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Centered logo */}
|
||||||
|
<div className="absolute left-1/2 top-1/2 h-10 w-[88.87px] -translate-x-1/2 -translate-y-1/2">
|
||||||
|
<IconAutoGPTLogo className="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right section */}
|
||||||
|
<div className="flex flex-1 items-center justify-end gap-4">
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<div className="flex items-center gap-4">
|
||||||
|
{profile && <Wallet />}
|
||||||
|
<AccountMenu
|
||||||
|
userName={profile?.username}
|
||||||
|
userEmail={profile?.name}
|
||||||
|
avatarSrc={profile?.avatar_url ?? ""}
|
||||||
|
menuItemGroups={accountMenuItems}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<LoginButton />
|
||||||
|
)}
|
||||||
|
{/* <ThemeToggle /> */}
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
{/* Mobile Navbar - Adjust positioning */}
|
||||||
|
<>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<div className="fixed right-4 top-4 z-50">
|
||||||
|
<MobileNavBar
|
||||||
|
userName={profile?.username}
|
||||||
|
menuItemGroups={[
|
||||||
|
{
|
||||||
|
groupName: "Navigation",
|
||||||
|
items: loggedInLinks.map((link) => ({
|
||||||
|
icon:
|
||||||
|
link.name === "Marketplace"
|
||||||
|
? IconType.Marketplace
|
||||||
|
: link.name === "Library"
|
||||||
|
? IconType.Library
|
||||||
|
: link.name === "Build"
|
||||||
|
? IconType.Builder
|
||||||
|
: link.name === "Monitor"
|
||||||
|
? IconType.Library
|
||||||
|
: IconType.LayoutDashboard,
|
||||||
|
text: link.name,
|
||||||
|
href: link.href,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
...accountMenuItems,
|
||||||
|
]}
|
||||||
|
userEmail={profile?.name}
|
||||||
|
avatarSrc={profile?.avatar_url ?? ""}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,74 +1,31 @@
|
|||||||
import * as React from "react";
|
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverTrigger,
|
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import {
|
|
||||||
IconType,
|
|
||||||
IconEdit,
|
|
||||||
IconLayoutDashboard,
|
|
||||||
IconUploadCloud,
|
|
||||||
IconSettings,
|
|
||||||
IconLogOut,
|
|
||||||
IconRefresh,
|
|
||||||
IconMarketplace,
|
|
||||||
IconLibrary,
|
|
||||||
IconBuilder,
|
|
||||||
} from "../ui/icons";
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { ProfilePopoutMenuLogoutButton } from "./ProfilePopoutMenuLogoutButton";
|
import * as React from "react";
|
||||||
import { PublishAgentPopout } from "./composite/PublishAgentPopout";
|
import { PublishAgentPopout } from "../../../../agptui/composite/PublishAgentPopout";
|
||||||
|
import { getAccountMenuOptionIcon, MenuItemGroup } from "../../helpers";
|
||||||
|
import { AccountLogoutOption } from "./components/AccountLogoutOption";
|
||||||
|
|
||||||
interface ProfilePopoutMenuProps {
|
interface Props {
|
||||||
userName?: string;
|
userName?: string;
|
||||||
userEmail?: string;
|
userEmail?: string;
|
||||||
avatarSrc?: string;
|
avatarSrc?: string;
|
||||||
hideNavBarUsername?: boolean;
|
hideNavBarUsername?: boolean;
|
||||||
menuItemGroups: {
|
menuItemGroups: MenuItemGroup[];
|
||||||
groupName?: string;
|
|
||||||
items: {
|
|
||||||
icon: IconType;
|
|
||||||
text: string;
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
}[];
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ProfilePopoutMenu({
|
export function AccountMenu({
|
||||||
userName,
|
userName,
|
||||||
userEmail,
|
userEmail,
|
||||||
avatarSrc,
|
avatarSrc,
|
||||||
menuItemGroups,
|
menuItemGroups,
|
||||||
}: ProfilePopoutMenuProps) {
|
}: Props) {
|
||||||
const popupId = React.useId();
|
const popupId = React.useId();
|
||||||
|
|
||||||
const getIcon = (icon: IconType) => {
|
|
||||||
const iconClass = "w-6 h-6";
|
|
||||||
switch (icon) {
|
|
||||||
case IconType.LayoutDashboard:
|
|
||||||
return <IconLayoutDashboard className={iconClass} />;
|
|
||||||
case IconType.UploadCloud:
|
|
||||||
return <IconUploadCloud className={iconClass} />;
|
|
||||||
case IconType.Edit:
|
|
||||||
return <IconEdit className={iconClass} />;
|
|
||||||
case IconType.Settings:
|
|
||||||
return <IconSettings className={iconClass} />;
|
|
||||||
case IconType.LogOut:
|
|
||||||
return <IconLogOut className={iconClass} />;
|
|
||||||
case IconType.Marketplace:
|
|
||||||
return <IconMarketplace className={iconClass} />;
|
|
||||||
case IconType.Library:
|
|
||||||
return <IconLibrary className={iconClass} />;
|
|
||||||
case IconType.Builder:
|
|
||||||
return <IconBuilder className={iconClass} />;
|
|
||||||
default:
|
|
||||||
return <IconRefresh className={iconClass} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popover>
|
<Popover>
|
||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
@@ -127,7 +84,7 @@ export function ProfilePopoutMenu({
|
|||||||
className="inline-flex w-full items-center justify-start gap-2.5"
|
className="inline-flex w-full items-center justify-start gap-2.5"
|
||||||
>
|
>
|
||||||
<div className="relative h-6 w-6">
|
<div className="relative h-6 w-6">
|
||||||
{getIcon(item.icon)}
|
{getAccountMenuOptionIcon(item.icon)}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
||||||
{item.text}
|
{item.text}
|
||||||
@@ -135,7 +92,7 @@ export function ProfilePopoutMenu({
|
|||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
} else if (item.text === "Log out") {
|
} else if (item.text === "Log out") {
|
||||||
return <ProfilePopoutMenuLogoutButton key={itemIndex} />;
|
return <AccountLogoutOption key={itemIndex} />;
|
||||||
} else if (item.text === "Publish an agent") {
|
} else if (item.text === "Publish an agent") {
|
||||||
return (
|
return (
|
||||||
<PublishAgentPopout
|
<PublishAgentPopout
|
||||||
@@ -143,7 +100,7 @@ export function ProfilePopoutMenu({
|
|||||||
trigger={
|
trigger={
|
||||||
<div className="inline-flex w-full items-center justify-start gap-2.5">
|
<div className="inline-flex w-full items-center justify-start gap-2.5">
|
||||||
<div className="relative h-6 w-6">
|
<div className="relative h-6 w-6">
|
||||||
{getIcon(item.icon)}
|
{getAccountMenuOptionIcon(item.icon)}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
||||||
{item.text}
|
{item.text}
|
||||||
@@ -165,7 +122,7 @@ export function ProfilePopoutMenu({
|
|||||||
tabIndex={0}
|
tabIndex={0}
|
||||||
>
|
>
|
||||||
<div className="relative h-6 w-6">
|
<div className="relative h-6 w-6">
|
||||||
{getIcon(item.icon)}
|
{getAccountMenuOptionIcon(item.icon)}
|
||||||
</div>
|
</div>
|
||||||
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
<div className="font-sans text-base font-medium leading-normal text-neutral-800 dark:text-neutral-200">
|
||||||
{item.text}
|
{item.text}
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { IconLogOut } from "@/components/ui/icons";
|
import { IconLogOut } from "@/components/ui/icons";
|
||||||
|
import { LoadingSpinner } from "@/components/ui/loading";
|
||||||
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import * as Sentry from "@sentry/nextjs";
|
import * as Sentry from "@sentry/nextjs";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useTransition } from "react";
|
import { useTransition } from "react";
|
||||||
import { LoadingSpinner } from "../ui/loading";
|
import { toast } from "@/components/molecules/Toast/use-toast";
|
||||||
import { toast } from "../molecules/Toast/use-toast";
|
|
||||||
|
|
||||||
export function ProfilePopoutMenuLogoutButton() {
|
export function AccountLogoutOption() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const supabase = useSupabase();
|
const supabase = useSupabase();
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Button } from "@/components/atoms/Button/Button";
|
||||||
|
import { SignInIcon } from "@phosphor-icons/react/dist/ssr";
|
||||||
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
export function LoginButton() {
|
||||||
|
const router = useRouter();
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isLoginPage = pathname.includes("/login");
|
||||||
|
|
||||||
|
if (isLoginPage) return null;
|
||||||
|
|
||||||
|
function handleLogin() {
|
||||||
|
router.push("/login");
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
onClick={handleLogin}
|
||||||
|
size="small"
|
||||||
|
className="flex items-center justify-end space-x-2"
|
||||||
|
leftIcon={<SignInIcon className="h-5 w-5" />}
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Log In
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,45 +1,26 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
import Link from "next/link";
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Popover,
|
Popover,
|
||||||
PopoverTrigger,
|
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverPortal,
|
PopoverPortal,
|
||||||
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import {
|
|
||||||
IconType,
|
|
||||||
IconMenu,
|
|
||||||
IconChevronUp,
|
|
||||||
IconEdit,
|
|
||||||
IconLayoutDashboard,
|
|
||||||
IconUploadCloud,
|
|
||||||
IconSettings,
|
|
||||||
IconLogOut,
|
|
||||||
IconMarketplace,
|
|
||||||
IconLibrary,
|
|
||||||
IconBuilder,
|
|
||||||
} from "../ui/icons";
|
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
|
import * as React from "react";
|
||||||
|
import { IconChevronUp, IconMenu } from "../../../../ui/icons";
|
||||||
|
import { MenuItemGroup } from "../../helpers";
|
||||||
|
import { MobileNavbarMenuItem } from "./components/MobileNavbarMenuItem";
|
||||||
|
|
||||||
interface MobileNavBarProps {
|
interface MobileNavBarProps {
|
||||||
userName?: string;
|
userName?: string;
|
||||||
userEmail?: string;
|
userEmail?: string;
|
||||||
avatarSrc?: string;
|
avatarSrc?: string;
|
||||||
menuItemGroups: {
|
menuItemGroups: MenuItemGroup[];
|
||||||
groupName?: string;
|
|
||||||
items: {
|
|
||||||
icon: IconType;
|
|
||||||
text: string;
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
}[];
|
|
||||||
}[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const Overlay = React.forwardRef<HTMLDivElement, { children: React.ReactNode }>(
|
const Overlay = React.forwardRef<HTMLDivElement, { children: React.ReactNode }>(
|
||||||
@@ -49,76 +30,15 @@ const Overlay = React.forwardRef<HTMLDivElement, { children: React.ReactNode }>(
|
|||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Overlay.displayName = "Overlay";
|
Overlay.displayName = "Overlay";
|
||||||
|
|
||||||
const PopoutMenuItem: React.FC<{
|
export function MobileNavBar({
|
||||||
icon: IconType;
|
|
||||||
isActive: boolean;
|
|
||||||
text: React.ReactNode;
|
|
||||||
href?: string;
|
|
||||||
onClick?: () => void;
|
|
||||||
}> = ({ icon, isActive, text, href, onClick }) => {
|
|
||||||
const getIcon = (iconType: IconType) => {
|
|
||||||
const iconClass = "w-6 h-6 relative";
|
|
||||||
switch (iconType) {
|
|
||||||
case IconType.Marketplace:
|
|
||||||
return <IconMarketplace className={iconClass} />;
|
|
||||||
case IconType.Library:
|
|
||||||
return <IconLibrary className={iconClass} />;
|
|
||||||
case IconType.Builder:
|
|
||||||
return <IconBuilder className={iconClass} />;
|
|
||||||
case IconType.Edit:
|
|
||||||
return <IconEdit className={iconClass} />;
|
|
||||||
case IconType.LayoutDashboard:
|
|
||||||
return <IconLayoutDashboard className={iconClass} />;
|
|
||||||
case IconType.UploadCloud:
|
|
||||||
return <IconUploadCloud className={iconClass} />;
|
|
||||||
case IconType.Settings:
|
|
||||||
return <IconSettings className={iconClass} />;
|
|
||||||
case IconType.LogOut:
|
|
||||||
return <IconLogOut className={iconClass} />;
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const content = (
|
|
||||||
<div className="inline-flex w-full items-center justify-start gap-4 hover:rounded hover:bg-[#e0e0e0] dark:hover:bg-[#3a3a3a]">
|
|
||||||
{getIcon(icon)}
|
|
||||||
<div className="relative">
|
|
||||||
<div
|
|
||||||
className={`font-sans text-base font-normal leading-7 text-[#474747] dark:text-[#cfcfcf] ${isActive ? "font-semibold text-[#272727] dark:text-[#ffffff]" : "text-[#474747] dark:text-[#cfcfcf]"}`}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</div>
|
|
||||||
{isActive && (
|
|
||||||
<div className="absolute bottom-[-4px] left-0 h-[2px] w-full bg-[#272727] dark:bg-[#ffffff]"></div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
if (onClick)
|
|
||||||
return (
|
|
||||||
<div className="w-full" onClick={onClick}>
|
|
||||||
{content}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
if (href)
|
|
||||||
return (
|
|
||||||
<Link href={href} className="w-full">
|
|
||||||
{content}
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
return content;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MobileNavBar: React.FC<MobileNavBarProps> = ({
|
|
||||||
userName,
|
userName,
|
||||||
userEmail,
|
userEmail,
|
||||||
avatarSrc,
|
avatarSrc,
|
||||||
menuItemGroups,
|
menuItemGroups,
|
||||||
}) => {
|
}: MobileNavBarProps) {
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const parts = pathname.split("/");
|
const parts = pathname.split("/");
|
||||||
@@ -173,7 +93,7 @@ export const MobileNavBar: React.FC<MobileNavBarProps> = ({
|
|||||||
{menuItemGroups.map((group, groupIndex) => (
|
{menuItemGroups.map((group, groupIndex) => (
|
||||||
<React.Fragment key={groupIndex}>
|
<React.Fragment key={groupIndex}>
|
||||||
{group.items.map((item, itemIndex) => (
|
{group.items.map((item, itemIndex) => (
|
||||||
<PopoutMenuItem
|
<MobileNavbarMenuItem
|
||||||
key={itemIndex}
|
key={itemIndex}
|
||||||
icon={item.icon}
|
icon={item.icon}
|
||||||
isActive={item.href === activeLink}
|
isActive={item.href === activeLink}
|
||||||
@@ -194,4 +114,4 @@ export const MobileNavBar: React.FC<MobileNavBarProps> = ({
|
|||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { IconType } from "@/components/ui/icons";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { getAccountMenuOptionIcon } from "../../../helpers";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
icon: IconType;
|
||||||
|
isActive: boolean;
|
||||||
|
text: string;
|
||||||
|
href?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MobileNavbarMenuItem({
|
||||||
|
icon,
|
||||||
|
isActive,
|
||||||
|
text,
|
||||||
|
href,
|
||||||
|
onClick,
|
||||||
|
}: Props) {
|
||||||
|
const content = (
|
||||||
|
<div className="inline-flex w-full items-center justify-start gap-4 hover:rounded hover:bg-[#e0e0e0] dark:hover:bg-[#3a3a3a]">
|
||||||
|
{getAccountMenuOptionIcon(icon)}
|
||||||
|
<div className="relative">
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"font-sans text-base font-normal leading-7",
|
||||||
|
isActive
|
||||||
|
? "font-semibold text-[#272727] dark:text-[#ffffff]"
|
||||||
|
: "text-[#474747] dark:text-[#cfcfcf]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
{isActive && (
|
||||||
|
<div className="absolute bottom-[-4px] left-0 h-[2px] w-full bg-[#272727] dark:bg-[#ffffff]"></div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (onClick)
|
||||||
|
return (
|
||||||
|
<div className="w-full" onClick={onClick}>
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
if (href)
|
||||||
|
return (
|
||||||
|
<Link href={href} className="w-full">
|
||||||
|
{content}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { IconLaptop } from "@/components/ui/icons";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import {
|
||||||
|
CubeIcon,
|
||||||
|
HouseIcon,
|
||||||
|
StorefrontIcon,
|
||||||
|
} from "@phosphor-icons/react/dist/ssr";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { usePathname } from "next/navigation";
|
||||||
|
import { Text } from "../../../atoms/Text/Text";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
name: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function NavbarLink({ name, href }: Props) {
|
||||||
|
const pathname = usePathname();
|
||||||
|
const isActive = pathname.includes(href);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
href={href}
|
||||||
|
data-testid={`navbar-link-${name.toLowerCase()}`}
|
||||||
|
className="font-poppins text-[20px] leading-[28px]"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"flex items-center justify-start gap-1 p-2",
|
||||||
|
isActive &&
|
||||||
|
"rounded-small bg-neutral-800 p-2 transition-all duration-300 dark:bg-neutral-200",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{href === "/marketplace" && (
|
||||||
|
<StorefrontIcon
|
||||||
|
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{href === "/build" && (
|
||||||
|
<CubeIcon
|
||||||
|
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{href === "/monitor" && (
|
||||||
|
<IconLaptop
|
||||||
|
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{href === "/library" && (
|
||||||
|
<HouseIcon
|
||||||
|
className={cn("h-6 w-6", isActive && "text-white dark:text-black")}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Text
|
||||||
|
variant="body-medium"
|
||||||
|
className={cn(
|
||||||
|
"hidden lg:block",
|
||||||
|
isActive ? "!text-white" : "!text-black",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { IconAutoGPTLogo } from "@/components/ui/icons";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
|
||||||
|
export function NavbarLoading() {
|
||||||
|
return (
|
||||||
|
<nav className="sticky top-0 z-40 hidden h-16 items-center rounded-bl-2xl rounded-br-2xl border border-white/50 bg-white/5 p-3 backdrop-blur-[26px] md:inline-flex">
|
||||||
|
<div className="flex flex-1 items-center gap-6">
|
||||||
|
<Skeleton className="h-4 w-20 bg-white/20" />
|
||||||
|
<Skeleton className="h-4 w-16 bg-white/20" />
|
||||||
|
<Skeleton className="h-4 w-12 bg-white/20" />
|
||||||
|
</div>
|
||||||
|
<div className="absolute left-1/2 top-1/2 h-10 w-[88.87px] -translate-x-1/2 -translate-y-1/2">
|
||||||
|
<IconAutoGPTLogo className="h-full w-full" />
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 items-center justify-end gap-4">
|
||||||
|
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
|
||||||
|
<Skeleton className="h-8 w-8 rounded-full bg-white/20" />
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { getV2GetUserProfile } from "@/app/api/__generated__/endpoints/store/store";
|
||||||
|
import { getServerUser } from "@/lib/supabase/server/getServerUser";
|
||||||
|
|
||||||
|
export async function getNavbarAccountData() {
|
||||||
|
const { user } = await getServerUser();
|
||||||
|
const isLoggedIn = Boolean(user);
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return {
|
||||||
|
profile: null,
|
||||||
|
isLoggedIn,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let profile = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const profileResponse = await getV2GetUserProfile();
|
||||||
|
profile = profileResponse.data || null;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching profile:", error);
|
||||||
|
profile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
profile,
|
||||||
|
isLoggedIn,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import {
|
||||||
|
IconBuilder,
|
||||||
|
IconEdit,
|
||||||
|
IconLayoutDashboard,
|
||||||
|
IconLibrary,
|
||||||
|
IconLogOut,
|
||||||
|
IconMarketplace,
|
||||||
|
IconRefresh,
|
||||||
|
IconSettings,
|
||||||
|
IconType,
|
||||||
|
IconUploadCloud,
|
||||||
|
} from "@/components/ui/icons";
|
||||||
|
|
||||||
|
type Link = {
|
||||||
|
name: string;
|
||||||
|
href: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const loggedInLinks: Link[] = [
|
||||||
|
{
|
||||||
|
name: "Marketplace",
|
||||||
|
href: "/marketplace",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Library",
|
||||||
|
href: "/library",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Build",
|
||||||
|
href: "/build",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const loggedOutLinks: Link[] = [
|
||||||
|
{
|
||||||
|
name: "Marketplace",
|
||||||
|
href: "/marketplace",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export type MenuItemGroup = {
|
||||||
|
groupName?: string;
|
||||||
|
items: {
|
||||||
|
icon: IconType;
|
||||||
|
text: string;
|
||||||
|
href?: string;
|
||||||
|
onClick?: () => void;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const accountMenuItems: MenuItemGroup[] = [
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: IconType.Edit,
|
||||||
|
text: "Edit profile",
|
||||||
|
href: "/profile",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: IconType.LayoutDashboard,
|
||||||
|
text: "Creator Dashboard",
|
||||||
|
href: "/profile/dashboard",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: IconType.UploadCloud,
|
||||||
|
text: "Publish an agent",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: IconType.Settings,
|
||||||
|
text: "Settings",
|
||||||
|
href: "/profile/settings",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
icon: IconType.LogOut,
|
||||||
|
text: "Log out",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getAccountMenuOptionIcon(icon: IconType) {
|
||||||
|
const iconClass = "w-6 h-6";
|
||||||
|
switch (icon) {
|
||||||
|
case IconType.LayoutDashboard:
|
||||||
|
return <IconLayoutDashboard className={iconClass} />;
|
||||||
|
case IconType.UploadCloud:
|
||||||
|
return <IconUploadCloud className={iconClass} />;
|
||||||
|
case IconType.Edit:
|
||||||
|
return <IconEdit className={iconClass} />;
|
||||||
|
case IconType.Settings:
|
||||||
|
return <IconSettings className={iconClass} />;
|
||||||
|
case IconType.LogOut:
|
||||||
|
return <IconLogOut className={iconClass} />;
|
||||||
|
case IconType.Marketplace:
|
||||||
|
return <IconMarketplace className={iconClass} />;
|
||||||
|
case IconType.Library:
|
||||||
|
return <IconLibrary className={iconClass} />;
|
||||||
|
case IconType.Builder:
|
||||||
|
return <IconBuilder className={iconClass} />;
|
||||||
|
default:
|
||||||
|
return <IconRefresh className={iconClass} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -71,6 +71,30 @@ export const colors = {
|
|||||||
800: "#0c5a29",
|
800: "#0c5a29",
|
||||||
900: "#09441f",
|
900: "#09441f",
|
||||||
},
|
},
|
||||||
|
purple: {
|
||||||
|
50: "#f1ebfe",
|
||||||
|
100: "#d5c0fc",
|
||||||
|
200: "#c0a1fa",
|
||||||
|
300: "#a476f8",
|
||||||
|
400: "#925cf7",
|
||||||
|
500: "#7733f5",
|
||||||
|
600: "#6c2edf",
|
||||||
|
700: "#5424ae",
|
||||||
|
800: "#411c87",
|
||||||
|
900: "#321567",
|
||||||
|
},
|
||||||
|
pink: {
|
||||||
|
50: "#fdedf5",
|
||||||
|
100: "#f9c6df",
|
||||||
|
200: "#f6abd0",
|
||||||
|
300: "#f284bb",
|
||||||
|
400: "#f06dad",
|
||||||
|
500: "#ec4899",
|
||||||
|
600: "#d7428b",
|
||||||
|
700: "#a8336d",
|
||||||
|
800: "#822854",
|
||||||
|
900: "#631e40",
|
||||||
|
},
|
||||||
|
|
||||||
// Special semantic colors
|
// Special semantic colors
|
||||||
white: "#fefefe",
|
white: "#fefefe",
|
||||||
|
|||||||
@@ -14,54 +14,54 @@ const meta: Meta = {
|
|||||||
export default meta;
|
export default meta;
|
||||||
|
|
||||||
// Border radius scale data based on Figma design tokens
|
// Border radius scale data based on Figma design tokens
|
||||||
// Custom naming convention: xs, s, m, l, xl, 2xl, full
|
// Custom naming convention: xsmall, small, medium, large, xlarge, 2xlarge, full
|
||||||
const borderRadiusScale = [
|
const borderRadiusScale = [
|
||||||
{
|
{
|
||||||
name: "xs",
|
name: "xsmall",
|
||||||
value: "0.25rem",
|
value: "0.25rem",
|
||||||
rem: "0.25rem",
|
rem: "0.25rem",
|
||||||
px: "4px",
|
px: "4px",
|
||||||
class: "rounded-xs",
|
class: "rounded-xsmall",
|
||||||
description: "Extra small - for subtle rounding",
|
description: "Extra small - for subtle rounding",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "s",
|
name: "small",
|
||||||
value: "0.5rem",
|
value: "0.5rem",
|
||||||
rem: "0.5rem",
|
rem: "0.5rem",
|
||||||
px: "8px",
|
px: "8px",
|
||||||
class: "rounded-s",
|
class: "rounded-small",
|
||||||
description: "Small - for cards and containers",
|
description: "Small - for cards and containers",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "m",
|
name: "medium",
|
||||||
value: "0.75rem",
|
value: "0.75rem",
|
||||||
rem: "0.75rem",
|
rem: "0.75rem",
|
||||||
px: "12px",
|
px: "12px",
|
||||||
class: "rounded-m",
|
class: "rounded-medium",
|
||||||
description: "Medium - for buttons and inputs",
|
description: "Medium - for buttons and inputs",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "l",
|
name: "large",
|
||||||
value: "1rem",
|
value: "1rem",
|
||||||
rem: "1rem",
|
rem: "1rem",
|
||||||
px: "16px",
|
px: "16px",
|
||||||
class: "rounded-l",
|
class: "rounded-large",
|
||||||
description: "Large - for panels and modals",
|
description: "Large - for panels and modals",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "xl",
|
name: "xlarge",
|
||||||
value: "1.25rem",
|
value: "1.25rem",
|
||||||
rem: "1.25rem",
|
rem: "1.25rem",
|
||||||
px: "20px",
|
px: "20px",
|
||||||
class: "rounded-xl",
|
class: "rounded-xlarge",
|
||||||
description: "Extra large - for hero sections",
|
description: "Extra large - for hero sections",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "2xl",
|
name: "2xlarge",
|
||||||
value: "1.5rem",
|
value: "1.5rem",
|
||||||
rem: "1.5rem",
|
rem: "1.5rem",
|
||||||
px: "24px",
|
px: "24px",
|
||||||
class: "rounded-2xl",
|
class: "rounded-2xlarge",
|
||||||
description: "2X large - for major containers",
|
description: "2X large - for major containers",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -84,10 +84,11 @@ export function AllVariants() {
|
|||||||
Border Radius
|
Border Radius
|
||||||
</Text>
|
</Text>
|
||||||
<Text variant="large" className="text-zinc-600">
|
<Text variant="large" className="text-zinc-600">
|
||||||
Our border radius system uses a simplified naming convention (xs, s,
|
Our border radius system uses a descriptive naming convention
|
||||||
m, l, xl, 2xl, full) based on our Figma design tokens. This creates
|
(xsmall, small, medium, large, xlarge, 2xlarge, full) based on our
|
||||||
visual hierarchy and maintains design consistency across all
|
Figma design tokens. This creates visual hierarchy and maintains
|
||||||
components.
|
design consistency across all components while avoiding conflicts
|
||||||
|
with Tailwind's built-in classes.
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -137,9 +138,10 @@ export function AllVariants() {
|
|||||||
</div>
|
</div>
|
||||||
<Text variant="body" className="mb-4 text-zinc-600">
|
<Text variant="body" className="mb-4 text-zinc-600">
|
||||||
We use a custom border radius system based on our Figma design
|
We use a custom border radius system based on our Figma design
|
||||||
tokens, with simplified naming (xs, s, m, l, xl, 2xl, full) that
|
tokens, with descriptive naming (xsmall, small, medium, large,
|
||||||
provides consistent radius values optimized for our design
|
xlarge, 2xlarge, full) that provides consistent radius values
|
||||||
system.
|
optimized for our design system while avoiding conflicts with
|
||||||
|
Tailwind's built-in classes.
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -188,7 +190,8 @@ export function AllVariants() {
|
|||||||
<Text variant="body" className="mb-6 text-zinc-600">
|
<Text variant="body" className="mb-6 text-zinc-600">
|
||||||
All border radius values from our Figma design tokens. Each value
|
All border radius values from our Figma design tokens. Each value
|
||||||
can be applied to all corners or specific corners/sides using our
|
can be applied to all corners or specific corners/sides using our
|
||||||
simplified naming convention.
|
descriptive naming convention (xsmall, small, medium, large, xlarge,
|
||||||
|
2xlarge, full).
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -232,30 +235,30 @@ export function AllVariants() {
|
|||||||
|
|
||||||
<StoryCode
|
<StoryCode
|
||||||
code={`// Border radius examples - Design System Tokens
|
code={`// Border radius examples - Design System Tokens
|
||||||
<div className="rounded-xs">Extra small rounding (4px)</div>
|
<div className="rounded-xsmall">Extra small rounding (4px)</div>
|
||||||
<div className="rounded-s">Small rounding (8px)</div>
|
<div className="rounded-small">Small rounding (8px)</div>
|
||||||
<div className="rounded-m">Medium rounding (12px)</div>
|
<div className="rounded-medium">Medium rounding (12px)</div>
|
||||||
<div className="rounded-l">Large rounding (16px)</div>
|
<div className="rounded-large">Large rounding (16px)</div>
|
||||||
<div className="rounded-xl">Extra large rounding (20px)</div>
|
<div className="rounded-xlarge">Extra large rounding (20px)</div>
|
||||||
<div className="rounded-2xl">2X large rounding (24px)</div>
|
<div className="rounded-2xlarge">2X large rounding (24px)</div>
|
||||||
<div className="rounded-full">Pill buttons (circular)</div>
|
<div className="rounded-full">Pill buttons (circular)</div>
|
||||||
|
|
||||||
// Directional rounding (works with all sizes)
|
// Directional rounding (works with all sizes)
|
||||||
<div className="rounded-t-m">Top corners only</div>
|
<div className="rounded-t-medium">Top corners only</div>
|
||||||
<div className="rounded-r-m">Right corners only</div>
|
<div className="rounded-r-medium">Right corners only</div>
|
||||||
<div className="rounded-b-m">Bottom corners only</div>
|
<div className="rounded-b-medium">Bottom corners only</div>
|
||||||
<div className="rounded-l-m">Left corners only</div>
|
<div className="rounded-l-medium">Left corners only</div>
|
||||||
|
|
||||||
// Individual corners
|
// Individual corners
|
||||||
<div className="rounded-tl-m">Top-left corner</div>
|
<div className="rounded-tl-medium">Top-left corner</div>
|
||||||
<div className="rounded-tr-m">Top-right corner</div>
|
<div className="rounded-tr-medium">Top-right corner</div>
|
||||||
<div className="rounded-bl-m">Bottom-left corner</div>
|
<div className="rounded-bl-medium">Bottom-left corner</div>
|
||||||
<div className="rounded-br-m">Bottom-right corner</div>
|
<div className="rounded-br-medium">Bottom-right corner</div>
|
||||||
|
|
||||||
// Usage recommendations
|
// Usage recommendations
|
||||||
<button className="rounded-full">Pill Button</button>
|
<button className="rounded-full">Pill Button</button>
|
||||||
<div className="rounded-m">Card Container</div>
|
<div className="rounded-medium">Card Container</div>
|
||||||
<input className="rounded-s">Input Field</input>`}
|
<input className="rounded-small">Input Field</input>`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ const colorCategories = Object.entries(colors)
|
|||||||
orange: "Warnings, notifications, and secondary call-to-actions",
|
orange: "Warnings, notifications, and secondary call-to-actions",
|
||||||
yellow: "Highlights, cautions, and attention-grabbing elements",
|
yellow: "Highlights, cautions, and attention-grabbing elements",
|
||||||
green: "Success states, confirmations, and positive actions",
|
green: "Success states, confirmations, and positive actions",
|
||||||
|
purple: "Brand accents, premium features, and creative elements",
|
||||||
|
pink: "Highlights, special promotions, and playful interactions",
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -312,6 +314,8 @@ export function AllVariants() {
|
|||||||
<div className="bg-green-50 border-green-200 text-green-800">Success</div>
|
<div className="bg-green-50 border-green-200 text-green-800">Success</div>
|
||||||
<div className="bg-red-50 border-red-200 text-red-800">Error</div>
|
<div className="bg-red-50 border-red-200 text-red-800">Error</div>
|
||||||
<div className="bg-yellow-50 border-yellow-200 text-yellow-800">Warning</div>
|
<div className="bg-yellow-50 border-yellow-200 text-yellow-800">Warning</div>
|
||||||
|
<div className="bg-purple-50 border-purple-200 text-purple-800">Premium</div>
|
||||||
|
<div className="bg-pink-50 border-pink-200 text-pink-800">Special</div>
|
||||||
|
|
||||||
// ❌ INCORRECT - Don't use these
|
// ❌ INCORRECT - Don't use these
|
||||||
<div className="bg-blue-500 text-purple-600">❌ Not approved</div>
|
<div className="bg-blue-500 text-purple-600">❌ Not approved</div>
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ const ScrollArea = React.forwardRef<
|
|||||||
>(({ className, children, ...props }, ref) => (
|
>(({ className, children, ...props }, ref) => (
|
||||||
<ScrollAreaPrimitive.Root
|
<ScrollAreaPrimitive.Root
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn("relative overflow-hidden", className)}
|
className={cn("relative", className)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<ScrollAreaPrimitive.Viewport
|
<ScrollAreaPrimitive.Viewport
|
||||||
className="h-full w-full rounded-[inherit]"
|
className="h-full w-full rounded-[inherit]"
|
||||||
style={{ overflow: "scroll" }}
|
style={{ overflowX: "hidden", overflowY: "scroll" }}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ScrollAreaPrimitive.Viewport>
|
</ScrollAreaPrimitive.Viewport>
|
||||||
|
|||||||
@@ -166,6 +166,8 @@ export async function serverLogout(options: ServerLogoutOptions = {}) {
|
|||||||
scope: options.globalLogout ? "global" : "local",
|
scope: options.globalLogout ? "global" : "local",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
revalidatePath("/");
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Error logging out:", error);
|
console.error("Error logging out:", error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ export function useSupabase() {
|
|||||||
const [isUserLoading, setIsUserLoading] = useState(true);
|
const [isUserLoading, setIsUserLoading] = useState(true);
|
||||||
const lastValidationRef = useRef<number>(0);
|
const lastValidationRef = useRef<number>(0);
|
||||||
const isValidatingRef = useRef(false);
|
const isValidatingRef = useRef(false);
|
||||||
|
const isLoggedIn = Boolean(user);
|
||||||
|
|
||||||
const supabase = useMemo(() => {
|
const supabase = useMemo(() => {
|
||||||
try {
|
try {
|
||||||
@@ -50,7 +51,9 @@ export function useSupabase() {
|
|||||||
await serverLogout(options);
|
await serverLogout(options);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error logging out:", error);
|
console.error("Error logging out:", error);
|
||||||
router.push("/login");
|
} finally {
|
||||||
|
setUser(null);
|
||||||
|
router.refresh();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,9 +165,9 @@ export function useSupabase() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
supabase, // Available for non-auth operations like real-time subscriptions
|
|
||||||
user,
|
user,
|
||||||
isLoggedIn: !isUserLoading ? !!user : null,
|
supabase, // Available for non-auth operations like real-time subscriptions
|
||||||
|
isLoggedIn,
|
||||||
isUserLoading,
|
isUserLoading,
|
||||||
logOut,
|
logOut,
|
||||||
validateSession: validateSessionServer,
|
validateSession: validateSessionServer,
|
||||||
|
|||||||
@@ -73,56 +73,56 @@ const config = {
|
|||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
// Tailwind spacing + custom sizes
|
// Tailwind spacing + custom sizes
|
||||||
0: "0rem",
|
0: "0rem", // 0px
|
||||||
0.5: "0.125rem",
|
0.5: "0.125rem", // 2px
|
||||||
1: "0.25rem",
|
1: "0.25rem", // 4px
|
||||||
1.5: "0.375rem",
|
1.5: "0.375rem", // 6px
|
||||||
2: "0.5rem",
|
2: "0.5rem", // 8px
|
||||||
2.5: "0.625rem",
|
2.5: "0.625rem", // 10px
|
||||||
3: "0.75rem",
|
3: "0.75rem", // 12px
|
||||||
3.5: "0.875rem",
|
3.5: "0.875rem", // 14px
|
||||||
4: "1rem",
|
4: "1rem", // 16px
|
||||||
5: "1.25rem",
|
5: "1.25rem", // 20px
|
||||||
6: "1.5rem",
|
6: "1.5rem", // 24px
|
||||||
7: "1.75rem",
|
7: "1.75rem", // 28px
|
||||||
7.5: "1.875rem",
|
7.5: "1.875rem", // 30px
|
||||||
8: "2rem",
|
8: "2rem", // 32px
|
||||||
8.5: "2.125rem",
|
8.5: "2.125rem", // 34px
|
||||||
9: "2.25rem",
|
9: "2.25rem", // 36px
|
||||||
10: "2.5rem",
|
10: "2.5rem", // 40px
|
||||||
11: "2.75rem",
|
11: "2.75rem", // 44px
|
||||||
12: "3rem",
|
12: "3rem", // 48px
|
||||||
14: "3.5rem",
|
14: "3.5rem", // 56px
|
||||||
16: "4rem",
|
16: "4rem", // 64px
|
||||||
18: "4.5rem",
|
18: "4.5rem", // 72px
|
||||||
20: "5rem",
|
20: "5rem", // 80px
|
||||||
24: "6rem",
|
24: "6rem", // 96px
|
||||||
28: "7rem",
|
28: "7rem", // 112px
|
||||||
32: "8rem",
|
32: "8rem", // 128px
|
||||||
36: "9rem",
|
36: "9rem", // 144px
|
||||||
40: "10rem",
|
40: "10rem", // 160px
|
||||||
44: "11rem",
|
44: "11rem", // 176px
|
||||||
48: "12rem",
|
48: "12rem", // 192px
|
||||||
52: "13rem",
|
52: "13rem", // 208px
|
||||||
56: "14rem",
|
56: "14rem", // 224px
|
||||||
60: "15rem",
|
60: "15rem", // 240px
|
||||||
64: "16rem",
|
64: "16rem", // 256px
|
||||||
68: "17rem",
|
68: "17rem", // 272px
|
||||||
70: "17.5rem",
|
70: "17.5rem", // 280px
|
||||||
71: "17.75rem",
|
71: "17.75rem", // 284px
|
||||||
72: "18rem",
|
72: "18rem", // 288px
|
||||||
76: "19rem",
|
76: "19rem", // 304px
|
||||||
80: "20rem",
|
80: "20rem", // 320px
|
||||||
96: "24rem",
|
96: "24rem", // 384px
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
// Design system border radius tokens from Figma
|
// Design system border radius tokens from Figma
|
||||||
xs: "0.25rem", // 4px
|
xsmall: "0.25rem", // 4px
|
||||||
s: "0.5rem", // 8px
|
small: "0.5rem", // 8px
|
||||||
m: "0.75rem", // 12px
|
medium: "0.75rem", // 12px
|
||||||
l: "1rem", // 16px
|
large: "1rem", // 16px
|
||||||
xl: "1.25rem", // 20px
|
xlarge: "1.25rem", // 20px
|
||||||
"2xl": "1.5rem", // 24px
|
"2xlarge": "1.5rem", // 24px
|
||||||
full: "9999px", // For pill buttons
|
full: "9999px", // For pill buttons
|
||||||
|
|
||||||
// Legacy values - kept for backward compatibility
|
// Legacy values - kept for backward compatibility
|
||||||
|
|||||||
Reference in New Issue
Block a user