mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-19 11:11:05 -05:00
feat: Automatic team assignment + skip onboarding (#1347)
Co-authored-by: jonas.hoebenreich <jonas.hoebenreich@flixbus.com> Co-authored-by: Matthias Nannt <mail@matthiasnannt.com>
This commit is contained in:
10
.env.example
10
.env.example
@@ -99,7 +99,6 @@ AZUREAD_AUTH_ENABLED=0
|
||||
AZUREAD_CLIENT_ID=
|
||||
AZUREAD_CLIENT_SECRET=
|
||||
AZUREAD_TENANT_ID=
|
||||
AZURE_DIRECT_REDIRECT=0
|
||||
|
||||
# Cron Secret
|
||||
CRON_SECRET=
|
||||
@@ -127,4 +126,13 @@ AIRTABLE_CLIENT_ID=
|
||||
# Enterprise License Key
|
||||
ENTERPRISE_LICENSE_KEY=
|
||||
|
||||
# Automatically assign new users to a specific team and role within that team
|
||||
# Insert an existing team id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
|
||||
# (Role Management is an Enterprise feature)
|
||||
# DEFAULT_TEAM_ID=
|
||||
# DEFAULT_TEAM_ROLE=admin
|
||||
|
||||
# set to 1 to skip onboarding for new users
|
||||
# ONBOARDING_DISABLED=1
|
||||
|
||||
*/
|
||||
|
||||
@@ -242,8 +242,10 @@ These variables can be provided at the runtime i.e. in your docker-compose file.
|
||||
| TELEMETRY_DISABLED | Disables telemetry if set to `1`. | optional | |
|
||||
| INSTANCE_ID | Instance ID for Formbricks Cloud to be sent to Telemetry. | optional | |
|
||||
| INTERNAL_SECRET | Internal Secret (Currently we overwrite the value with a random value). | optional | |
|
||||
| IS_FORMBRICKS_CLOUD | Uses Formbricks Cloud if set to `1` | optional | |
|
||||
| DEFAULT_BRAND_COLOR | Default brand color for your app (Can be overwritten from the UI as well). | optional | `#64748b` |
|
||||
| DEFAULT_TEAM_ID | Automatically assign new users to a specific team when joining | optional | |
|
||||
| DEFAULT_TEAM_ROLE | Role of the user in the default team. | optional | `admin` |
|
||||
| ONBOARDING_DISABLED | Disables onboarding for new users if set to `1` | optional | |
|
||||
|
||||
## Build-time Variables
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import TeamActions from "@/app/(app)/environments/[environmentId]/settings/members/components/EditMemberships/TeamActions";
|
||||
import { getIsEnterpriseEdition } from "@formbricks/ee/lib/service";
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { getMembershipsByUserId, getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { INVITE_DISABLED } from "@formbricks/lib/constants";
|
||||
import { getMembershipByUserIdTeamId, getMembershipsByUserId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { SettingsId } from "@formbricks/ui/SettingsId";
|
||||
import { Skeleton } from "@formbricks/ui/Skeleton";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { Suspense } from "react";
|
||||
@@ -10,9 +14,6 @@ import SettingsTitle from "../components/SettingsTitle";
|
||||
import DeleteTeam from "./components/DeleteTeam";
|
||||
import { EditMemberships } from "./components/EditMemberships";
|
||||
import EditTeamName from "./components/EditTeamName";
|
||||
import { INVITE_DISABLED } from "@formbricks/lib/constants";
|
||||
import { getIsEnterpriseEdition } from "@formbricks/ee/lib/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
|
||||
const MembersLoading = () => (
|
||||
<div className="rounded-lg border border-slate-200">
|
||||
@@ -103,6 +104,7 @@ export default async function MembersSettingsPage({ params }: { params: { enviro
|
||||
isUserOwner={currentUserRole === "owner"}
|
||||
/>
|
||||
</SettingsCard>
|
||||
<SettingsId title="Team" id={team.id}></SettingsId>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
|
||||
import SettingsCard from "../components/SettingsCard";
|
||||
import SettingsTitle from "../components/SettingsTitle";
|
||||
|
||||
import EditProductName from "./components/EditProductName";
|
||||
import EditWaitingTime from "./components/EditWaitingTime";
|
||||
import DeleteProduct from "./components/DeleteProduct";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { getEnvironment } from "@formbricks/lib/environment/service";
|
||||
import { getMembershipByUserIdTeamId } from "@formbricks/lib/membership/service";
|
||||
import { getAccessFlags } from "@formbricks/lib/membership/utils";
|
||||
import { getProductByEnvironmentId } from "@formbricks/lib/product/service";
|
||||
import { getTeamByEnvironmentId } from "@formbricks/lib/team/service";
|
||||
import { ErrorComponent } from "@formbricks/ui/ErrorComponent";
|
||||
import { SettingsId } from "@formbricks/ui/SettingsId";
|
||||
import { getServerSession } from "next-auth";
|
||||
import SettingsCard from "../components/SettingsCard";
|
||||
import SettingsTitle from "../components/SettingsTitle";
|
||||
import DeleteProduct from "./components/DeleteProduct";
|
||||
import EditProductName from "./components/EditProductName";
|
||||
import EditWaitingTime from "./components/EditWaitingTime";
|
||||
|
||||
export default async function ProfileSettingsPage({ params }: { params: { environmentId: string } }) {
|
||||
const [, product, session, team] = await Promise.all([
|
||||
@@ -60,6 +59,7 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro
|
||||
description="Delete product with all surveys, responses, people, actions and attributes. This cannot be undone.">
|
||||
<DeleteProduct environmentId={params.environmentId} product={product} />
|
||||
</SettingsCard>
|
||||
<SettingsId title="Product" id={product.id}></SettingsId>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
export const revalidate = REVALIDATION_INTERVAL;
|
||||
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getServerSession } from "next-auth";
|
||||
import AccountSecurity from "@/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity";
|
||||
import { authOptions } from "@formbricks/lib/authOptions";
|
||||
import { REVALIDATION_INTERVAL } from "@formbricks/lib/constants";
|
||||
import { getProfile } from "@formbricks/lib/profile/service";
|
||||
import { SettingsId } from "@formbricks/ui/SettingsId";
|
||||
import { getServerSession } from "next-auth";
|
||||
import SettingsCard from "../components/SettingsCard";
|
||||
import SettingsTitle from "../components/SettingsTitle";
|
||||
import { DeleteAccount } from "./components/DeleteAccount";
|
||||
import { EditName } from "./components/EditName";
|
||||
import { EditAvatar } from "./components/EditAvatar";
|
||||
import AccountSecurity from "@/app/(app)/environments/[environmentId]/settings/profile/components/AccountSecurity";
|
||||
import { getProfile } from "@formbricks/lib/profile/service";
|
||||
import { EditName } from "./components/EditName";
|
||||
|
||||
export default async function ProfileSettingsPage({ params }: { params: { environmentId: string } }) {
|
||||
const { environmentId } = params;
|
||||
@@ -38,6 +39,7 @@ export default async function ProfileSettingsPage({ params }: { params: { enviro
|
||||
description="Delete your account with all of your personal information and data.">
|
||||
<DeleteAccount session={session} />
|
||||
</SettingsCard>
|
||||
<SettingsId title="Profile" id={profile.id}></SettingsId>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { sendInviteAcceptedEmail, sendVerificationEmail } from "@/app/lib/email";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { EMAIL_VERIFICATION_DISABLED, INVITE_DISABLED, SIGNUP_ENABLED } from "@formbricks/lib/constants";
|
||||
import { verifyInviteToken } from "@formbricks/lib/jwt";
|
||||
import { env } from "@formbricks/lib/env.mjs";
|
||||
import { deleteInvite } from "@formbricks/lib/invite/service";
|
||||
import { verifyInviteToken } from "@formbricks/lib/jwt";
|
||||
import { createMembership } from "@formbricks/lib/membership/service";
|
||||
import { createProduct } from "@formbricks/lib/product/service";
|
||||
import { createProfile } from "@formbricks/lib/profile/service";
|
||||
import { createTeam } from "@formbricks/lib/team/service";
|
||||
import { createTeam, getTeam } from "@formbricks/lib/team/service";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export async function POST(request: Request) {
|
||||
@@ -21,6 +22,10 @@ export async function POST(request: Request) {
|
||||
try {
|
||||
let invite;
|
||||
|
||||
// create the user
|
||||
user = await createProfile(user);
|
||||
|
||||
// User is invited to team
|
||||
if (inviteToken) {
|
||||
let inviteTokenData = await verifyInviteToken(inviteToken);
|
||||
inviteId = inviteTokenData?.inviteId;
|
||||
@@ -36,35 +41,47 @@ export async function POST(request: Request) {
|
||||
return NextResponse.json({ error: "Invalid invite ID" }, { status: 400 });
|
||||
}
|
||||
|
||||
// create a user and assign him to the team
|
||||
|
||||
const profile = await createProfile(user);
|
||||
await createMembership(invite.teamId, profile.id, {
|
||||
// assign user to existing team
|
||||
await createMembership(invite.teamId, user.id, {
|
||||
accepted: true,
|
||||
role: invite.role,
|
||||
});
|
||||
|
||||
if (!EMAIL_VERIFICATION_DISABLED) {
|
||||
await sendVerificationEmail(profile);
|
||||
await sendVerificationEmail(user);
|
||||
}
|
||||
|
||||
await sendInviteAcceptedEmail(invite.creator.name, user.name, invite.creator.email);
|
||||
await deleteInvite(inviteId);
|
||||
|
||||
return NextResponse.json(profile);
|
||||
} else {
|
||||
const team = await createTeam({
|
||||
name: `${user.name}'s Team`,
|
||||
});
|
||||
await createProduct(team.id, { name: "My Product" });
|
||||
const profile = await createProfile(user);
|
||||
await createMembership(team.id, profile.id, { role: "owner", accepted: true });
|
||||
|
||||
if (!EMAIL_VERIFICATION_DISABLED) {
|
||||
await sendVerificationEmail(profile);
|
||||
}
|
||||
return NextResponse.json(profile);
|
||||
return NextResponse.json(user);
|
||||
}
|
||||
|
||||
// User signs up without invite
|
||||
// Default team assignment is enabled
|
||||
if (env.DEFAULT_TEAM_ID && env.DEFAULT_TEAM_ID.length > 0) {
|
||||
// check if team exists
|
||||
let team = await getTeam(env.DEFAULT_TEAM_ID);
|
||||
let isNewTeam = false;
|
||||
if (!team) {
|
||||
// create team with id from env
|
||||
team = await createTeam({ id: env.DEFAULT_TEAM_ID, name: user.name + "'s Team" });
|
||||
isNewTeam = true;
|
||||
}
|
||||
const role = isNewTeam ? "owner" : env.DEFAULT_TEAM_ROLE || "admin";
|
||||
await createMembership(team.id, user.id, { role, accepted: true });
|
||||
}
|
||||
// Without default team assignment
|
||||
else {
|
||||
const team = await createTeam({ name: user.name + "'s Team" });
|
||||
await createMembership(team.id, user.id, { role: "owner", accepted: true });
|
||||
await createProduct(team.id, { name: "My Product" });
|
||||
}
|
||||
// send verification email amd return user
|
||||
if (!EMAIL_VERIFICATION_DISABLED) {
|
||||
await sendVerificationEmail(user);
|
||||
}
|
||||
return NextResponse.json(user);
|
||||
} catch (e) {
|
||||
if (e.code === "P2002") {
|
||||
return NextResponse.json(
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hashPassword } from "../auth";
|
||||
import { hashPassword } from "@formbricks/lib/auth/util";
|
||||
|
||||
export const createUser = async (
|
||||
name: string,
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getFirstEnvironmentByUserId } from "@formbricks/lib/environment/service
|
||||
import type { Session } from "next-auth";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { redirect } from "next/navigation";
|
||||
import { ONBOARDING_DISABLED } from "@formbricks/lib/constants";
|
||||
|
||||
export default async function Home() {
|
||||
const session: Session | null = await getServerSession(authOptions);
|
||||
@@ -12,7 +13,7 @@ export default async function Home() {
|
||||
redirect("/auth/login");
|
||||
}
|
||||
|
||||
if (session?.user && !session?.user?.onboardingCompleted) {
|
||||
if (!ONBOARDING_DISABLED && session?.user && !session?.user?.onboardingCompleted) {
|
||||
return redirect(`/onboarding`);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,8 +61,10 @@ x-google-client-secret: &google_client_secret
|
||||
|
||||
x-sentry-ignore-api-resolution-error: &sentry_ignore_api_resolution_error # Disable Sentry warning
|
||||
|
||||
|
||||
x-next-public-sentry-dsn: &next_public_sentry_dsn # Enable Sentry Error Tracking
|
||||
|
||||
|
||||
x-cron-secret: &cron_secret YOUR_CRON_SECRET # Set this to a random string to secure your cron endpoints
|
||||
|
||||
services:
|
||||
|
||||
@@ -23,6 +23,10 @@ x-environment: &environment
|
||||
# PostgreSQL password
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
||||
# Enterprise License Key
|
||||
# Required to access Enterprise-only features
|
||||
# ENTERPRISE_LICENSE_KEY:
|
||||
|
||||
# Email Configuration
|
||||
# MAIL_FROM:
|
||||
# SMTP_HOST:
|
||||
@@ -65,6 +69,15 @@ x-environment: &environment
|
||||
# GOOGLE_CLIENT_ID:
|
||||
# GOOGLE_CLIENT_SECRET:
|
||||
|
||||
# Uncomment the below to automatically assign new users to a specific team and role within that team
|
||||
# Insert an existing team id or generate a valid CUID for a new one at https://www.getuniqueid.com/cuid (e.g. cjld2cjxh0000qzrmn831i7rn)
|
||||
# (Role Management is an Enterprise feature)
|
||||
# DEFAULT_TEAM_ID:
|
||||
# DEFAULT_TEAM_ROLE: admin
|
||||
|
||||
# Uncomment and set to 1 to skip onboarding for new users
|
||||
# ONBOARDING_DISABLED: 1
|
||||
|
||||
services:
|
||||
postgres:
|
||||
restart: always
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { env } from "./env.mjs";
|
||||
import { verifyPassword } from "@/app/lib/auth";
|
||||
import { prisma } from "@formbricks/database";
|
||||
import { EMAIL_VERIFICATION_DISABLED } from "./constants";
|
||||
import { verifyToken } from "./jwt";
|
||||
import { createProfile, getProfileByEmail, updateProfile } from "./profile/service";
|
||||
import type { IdentityProvider } from "@prisma/client";
|
||||
import type { NextAuthOptions } from "next-auth";
|
||||
import AzureAD from "next-auth/providers/azure-ad";
|
||||
import CredentialsProvider from "next-auth/providers/credentials";
|
||||
import GitHubProvider from "next-auth/providers/github";
|
||||
import GoogleProvider from "next-auth/providers/google";
|
||||
import AzureAD from "next-auth/providers/azure-ad";
|
||||
import { createTeam } from "./team/service";
|
||||
import { createProduct } from "./product/service";
|
||||
import { createAccount } from "./account/service";
|
||||
import { verifyPassword } from "./auth/util";
|
||||
import { EMAIL_VERIFICATION_DISABLED } from "./constants";
|
||||
import { env } from "./env.mjs";
|
||||
import { verifyToken } from "./jwt";
|
||||
import { createMembership } from "./membership/service";
|
||||
import { createProduct } from "./product/service";
|
||||
import { createProfile, getProfileByEmail, updateProfile } from "./profile/service";
|
||||
import { createTeam, getTeam } from "./team/service";
|
||||
|
||||
export const authOptions: NextAuthOptions = {
|
||||
providers: [
|
||||
@@ -219,14 +219,35 @@ export const authOptions: NextAuthOptions = {
|
||||
identityProvider: provider,
|
||||
identityProviderAccountId: account.providerAccountId,
|
||||
});
|
||||
const team = await createTeam({ name: userProfile.name + "'s Team" });
|
||||
await createAccount({
|
||||
...account,
|
||||
userId: userProfile.id,
|
||||
});
|
||||
await createProduct(team.id, { name: "My Product" });
|
||||
await createMembership(team.id, userProfile.id, { role: "owner", accepted: true });
|
||||
return true;
|
||||
// Default team assignment if env variable is set
|
||||
if (env.DEFAULT_TEAM_ID && env.DEFAULT_TEAM_ID.length > 0) {
|
||||
// check if team exists
|
||||
let team = await getTeam(env.DEFAULT_TEAM_ID);
|
||||
let isNewTeam = false;
|
||||
if (!team) {
|
||||
// create team with id from env
|
||||
team = await createTeam({ id: env.DEFAULT_TEAM_ID, name: userProfile.name + "'s Team" });
|
||||
isNewTeam = true;
|
||||
}
|
||||
const role = isNewTeam ? "owner" : env.DEFAULT_TEAM_ROLE || "admin";
|
||||
await createMembership(team.id, userProfile.id, { role, accepted: true });
|
||||
await createAccount({
|
||||
...account,
|
||||
userId: userProfile.id,
|
||||
});
|
||||
return true;
|
||||
}
|
||||
// Without default team assignment
|
||||
else {
|
||||
const team = await createTeam({ name: userProfile.name + "'s Team" });
|
||||
await createMembership(team.id, userProfile.id, { role: "owner", accepted: true });
|
||||
await createAccount({
|
||||
...account,
|
||||
userId: userProfile.id,
|
||||
});
|
||||
await createProduct(team.id, { name: "My Product" });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -57,6 +57,10 @@ export const ITEMS_PER_PAGE = 50;
|
||||
export const RESPONSES_PER_PAGE = 10;
|
||||
export const TEXT_RESPONSES_PER_PAGE = 5;
|
||||
|
||||
export const DEFAULT_TEAM_ID = env.DEFAULT_TEAM_ID;
|
||||
export const DEFAULT_TEAM_ROLE = env.DEFAULT_TEAM_ROLE || "";
|
||||
export const ONBOARDING_DISABLED = env.ONBOARDING_DISABLED;
|
||||
|
||||
// Storage constants
|
||||
export const UPLOADS_DIR = "./uploads";
|
||||
export const MAX_SIZES = {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { createEnv } from "@t3-oss/env-nextjs";
|
||||
import { z } from "zod";
|
||||
/* import { config } from 'dotenv';
|
||||
config({ path: '../../.env' }); */
|
||||
|
||||
export const env = createEnv({
|
||||
/*
|
||||
@@ -66,6 +64,9 @@ export const env = createEnv({
|
||||
AZUREAD_CLIENT_SECRET: z.string().optional(),
|
||||
AZUREAD_TENANT_ID: z.string().optional(),
|
||||
AZUREAD_CLIENT_ID: z.string().optional(),
|
||||
DEFAULT_TEAM_ID: z.string().optional(),
|
||||
DEFAULT_TEAM_ROLE: z.enum(["owner", "admin", "editor", "developer", "viewer"]).optional(),
|
||||
ONBOARDING_DISABLED: z.string().optional(),
|
||||
ENTERPRISE_LICENSE_KEY: z.string().optional(),
|
||||
},
|
||||
|
||||
@@ -141,7 +142,10 @@ export const env = createEnv({
|
||||
AZUREAD_CLIENT_ID: process.env.AZUREAD_CLIENT_ID,
|
||||
AZUREAD_CLIENT_SECRET: process.env.AZUREAD_CLIENT_SECRET,
|
||||
AZUREAD_TENANT_ID: process.env.AZUREAD_TENANT_ID,
|
||||
AIRTABLE_CLIENT_ID: process.env.AIRTABLE_CLIENT_ID,
|
||||
AIR_TABLE_CLIENT_ID: process.env.AIR_TABLE_CLIENT_ID,
|
||||
DEFAULT_TEAM_ID: process.env.DEFAULT_TEAM_ID,
|
||||
DEFAULT_TEAM_ROLE: process.env.DEFAULT_TEAM_ROLE,
|
||||
ONBOARDING_DISABLED: process.env.ONBOARDING_DISABLED,
|
||||
ENTERPRISE_LICENSE_KEY: process.env.ENTERPRISE_LICENSE_KEY,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -114,6 +114,7 @@ export const createMembership = async (
|
||||
data: Partial<TMembership>
|
||||
): Promise<TMembership> => {
|
||||
validateInputs([teamId, ZString], [userId, ZString], [data, ZMembership.partial()]);
|
||||
console.log("createMembership", teamId, userId, data);
|
||||
|
||||
try {
|
||||
const membership = await prisma.membership.create({
|
||||
|
||||
@@ -4,15 +4,22 @@ import { prisma } from "@formbricks/database";
|
||||
import { ZOptionalNumber, ZString } from "@formbricks/types/common";
|
||||
import { ZId } from "@formbricks/types/environment";
|
||||
import { DatabaseError, ResourceNotFoundError } from "@formbricks/types/errors";
|
||||
import { TTeam, TTeamBilling, TTeamUpdateInput, ZTeam, ZTeamUpdateInput } from "@formbricks/types/teams";
|
||||
import {
|
||||
TTeam,
|
||||
TTeamBilling,
|
||||
TTeamCreateInput,
|
||||
TTeamUpdateInput,
|
||||
ZTeam,
|
||||
ZTeamCreateInput,
|
||||
} from "@formbricks/types/teams";
|
||||
import { Prisma } from "@prisma/client";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { ITEMS_PER_PAGE, SERVICES_REVALIDATION_INTERVAL } from "../constants";
|
||||
import { environmentCache } from "../environment/cache";
|
||||
import { getProducts } from "../product/service";
|
||||
import { formatDateFields } from "../utils/datetime";
|
||||
import { validateInputs } from "../utils/validate";
|
||||
import { teamCache } from "./cache";
|
||||
import { formatDateFields } from "../utils/datetime";
|
||||
|
||||
export const select = {
|
||||
id: true,
|
||||
@@ -135,9 +142,9 @@ export const getTeam = async (teamId: string): Promise<TTeam | null> => {
|
||||
return team ? formatDateFields(team, ZTeam) : null;
|
||||
};
|
||||
|
||||
export const createTeam = async (teamInput: TTeamUpdateInput): Promise<TTeam> => {
|
||||
export const createTeam = async (teamInput: TTeamCreateInput): Promise<TTeam> => {
|
||||
try {
|
||||
validateInputs([teamInput, ZTeamUpdateInput]);
|
||||
validateInputs([teamInput, ZTeamCreateInput]);
|
||||
|
||||
const team = await prisma.team.create({
|
||||
data: teamInput,
|
||||
|
||||
@@ -26,6 +26,14 @@ export const ZTeam = z.object({
|
||||
billing: ZTeamBilling,
|
||||
});
|
||||
|
||||
export const ZTeamCreateInput = z.object({
|
||||
id: z.string().cuid2().optional(),
|
||||
name: z.string(),
|
||||
billing: ZTeamBilling.optional(),
|
||||
});
|
||||
|
||||
export type TTeamCreateInput = z.infer<typeof ZTeamCreateInput>;
|
||||
|
||||
export const ZTeamUpdateInput = z.object({
|
||||
name: z.string(),
|
||||
billing: ZTeamBilling.optional(),
|
||||
|
||||
12
packages/ui/SettingsId/index.tsx
Normal file
12
packages/ui/SettingsId/index.tsx
Normal file
@@ -0,0 +1,12 @@
|
||||
interface SettingsIdProps {
|
||||
title: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
export function SettingsId({ title, id }: SettingsIdProps) {
|
||||
return (
|
||||
<p className="pb-3 text-xs text-slate-400">
|
||||
{title} ID: {id}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
@@ -54,6 +54,9 @@
|
||||
"AZUREAD_CLIENT_ID",
|
||||
"AZUREAD_CLIENT_SECRET",
|
||||
"AZUREAD_TENANT_ID",
|
||||
"DEFAULT_TEAM_ID",
|
||||
"DEFAULT_TEAM_ROLE",
|
||||
"ONBOARDING_DISABLED",
|
||||
"CRON_SECRET",
|
||||
"DEBUG",
|
||||
"EMAIL_VERIFICATION_DISABLED",
|
||||
|
||||
Reference in New Issue
Block a user