Skip to content

[Dashboard] Add staff mode for viewing teams without membership #7299

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions apps/dashboard/src/app/(app)/team/[team_slug]/(team)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { getProjects } from "@/api/projects";
import { getTeams } from "@/api/team";
import { getTeamBySlug, getTeams } from "@/api/team";
import { AppFooter } from "@/components/blocks/app-footer";
import { Button } from "@/components/ui/button";
import { TabPathLinks } from "@/components/ui/tabs";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { AnnouncementBanner } from "components/notices/AnnouncementBanner";
import Link from "next/link";
import { redirect } from "next/navigation";
import { siwaExamplePrompts } from "../../../(dashboard)/support/page";
import { CustomChatButton } from "../../../../nebula-app/(app)/components/CustomChat/CustomChatButton";
Expand All @@ -20,25 +22,18 @@ export default async function TeamLayout(props: {
}) {
const params = await props.params;

const [accountAddress, account, teams, authToken] = await Promise.all([
const [accountAddress, account, teams, authToken, team] = await Promise.all([
getAuthTokenWalletAddress(),
getValidAccount(`/team/${params.team_slug}`),
getTeams(),
getAuthToken(),
getTeamBySlug(params.team_slug),
]);

if (!teams || !accountAddress || !authToken) {
if (!teams || !accountAddress || !authToken || !team) {
redirect("/login");
}

const team = teams.find(
(t) => t.slug === decodeURIComponent(params.team_slug),
);

if (!team) {
redirect("/team");
}

const teamsAndProjects = await Promise.all(
teams.map(async (team) => ({
team,
Expand All @@ -53,6 +48,21 @@ export default async function TeamLayout(props: {

return (
<div className="flex h-full grow flex-col">
{!teams.some((t) => t.slug === team.slug) && (
<div className="bg-warning-text">
<div className="container flex items-center justify-between py-4">
<div className="flex flex-col gap-2">
<p className="font-bold text-white text-xl">👀 STAFF MODE 👀</p>
<p className="text-sm text-white">
You can only view this team, not take any actions.
</p>
</div>
<Button variant="default" asChild>
<Link href="/team/~">Leave Staff Mode</Link>
</Button>
</div>
</div>
)}
<AnnouncementBanner />
<div className="bg-card">
<TeamHeaderLoggedIn
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { getProjects } from "@/api/projects";
import { getTeams } from "@/api/team";
import { getProject, getProjects } from "@/api/projects";
import { getTeamBySlug, getTeams } from "@/api/team";
import { Button } from "@/components/ui/button";
import { SidebarProvider } from "@/components/ui/sidebar";
import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
import { AnnouncementBanner } from "components/notices/AnnouncementBanner";
import Link from "next/link";
import { redirect } from "next/navigation";
import { siwaExamplePrompts } from "../../../../(dashboard)/support/page";
import { CustomChatButton } from "../../../../../nebula-app/(app)/components/CustomChat/CustomChatButton";
Expand All @@ -21,21 +23,20 @@ export default async function ProjectLayout(props: {
params: Promise<{ team_slug: string; project_slug: string }>;
}) {
const params = await props.params;
const [accountAddress, teams, account, authToken] = await Promise.all([
getAuthTokenWalletAddress(),
getTeams(),
getValidAccount(`/team/${params.team_slug}/${params.project_slug}`),
getAuthToken(),
]);
const [accountAddress, teams, account, authToken, team, project] =
await Promise.all([
getAuthTokenWalletAddress(),
getTeams(),
getValidAccount(`/team/${params.team_slug}/${params.project_slug}`),
getAuthToken(),
getTeamBySlug(params.team_slug),
getProject(params.team_slug, params.project_slug),
]);

if (!teams || !accountAddress || !authToken) {
redirect("/login");
}

const team = teams.find(
(t) => t.slug === decodeURIComponent(params.team_slug),
);

if (!team) {
redirect("/team");
}
Expand All @@ -47,10 +48,6 @@ export default async function ProjectLayout(props: {
})),
);

const project = teamsAndProjects
.find((t) => t.team.slug === decodeURIComponent(params.team_slug))
?.projects.find((p) => p.slug === params.project_slug);

if (!project) {
// not a valid project, redirect back to team page
redirect(`/team/${params.team_slug}`);
Expand All @@ -65,6 +62,21 @@ export default async function ProjectLayout(props: {
return (
<SidebarProvider>
<div className="flex h-dvh min-w-0 grow flex-col">
{!teams.some((t) => t.slug === team.slug) && (
<div className="bg-warning-text">
<div className="container flex items-center justify-between py-4">
<div className="flex flex-col gap-2">
<p className="font-bold text-white text-xl">👀 STAFF MODE 👀</p>
<p className="text-sm text-white">
You can only view this team, not take any actions.
</p>
</div>
<Button variant="default" asChild>
<Link href="/team/~">Leave Staff Mode</Link>
</Button>
</div>
</div>
)}
<div className="sticky top-0 z-10 border-border border-b bg-card">
<AnnouncementBanner />
<TeamHeaderLoggedIn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,23 @@ export function TeamHeaderDesktopUI(props: TeamHeaderCompProps) {
<SlashSeparator />

<div className="flex items-center gap-1">
<Link
href={`/team/${currentTeam.slug}`}
className="flex flex-row items-center gap-2 font-normal text-sm"
>
<GradientAvatar
id={currentTeam.id}
src={currentTeam.image || ""}
className="size-6"
client={props.client}
/>
<span> {currentTeam.name} </span>
<TeamVerifiedIcon domain={currentTeam.verifiedDomain} />
<span className="flex flex-row items-center gap-2 font-normal text-sm">
<Link
href={`/team/${currentTeam.slug}`}
className="flex flex-row items-center gap-2 font-normal text-sm"
>
<GradientAvatar
id={currentTeam.id}
src={currentTeam.image || ""}
className="size-6"
client={props.client}
/>
<span> {currentTeam.name} </span>
<TeamVerifiedIcon domain={currentTeam.verifiedDomain} />
</Link>
{/* may render its own link so has to be outside of the link */}
<TeamPlanBadge plan={teamPlan} teamSlug={currentTeam.slug} />
</Link>
</span>

<TeamAndProjectSelectorPopoverButton
currentProject={props.currentProject}
Expand Down
Loading