fix(frontend): fix all lint errors and add next/typescript (#10182)

## Changes 🏗️

### ESLint Config
1. **Disabled `react-hooks/exhaustive-deps`:** 
- to prevent unnecessary dependency proliferation and rely on code
review instead
2. **Added
[`next/typescript`](https://nextjs.org/docs/app/api-reference/config/eslint#with-typescript):**
- to the ESLint config to make sure we also have TS linting rules
3. **Added custom rule for `@typescript-eslint/no-unused-vars`:** 
- to allow underscore-prefixed variables (convention for intentionally
unused), in some cases helpful

From now on, whenever we have unused variables or imports, the `lint` CI
will fail 🔴 , thanks to `next/typescript` that adds
`typescript-eslint/no-unused-vars` 💆🏽

### Minor Fixes
- Replaced empty interfaces with type aliases to resolve
`@typescript-eslint/no-empty-object-type` warnings
- Fixed unsafe non-null assertions with proper null checks
- Removed `@ts-ignore` comments in favour of proper type casting ( _when
possible_ 🙏🏽 )

### Google Analytics Component
- Changed Next.js Script strategy from `beforeInteractive` to
`afterInteractive` to resolve Next.js warnings
- this make sure loading analytics does not block page render 🙏🏽 (
_better page load time_ )

### Are these changes safe?

As long as the Typescript compiler does not complain ( check the
`type-check` job ) we should be save. Most changes are removing unused
code, if that code would be used somewhere else the compiler should
catch it and tell us 🫶

I also typed some code when possible, or bypassed the linter when I
thought it was fair for now. I disabled a couple ESLint rules. Most
importantly the `no-explicity-any` one as we have loads of stuff untyped
yet ( _this should be improved once API types are generated for us_ ).

### DX

Added some settings on `.vscode` folder 📁 so that files will be
formatted on save and also ESLint will fix errors on save when able 💯

### 📈 **Result:**

-  All linting errors resolved
-  Improved TypeScript strict mode compliance  
-  Better developer experience with cleaner code

## 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] Lint CI job passes
- [x] There is not type errors ( _TS will catch issue related to these
changes_ )
This commit is contained in:
Ubbe
2025-06-17 18:29:21 +04:00
committed by GitHub
parent b477d31641
commit 86361fc1ae
89 changed files with 220 additions and 349 deletions

View File

@@ -1,3 +1,25 @@
{
"extends": ["next/core-web-vitals", "plugin:storybook/recommended"]
"extends": [
"next/core-web-vitals",
"next/typescript",
"plugin:storybook/recommended"
],
"rules": {
// Disabling exhaustive-deps to avoid forcing unnecessary dependencies and useCallback proliferation.
// We rely on code review for proper dependency management instead of mechanical rule following.
// See: https://kentcdodds.com/blog/usememo-and-usecallback
"react-hooks/exhaustive-deps": "off",
// Disable temporarily as we have some `any` in the codebase and we need to got case by case
// and see if they can be fixed.
"@typescript-eslint/no-explicit-any": "off",
// Allow unused vars that start with underscore (convention for intentionally unused)
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_",
"varsIgnorePattern": "^_",
"caughtErrorsIgnorePattern": "^_"
}
]
}
}

View File

@@ -1,5 +1,6 @@
node_modules
pnpm-lock.yaml
.next
.auth
build
public

View File

@@ -9,7 +9,6 @@ import { OnboardingText } from "@/components/onboarding/OnboardingText";
import { OnboardingGrid } from "@/components/onboarding/OnboardingGrid";
import { useCallback } from "react";
import OnboardingInput from "@/components/onboarding/OnboardingInput";
import { isEmptyOrWhitespace } from "@/lib/utils";
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
const services = [

View File

@@ -49,6 +49,7 @@ export default function Page() {
.getAgentMetaByStoreListingVersionId(state?.selectedStoreListingVersionId)
.then((agent) => {
setAgent(agent);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const update: { [key: string]: any } = {};
// Set default values from schema
Object.entries(agent.input_schema.properties).forEach(

View File

@@ -1,6 +1,5 @@
import { ShoppingBag } from "lucide-react";
import { Sidebar } from "@/components/agptui/Sidebar";
import { Users, DollarSign, LogOut } from "lucide-react";
import { Users, DollarSign } from "lucide-react";
import { IconSliders } from "@/components/ui/icons";

View File

@@ -3,9 +3,7 @@
import { revalidatePath } from "next/cache";
import BackendApi from "@/lib/autogpt-server-api";
import {
NotificationPreferenceDTO,
StoreListingsWithVersionsResponse,
StoreSubmissionsResponse,
SubmissionStatus,
} from "@/lib/autogpt-server-api/types";

View File

@@ -29,6 +29,7 @@ export async function getUsersTransactionHistory(
search?: string,
transactionType?: CreditTransactionType,
): Promise<UsersBalanceHistoryResponse> {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const data: Record<string, any> = {
page,
page_size: pageSize,

View File

@@ -38,7 +38,7 @@ export async function GET(request: Request) {
return NextResponse.redirect(`${origin}/error`);
}
const { data, error } = await supabase.auth.exchangeCodeForSession(code);
const { error } = await supabase.auth.exchangeCodeForSession(code);
// data.session?.refresh_token is available if you need to store it for later use
if (!error) {
try {

View File

@@ -5,7 +5,7 @@ import { NextResponse } from "next/server";
// controlled by the CredentialsInput component. The CredentialsInput opens the login
// page in a pop-up window, which then redirects to this route to close the loop.
export async function GET(request: Request) {
const { searchParams, origin } = new URL(request.url);
const { searchParams } = new URL(request.url);
const code = searchParams.get("code");
const state = searchParams.get("state");

View File

@@ -1,6 +1,5 @@
"use server";
import { revalidatePath } from "next/cache";
import BackendAPI from "@/lib/autogpt-server-api/client";
import { OttoQuery, OttoResponse } from "@/lib/autogpt-server-api/types";

View File

@@ -92,7 +92,7 @@ export default async function Page({
</main>
</div>
);
} catch (error) {
} catch {
return (
<div className="flex h-screen w-full items-center justify-center">
<div className="text-2xl text-neutral-900">Creator not found</div>

View File

@@ -8,6 +8,7 @@ import { Separator } from "@/components/ui/separator";
import { SearchFilterChips } from "@/components/agptui/SearchFilterChips";
import { SortDropdown } from "@/components/agptui/SortDropdown";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
import { Creator, StoreAgent } from "@/lib/autogpt-server-api";
type MarketplaceSearchPageSearchParams = { searchTerm?: string; sort?: string };
@@ -33,8 +34,8 @@ function SearchResults({
}): React.ReactElement {
const [showAgents, setShowAgents] = useState(true);
const [showCreators, setShowCreators] = useState(true);
const [agents, setAgents] = useState<any[]>([]);
const [creators, setCreators] = useState<any[]>([]);
const [agents, setAgents] = useState<StoreAgent[]>([]);
const [creators, setCreators] = useState<Creator[]>([]);
const [isLoading, setIsLoading] = useState(true);
const api = useBackendAPI();

View File

@@ -14,7 +14,7 @@ import {
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
export default function Page({}: {}) {
export default function Page() {
const { supabase } = useSupabase();
const api = useBackendAPI();
const [submissions, setSubmissions] = useState<StoreSubmissionsResponse>();

View File

@@ -1,4 +1,4 @@
import React, { Suspense } from "react";
import React from "react";
import type { Metadata } from "next";
import { fonts } from "@/components/styles/fonts";

View File

@@ -17,8 +17,8 @@ export default function ConnectionLine<NodeType extends Node>({
}: ConnectionLineComponentProps<NodeType>) {
const sourceX =
fromPosition === Position.Right
? fromX + (fromHandle?.width! / 2 - 5)
: fromX - (fromHandle?.width! / 2 - 5);
? fromX + ((fromHandle?.width ?? 0) / 2 - 5)
: fromX - ((fromHandle?.width ?? 0) / 2 - 5);
const [path] = getBezierPath({
sourceX: sourceX,

View File

@@ -93,7 +93,7 @@ export function CustomEdge({
return;
}
const beadUp = data?.beadUp!;
const beadUp: number = data?.beadUp ?? 0;
// Add beads
setBeads(({ beads, created, destroyed }) => {
@@ -114,7 +114,7 @@ export function CustomEdge({
const newBeads = beads
.map((bead) => ({ ...bead }))
.filter((bead, index) => {
const beadDown = data?.beadDown!;
const beadDown: number = data?.beadDown ?? 0;
const removeCount = beadDown - destroyed;
if (bead.t >= bead.targetT && index < removeCount) {
destroyedCount++;
@@ -151,7 +151,7 @@ export function CustomEdge({
};
})
.filter((bead, index) => {
const beadDown = data?.beadDown!;
const beadDown: number = data?.beadDown ?? 0;
const removeCount = beadDown - destroyed;
if (bead.t >= bead.targetT && index < removeCount) {
destroyedCount++;

View File

@@ -95,13 +95,7 @@ export type CustomNodeData = {
export type CustomNode = XYNode<CustomNodeData, "custom">;
export const CustomNode = React.memo(
function CustomNode({
data,
id,
width,
height,
selected,
}: NodeProps<CustomNode>) {
function CustomNode({ data, id, height, selected }: NodeProps<CustomNode>) {
const [isOutputOpen, setIsOutputOpen] = useState(
data.isOutputOpen || false,
);
@@ -199,10 +193,6 @@ export const CustomNode = React.memo(
[id, updateNodeData],
);
const toggleOutput = (checked: boolean) => {
setIsOutputOpen(checked);
};
const toggleAdvancedSettings = (checked: boolean) => {
setIsAdvancedOpen(checked);
};
@@ -256,7 +246,7 @@ export const CustomNode = React.memo(
nodeType: BlockUIType,
) => {
if (!schema?.properties) return null;
let keys = Object.entries(schema.properties);
const keys = Object.entries(schema.properties);
switch (nodeType) {
case BlockUIType.NOTE:
// For NOTE blocks, don't render any input handles
@@ -443,7 +433,7 @@ export const CustomNode = React.memo(
// For primitive values, use the original string
handleInputChange(activeKey, value);
}
} catch (error) {
} catch {
// If JSON parsing fails, treat as plain text
handleInputChange(activeKey, value);
}

View File

@@ -1,6 +1,8 @@
import React from "react";
import { beautifyString } from "@/lib/utils";
import { Clipboard } from "lucide-react";
import React from "react";
import { Button } from "./ui/button";
import { ContentRenderer } from "./ui/render";
import {
Table,
TableBody,
@@ -9,9 +11,7 @@ import {
TableHeader,
TableRow,
} from "./ui/table";
import { Clipboard } from "lucide-react";
import { useToast } from "./ui/use-toast";
import { ContentRenderer } from "./ui/render";
type DataTableProps = {
title?: string;
@@ -25,7 +25,6 @@ export default function DataTable({
data,
}: DataTableProps) {
const { toast } = useToast();
const maxChars = 100;
const copyData = (pin: string, data: string) => {
navigator.clipboard.writeText(data).then(() => {

View File

@@ -90,9 +90,7 @@ const FlowEditor: React.FC<{
} = useReactFlow<CustomNode, CustomEdge>();
const [nodeId, setNodeId] = useState<number>(1);
const [isAnyModalOpen, setIsAnyModalOpen] = useState(false);
const [visualizeBeads, setVisualizeBeads] = useState<
"no" | "static" | "animate"
>("animate");
const [visualizeBeads] = useState<"no" | "static" | "animate">("animate");
const [flowExecutionID, setFlowExecutionID] = useState<
GraphExecutionID | undefined
>();
@@ -366,10 +364,7 @@ const FlowEditor: React.FC<{
replaceEdges = edgeChanges.filter(
(change) => change.type === "replace",
),
removedEdges = edgeChanges.filter((change) => change.type === "remove"),
selectedEdges = edgeChanges.filter(
(change) => change.type === "select",
);
removedEdges = edgeChanges.filter((change) => change.type === "remove");
if (addedEdges.length > 0 || removedEdges.length > 0) {
setNodes((nds) => {

View File

@@ -212,7 +212,7 @@ export default function OttoChatWidget({
<p className="mb-2 last:mb-0">{children}</p>
),
code(props) {
const { children, className, node, ...rest } = props;
const { children, className, node: _, ...rest } = props;
const match = /language-(\w+)/.exec(className || "");
return match ? (
<pre className="overflow-x-auto rounded-md bg-muted-foreground/20 p-3">

View File

@@ -22,7 +22,7 @@ const SchemaTooltip: React.FC<{ description?: string }> = ({ description }) => {
<TooltipContent className="tooltip-content max-w-xs bg-white text-gray-900 dark:bg-gray-800 dark:text-gray-100">
<ReactMarkdown
components={{
a: ({ node, ...props }) => (
a: ({ node: _, ...props }) => (
<a
target="_blank"
className="text-blue-400 underline dark:text-blue-300"

View File

@@ -1,5 +1,5 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Text, textVariants, type TextVariant } from "./Text";
import { Text, textVariants } from "./Text";
import { StoryCode } from "@/stories/helpers/StoryCode";
const meta: Meta<typeof Text> = {

View File

@@ -9,9 +9,8 @@ import {
TableHead,
TableBody,
} from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ChevronDown, ChevronRight, ExternalLink } from "lucide-react";
import { ChevronDown, ChevronRight } from "lucide-react";
import { formatDistanceToNow } from "date-fns";
import {
type StoreListingWithVersions,
@@ -19,7 +18,6 @@ import {
SubmissionStatus,
} from "@/lib/autogpt-server-api/types";
import { ApproveRejectButtons } from "./approve-reject-buttons";
import { downloadAsAdmin } from "@/app/(platform)/admin/marketplace/actions";
import { DownloadAgentAdminButton } from "./download-agent-button";
// Moved the getStatusBadge function into the client component

View File

@@ -15,7 +15,6 @@ import {
import { SubmissionStatus } from "@/lib/autogpt-server-api/types";
export function SearchAndFilterAdminMarketplace({
initialStatus,
initialSearch,
}: {
initialStatus?: SubmissionStatus;

View File

@@ -15,7 +15,6 @@ import { Textarea } from "@/components/ui/textarea";
import { Input } from "@/components/ui/input";
import { useRouter } from "next/navigation";
import { addDollars } from "@/app/(platform)/admin/spending/actions";
import useCredits from "@/hooks/useCredits";
export function AdminAddMoneyButton({
userId,
@@ -36,8 +35,6 @@ export function AdminAddMoneyButton({
defaultAmount ? Math.abs(defaultAmount / 100).toFixed(2) : "1.00",
);
const { formatCredits } = useCredits();
const handleApproveSubmit = async (formData: FormData) => {
setIsAddMoneyDialogOpen(false);
try {

View File

@@ -48,7 +48,7 @@ export async function AdminUserGrantHistory({
const isPurchased = type === CreditTransactionType.TOP_UP;
const isSpent = type === CreditTransactionType.USAGE;
let displayText = type;
const displayText = type;
let bgColor = "";
if (isGrant) {

View File

@@ -15,7 +15,6 @@ import {
} from "@/components/ui/select";
export function SearchAndFilterAdminSpending({
initialStatus,
initialSearch,
}: {
initialStatus?: CreditTransactionType;
@@ -74,7 +73,7 @@ export function SearchAndFilterAdminSpending({
<Select
value={selectedStatus}
onValueChange={(value) => {
onValueChange={(value: string) => {
setSelectedStatus(value);
const params = new URLSearchParams(searchParams.toString());
if (value === "ALL") {

View File

@@ -2,7 +2,6 @@ import type { Meta, StoryObj } from "@storybook/react";
import { AgentTable } from "./AgentTable";
import { AgentTableRowProps } from "./AgentTableRow";
import { userEvent, within, expect } from "@storybook/test";
import { StatusType } from "./Status";
const meta: Meta<typeof AgentTable> = {
title: "AGPT UI/Agent Table",
@@ -104,6 +103,6 @@ export const EmptyTableTest: Story = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const emptyMessage = canvas.getByText("No agents found");
expect(emptyMessage).toBeTruthy();
await expect(emptyMessage).toBeTruthy();
},
};

View File

@@ -90,7 +90,7 @@ export const AgentTable: React.FC<AgentTableProps> = ({
{/* Table body */}
{agents.length > 0 ? (
<div className="flex flex-col">
{agents.map((agent, index) => (
{agents.map((agent) => (
<div key={agent.id} className="md:block">
<AgentTableRow
{...agent}

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { AgentTableCard } from "./AgentTableCard";
import { userEvent, within, expect } from "@storybook/test";
import { userEvent, within } from "@storybook/test";
import { type StatusType } from "./Status";
const meta: Meta<typeof AgentTableCard> = {

View File

@@ -32,7 +32,6 @@ export const AgentTableCard: React.FC<AgentTableCardProps> = ({
status,
runs,
rating,
id,
onEditSubmission,
}) => {
const onEdit = () => {

View File

@@ -1,6 +1,5 @@
import * as React from "react";
import Link from "next/link";
import { IconLeftArrow, IconRightArrow } from "@/components/ui/icons";
interface BreadcrumbItem {
name: string;

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { FeaturedAgentCard } from "./FeaturedAgentCard";
import { userEvent, within } from "@storybook/test";
import { FeaturedAgentCard } from "./FeaturedAgentCard";
const meta = {
title: "AGPT UI/Featured Store Card",
@@ -35,6 +35,7 @@ type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
agent: {
updated_at: "2024-01-10T15:30:00.000Z",
agent_name:
"Personalized Morning Coffee Newsletter example of three lines",
sub_heading:
@@ -57,6 +58,7 @@ export const Default: Story = {
export const WithInteraction: Story = {
args: {
agent: {
updated_at: "2024-01-10T15:30:00.000Z",
slug: "",
agent_name: "AI Writing Assistant",
sub_heading: "Enhance your writing",

View File

@@ -46,7 +46,7 @@ export const WithSelectedFilters: Story = {
badges: defaultBadges,
multiSelect: true,
},
play: async ({ canvasElement, args }) => {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const marketingChip = canvas.getByText("Marketing").parentElement;
const salesChip = canvas.getByText("Sales").parentElement;
@@ -70,7 +70,7 @@ export const WithFilterChangeCallback: Story = {
console.log("Selected filters:", selectedFilters);
},
},
play: async ({ canvasElement, args }) => {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const salesChip = canvas.getByText("Sales");
const marketingChip = canvas.getByText("Marketing");
@@ -104,7 +104,7 @@ export const SingleSelectBehavior: Story = {
badges: defaultBadges,
multiSelect: false,
},
play: async ({ canvasElement, args }) => {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const salesChip = canvas.getByText("Sales").parentElement;
const marketingChip = canvas.getByText("Marketing").parentElement;

View File

@@ -2,32 +2,6 @@ import type { Meta, StoryObj } from "@storybook/react";
import { Navbar } from "./Navbar";
import { userEvent, within } from "@storybook/test";
import { IconType } from "../ui/icons";
import { ProfileDetails } from "@/lib/autogpt-server-api/types";
// You can't import this here, jest is not available in storybook and will crash it
// import { jest } from "@jest/globals";
// Mock the API responses
const mockProfileData: ProfileDetails = {
name: "John Doe",
username: "johndoe",
description: "",
links: [],
avatar_url: "https://avatars.githubusercontent.com/u/123456789?v=4",
};
const mockCreditData = {
credits: 1500,
};
// Mock the API module
// jest.mock("@/lib/autogpt-server-api", () => {
// return function () {
// return {
// getStoreProfile: () => Promise.resolve(mockProfileData),
// getUserCredit: () => Promise.resolve(mockCreditData),
// };
// };
// });
const meta = {
title: "AGPT UI/Navbar",

View File

@@ -1,12 +1,12 @@
"use client";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { IconLogOut } from "@/components/ui/icons";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
import { cn } from "@/lib/utils";
import * as Sentry from "@sentry/nextjs";
import { useRouter } from "next/navigation";
import { useTransition } from "react";
import { LoadingSpinner } from "../ui/loading";
import { cn } from "@/lib/utils";
import { useRouter } from "next/navigation";
import { toast } from "../ui/use-toast";
import * as Sentry from "@sentry/nextjs";
export function ProfilePopoutMenuLogoutButton() {
const router = useRouter();
@@ -16,7 +16,7 @@ export function ProfilePopoutMenuLogoutButton() {
function handleLogout() {
startTransition(async () => {
try {
await supabase.logOut({ scope: "global" });
await supabase.logOut();
router.refresh();
} catch (e) {
Sentry.captureException(e);

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { StoreCard } from "./StoreCard";
import { userEvent, within, expect } from "@storybook/test";
import { userEvent, within } from "@storybook/test";
const meta = {
title: "AGPT UI/StoreCard",

View File

@@ -3,7 +3,6 @@
import * as React from "react";
import { useTheme } from "next-themes";
import { IconMoon, IconSun } from "@/components/ui/icons";
import { Button } from "./Button";
export function ThemeToggle() {
const { theme, setTheme } = useTheme();

View File

@@ -60,7 +60,6 @@ export default function WalletRefill() {
// Pre-fill the auto-refill form with existing values
useEffect(() => {
const values = autoRefillForm.getValues();
if (
autoTopUpConfig &&
autoTopUpConfig.amount > 0 &&

View File

@@ -84,7 +84,7 @@ export function APIKeysSection() {
setIsCreateOpen(false);
setIsKeyDialogOpen(true);
loadAPIKeys();
} catch (error) {
} catch {
toast({
title: "Error",
description: "Failed to create AutoGPT Platform API key",
@@ -109,7 +109,7 @@ export function APIKeysSection() {
description: "AutoGPT Platform API key revoked successfully",
});
loadAPIKeys();
} catch (error) {
} catch {
toast({
title: "Error",
description: "Failed to revoke AutoGPT Platform API key",
@@ -168,7 +168,7 @@ export function APIKeysSection() {
<Checkbox
id={permission}
checked={selectedPermissions.includes(permission)}
onCheckedChange={(checked) => {
onCheckedChange={(checked: boolean) => {
setSelectedPermissions(
checked
? [...selectedPermissions, permission]

View File

@@ -1,7 +1,7 @@
import type { Meta, StoryObj } from "@storybook/react";
import { FeaturedSection } from "./FeaturedSection";
import { userEvent, within } from "@storybook/test";
import { StoreAgent } from "@/lib/autogpt-server-api";
import type { Meta, StoryObj } from "@storybook/react";
import { userEvent, within } from "@storybook/test";
import { FeaturedSection } from "./FeaturedSection";
const meta = {
title: "AGPT UI/Composite/Featured Agents",
@@ -24,6 +24,7 @@ type Story = StoryObj<typeof meta>;
const mockFeaturedAgents = [
{
updated_at: "2024-01-10T15:30:00.000Z",
agent_name: "Personalized Morning Coffee Newsletter example of three lines",
sub_heading:
"Transform ideas into breathtaking images with this AI-powered Image Generator.",
@@ -39,6 +40,7 @@ const mockFeaturedAgents = [
slug: "personalized-morning-coffee-newsletter",
},
{
updated_at: "2024-01-10T15:30:00.000Z",
agent_name: "Data Analyzer Lite",
sub_heading: "Basic data analysis tool",
creator: "DataTech",
@@ -53,6 +55,7 @@ const mockFeaturedAgents = [
slug: "data-analyzer-lite",
},
{
updated_at: "2024-01-10T15:30:00.000Z",
agent_name: "CodeAssist AI",
sub_heading: "Your AI coding companion",
creator: "DevTools Co.",
@@ -67,6 +70,7 @@ const mockFeaturedAgents = [
slug: "codeassist-ai",
},
{
updated_at: "2024-01-10T15:30:00.000Z",
agent_name: "MultiTasker",
sub_heading: "All-in-one productivity suite",
creator: "Productivity Plus",
@@ -81,6 +85,7 @@ const mockFeaturedAgents = [
slug: "multitasker",
},
{
updated_at: "2024-01-10T15:30:00.000Z",
agent_name: "QuickTask",
sub_heading: "Fast task automation",
creator: "EfficientWorks",

View File

@@ -27,7 +27,7 @@ interface FeaturedSectionProps {
export const FeaturedSection: React.FC<FeaturedSectionProps> = ({
featuredAgents,
}) => {
const [currentSlide, setCurrentSlide] = useState(0);
const [_, setCurrentSlide] = useState(0);
const handlePrevSlide = useCallback(() => {
setCurrentSlide((prev) =>

View File

@@ -5,7 +5,6 @@ import { SearchBar } from "@/components/agptui/SearchBar";
import { FilterChips } from "@/components/agptui/FilterChips";
import { useRouter } from "next/navigation";
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
import { useSupabase } from "@/lib/supabase/hooks/useSupabase";
export const HeroSection: React.FC = () => {
const router = useRouter();

View File

@@ -1,6 +1,6 @@
import type { Meta, StoryObj } from "@storybook/react";
import { PublishAgentPopout } from "@/components/agptui/composite/PublishAgentPopout";
import { userEvent, within, expect } from "@storybook/test";
import { userEvent, within } from "@storybook/test";
const meta = {
title: "AGPT UI/Composite/Publish Agent Popout",

View File

@@ -47,7 +47,7 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
inputStep,
);
const [myAgents, setMyAgents] = React.useState<MyAgentsResponse | null>(null);
const [selectedAgent, setSelectedAgent] = React.useState<string | null>(null);
const [_, setSelectedAgent] = React.useState<string | null>(null);
const [initialData, setInitialData] =
React.useState<PublishAgentInfoInitialData>({
agent_id: "",
@@ -181,7 +181,7 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
// Create store submission
try {
const submission = await api.createStoreSubmission({
await api.createStoreSubmission({
name: name,
sub_heading: subHeading,
description: description,

View File

@@ -5,9 +5,9 @@
"use client";
import { useEffect } from "react";
import Script from "next/script";
import type { GAParams } from "@/types/google";
import Script from "next/script";
import { useEffect } from "react";
let currDataLayerName: string | undefined = undefined;
@@ -31,7 +31,8 @@ export function GoogleAnalytics(props: GAParams) {
<>
<Script
id="_custom-ga-init"
strategy="beforeInteractive"
// Using "afterInteractive" to avoid blocking the initial page rendering
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `
window['${dataLayerName}'] = window['${dataLayerName}'] || [];
@@ -44,7 +45,7 @@ export function GoogleAnalytics(props: GAParams) {
/>
<Script
id="_custom-ga"
strategy="beforeInteractive"
strategy="afterInteractive"
src="/gtag.js"
nonce={nonce}
/>
@@ -52,15 +53,15 @@ export function GoogleAnalytics(props: GAParams) {
);
}
export function sendGAEvent(..._args: any[]) {
export function sendGAEvent(...args: any[]) {
if (currDataLayerName === undefined) {
console.warn(`Custom GA: GA has not been initialized`);
return;
}
//@ts-ignore
if (window[currDataLayerName]) {
//@ts-ignore
window[currDataLayerName].push(arguments);
const dataLayer = (window as any)[currDataLayerName];
if (dataLayer) {
dataLayer.push(...args);
} else {
console.warn(`Custom GA: dataLayer ${currDataLayerName} does not exist`);
}

View File

@@ -1,5 +1,5 @@
import { AlertCircle, CheckCircle } from "lucide-react";
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Card, CardContent } from "@/components/ui/card";
import { HelpItem } from "@/components/auth/help-item";
import { BehaveAs } from "@/lib/utils";

View File

@@ -1,4 +1,3 @@
import { useState } from "react";
import { FaGoogle, FaSpinner } from "react-icons/fa";
import { Button } from "../ui/button";

View File

@@ -1,6 +1,6 @@
"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { cn } from "@/lib/utils";
import { useEffect, useRef, useState } from "react";
export interface TurnstileProps {
siteKey: string;
@@ -120,7 +120,7 @@ export function Turnstile({
]);
// Method to reset the widget manually
const reset = useCallback(() => {
useEffect(() => {
if (loaded && widgetIdRef.current && window.turnstile && shouldRender) {
window.turnstile.reset(widgetIdRef.current);
}

View File

@@ -9,12 +9,7 @@ import {
import { Label } from "@/components/ui/label";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
import { Separator } from "./ui/separator";
import { CronExpressionManager } from "@/lib/monitor/cronExpressionManager";

View File

@@ -2,7 +2,7 @@
import LibraryUploadAgentDialog from "./library-upload-agent-dialog";
import LibrarySearchBar from "./library-search-bar";
interface LibraryActionHeaderProps {}
type LibraryActionHeaderProps = Record<string, never>;
/**
* LibraryActionHeader component - Renders a header with search, notifications and filters

View File

@@ -24,7 +24,7 @@ interface NotificationCardProps {
}
const NotificationCard = ({
notification: { id, type, title, content, mediaUrl },
notification: { type, title, content, mediaUrl },
onClose,
}: NotificationCardProps) => {
const barHeights = Array.from({ length: 60 }, () =>

View File

@@ -19,7 +19,7 @@ export default function LibrarySortMenu(): React.ReactNode {
setLibrarySort(value);
setAgentLoading(true);
await new Promise((resolve) => setTimeout(resolve, 1000));
let response = await api.listLibraryAgents({
const response = await api.listLibraryAgents({
search_term: searchTerm,
sort_by: value,
page: 1,

View File

@@ -35,7 +35,6 @@ import {
DialogDescription,
DialogFooter,
} from "@/components/ui/dialog";
import { useToast } from "@/components/ui/use-toast";
import RunnerInputUI from "@/components/runner-ui/RunnerInputUI";
import useAgentGraph from "@/hooks/useAgentGraph";
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
@@ -52,7 +51,6 @@ export const FlowInfo: React.FC<
useAgentGraph(flow.graph_id, flow.graph_version, undefined, false);
const api = useBackendAPI();
const { toast } = useToast();
const [flowVersions, setFlowVersions] = useState<Graph[] | null>(null);
const [selectedVersion, setSelectedFlowVersion] = useState(
@@ -170,7 +168,7 @@ export const FlowInfo: React.FC<
<DropdownMenuSeparator />
<DropdownMenuRadioGroup
value={String(selectedVersion)}
onValueChange={(choice) =>
onValueChange={(choice: string) =>
setSelectedFlowVersion(
choice == "all" ? choice : Number(choice),
)

View File

@@ -58,7 +58,7 @@ export const FlowRunsTimeline = ({
tickFormatter={(s) => (s > 90 ? `${Math.round(s / 60)}m` : `${s}s`)}
/>
<Tooltip
content={({ payload, label }) => {
content={({ payload }) => {
if (payload && payload.length) {
const data: GraphExecutionMeta & {
time: number;

View File

@@ -77,7 +77,6 @@ const NodeObjectInputTree: FC<NodeObjectInputTreeProps> = ({
handleInputChange,
errors,
className,
displayName,
}) => {
object ||= ("default" in schema ? schema.default : null) ?? {};
return (
@@ -126,12 +125,10 @@ const NodeDateTimeInput: FC<{
hideTime?: boolean;
}> = ({
selfKey,
schema,
value = "",
error,
handleInputChange,
className,
displayName,
hideDate = false,
hideTime = false,
}) => {
@@ -219,7 +216,6 @@ const NodeFileInput: FC<{
displayName: string;
}> = ({
selfKey,
schema,
value = "",
error,
handleInputChange,
@@ -279,7 +275,7 @@ const NodeFileInput: FC<{
variant="ghost"
className="text-red-500 hover:text-red-700"
onClick={() => {
inputRef.current && (inputRef.current!.value = "");
if (inputRef.current) inputRef.current.value = "";
handleInputChange(selfKey, "");
}}
>
@@ -884,13 +880,6 @@ const NodeKeyValueInput: FC<{
);
};
// Checking if schema is type of string
function isStringSubSchema(
schema: BlockIOSimpleTypeSubSchema,
): schema is BlockIOStringSubSchema {
return "type" in schema && schema.type === "string";
}
const NodeArrayInput: FC<{
nodeId: string;
selfKey: string;
@@ -1229,15 +1218,7 @@ const NodeBooleanInput: FC<{
handleInputChange: NodeObjectInputTreeProps["handleInputChange"];
className?: string;
displayName: string;
}> = ({
selfKey,
schema,
value,
error,
handleInputChange,
className,
displayName,
}) => {
}> = ({ selfKey, schema, value, error, handleInputChange, className }) => {
value ||= schema.default ?? false;
return (
<div className={className}>

View File

@@ -21,7 +21,6 @@ import {
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import Link from "next/link";
import { set } from "lodash";
const OnboardingContext = createContext<
| {

View File

@@ -20,10 +20,7 @@ import { Switch } from "@/components/ui/switch";
import { Separator } from "@/components/ui/separator";
import { updateSettings } from "@/app/(platform)/profile/(user)/settings/actions";
import { toast } from "@/components/ui/use-toast";
import {
NotificationPreference,
NotificationPreferenceDTO,
} from "@/lib/autogpt-server-api";
import { NotificationPreferenceDTO } from "@/lib/autogpt-server-api";
const formSchema = z
.object({

View File

@@ -1,4 +1,4 @@
import React, { useEffect, useRef } from "react";
import React from "react";
import {
Sheet,
SheetContent,

View File

@@ -91,7 +91,7 @@ export const TypeBasedInput: FC<
<Switch
className="ml-auto"
checked={!!value}
onCheckedChange={(checked) => onChange(checked)}
onCheckedChange={(checked: boolean) => onChange(checked)}
{...props}
/>
</>
@@ -145,7 +145,10 @@ export const TypeBasedInput: FC<
schema.enum.length > 0
) {
innerInputElement = (
<Select value={value ?? ""} onValueChange={(val) => onChange(val)}>
<Select
value={value ?? ""}
onValueChange={(val: string) => onChange(val)}
>
<SelectTrigger
className={cn(
inputClasses,
@@ -253,7 +256,7 @@ export function TimePicker({ value, onChange }: TimePickerProps) {
<div className="flex flex-col items-center">
<Select
value={hour}
onValueChange={(val) => changeTime(val, minute, meridiem)}
onValueChange={(val: string) => changeTime(val, minute, meridiem)}
>
<SelectTrigger
className={cn("agpt-border-input ml-1 text-center", inputClasses)}
@@ -277,7 +280,7 @@ export function TimePicker({ value, onChange }: TimePickerProps) {
<div className="flex flex-col items-center">
<Select
value={minute}
onValueChange={(val) => changeTime(hour, val, meridiem)}
onValueChange={(val: string) => changeTime(hour, val, meridiem)}
>
<SelectTrigger
className={cn("agpt-border-input text-center", inputClasses)}
@@ -297,7 +300,7 @@ export function TimePicker({ value, onChange }: TimePickerProps) {
<div className="flex flex-col items-center">
<Select
value={meridiem}
onValueChange={(val) => changeTime(hour, minute, val)}
onValueChange={(val: string) => changeTime(hour, minute, val)}
>
<SelectTrigger
className={cn("agpt-border-input text-center", inputClasses)}
@@ -357,12 +360,7 @@ interface FileInputProps {
className?: string;
}
const FileInput: FC<FileInputProps> = ({
value,
placeholder,
onChange,
className,
}) => {
const FileInput: FC<FileInputProps> = ({ value, onChange, className }) => {
const loadFile = (file: File) => {
const reader = new FileReader();
reader.onload = (e) => {
@@ -402,7 +400,7 @@ const FileInput: FC<FileInputProps> = ({
<Cross2Icon
className="h-5 w-5 cursor-pointer text-black"
onClick={() => {
inputRef.current && (inputRef.current.value = "");
if (inputRef.current) inputRef.current.value = "";
onChange("");
}}
/>

View File

@@ -123,7 +123,6 @@ export const Sizes: Story = {
const canvas = within(canvasElement);
const buttons = canvas.getAllByRole("button");
await expect(buttons).toHaveLength(5);
const sizes = ["sm", "default", "lg", "primary", "icon"];
const sizeClasses = [
"h-8 rounded-md px-3 text-xs",
"h-9 px-4 py-2",

View File

@@ -23,7 +23,9 @@ const Command = React.forwardRef<
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
interface CommandDialogProps extends DialogProps {
children: React.ReactNode;
}
const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (

View File

@@ -1842,11 +1842,10 @@ export function getIconForSocial(
url: string,
props: IconProps,
): React.ReactNode {
const lowerCaseUrl = url.toLowerCase();
let host;
try {
host = new URL(url).host;
} catch (e) {
} catch {
return <IconGlobe {...props} />;
}

View File

@@ -2,8 +2,7 @@ import * as React from "react";
import { cn } from "@/lib/utils";
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {}
export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
const Input = React.forwardRef<HTMLInputElement, InputProps>(
({ className, type, ...props }, ref) => {

View File

@@ -56,7 +56,7 @@ export const Default: Story = {
render: (args) => <MultiSelectorExample {...args} />,
args: {
values: [],
onValuesChange: (value: string[]) => {},
onValuesChange: () => {},
},
};
@@ -64,7 +64,7 @@ export const WithLoop: Story = {
render: (args) => <MultiSelectorExample {...args} />,
args: {
values: [],
onValuesChange: (value: string[]) => {},
onValuesChange: () => {},
loop: true,
},
};
@@ -73,7 +73,7 @@ export const WithInitialValues: Story = {
render: (args) => <MultiSelectorExample {...args} />,
args: {
values: ["apple", "banana"],
onValuesChange: (value: string[]) => {},
onValuesChange: () => {},
},
};
@@ -98,6 +98,6 @@ export const WithDisabledItem: Story = {
),
args: {
values: [],
onValuesChange: (value: string[]) => {},
onValuesChange: () => {},
},
};

View File

@@ -16,7 +16,7 @@ const meta = {
export default meta;
type Story = StoryObj<typeof meta>;
const PopoverExample = (args: any) => (
const PopoverExample = () => (
<Popover>
<PopoverTrigger asChild>
<Button variant="outline">Open Popover</Button>

View File

@@ -1,4 +1,3 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { ContentRenderer } from "./render";

View File

@@ -1,7 +1,6 @@
"use client";
import * as React from "react";
import Image from "next/image";
const getYouTubeVideoId = (url: string) => {
const regExp =

View File

@@ -2,8 +2,7 @@ import * as React from "react";
import { cn } from "@/lib/utils";
export interface TextareaProps
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
({ className, ...props }, ref) => {

View File

@@ -16,12 +16,12 @@ type ToasterToast = ToastProps & {
dismissable?: boolean;
};
const actionTypes = {
ADD_TOAST: "ADD_TOAST",
UPDATE_TOAST: "UPDATE_TOAST",
DISMISS_TOAST: "DISMISS_TOAST",
REMOVE_TOAST: "REMOVE_TOAST",
} as const;
type ActionTypes = {
ADD_TOAST: "ADD_TOAST";
UPDATE_TOAST: "UPDATE_TOAST";
DISMISS_TOAST: "DISMISS_TOAST";
REMOVE_TOAST: "REMOVE_TOAST";
};
let count = 0;
@@ -30,7 +30,7 @@ function genId() {
return count.toString();
}
type ActionType = typeof actionTypes;
type ActionType = ActionTypes;
type Action =
| {

View File

@@ -1,5 +1,8 @@
import { CustomEdge } from "@/components/CustomEdge";
import { CustomNode } from "@/components/CustomNode";
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
import { InputItem } from "@/components/RunnerUIWrapper";
import { useToast } from "@/components/ui/use-toast";
import BackendAPI, {
Block,
BlockIOSubSchema,
@@ -8,6 +11,7 @@ import BackendAPI, {
Graph,
GraphExecutionID,
GraphID,
GraphMeta,
NodeExecutionResult,
SpecialBlockID,
} from "@/lib/autogpt-server-api";
@@ -19,13 +23,9 @@ import {
} from "@/lib/utils";
import { MarkerType } from "@xyflow/react";
import Ajv from "ajv";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useRouter, useSearchParams, usePathname } from "next/navigation";
import { useToast } from "@/components/ui/use-toast";
import { InputItem } from "@/components/RunnerUIWrapper";
import { GraphMeta } from "@/lib/autogpt-server-api";
import { default as NextLink } from "next/link";
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
const ajv = new Ajv({ strict: false, allErrors: true });
@@ -279,9 +279,6 @@ export default function useAgentGraph(
const cleanupSourceName = (sourceName: string) =>
isToolSourceName(sourceName) ? "tools" : sourceName;
const getToolArgName = (sourceName: string) =>
isToolSourceName(sourceName) ? sourceName.split("_~_")[1] : null;
const getToolFuncName = (nodeId: string) => {
const sinkNode = nodes.find((node) => node.id === nodeId);
const sinkNodeName = sinkNode
@@ -312,7 +309,7 @@ export default function useAgentGraph(
new Map<string, NodeExecutionResult["status"]>();
// Update execution status for input edges
for (let key in executionData.input_data) {
for (const key in executionData.input_data) {
if (
edge.target !== getFrontendId(executionData.node_id, nodes) ||
edge.targetHandle !== key
@@ -328,7 +325,7 @@ export default function useAgentGraph(
let beadUp = 0;
let beadDown = 0;
execStatus.forEach((status) => {
execStatus.forEach((status: NodeExecutionResult["status"]) => {
beadUp++;
if (status !== "INCOMPLETE") {
// Count any non-incomplete execution as consumed
@@ -831,7 +828,7 @@ export default function useAgentGraph(
return inputData;
};
let inputData = getNestedData(blockSchema, node.data.hardcodedValues);
const inputData = getNestedData(blockSchema, node.data.hardcodedValues);
console.debug(
`Final prepared input for ${node.data.blockType} (${node.id}):`,
@@ -904,7 +901,6 @@ export default function useAgentGraph(
});
const payload = {
id: savedAgent?.id!,
name: agentName || `New Agent ${new Date().toISOString()}`,
description: agentDescription || "",
nodes: formattedNodes,
@@ -914,11 +910,15 @@ export default function useAgentGraph(
// To avoid saving the same graph, we compare the payload with the saved agent.
// Differences in IDs are ignored.
const comparedPayload = {
...(({ id, ...rest }) => rest)(payload),
name: payload.name,
description: payload.description,
nodes: payload.nodes.map(
({ id, data, input_nodes, output_nodes, ...rest }) => rest,
({ id: _, data: __, input_nodes: ___, output_nodes: ____, ...rest }) =>
rest,
),
links: payload.links.map(
({ source_id: _, sink_id: __, ...rest }) => rest,
),
links: payload.links.map(({ source_id, sink_id, ...rest }) => rest),
};
const comparedSavedAgent = {
name: savedAgent?.name,
@@ -947,7 +947,10 @@ export default function useAgentGraph(
setNodesSyncedWithSavedAgent(false);
newSavedAgent = savedAgent
? await api.updateGraph(savedAgent.id, payload)
? await api.updateGraph(savedAgent.id, {
...payload,
id: savedAgent.id,
})
: await api.createGraph(payload);
console.debug("Response from the API:", newSavedAgent);
@@ -996,7 +999,7 @@ export default function useAgentGraph(
...edge,
data: {
...edge.data,
edgeColor: edge.data?.edgeColor!,
edgeColor: edge.data?.edgeColor ?? "grey",
beadUp: 0,
beadDown: 0,
},

View File

@@ -123,7 +123,7 @@ export default class BackendAPI {
getUserCredit(): Promise<{ credits: number }> {
try {
return this._get("/credits");
} catch (error) {
} catch {
return Promise.resolve({ credits: 0 });
}
}
@@ -223,7 +223,7 @@ export default class BackendAPI {
version?: number,
for_export?: boolean,
): Promise<Graph> {
let query: Record<string, any> = {};
const query: Record<string, any> = {};
if (version !== undefined) {
query["version"] = version;
}
@@ -240,7 +240,7 @@ export default class BackendAPI {
}
createGraph(graph: GraphCreatable): Promise<Graph> {
let requestBody = { graph } as GraphCreateRequestBody;
const requestBody = { graph } as GraphCreateRequestBody;
return this._request("POST", "/graphs", requestBody);
}
@@ -872,7 +872,7 @@ export default class BackendAPI {
} else {
errorDetail = errorData.detail || response.statusText;
}
} catch (e) {
} catch {
errorDetail = response.statusText;
}

View File

@@ -630,6 +630,7 @@ export type StoreAgent = {
description: string;
runs: number;
rating: number;
updated_at: string;
};
export type StoreAgentsResponse = {

View File

@@ -1,7 +1,7 @@
"use client";
import { useEffect, useMemo, useState, useRef } from "react";
import { createBrowserClient } from "@supabase/ssr";
import { SignOut, User } from "@supabase/supabase-js";
import { User } from "@supabase/supabase-js";
import { useRouter } from "next/navigation";
import {
broadcastLogout,
@@ -29,7 +29,7 @@ export function useSupabase() {
}
}, []);
async function logOut(options?: SignOut) {
async function logOut() {
if (!supabase) return;
broadcastLogout();
@@ -121,7 +121,7 @@ export function useSupabase() {
const {
data: { subscription },
} = supabase.auth.onAuthStateChange((event, session) => {
} = supabase.auth.onAuthStateChange((_, session) => {
const newUser = session?.user ?? null;
// Only update if user actually changed to prevent unnecessary re-renders

View File

@@ -1,11 +1,6 @@
import { createServerClient } from "@supabase/ssr";
import { NextResponse, type NextRequest } from "next/server";
import {
PROTECTED_PAGES,
ADMIN_PAGES,
isProtectedPage,
isAdminPage,
} from "./helpers";
import { isAdminPage, isProtectedPage } from "./helpers";
export async function updateSession(request: NextRequest) {
let supabaseResponse = NextResponse.next({
@@ -31,7 +26,7 @@ export async function updateSession(request: NextRequest) {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookiesToSet.forEach(({ name, value }) =>
request.cookies.set(name, value),
);
supabaseResponse = NextResponse.next({
@@ -51,7 +46,6 @@ export async function updateSession(request: NextRequest) {
const {
data: { user },
error,
} = await supabase.auth.getUser();
const userRole = user?.role;

View File

@@ -1,8 +1,10 @@
import type { UnsafeUnwrappedCookies } from "next/headers";
import { createServerClient } from "@supabase/ssr";
import { createServerClient, type CookieOptions } from "@supabase/ssr";
type Cookies = { name: string; value: string; options?: CookieOptions }[];
export async function getServerSupabase() {
// Need require here, so Next.js doesn't complain about importing this on client side
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { cookies } = require("next/headers");
const cookieStore = await cookies();
@@ -15,7 +17,7 @@ export async function getServerSupabase() {
getAll() {
return cookieStore.getAll();
},
setAll(cookiesToSet) {
setAll(cookiesToSet: Cookies) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options),

View File

@@ -10,7 +10,6 @@ export async function getServerUser() {
try {
const {
data: { user },
error: _,
} = await supabase.auth.getUser();
// if (error) {
// // FIX: Suppressing error for now. Need to stop the nav bar calling this all the time

View File

@@ -1,7 +1,7 @@
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
import { Category, Graph } from "@/lib/autogpt-server-api/types";
import { Category } from "@/lib/autogpt-server-api/types";
import { NodeDimension } from "@/components/Flow";
export function cn(...inputs: ClassValue[]) {

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "@storybook/react";
import type { Meta } from "@storybook/react";
import { Text } from "@/components/_new/Text/Text";
import { StoryCode } from "@/stories/helpers/StoryCode";
import { SquareArrowOutUpRight } from "lucide-react";
@@ -271,5 +271,3 @@ export function AllVariants() {
</div>
);
}
type Story = StoryObj<typeof meta>;

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "@storybook/react";
import type { Meta } from "@storybook/react";
import { Text } from "@/components/_new/Text/Text";
import { StoryCode } from "@/stories/helpers/StoryCode";

View File

@@ -1,4 +1,4 @@
import type { Meta, StoryObj } from "@storybook/react";
import type { Meta } from "@storybook/react";
import { Text } from "@/components/_new/Text/Text";
import { StoryCode } from "@/stories/helpers/StoryCode";
import { SquareArrowOutUpRight } from "lucide-react";
@@ -248,5 +248,3 @@ export function AllVariants() {
</div>
);
}
type Story = StoryObj<typeof meta>;

View File

@@ -1,62 +1,45 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Text } from "@/components/_new/Text/Text";
import { StoryCode } from "@/stories/helpers/StoryCode";
import { SquareArrowOutUpRight } from "lucide-react";
import {
User,
UserPlus,
Key,
FlowArrow,
Play,
Square,
Alien,
ArrowClockwise,
FloppyDisk,
ArrowCounterClockwise,
ArrowClockwise as Redo,
Cube,
Warning,
UserCircle,
Package,
Megaphone,
List,
Coin,
PencilSimple,
SignOut,
Gear,
SquaresFour,
CloudArrowUp,
MediumLogo,
YoutubeLogo,
TiktokLogo,
Globe,
Hammer,
Books,
GithubLogo,
LinkedinLogo,
FacebookLogo,
XLogo,
InstagramLogo,
ArrowLeft,
ArrowRight,
Heart,
Star,
Bell,
MagnifyingGlass,
Plus,
X,
Books,
Check,
Info,
Download,
Upload,
Calendar,
Clock,
Eye,
EyeSlash,
CloudArrowUp,
Copy,
Cube,
Download,
FacebookLogo,
FloppyDisk,
FlowArrow,
Gear,
GithubLogo,
Info,
InstagramLogo,
Key,
LinkedinLogo,
List,
Package,
PencilSimple,
Play,
ArrowClockwise as Redo,
SignOut,
SquaresFour,
Trash,
DotsThreeVertical,
Alien,
User,
UserCircle,
UserPlus,
Warning,
X,
XLogo,
YoutubeLogo,
} from "@phosphor-icons/react";
import type { Meta } from "@storybook/react";
import { SquareArrowOutUpRight } from "lucide-react";
const meta: Meta = {
title: "Design System/ Tokens /Icons",
@@ -458,5 +441,3 @@ import { User, Heart, Star, Bell } from "@phosphor-icons/react";
</div>
);
}
type Story = StoryObj<typeof meta>;

View File

@@ -13,7 +13,7 @@ test.describe("Build", () => { //(1)!
// Reason Ignore: admonishment is in the wrong place visually with correct prettier rules
// prettier-ignore
test.beforeEach(async ({ page, loginPage, testUser }, testInfo) => { //(3)! ts-ignore
test.beforeEach(async ({ page, loginPage, testUser }) => { //(3)! ts-ignore
buildPage = new BuildPage(page);
// Start each test with login using worker auth

View File

@@ -1,42 +1,6 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { createClient, SupabaseClient } from "@supabase/supabase-js";
export type TestUser = {
email: string;
password: string;
id?: string;
};
let supabase: SupabaseClient;
function getSupabaseAdmin() {
if (!supabase) {
supabase = createClient(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!,
{
auth: {
autoRefreshToken: false,
persistSession: false,
},
},
);
}
return supabase;
}
async function deleteTestUser(userId: string) {
const supabase = getSupabaseAdmin();
try {
const { error } = await supabase.auth.admin.deleteUser(userId);
if (error) {
console.warn(`Warning: Failed to delete test user: ${error.message}`);
}
} catch (error) {
console.warn(
`Warning: Error during user cleanup: ${(error as Error).message}`,
);
}
}

View File

@@ -1,4 +1,4 @@
import { expect, TestInfo } from "@playwright/test";
import { TestInfo } from "@playwright/test";
import { test } from "./fixtures";
import { BuildPage } from "./pages/build.page";
import { MonitorPage } from "./pages/monitor.page";
@@ -48,7 +48,7 @@ test.describe("Monitor", () => {
});
});
test("user can view agents", async ({ page }) => {
test("user can view agents", async () => {
const agents = await monitorPage.listAgents();
// there should be at least one agent
await test.expect(agents.length).toBeGreaterThan(0);
@@ -117,7 +117,7 @@ test.describe("Monitor", () => {
await test.expect(importedAgent).toBeDefined();
});
test("user can view runs", async ({ page }) => {
test("user can view runs", async () => {
const runs = await monitorPage.listRuns();
console.log(runs);
// there should be at least one run

View File

@@ -1,4 +1,4 @@
import { ElementHandle, Locator, Page } from "@playwright/test";
import { Locator, Page } from "@playwright/test";
import { BasePage } from "./base.page";
export interface Block {
@@ -136,7 +136,7 @@ export class BuildPage extends BasePage {
}
}
async getBlockOutputs(blockId: string): Promise<string[]> {
async getBlockOutputs(): Promise<string[]> {
throw new Error("Not implemented");
// try {
// const node = await this.page
@@ -151,7 +151,7 @@ export class BuildPage extends BasePage {
}
async _buildBlockSelector(blockId: string, dataId?: string): Promise<string> {
let selector = dataId
const selector = dataId
? `[data-id="${dataId}"] [data-blockid="${blockId}"]`
: `[data-blockid="${blockId}"]`;
return selector;
@@ -283,7 +283,7 @@ export class BuildPage extends BasePage {
try {
await this.page.waitForLoadState("domcontentloaded", { timeout: 10_000 });
return true;
} catch (error) {
} catch {
return false;
}
}
@@ -432,8 +432,8 @@ export class BuildPage extends BasePage {
}
// Parse current coordinates
let currentX = parseFloat(matches[1]);
let currentY = parseFloat(matches[2]);
const currentX = parseFloat(matches[1]);
const currentY = parseFloat(matches[2]);
// Calculate new position
let newX = currentX;

View File

@@ -1,4 +1,4 @@
import { ElementHandle, Locator, Page } from "@playwright/test";
import { Page } from "@playwright/test";
import { BasePage } from "./base.page";
import path from "path";
@@ -18,10 +18,6 @@ interface Run {
status: string;
}
interface AgentRun extends Agent {
runs: Run[];
}
interface Schedule {
id: string;
graphName: string;
@@ -72,7 +68,7 @@ export class MonitorPage extends BasePage {
]);
return true;
} catch (error) {
} catch {
return false;
}
}

View File

@@ -13,10 +13,7 @@ test.describe("Profile", () => {
await test.expect(page).toHaveURL("/marketplace");
});
test("user can view their profile information", async ({
page,
testUser,
}) => {
test("user can view their profile information", async ({ page }) => {
await profilePage.navbar.clickProfileLink();
// workaround for #8788
// sleep for 10 seconds to allow page to load due to bug in our system
@@ -39,7 +36,7 @@ test.describe("Profile", () => {
await test.expect(profilePage.isLoaded()).resolves.toBeTruthy();
});
test("profile displays user Credential providers", async ({ page }) => {
test("profile displays user Credential providers", async () => {
await profilePage.navbar.clickProfileLink();
// await test

View File

@@ -99,6 +99,7 @@ test.describe("Nested Property Setter Tests", () => {
}).toThrow("Invalid property name: __proto__");
// Verify no pollution occurred
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect({}.polluted).toBeUndefined();
});