Skip to content

Commit eadb3e8

Browse files
authored
Merge branch 'dev' into abhi/ci-chromatic
2 parents 5d198e4 + afb66f7 commit eadb3e8

File tree

13 files changed

+226
-125
lines changed

13 files changed

+226
-125
lines changed

autogpt_platform/backend/backend/blocks/google/sheets.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema
55
from backend.data.model import SchemaField
6-
from backend.util.settings import Settings
6+
from backend.util.settings import AppEnvironment, Settings
77

88
from ._auth import (
99
GOOGLE_OAUTH_IS_CONFIGURED,
@@ -36,13 +36,15 @@ class Output(BlockSchema):
3636
)
3737

3838
def __init__(self):
39+
settings = Settings()
3940
super().__init__(
4041
id="5724e902-3635-47e9-a108-aaa0263a4988",
4142
description="This block reads data from a Google Sheets spreadsheet.",
4243
categories={BlockCategory.DATA},
4344
input_schema=GoogleSheetsReadBlock.Input,
4445
output_schema=GoogleSheetsReadBlock.Output,
45-
disabled=not GOOGLE_OAUTH_IS_CONFIGURED,
46+
disabled=not GOOGLE_OAUTH_IS_CONFIGURED
47+
or settings.config.app_env == AppEnvironment.PRODUCTION,
4648
test_input={
4749
"spreadsheet_id": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
4850
"range": "Sheet1!A1:B2",

autogpt_platform/backend/backend/data/credit.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -438,22 +438,19 @@ async def top_up_credits(
438438
)
439439

440440
async def onboarding_reward(self, user_id: str, credits: int, step: OnboardingStep):
441-
key = f"REWARD-{user_id}-{step.value}"
442-
if not await CreditTransaction.prisma().find_first(
443-
where={
444-
"userId": user_id,
445-
"transactionKey": key,
446-
}
447-
):
441+
try:
448442
await self._add_transaction(
449443
user_id=user_id,
450444
amount=credits,
451445
transaction_type=CreditTransactionType.GRANT,
452-
transaction_key=key,
446+
transaction_key=f"REWARD-{user_id}-{step.value}",
453447
metadata=Json(
454448
{"reason": f"Reward for completing {step.value} onboarding step."}
455449
),
456450
)
451+
except UniqueViolationError:
452+
# Already rewarded for this step
453+
pass
457454

458455
async def top_up_refund(
459456
self, user_id: str, transaction_key: str, metadata: dict[str, str]

autogpt_platform/frontend/src/app/(no-navbar)/onboarding/3-services/page.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -163,14 +163,7 @@ export default function Page() {
163163
</div>
164164

165165
<OnboardingFooter>
166-
<OnboardingButton
167-
className="mb-2"
168-
href="/onboarding/4-agent"
169-
disabled={
170-
state?.integrations.length === 0 &&
171-
isEmptyOrWhitespace(state.otherIntegrations)
172-
}
173-
>
166+
<OnboardingButton className="mb-2" href="/onboarding/4-agent">
174167
Next
175168
</OnboardingButton>
176169
</OnboardingFooter>

autogpt_platform/frontend/src/app/(no-navbar)/onboarding/4-agent/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export default function Page() {
5959

6060
<div className="my-12 flex items-center justify-between gap-5">
6161
<OnboardingAgentCard
62-
{...(agents[0] || {})}
62+
agent={agents[0]}
6363
selected={
6464
agents[0] !== undefined
6565
? state?.selectedStoreListingVersionId ==
@@ -74,7 +74,7 @@ export default function Page() {
7474
}
7575
/>
7676
<OnboardingAgentCard
77-
{...(agents[1] || {})}
77+
agent={agents[1]}
7878
selected={
7979
agents[1] !== undefined
8080
? state?.selectedStoreListingVersionId ==

autogpt_platform/frontend/src/app/(no-navbar)/onboarding/5-run/page.tsx

Lines changed: 23 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@ import StarRating from "@/components/onboarding/StarRating";
99
import { Play } from "lucide-react";
1010
import { cn } from "@/lib/utils";
1111
import { useCallback, useEffect, useState } from "react";
12-
import Image from "next/image";
1312
import { GraphMeta, StoreAgentDetails } from "@/lib/autogpt-server-api";
1413
import { useBackendAPI } from "@/lib/autogpt-server-api/context";
1514
import { useRouter } from "next/navigation";
1615
import { useOnboarding } from "@/components/onboarding/onboarding-provider";
1716
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
1817
import SchemaTooltip from "@/components/SchemaTooltip";
1918
import { TypeBasedInput } from "@/components/type-based-input";
19+
import SmartImage from "@/components/agptui/SmartImage";
2020

2121
export default function Page() {
2222
const { state, updateState, setStep } = useOnboarding(
@@ -99,7 +99,7 @@ export default function Page() {
9999
}, [api, agent, router, state?.agentInput, storeAgent, updateState]);
100100

101101
const runYourAgent = (
102-
<div className="ml-[54px] w-[481px] pl-5">
102+
<div className="ml-[104px] w-[481px] pl-5">
103103
<div className="flex flex-col">
104104
<OnboardingText variant="header">Run your first agent</OnboardingText>
105105
<span className="mt-9 text-base font-normal leading-normal text-zinc-600">
@@ -147,32 +147,25 @@ export default function Page() {
147147
return (
148148
<OnboardingStep dotted>
149149
<OnboardingHeader backHref={"/onboarding/4-agent"} transparent />
150-
<div
151-
className={cn(
152-
"flex w-full items-center justify-center",
153-
showInput ? "mt-[32px]" : "mt-[192px]",
154-
)}
155-
>
156-
{/* Left side */}
157-
<div className="mr-[52px] w-[481px]">
158-
<div className="h-[156px] w-[481px] rounded-xl bg-white px-6 pb-5 pt-4">
159-
<span className="font-sans text-xs font-medium tracking-wide text-zinc-500">
160-
SELECTED AGENT
161-
</span>
150+
{/* Agent card */}
151+
<div className="fixed left-1/4 top-1/2 w-[481px] -translate-x-1/2 -translate-y-1/2">
152+
<div className="h-[156px] w-[481px] rounded-xl bg-white px-6 pb-5 pt-4">
153+
<span className="font-sans text-xs font-medium tracking-wide text-zinc-500">
154+
SELECTED AGENT
155+
</span>
156+
{storeAgent ? (
162157
<div className="mt-4 flex h-20 rounded-lg bg-violet-50 p-2">
163158
{/* Left image */}
164-
<Image
165-
src={storeAgent?.agent_image[0] || ""}
166-
alt="Description"
167-
width={350}
168-
height={196}
169-
className="h-full w-auto rounded-lg object-contain"
159+
<SmartImage
160+
src={storeAgent?.agent_image[0]}
161+
alt="Agent cover"
162+
imageContain
163+
className="w-[350px] rounded-lg"
170164
/>
171-
172165
{/* Right content */}
173166
<div className="ml-2 flex flex-1 flex-col">
174167
<span className="w-[292px] truncate font-sans text-[14px] font-medium leading-normal text-zinc-800">
175-
{agent?.name}
168+
{storeAgent?.agent_name}
176169
</span>
177170
<span className="mt-[5px] w-[292px] truncate font-sans text-xs font-normal leading-tight text-zinc-600">
178171
by {storeAgent?.creator}
@@ -189,13 +182,19 @@ export default function Page() {
189182
</div>
190183
</div>
191184
</div>
192-
</div>
185+
) : (
186+
<div className="mt-4 flex h-20 animate-pulse rounded-lg bg-gray-300 p-2" />
187+
)}
193188
</div>
189+
</div>
190+
<div className="flex min-h-[80vh] items-center justify-center">
191+
{/* Left side */}
192+
<div className="w-[481px]" />
194193
{/* Right side */}
195194
{!showInput ? (
196195
runYourAgent
197196
) : (
198-
<div className="ml-[54px] w-[481px] pl-5">
197+
<div className="ml-[104px] w-[481px] pl-5">
199198
<div className="flex flex-col">
200199
<OnboardingText variant="header">
201200
Provide details for your agent

autogpt_platform/frontend/src/app/(no-navbar)/onboarding/6-congrats/actions.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ import { redirect } from "next/navigation";
66
export async function finishOnboarding() {
77
const api = new BackendAPI();
88
const onboarding = await api.getUserOnboarding();
9-
revalidatePath("/library", "layout");
10-
redirect("/library");
9+
const listingId = onboarding?.selectedStoreListingVersionId;
10+
if (listingId) {
11+
const libraryAgent = await api.addMarketplaceAgentToLibrary(listingId);
12+
revalidatePath(`/library/agents/${libraryAgent.id}`, "layout");
13+
redirect(`/library/agents/${libraryAgent.id}`);
14+
} else {
15+
revalidatePath("/library", "layout");
16+
redirect("/library");
17+
}
1118
}

autogpt_platform/frontend/src/app/(no-navbar)/onboarding/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export default async function OnboardingPage() {
1313
// CONGRATS is the last step in intro onboarding
1414
if (onboarding.completedSteps.includes("CONGRATS")) redirect("/marketplace");
1515
else if (onboarding.completedSteps.includes("AGENT_INPUT"))
16-
redirect("/onboarding/6-congrats");
16+
redirect("/onboarding/5-run");
1717
else if (onboarding.completedSteps.includes("AGENT_NEW_RUN"))
1818
redirect("/onboarding/5-run");
1919
else if (onboarding.completedSteps.includes("AGENT_CHOICE"))
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"use client";
2+
import { cn } from "@/lib/utils";
3+
import Image from "next/image";
4+
import { useState } from "react";
5+
6+
interface SmartImageProps {
7+
src?: string | null;
8+
alt: string;
9+
imageContain?: boolean;
10+
className?: string;
11+
}
12+
13+
export default function SmartImage({
14+
src,
15+
alt,
16+
imageContain,
17+
className,
18+
}: SmartImageProps) {
19+
const [isLoading, setIsLoading] = useState(true);
20+
const shouldShowSkeleton = isLoading || !src;
21+
22+
return (
23+
<div className={cn("relative overflow-hidden", className)}>
24+
{src && (
25+
<Image
26+
src={src}
27+
alt={alt}
28+
fill
29+
onLoad={() => setIsLoading(false)}
30+
className={cn(
31+
"rounded-inherit object-center transition-opacity duration-300",
32+
isLoading ? "opacity-0" : "opacity-100",
33+
imageContain ? "object-contain" : "object-cover",
34+
)}
35+
sizes="100%"
36+
/>
37+
)}
38+
{shouldShowSkeleton && (
39+
<div className="rounded-inherit absolute inset-0 animate-pulse bg-gray-300 dark:bg-gray-700" />
40+
)}
41+
</div>
42+
);
43+
}

autogpt_platform/frontend/src/components/agptui/Wallet.tsx

Lines changed: 74 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { PopoverClose } from "@radix-ui/react-popover";
1111
import { TaskGroups } from "../onboarding/WalletTaskGroups";
1212
import { ScrollArea } from "../ui/scroll-area";
1313
import { useOnboarding } from "../onboarding/onboarding-provider";
14-
import { useCallback, useEffect, useRef } from "react";
14+
import { useCallback, useEffect, useRef, useState } from "react";
1515
import { cn } from "@/lib/utils";
1616
import * as party from "party-js";
1717
import WalletRefill from "./WalletRefill";
@@ -21,6 +21,11 @@ export default function Wallet() {
2121
fetchInitialCredits: true,
2222
});
2323
const { state, updateState } = useOnboarding();
24+
const [prevCredits, setPrevCredits] = useState<number | null>(credits);
25+
const [flash, setFlash] = useState(false);
26+
const [stepsLength, setStepsLength] = useState<number | null>(
27+
state?.completedSteps?.length || null,
28+
);
2429
const walletRef = useRef<HTMLButtonElement | null>(null);
2530

2631
const onWalletOpen = useCallback(async () => {
@@ -37,49 +42,81 @@ export default function Wallet() {
3742
.through("lifetime")
3843
.build();
3944

45+
// Confetti effect on the wallet button
4046
useEffect(() => {
41-
// Check if there are any completed tasks (state?.completedTasks) that
42-
// are not in the state?.notified array and play confetti if so
43-
const pending = state?.completedSteps
44-
.filter((step) => !state?.notified.includes(step))
45-
// Ignore steps that are not relevant for notifications
46-
.filter(
47-
(step) =>
48-
step !== "WELCOME" &&
49-
step !== "USAGE_REASON" &&
50-
step !== "INTEGRATIONS" &&
51-
step !== "AGENT_CHOICE" &&
52-
step !== "AGENT_NEW_RUN" &&
53-
step !== "AGENT_INPUT",
54-
);
55-
if ((pending?.length || 0) > 0 && walletRef.current) {
56-
party.confetti(walletRef.current, {
57-
count: 30,
58-
spread: 120,
59-
shapes: ["square", "circle"],
60-
size: party.variation.range(1, 2),
61-
speed: party.variation.range(200, 300),
62-
modules: [fadeOut],
63-
});
47+
if (!state?.completedSteps) {
48+
return;
49+
}
50+
// If we haven't set the length yet, just set it and return
51+
if (stepsLength === null) {
52+
setStepsLength(state?.completedSteps?.length);
53+
return;
54+
}
55+
// It's enough to compare array lengths,
56+
// because the order of completed steps is not important
57+
// If the length is the same, we don't need to do anything
58+
if (state?.completedSteps?.length === stepsLength) {
59+
return;
60+
}
61+
// Otherwise, we need to set the new length
62+
setStepsLength(state?.completedSteps?.length);
63+
// And make confetti
64+
if (walletRef.current) {
65+
setTimeout(() => {
66+
fetchCredits();
67+
party.confetti(walletRef.current!, {
68+
count: 30,
69+
spread: 120,
70+
shapes: ["square", "circle"],
71+
size: party.variation.range(1, 2),
72+
speed: party.variation.range(200, 300),
73+
modules: [fadeOut],
74+
});
75+
}, 800);
6476
}
6577
}, [state?.completedSteps, state?.notified]);
6678

79+
// Wallet flash on credits change
80+
useEffect(() => {
81+
if (credits === prevCredits) {
82+
return;
83+
}
84+
setPrevCredits(credits);
85+
if (prevCredits === null) {
86+
return;
87+
}
88+
setFlash(true);
89+
setTimeout(() => {
90+
setFlash(false);
91+
}, 300);
92+
}, [credits]);
93+
6794
return (
6895
<Popover>
6996
<PopoverTrigger asChild>
70-
<button
71-
ref={walletRef}
72-
className="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"
73-
onClick={onWalletOpen}
74-
>
75-
Wallet{" "}
76-
<span className="text-sm font-semibold">
77-
{formatCredits(credits)}
78-
</span>
79-
{state?.notificationDot && (
80-
<span className="absolute right-1 top-1 h-2 w-2 rounded-full bg-violet-600"></span>
81-
)}
82-
</button>
97+
<div className="relative inline-block">
98+
<button
99+
ref={walletRef}
100+
className={cn(
101+
"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",
102+
)}
103+
onClick={onWalletOpen}
104+
>
105+
Wallet{" "}
106+
<span className="text-sm font-semibold">
107+
{formatCredits(credits)}
108+
</span>
109+
{state?.notificationDot && (
110+
<span className="absolute right-1 top-1 h-2 w-2 rounded-full bg-violet-600"></span>
111+
)}
112+
</button>
113+
<div
114+
className={cn(
115+
"pointer-events-none absolute inset-0 rounded-md bg-violet-400 duration-2000 ease-in-out",
116+
flash ? "opacity-50 duration-0" : "opacity-0",
117+
)}
118+
/>
119+
</div>
83120
</PopoverTrigger>
84121
<PopoverContent
85122
className={cn(

0 commit comments

Comments
 (0)