/** * It could be an admin feature to move a user to an organization but because it's a temporary thing before mono-user orgs are implemented, it's not right to spend time on it. * Plus, we need to do it only for cal.com and not provide as a feature to our self hosters. */ import { zodResolver } from "@hookform/resolvers/zod"; import type { GetServerSidePropsContext } from "next"; import { getSession } from "next-auth/react"; import type { TFunction } from "next-i18next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { useState } from "react"; import { Controller, useForm } from "react-hook-form"; import z from "zod"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { MembershipRole } from "@calcom/prisma/client"; import { UserPermissionRole } from "@calcom/prisma/enums"; import { getStringAsNumberRequiredSchema } from "@calcom/prisma/zod-utils"; import { Button, Form, Meta, SelectField, TextField, showToast } from "@calcom/ui"; import PageWrapper from "@components/PageWrapper"; import { getLayout } from "./_OrgMigrationLayout"; function Wrapper({ children }: { children: React.ReactNode }) { return (
{children}
); } export const getFormSchema = (t: TFunction) => z.object({ userId: z.union([z.string().pipe(z.coerce.number()), z.number()]).optional(), userName: z.string().optional(), targetOrgId: z.union([getStringAsNumberRequiredSchema(t), z.number()]), targetOrgUsername: z.string().min(1, t("error_required_field")), shouldMoveTeams: z.boolean(), targetOrgRole: z.union([ z.literal(MembershipRole.ADMIN), z.literal(MembershipRole.MEMBER), z.literal(MembershipRole.OWNER), ]), }); const enum State { IDLE, LOADING, SUCCESS, ERROR, } export default function MoveUserToOrg() { const [state, setState] = useState(State.IDLE); const roles = Object.values(MembershipRole).map((role) => ({ label: role, value: role, })); const moveTeamsOptions = [ { label: "Yes", value: "true", }, { label: "No", value: "false", }, ]; const { t } = useLocale(); const formSchema = getFormSchema(t); const form = useForm({ mode: "onSubmit", resolver: zodResolver(formSchema), }); const shouldMoveTeams = form.watch("shouldMoveTeams"); const register = form.register; return ( {/* Due to some reason auth from website doesn't work if /api endpoint is used. Spent a lot of time and in the end went with submitting data to the same page, because you can't do POST request to a page in Next.js, doing a GET request */}
{ setState(State.LOADING); const res = await fetch(`/api/orgMigration/moveUserToOrg`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify(values), }); let response = null; try { response = await res.json(); } catch (e) { if (e instanceof Error) { showToast(e.message, "error", 10000); } else { showToast(t("something_went_wrong"), "error", 10000); } setState(State.ERROR); return; } if (res.status === 200) { setState(State.SUCCESS); showToast(response.message, "success", 10000); } else { setState(State.ERROR); showToast(response.message, "error", 10000); } }}>
( { if (!option) return; onChange(option.value); }} value={roles.find((role) => role.value === value)} required placeholder="Enter userId" /> )} /> ( { if (!option) return; onChange(option.value === "true"); }} value={moveTeamsOptions.find((opt) => opt.value === value)} required options={moveTeamsOptions} /> )} />
); } export async function getServerSideProps(ctx: GetServerSidePropsContext) { const session = await getSession(ctx); if (!session || !session.user) { return { redirect: { destination: "/login", permanent: false, }, }; } const isAdmin = session.user.role === UserPermissionRole.ADMIN; if (!isAdmin) { return { redirect: { destination: "/", permanent: false, }, }; } return { props: { ...(await serverSideTranslations(ctx.locale || "en", ["common"])), }, }; } MoveUserToOrg.PageWrapper = PageWrapper; MoveUserToOrg.getLayout = getLayout;