mirror of
https://github.com/formbricks/formbricks.git
synced 2026-02-18 04:49:17 -06:00
Add Team switch (#259)
* add team switch when user has multiple teams * fix hydration error by changing the way env variables load --------- Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import FaveIcon from "@/app/favicon.ico";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@@ -17,6 +18,8 @@ import {
|
||||
} from "@/components/shared/DropdownMenu";
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useEnvironment } from "@/lib/environments/environments";
|
||||
import { useMemberships } from "@/lib/memberships";
|
||||
import { useTeam } from "@/lib/teams/teams";
|
||||
import { capitalizeFirstLetter } from "@/lib/utils";
|
||||
import {
|
||||
CustomersIcon,
|
||||
@@ -47,7 +50,6 @@ import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import AddProductModal from "./AddProductModal";
|
||||
import FaveIcon from "@/app/favicon.ico";
|
||||
|
||||
interface EnvironmentsNavbarProps {
|
||||
environmentId: string;
|
||||
@@ -56,12 +58,16 @@ interface EnvironmentsNavbarProps {
|
||||
|
||||
export default function EnvironmentsNavbar({ environmentId, session }: EnvironmentsNavbarProps) {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { environment, isErrorEnvironment, isLoadingEnvironment } = useEnvironment(environmentId);
|
||||
const pathname = usePathname();
|
||||
|
||||
const [widgetSetupCompleted, setWidgetSetupCompleted] = useState(false);
|
||||
const { environment, isErrorEnvironment, isLoadingEnvironment } = useEnvironment(environmentId);
|
||||
const { memberships, isErrorMemberships, isLoadingMemberships } = useMemberships();
|
||||
const { team } = useTeam(environmentId);
|
||||
|
||||
const [currentTeamName, setCurrentTeamName] = useState("");
|
||||
const [currentTeamId, setCurrentTeamId] = useState("");
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [widgetSetupCompleted, setWidgetSetupCompleted] = useState(false);
|
||||
const [showAddProductModal, setShowAddProductModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -72,6 +78,13 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
}
|
||||
}, [environment]);
|
||||
|
||||
useEffect(() => {
|
||||
if (team && team.name !== "") {
|
||||
setCurrentTeamName(team.name);
|
||||
setCurrentTeamId(team.id);
|
||||
}
|
||||
}, [team]);
|
||||
|
||||
const navigation = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -139,11 +152,6 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
href: `/environments/${environmentId}/settings/billing`,
|
||||
hidden: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1",
|
||||
},
|
||||
/* {
|
||||
icon: RocketLaunchIcon,
|
||||
label: "Upgrade account",
|
||||
href: `/environments/${environmentId}/settings/billing`,
|
||||
}, */
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -182,11 +190,24 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
router.push(`/environments/${newEnvironmentId}/`);
|
||||
};
|
||||
|
||||
if (isLoadingEnvironment || loading) {
|
||||
const changeEnvironmentByTeam = (teamId: string) => {
|
||||
const newTeamMembership = memberships.find((m) => m.teamId === teamId);
|
||||
const newTeamProduct = newTeamMembership?.team?.products?.[0];
|
||||
|
||||
if (newTeamProduct) {
|
||||
const newEnvironmentId = newTeamProduct.environments.find((e) => e.type === "production")?.id;
|
||||
|
||||
if (newEnvironmentId) {
|
||||
router.push(`/environments/${newEnvironmentId}/`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoadingEnvironment || loading || isLoadingMemberships) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
|
||||
if (isErrorEnvironment) {
|
||||
if (isErrorEnvironment || isErrorMemberships) {
|
||||
return <ErrorComponent />;
|
||||
}
|
||||
|
||||
@@ -255,6 +276,8 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
{/* Product Switch */}
|
||||
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<div>
|
||||
@@ -286,6 +309,8 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
|
||||
{/* Environment Switch */}
|
||||
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<div>
|
||||
@@ -309,6 +334,34 @@ export default function EnvironmentsNavbar({ environmentId, session }: Environme
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
|
||||
{/* Team Switch */}
|
||||
{memberships.length > 1 && (
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger>
|
||||
<div>
|
||||
<p>{currentTeamName}</p>
|
||||
<p className="block text-xs text-slate-500">Team</p>
|
||||
</div>
|
||||
</DropdownMenuSubTrigger>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuSubContent>
|
||||
<DropdownMenuRadioGroup
|
||||
value={currentTeamId}
|
||||
onValueChange={(teamId) => changeEnvironmentByTeam(teamId)}>
|
||||
{memberships?.map((membership) => (
|
||||
<DropdownMenuRadioItem
|
||||
value={membership.teamId}
|
||||
className="cursor-pointer"
|
||||
key={membership.teamId}>
|
||||
{membership.team.name}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuSubContent>
|
||||
</DropdownMenuPortal>
|
||||
</DropdownMenuSub>
|
||||
)}
|
||||
|
||||
{dropdownnavigation.map((item) => (
|
||||
<DropdownMenuGroup key={item.title}>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
@@ -1,155 +1,155 @@
|
||||
"use client";
|
||||
|
||||
import { IS_FORMBRICKS_CLOUD } from "@formbricks/lib/constants";
|
||||
import {
|
||||
AdjustmentsVerticalIcon,
|
||||
ChatBubbleLeftEllipsisIcon,
|
||||
CreditCardIcon,
|
||||
DocumentCheckIcon,
|
||||
DocumentMagnifyingGlassIcon,
|
||||
KeyIcon,
|
||||
LinkIcon,
|
||||
PaintBrushIcon,
|
||||
UserCircleIcon,
|
||||
UsersIcon,
|
||||
KeyIcon,
|
||||
} from "@heroicons/react/24/solid";
|
||||
import clsx from "clsx";
|
||||
import Link from "next/link";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useMemo } from "react";
|
||||
|
||||
export default function SettingsNavbar({ environmentId }: { environmentId: string }) {
|
||||
const pathname = usePathname();
|
||||
const navigation = [
|
||||
{
|
||||
title: "Account",
|
||||
links: [
|
||||
{
|
||||
name: "Profile",
|
||||
href: `/environments/${environmentId}/settings/profile`,
|
||||
icon: UserCircleIcon,
|
||||
current: pathname?.includes("/profile"),
|
||||
hidden: false,
|
||||
},
|
||||
/* {
|
||||
name: "Notifications",
|
||||
href: `/environments/${environmentId}/settings/notifications`,
|
||||
icon: MegaphoneIcon,
|
||||
current: pathname?.includes("/notifications"),
|
||||
}, */
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Product",
|
||||
links: [
|
||||
{
|
||||
name: "Settings",
|
||||
href: `/environments/${environmentId}/settings/product`,
|
||||
icon: AdjustmentsVerticalIcon,
|
||||
current: pathname?.includes("/product"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Look & Feel",
|
||||
href: `/environments/${environmentId}/settings/lookandfeel`,
|
||||
icon: PaintBrushIcon,
|
||||
current: pathname?.includes("/lookandfeel"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "API Keys",
|
||||
href: `/environments/${environmentId}/settings/api-keys`,
|
||||
icon: KeyIcon,
|
||||
current: pathname?.includes("/api-keys"),
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
links: [
|
||||
{
|
||||
name: "Members",
|
||||
href: `/environments/${environmentId}/settings/members`,
|
||||
icon: UsersIcon,
|
||||
current: pathname?.includes("/members"),
|
||||
hidden: false,
|
||||
},
|
||||
/*
|
||||
const navigation = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: "Account",
|
||||
links: [
|
||||
{
|
||||
name: "Profile",
|
||||
href: `/environments/${environmentId}/settings/profile`,
|
||||
icon: UserCircleIcon,
|
||||
current: pathname?.includes("/profile"),
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Product",
|
||||
links: [
|
||||
{
|
||||
name: "Settings",
|
||||
href: `/environments/${environmentId}/settings/product`,
|
||||
icon: AdjustmentsVerticalIcon,
|
||||
current: pathname?.includes("/product"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Look & Feel",
|
||||
href: `/environments/${environmentId}/settings/lookandfeel`,
|
||||
icon: PaintBrushIcon,
|
||||
current: pathname?.includes("/lookandfeel"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "API Keys",
|
||||
href: `/environments/${environmentId}/settings/api-keys`,
|
||||
icon: KeyIcon,
|
||||
current: pathname?.includes("/api-keys"),
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Team",
|
||||
links: [
|
||||
{
|
||||
name: "Members",
|
||||
href: `/environments/${environmentId}/settings/members`,
|
||||
icon: UsersIcon,
|
||||
current: pathname?.includes("/members"),
|
||||
hidden: false,
|
||||
},
|
||||
/*
|
||||
{
|
||||
name: "Tags",
|
||||
href: `/environments/${environmentId}/settings/tags`,
|
||||
icon: PlusCircleIcon,
|
||||
current: pathname?.includes("/tags"),
|
||||
}, */
|
||||
{
|
||||
name: "Billing & Plan",
|
||||
href: `/environments/${environmentId}/settings/billing`,
|
||||
icon: CreditCardIcon,
|
||||
hidden: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1",
|
||||
current: pathname?.includes("/billing"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Setup",
|
||||
links: [
|
||||
{
|
||||
name: "Setup Checklist",
|
||||
href: `/environments/${environmentId}/settings/setup`,
|
||||
icon: DocumentCheckIcon,
|
||||
current: pathname?.includes("/setup"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Documentation",
|
||||
href: "https://formbricks.com/docs",
|
||||
icon: DocumentMagnifyingGlassIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Join Discord",
|
||||
href: "https://formbricks.com/discord",
|
||||
icon: ChatBubbleLeftEllipsisIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Compliance",
|
||||
links: [
|
||||
{
|
||||
name: "GDPR & CCPA",
|
||||
href: "https://formbricks.com/gdpr",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1",
|
||||
},
|
||||
{
|
||||
name: "Privacy",
|
||||
href: "https://formbricks.com/privacy",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1",
|
||||
},
|
||||
{
|
||||
name: "Terms",
|
||||
href: "https://formbricks.com/terms",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1",
|
||||
},
|
||||
{
|
||||
name: "License",
|
||||
href: "https://github.com/formbricks/formbricks/blob/main/LICENSE",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
{
|
||||
name: "Billing & Plan",
|
||||
href: `/environments/${environmentId}/settings/billing`,
|
||||
icon: CreditCardIcon,
|
||||
hidden: !IS_FORMBRICKS_CLOUD,
|
||||
current: pathname?.includes("/billing"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Setup",
|
||||
links: [
|
||||
{
|
||||
name: "Setup Checklist",
|
||||
href: `/environments/${environmentId}/settings/setup`,
|
||||
icon: DocumentCheckIcon,
|
||||
current: pathname?.includes("/setup"),
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Documentation",
|
||||
href: "https://formbricks.com/docs",
|
||||
icon: DocumentMagnifyingGlassIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
{
|
||||
name: "Join Discord",
|
||||
href: "https://formbricks.com/discord",
|
||||
icon: ChatBubbleLeftEllipsisIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Compliance",
|
||||
links: [
|
||||
{
|
||||
name: "GDPR & CCPA",
|
||||
href: "https://formbricks.com/gdpr",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: !IS_FORMBRICKS_CLOUD,
|
||||
},
|
||||
{
|
||||
name: "Privacy",
|
||||
href: "https://formbricks.com/privacy",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: !IS_FORMBRICKS_CLOUD,
|
||||
},
|
||||
{
|
||||
name: "Terms",
|
||||
href: "https://formbricks.com/terms",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: !IS_FORMBRICKS_CLOUD,
|
||||
},
|
||||
{
|
||||
name: "License",
|
||||
href: "https://github.com/formbricks/formbricks/blob/main/LICENSE",
|
||||
icon: LinkIcon,
|
||||
target: "_blank",
|
||||
hidden: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
[environmentId, pathname]
|
||||
);
|
||||
|
||||
if (!navigation) return null;
|
||||
|
||||
return (
|
||||
<div className="fixed h-full bg-white py-2 pl-4 pr-10">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useTeam } from "@/lib/teams";
|
||||
import { useTeam } from "@/lib/teams/teams";
|
||||
import { Badge, Button, ErrorComponent } from "@formbricks/ui";
|
||||
import type { Session } from "next-auth";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
@@ -1,18 +1,51 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@formbricks/ui";
|
||||
import { Input } from "@formbricks/ui";
|
||||
import { Label } from "@formbricks/ui";
|
||||
import { useEffect, useState } from "react";
|
||||
import LoadingSpinner from "@/components/shared/LoadingSpinner";
|
||||
import { useTeamMutation } from "@/lib/teams/mutateTeams";
|
||||
import { useTeam } from "@/lib/teams/teams";
|
||||
import { Button, ErrorComponent, Input, Label } from "@formbricks/ui";
|
||||
import { useForm } from "react-hook-form";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
export default function EditTeamName({ environmentId }) {
|
||||
const { team, isLoadingTeam, isErrorTeam } = useTeam(environmentId);
|
||||
const { register, handleSubmit } = useForm();
|
||||
const [teamId, setTeamId] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
if (team && team.id !== "") {
|
||||
setTeamId(team.id);
|
||||
}
|
||||
}, [team]);
|
||||
|
||||
const { isMutatingTeam, triggerTeamMutate } = useTeamMutation(teamId);
|
||||
|
||||
if (isLoadingTeam) {
|
||||
return <LoadingSpinner />;
|
||||
}
|
||||
if (isErrorTeam) {
|
||||
return <ErrorComponent />;
|
||||
}
|
||||
|
||||
export function EditTeamName() {
|
||||
return (
|
||||
<div className="w-full max-w-sm items-center">
|
||||
<form
|
||||
className="w-full max-w-sm items-center"
|
||||
onSubmit={handleSubmit((data) => {
|
||||
triggerTeamMutate({ ...data })
|
||||
.catch((error) => {
|
||||
toast.error(`Error: ${error.message}`);
|
||||
})
|
||||
.then(() => {
|
||||
toast.success("Team name updated successfully.");
|
||||
});
|
||||
})}>
|
||||
<Label htmlFor="teamname">Team Name</Label>
|
||||
<Input type="text" id="teamname" />
|
||||
<Input type="text" id="teamname" defaultValue={team.name} {...register("name")} />
|
||||
|
||||
<Button type="submit" className="mt-4" onClick={() => new Error("Not implemented yet")}>
|
||||
<Button type="submit" className="mt-4" loading={isMutatingTeam}>
|
||||
Update
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
import SettingsCard from "../SettingsCard";
|
||||
import SettingsTitle from "../SettingsTitle";
|
||||
import { EditMemberships } from "./EditMemberships";
|
||||
import EditTeamName from "./EditTeamName";
|
||||
|
||||
export default function MembersSettingsPage({ params }) {
|
||||
return (
|
||||
<div>
|
||||
<SettingsTitle title="Team Members" />
|
||||
<SettingsTitle title="Team" />
|
||||
<SettingsCard title="Manage members" description="Add or remove members in your team.">
|
||||
<EditMemberships environmentId={params.environmentId} />
|
||||
</SettingsCard>
|
||||
<SettingsCard title="Team Name" description="Give your team a descriptive name.">
|
||||
<EditTeamName environmentId={params.environmentId} />
|
||||
</SettingsCard>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ export default function SummaryList({ environmentId, surveyId }) {
|
||||
|
||||
const responses = responsesData?.responses;
|
||||
|
||||
console.log(responses);
|
||||
|
||||
const summaryData: QuestionSummary[] = useMemo(() => {
|
||||
if (survey && responses) {
|
||||
return survey.questions.map((question) => {
|
||||
|
||||
11
apps/web/lib/teams/mutateTeams.ts
Normal file
11
apps/web/lib/teams/mutateTeams.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import useSWRMutation from "swr/mutation";
|
||||
import { updateRessource } from "@formbricks/lib/fetcher";
|
||||
|
||||
export function useTeamMutation(teamId: string) {
|
||||
const { trigger, isMutating } = useSWRMutation(`/api/v1/teams/${teamId}`, updateRessource);
|
||||
|
||||
return {
|
||||
triggerTeamMutate: trigger,
|
||||
isMutatingTeam: isMutating,
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,10 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
|
||||
const path = require("path");
|
||||
const Dotenv = require("dotenv-webpack");
|
||||
|
||||
const rootPath = path.join(__dirname, "..", "..");
|
||||
|
||||
const { createId } = require("@paralleldrive/cuid2");
|
||||
|
||||
const nextConfig = {
|
||||
@@ -48,6 +53,14 @@ const nextConfig = {
|
||||
},
|
||||
];
|
||||
},
|
||||
webpack: (config) => {
|
||||
config.plugins.push(
|
||||
new Dotenv({
|
||||
path: path.resolve(rootPath, ".env"),
|
||||
})
|
||||
);
|
||||
return config;
|
||||
},
|
||||
env: {
|
||||
INSTANCE_ID: createId(),
|
||||
},
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-variance-authority": "^0.5.2",
|
||||
"date-fns": "^2.29.3",
|
||||
"dotenv-webpack": "^8.0.1",
|
||||
"eslint": "8.38.0",
|
||||
"eslint-config-next": "^13.3.0",
|
||||
"jsonwebtoken": "^9.0.0",
|
||||
|
||||
@@ -16,6 +16,26 @@ export default async function handle(req: NextApiRequest, res: NextApiResponse)
|
||||
where: {
|
||||
userId: user.id,
|
||||
},
|
||||
include: {
|
||||
team: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
products: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
environments: {
|
||||
select: {
|
||||
id: true,
|
||||
type: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
return res.json(memberships);
|
||||
}
|
||||
|
||||
60
apps/web/pages/api/v1/teams/[teamId]/index.ts
Normal file
60
apps/web/pages/api/v1/teams/[teamId]/index.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { getSessionUser, hasTeamAccess } from "@/lib/api/apiHelper";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handle(req: NextApiRequest, res: NextApiResponse) {
|
||||
// Check Authentication
|
||||
|
||||
const currentUser: any = await getSessionUser(req, res);
|
||||
if (!currentUser) {
|
||||
return res.status(401).json({ message: "Not authenticated" });
|
||||
}
|
||||
|
||||
const teamId = req.query.teamId?.toString();
|
||||
if (teamId === undefined) {
|
||||
return res.status(400).json({ message: "Missing teamId" });
|
||||
}
|
||||
|
||||
const hasAccess = await hasTeamAccess(currentUser, teamId);
|
||||
if (!hasAccess) {
|
||||
return res.status(403).json({ message: "Not authorized" });
|
||||
}
|
||||
|
||||
// PUT /api/v1/teams/[teamId]
|
||||
// Update a team
|
||||
if (req.method === "PUT") {
|
||||
const { name } = req.body;
|
||||
if (name === undefined) {
|
||||
return res.status(400).json({ message: "Missing name" });
|
||||
}
|
||||
|
||||
// check if currentUser is owner of the team
|
||||
const membership = await prisma.membership.findUnique({
|
||||
where: {
|
||||
userId_teamId: {
|
||||
userId: currentUser.id,
|
||||
teamId,
|
||||
},
|
||||
},
|
||||
});
|
||||
if (membership?.role !== "owner") {
|
||||
return res.status(403).json({ message: "You are not allowed to update this team" });
|
||||
}
|
||||
|
||||
// update team
|
||||
const team = await prisma.team.update({
|
||||
where: {
|
||||
id: teamId,
|
||||
},
|
||||
data: {
|
||||
name,
|
||||
},
|
||||
});
|
||||
return res.json(team);
|
||||
}
|
||||
|
||||
// Unknown HTTP Method
|
||||
else {
|
||||
throw new Error(`The HTTP ${req.method} method is not supported by this route.`);
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export const RESPONSES_LIMIT_FREE = 100;
|
||||
export const IS_FORMBRICKS_CLOUD = process.env.NEXT_PUBLIC_IS_FORMBRICKS_CLOUD !== "1";
|
||||
|
||||
26
pnpm-lock.yaml
generated
26
pnpm-lock.yaml
generated
@@ -219,6 +219,9 @@ importers:
|
||||
date-fns:
|
||||
specifier: ^2.29.3
|
||||
version: 2.29.3
|
||||
dotenv-webpack:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1(webpack@5.75.0)
|
||||
eslint:
|
||||
specifier: 8.38.0
|
||||
version: 8.38.0
|
||||
@@ -4645,7 +4648,7 @@ packages:
|
||||
tailwindcss: '>=3.0.0 || >= 3.0.0-alpha.1'
|
||||
dependencies:
|
||||
mini-svg-data-uri: 1.4.4
|
||||
tailwindcss: 3.3.1(postcss@8.4.21)
|
||||
tailwindcss: 3.3.1(postcss@8.4.22)
|
||||
dev: true
|
||||
|
||||
/@tailwindcss/typography@0.5.9(tailwindcss@3.3.1):
|
||||
@@ -8445,6 +8448,22 @@ packages:
|
||||
dependencies:
|
||||
is-obj: 2.0.0
|
||||
|
||||
/dotenv-defaults@2.0.2:
|
||||
resolution: {integrity: sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==}
|
||||
dependencies:
|
||||
dotenv: 8.6.0
|
||||
dev: false
|
||||
|
||||
/dotenv-webpack@8.0.1(webpack@5.75.0):
|
||||
resolution: {integrity: sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
webpack: ^4 || ^5
|
||||
dependencies:
|
||||
dotenv-defaults: 2.0.2
|
||||
webpack: 5.75.0
|
||||
dev: false
|
||||
|
||||
/dotenv@16.0.3:
|
||||
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -8455,6 +8474,11 @@ packages:
|
||||
engines: {node: '>=4.6.0'}
|
||||
dev: false
|
||||
|
||||
/dotenv@8.6.0:
|
||||
resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/duplexer3@0.1.5:
|
||||
resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user