Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import { groupBy } from "@stackframe/stack-shared/dist/utils/arrays";
import { wait } from "@stackframe/stack-shared/dist/utils/promises";
import { stringCompare } from "@stackframe/stack-shared/dist/utils/strings";
import { Button, Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, Typography, toast } from "@stackframe/stack-ui";
import { UserPlus } from "lucide-react";
import { Suspense, useEffect, useMemo, useState } from "react";
import { Settings } from "lucide-react";
import { useEffect, useMemo, useState } from "react";
import * as yup from "yup";


Expand Down Expand Up @@ -96,26 +96,29 @@ export default function PageClient(props: { inviteUser: (origin: string, teamId:
</div>
</div>

{projectsByTeam.map(({ teamId, projects }) => (
<div key={teamId} className="mb-4">
<Typography type="label" className="flex items-center">
{teamId && teams.find(t => t.id === teamId) && (
<Suspense fallback={<Button size="icon" variant="ghost" disabled><UserPlus className="w-4 h-4" /></Button>}>
{projectsByTeam.map(({ teamId, projects }) => {
const team = teamId ? teams.find((t) => t.id === teamId) : undefined;
return (
<div key={teamId} className="mb-4">
<div className="mb-2 flex items-center gap-1">
<Typography>
{teamId ? teamIdMap.get(teamId) : "No Team"}
</Typography>
{team && (
<TeamAddUserDialog
team={teams.find(t => t.id === teamId)!}
onSubmit={(email) => props.inviteUser(window.location.origin, teamId, email)}
team={team}
onSubmit={(email) => props.inviteUser(window.location.origin, team.id, email)}
/>
</Suspense>
)}
{teamId ? teamIdMap.get(teamId) : "No Team"}
</Typography>
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
{projects.map((project) => (
<ProjectCard key={project.id} project={project} />
))}
)}
</div>
<div className="grid gap-4 grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4">
{projects.map((project) => (
<ProjectCard key={project.id} project={project} />
))}
</div>
</div>
</div>
))}
);
})}
</div>
);
}
Expand All @@ -128,11 +131,12 @@ function TeamAddUserDialog(props: {
team: Team,
onSubmit: (email: string) => Promise<void>,
}) {
const users = props.team.useUsers();
const { quantity } = props.team.useItem("dashboard_admins");

const onSubmit = async (values: yup.InferType<typeof inviteFormSchema>) => {
if (users.length + 1 > quantity) {
const [users, admins] = await Promise.all([
props.team.listUsers(),
props.team.getItem("dashboard_admins"),
]);
if (users.length + 1 > admins.quantity) {
alert("You have reached the maximum number of dashboard admins. Please upgrade your plan to add more admins.");
const checkoutUrl = await props.team.createCheckoutUrl({
productId: "team",
Expand All @@ -146,11 +150,20 @@ function TeamAddUserDialog(props: {
};

return <FormDialog
title={"Invite a new user"}
title={`Invite a new user to ${JSON.stringify(props.team.displayName)}`}
formSchema={inviteFormSchema}
okButton={{ label: "Invite" }}
onSubmit={onSubmit}
trigger={<Button size="icon" variant="ghost"><UserPlus className="w-4 h-4" /></Button>}
trigger={
<Button
variant="ghost"
size="icon"
aria-label={`Invite teammates to ${props.team.displayName}`}
title={`Invite teammates to ${props.team.displayName}`}
>
<Settings className="h-4 w-4" />
</Button>
}
render={(form) => <InputField control={form.control} name="email" placeholder="Email" />}
/>;
}
Loading