Skip to content

Commit ab17388

Browse files
committed
[TOOL-4483] Move changelog to team landing page from notifications (#7037)
<!-- ## title your PR with this format: "[SDK/Dashboard/Portal] Feature/Fix: Concise title for the changes" If you did not copy the branch name from Linear, paste the issue tag here (format is TEAM-0000): ## Notes for the reviewer Anything important to call out? Be sure to also clarify these in your comments. ## How to test Unit tests, playground, etc. --> <!-- start pr-codex --> --- ## PR-Codex overview This PR focuses on refactoring notification handling in the application, removing unused functions, and enhancing the UI components related to notifications and alerts. ### Detailed summary - Removed `getChangelogNotifications` and related props from several components. - Introduced `DismissibleAlert` component for better alert management. - Updated `NotificationButtonUI` to streamline notification handling. - Adjusted UI elements for consistency and clarity. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` <!-- end pr-codex -->
1 parent df06bec commit ab17388

File tree

15 files changed

+207
-295
lines changed

15 files changed

+207
-295
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use client";
2+
3+
import { XIcon } from "lucide-react";
4+
import { useLocalStorage } from "../../../hooks/useLocalStorage";
5+
import { Button } from "../ui/button";
6+
7+
export function DismissibleAlert(props: {
8+
title: React.ReactNode;
9+
description: React.ReactNode;
10+
localStorageId: string;
11+
}) {
12+
const [isVisible, setIsVisible] = useLocalStorage(
13+
props.localStorageId,
14+
true,
15+
false,
16+
);
17+
18+
if (!isVisible) return null;
19+
20+
return (
21+
<div className="relative rounded-lg border border-border bg-card p-4">
22+
<Button
23+
onClick={() => setIsVisible(false)}
24+
className="absolute top-4 right-4 h-auto w-auto p-1 text-muted-foreground"
25+
aria-label="Close alert"
26+
variant="ghost"
27+
>
28+
<XIcon className="size-5" />
29+
</Button>
30+
<div>
31+
<h2 className="mb-0.5 font-semibold">{props.title} </h2>
32+
<div className="text-muted-foreground text-sm">{props.description}</div>
33+
</div>
34+
</div>
35+
);
36+
}

apps/dashboard/src/app/(app)/account/components/AccountHeader.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { useCallback, useState } from "react";
1111
import { useActiveWallet, useDisconnect } from "thirdweb/react";
1212
import { doLogout } from "../../login/auth-actions";
1313
import {
14-
getChangelogNotifications,
1514
getInboxNotifications,
1615
markNotificationAsRead,
1716
} from "../../team/components/NotificationButton/fetch-notifications";
@@ -59,7 +58,6 @@ export function AccountHeader(props: {
5958
account: props.account,
6059
client,
6160
accountAddress: props.accountAddress,
62-
getChangelogNotifications: getChangelogNotifications,
6361
getInboxNotifications: getInboxNotifications,
6462
markNotificationAsRead: markNotificationAsRead,
6563
};

apps/dashboard/src/app/(app)/account/components/AccountHeaderUI.stories.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ function Variants(props: {
6464
email: "foo@example.com",
6565
}}
6666
client={storybookThirdwebClient}
67-
getChangelogNotifications={() => Promise.resolve([])}
6867
getInboxNotifications={() => Promise.resolve([])}
6968
markNotificationAsRead={() => Promise.resolve()}
7069
/>

apps/dashboard/src/app/(app)/account/components/AccountHeaderUI.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export type AccountHeaderCompProps = {
2424
account: Pick<Account, "email" | "id" | "image">;
2525
client: ThirdwebClient;
2626
accountAddress: string;
27-
getChangelogNotifications: () => Promise<NotificationMetadata[]>;
2827
getInboxNotifications: () => Promise<NotificationMetadata[]>;
2928
markNotificationAsRead: (id: string) => Promise<void>;
3029
};
@@ -78,7 +77,6 @@ export function AccountHeaderDesktopUI(props: AccountHeaderCompProps) {
7877
connectButton={props.connectButton}
7978
client={props.client}
8079
accountAddress={props.accountAddress}
81-
getChangelogs={props.getChangelogNotifications}
8280
getInboxNotifications={props.getInboxNotifications}
8381
markNotificationAsRead={props.markNotificationAsRead}
8482
/>
@@ -126,7 +124,6 @@ export function AccountHeaderMobileUI(props: AccountHeaderCompProps) {
126124

127125
<div className="flex items-center gap-3">
128126
<NotificationButtonUI
129-
getChangelogs={props.getChangelogNotifications}
130127
getInboxNotifications={props.getInboxNotifications}
131128
markNotificationAsRead={props.markNotificationAsRead}
132129
/>

apps/dashboard/src/app/(app)/components/Header/SecondaryNav/SecondaryNav.tsx

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ export function SecondaryNav(props: {
1515
connectButton: React.ReactNode;
1616
client: ThirdwebClient;
1717
accountAddress: string;
18-
getChangelogs: () => Promise<NotificationMetadata[]>;
1918
getInboxNotifications: () => Promise<NotificationMetadata[]>;
2019
markNotificationAsRead: (id: string) => Promise<void>;
2120
}) {
@@ -24,7 +23,6 @@ export function SecondaryNav(props: {
2423
<SecondaryNavLinks />
2524
<div className="flex items-center gap-3">
2625
<NotificationButtonUI
27-
getChangelogs={props.getChangelogs}
2826
getInboxNotifications={props.getInboxNotifications}
2927
markNotificationAsRead={props.markNotificationAsRead}
3028
/>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { formatDistance } from "date-fns";
2+
import { ArrowRightIcon } from "lucide-react";
3+
import { unstable_cache } from "next/cache";
4+
import Link from "next/link";
5+
6+
type ChangelogItem = {
7+
published_at: string;
8+
title: string;
9+
url: string;
10+
};
11+
12+
export async function Changelog() {
13+
const changelog = await getChangelog();
14+
15+
return (
16+
<div className="relative flex flex-col gap-6 border-border border-l py-2">
17+
{changelog.map((item) => (
18+
<div className="flex flex-row gap-2" key={item.title}>
19+
<div className="-translate-x-1/2 size-2.5 shrink-0 translate-y-1/2 rounded-full bg-border" />
20+
21+
<div className="flex flex-col">
22+
<Link
23+
target="_blank"
24+
href={`${item.url}?utm_source=thirdweb&utm_campaign=changelog`}
25+
role="group"
26+
className="line-clamp-2 text-foreground text-sm hover:underline"
27+
>
28+
{item.title}
29+
</Link>
30+
<div className="mt-1 text-muted-foreground text-xs">
31+
{formatDistance(new Date(item.published_at), Date.now(), {
32+
addSuffix: true,
33+
})}
34+
</div>
35+
</div>
36+
</div>
37+
))}
38+
<Link
39+
href="https://blog.thirdweb.com/changelog?utm_source=thirdweb&utm_campaign=changelog"
40+
target="_blank"
41+
className="flex items-center gap-2 pl-5 text-foreground text-sm hover:underline"
42+
>
43+
View More <ArrowRightIcon className="size-4" />
44+
</Link>
45+
</div>
46+
);
47+
}
48+
49+
const getChangelog = unstable_cache(
50+
async () => {
51+
const res = await fetch(
52+
"https://thirdweb.ghost.io/ghost/api/content/posts/?key=49c62b5137df1c17ab6b9e46e3&fields=title,url,published_at&filter=tag:changelog&visibility:public&limit=10",
53+
);
54+
if (!res.ok) {
55+
return [];
56+
}
57+
const json = await res.json();
58+
return json.posts as ChangelogItem[];
59+
},
60+
["changelog"],
61+
{
62+
revalidate: 60 * 60, // 1 hour
63+
},
64+
);

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/page.tsx

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { getWalletConnections } from "@/api/analytics";
22
import { type Project, getProjects } from "@/api/projects";
33
import { getTeamBySlug } from "@/api/team";
4-
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
4+
import { DismissibleAlert } from "@/components/blocks/dismissible-alert";
55
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
66
import { subDays } from "date-fns";
7-
import { CircleAlertIcon } from "lucide-react";
87
import { redirect } from "next/navigation";
98
import { getAuthToken } from "../../../api/lib/getAuthToken";
109
import { loginRedirect } from "../../../login/loginRedirect";
10+
import { Changelog } from "./_components/Changelog";
1111
import { InviteTeamMembersButton } from "./_components/invite-team-members-button";
1212
import {
1313
type ProjectWithAnalytics,
@@ -52,20 +52,30 @@ export default async function Page(props: {
5252
</div>
5353
</div>
5454

55-
<div className="container flex grow flex-col gap-6 pt-8 pb-20">
56-
<Alert variant={"info"}>
57-
<CircleAlertIcon className="h-4 w-4" />
58-
<AlertTitle>Looking for Engines?</AlertTitle>
59-
<AlertDescription>
60-
Engines, contracts, project settings, and more are now managed
61-
within projects. Open or create a project to access them.
62-
</AlertDescription>
63-
</Alert>
64-
<TeamProjectsPage
65-
projects={projectsWithTotalWallets}
66-
team={team}
67-
client={client}
68-
/>
55+
<div className="container flex grow flex-col gap-4 lg:flex-row">
56+
{/* left */}
57+
<div className="flex grow flex-col gap-6 pt-8 lg:pb-20">
58+
<DismissibleAlert
59+
title="Looking for Engines?"
60+
description="Engines, contracts, project settings, and more are now managed within projects. Open or create a project to access them."
61+
localStorageId={`${team.id}-engines-alert`}
62+
/>
63+
64+
<TeamProjectsPage
65+
projects={projectsWithTotalWallets}
66+
team={team}
67+
client={client}
68+
/>
69+
</div>
70+
71+
{/* right */}
72+
<div className="w-full px-4 py-8 lg:w-[300px]">
73+
<h2 className="mb-3 font-semibold text-2xl tracking-tight">
74+
Changelog
75+
</h2>
76+
77+
<Changelog />
78+
</div>
6979
</div>
7080
</div>
7181
);

apps/dashboard/src/app/(app)/team/[team_slug]/(team)/~/projects/TeamProjectsPage.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { useDashboardRouter } from "@/lib/DashboardRouter";
2626
import { cn } from "@/lib/utils";
2727
import { LazyCreateProjectDialog } from "components/settings/ApiKeys/Create/LazyCreateAPIKeyDialog";
2828
import { formatDate } from "date-fns";
29-
import { PlusIcon, SearchIcon } from "lucide-react";
29+
import { ArrowDownNarrowWideIcon, PlusIcon, SearchIcon } from "lucide-react";
3030
import Link from "next/link";
3131
import { useMemo, useState } from "react";
3232
import type { ThirdwebClient } from "thirdweb";
@@ -251,10 +251,10 @@ function SearchInput(props: {
251251
return (
252252
<div className="relative grow">
253253
<Input
254-
placeholder="Search Projects by name or Client ID"
254+
placeholder="Project name or Client ID"
255255
value={props.value}
256256
onChange={(e) => props.onValueChange(e.target.value)}
257-
className="bg-background pl-9 lg:w-[400px]"
257+
className="bg-background pl-9 lg:w-[320px]"
258258
/>
259259
<SearchIcon className="-translate-y-1/2 absolute top-1/2 left-3 size-4 text-muted-foreground" />
260260
</div>
@@ -298,8 +298,8 @@ function SelectBy(props: {
298298
}}
299299
>
300300
<SelectTrigger className="min-w-[200px] bg-background capitalize">
301-
<div className="flex items-center gap-1.5">
302-
<span className="text-muted-foreground">Sort by</span>
301+
<div className="flex items-center gap-2">
302+
<ArrowDownNarrowWideIcon className="size-4 text-muted-foreground" />
303303
{valueToLabel[props.value]}
304304
</div>
305305
</SelectTrigger>

apps/dashboard/src/app/(app)/team/[team_slug]/[project_slug]/connect/account-abstraction/AccountAbstractionPage.tsx

Lines changed: 26 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
"use client";
22

3+
import { DismissibleAlert } from "@/components/blocks/dismissible-alert";
34
import { Button } from "@/components/ui/button";
45
import { TabPathLinks } from "@/components/ui/tabs";
56
import {
67
TrackedLinkTW,
78
TrackedUnderlineLink,
89
} from "@/components/ui/tracked-link";
910
import { SmartWalletsBillingAlert } from "components/settings/ApiKeys/Alerts";
10-
import { useLocalStorage } from "hooks/useLocalStorage";
1111
import { ArrowRightIcon } from "lucide-react";
12-
import { XIcon } from "lucide-react";
1312
import { AAFooter } from "./AAFooterSection";
1413

1514
const TRACKING_CATEGORY = "smart-wallet";
@@ -100,27 +99,12 @@ function GasCreditAlert(props: {
10099
teamSlug: string;
101100
projectId: string;
102101
}) {
103-
const [isVisible, setIsVisible] = useLocalStorage(
104-
`gas-credit-${props.projectId}`,
105-
true,
106-
false,
107-
);
108-
109-
if (!isVisible) return null;
110-
111102
return (
112-
<div className="relative rounded-lg border border-border bg-card p-4">
113-
<Button
114-
onClick={() => setIsVisible(false)}
115-
className="absolute top-4 right-4 h-auto w-auto p-1 text-muted-foreground"
116-
aria-label="Close alert"
117-
variant="ghost"
118-
>
119-
<XIcon className="size-5" />
120-
</Button>
121-
<div>
122-
<h2 className="mb-0.5 font-semibold">OP Gas Credit Program</h2>
123-
<p className="text-muted-foreground text-sm">
103+
<DismissibleAlert
104+
localStorageId={`${props.projectId}-gas-credit-alert`}
105+
title="OP Gas Credit Program"
106+
description={
107+
<>
124108
Redeem credits towards gas Sponsorship. <br className="lg:hidden" />
125109
<TrackedUnderlineLink
126110
target="_blank"
@@ -130,22 +114,26 @@ function GasCreditAlert(props: {
130114
>
131115
Learn More
132116
</TrackedUnderlineLink>
133-
</p>
134-
135-
<div className="mt-4 flex items-center gap-4">
136-
<Button asChild variant="outline" size="sm" className="bg-background">
137-
<TrackedLinkTW
138-
href={`/team/${props.teamSlug}/~/settings/credits`}
139-
target="_blank"
140-
className="gap-2"
141-
category={TRACKING_CATEGORY}
142-
label="claim-credits"
117+
<div className="mt-4 flex items-center gap-4">
118+
<Button
119+
asChild
120+
variant="outline"
121+
size="sm"
122+
className="bg-background"
143123
>
144-
Claim your credits <ArrowRightIcon className="size-4" />
145-
</TrackedLinkTW>
146-
</Button>
147-
</div>
148-
</div>
149-
</div>
124+
<TrackedLinkTW
125+
href={`/team/${props.teamSlug}/~/settings/credits`}
126+
target="_blank"
127+
className="gap-2"
128+
category={TRACKING_CATEGORY}
129+
label="claim-credits"
130+
>
131+
Claim your credits <ArrowRightIcon className="size-4" />
132+
</TrackedLinkTW>
133+
</Button>
134+
</div>
135+
</>
136+
}
137+
/>
150138
);
151139
}

0 commit comments

Comments
 (0)