diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/actions.ts b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/actions.ts new file mode 100644 index 0000000000..9d240ed673 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/actions.ts @@ -0,0 +1,7 @@ +"use server"; + +import { revalidatePath } from "next/cache"; + +export async function revalidateAvailabilityList() { + revalidatePath("/availability"); +} diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/loading.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/loading.tsx new file mode 100644 index 0000000000..59e435930e --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/loading.tsx @@ -0,0 +1,5 @@ +import AvailabilityLoader from "app/(use-page-wrapper)/(main-nav)/availability/skeleton"; + +export default function Loading() { + return ; +} diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx index d77ad8719a..a7bea5611f 100644 --- a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx @@ -1,11 +1,14 @@ +import { createRouterCaller } from "app/_trpc/context"; +import type { PageProps } from "app/_types"; import { _generateMetadata, getTranslate } from "app/_utils"; -// import { cookies, headers } from "next/headers"; import { notFound } from "next/navigation"; -// import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -// import { buildLegacyRequest } from "@lib/buildLegacyCtx"; -// import { OrganizationRepository } from "@calcom/lib/server/repository/organization"; -import AvailabilityPage, { AvailabilityCTA } from "~/availability/availability-view"; +import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable"; +import { OrganizationRepository } from "@calcom/lib/server/repository/organization"; +import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router"; +import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router"; + +import { AvailabilityList, AvailabilityCTA } from "~/availability/availability-view"; import { ShellMainAppDir } from "../ShellMainAppDir"; @@ -19,29 +22,44 @@ export const generateMetadata = async () => { ); }; -const Page = async () => { - // const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) }); - // const userId = session?.user?.id; - // const orgId = session?.user?.org?.id; - // if (!userId || !orgId) { - // notFound(); - // } - +const Page = async ({ searchParams: _searchParams }: PageProps) => { + const searchParams = await _searchParams; + const t = await getTranslate(); try { - // const currentOrg = await OrganizationRepository.findCurrentOrg({ - // orgId, - // userId, - // }); - const t = await getTranslate(); + const [meCaller, availabilityCaller] = await Promise.all([ + createRouterCaller(meRouter), + createRouterCaller(availabilityRouter), + ]); + + const [me, availabilities] = await Promise.all([meCaller.get(), availabilityCaller.list()]); + const organizationId = me.organization?.id ?? me.profiles[0]?.organizationId; + const isOrgPrivate = organizationId + ? await OrganizationRepository.checkIfPrivate({ + orgId: organizationId, + }) + : false; + const canViewTeamAvailability = me.organization?.isOrgAdmin || !isOrgPrivate; + return ( }> - + CTA={ + + }> + {searchParams?.type === "team" && canViewTeamAvailability ? ( + + ) : ( + + )} ); - } catch { + } catch (error) { notFound(); } }; diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/skeleton.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/skeleton.tsx new file mode 100644 index 0000000000..d6ad66b184 --- /dev/null +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/skeleton.tsx @@ -0,0 +1,25 @@ +"use client"; + +import { ShellMainAppDir } from "app/(use-page-wrapper)/(main-nav)/ShellMainAppDir"; + +import SkeletonLoader from "@calcom/features/availability/components/SkeletonLoader"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; + +import { AvailabilityCTA } from "~/availability/availability-view"; + +export default function AvailabilityLoader() { + const { t } = useLocale(); + const toggleGroupOptions = [ + { value: "mine", label: t("my_availability") }, + { value: "team", label: t("team_availability") }, + ]; + + return ( + }> + + + ); +} diff --git a/apps/web/modules/availability/[schedule]/schedule-view.tsx b/apps/web/modules/availability/[schedule]/schedule-view.tsx index fcb71b8b40..71822472d6 100644 --- a/apps/web/modules/availability/[schedule]/schedule-view.tsx +++ b/apps/web/modules/availability/[schedule]/schedule-view.tsx @@ -1,5 +1,6 @@ "use client"; +import { revalidateAvailabilityList } from "app/(use-page-wrapper)/(main-nav)/availability/actions"; import { revalidateSchedulePage } from "app/(use-page-wrapper)/availability/[schedule]/actions"; import { useRouter, useSearchParams } from "next/navigation"; import { useState } from "react"; @@ -47,6 +48,7 @@ export const AvailabilitySettingsWebWrapper = ({ { onSuccess: () => { utils.viewer.availability.list.invalidate(); + revalidateAvailabilityList(); callback(); showToast(t("success"), "success"); }, @@ -73,6 +75,7 @@ export const AvailabilitySettingsWebWrapper = ({ utils.viewer.availability.schedule.get.invalidate({ scheduleId: data.schedule.id }); revalidateSchedulePage(scheduleId); utils.viewer.availability.list.invalidate(); + revalidateAvailabilityList(); showToast( t("availability_updated_successfully", { scheduleName: data.schedule.name, @@ -97,6 +100,7 @@ export const AvailabilitySettingsWebWrapper = ({ }, onSuccess: () => { showToast(t("schedule_deleted_successfully"), "success"); + revalidateAvailabilityList(); router.push("/availability"); }, }); diff --git a/apps/web/modules/availability/availability-view.tsx b/apps/web/modules/availability/availability-view.tsx index 852fdc7636..b496856e00 100644 --- a/apps/web/modules/availability/availability-view.tsx +++ b/apps/web/modules/availability/availability-view.tsx @@ -1,33 +1,31 @@ "use client"; import { useAutoAnimate } from "@formkit/auto-animate/react"; +import { revalidateAvailabilityList } from "app/(use-page-wrapper)/(main-nav)/availability/actions"; import Link from "next/link"; import { useRouter, usePathname } from "next/navigation"; import { useCallback, useState } from "react"; -import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner"; -import SkeletonLoader from "@calcom/features/availability/components/SkeletonLoader"; import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules"; -import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { HttpError } from "@calcom/lib/http-error"; -import type { OrganizationRepository } from "@calcom/lib/server/repository/organization"; +import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; -import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery"; import { EmptyScreen } from "@calcom/ui/components/empty-screen"; import { ToggleGroup } from "@calcom/ui/components/form"; import { showToast } from "@calcom/ui/components/toast"; -export function AvailabilityList() { +type AvailabilityListProps = { + me: RouterOutputs["viewer"]["me"]["get"]; + availabilities: RouterOutputs["viewer"]["availability"]["list"]; +}; +export function AvailabilityList({ availabilities, me }: AvailabilityListProps) { const { t } = useLocale(); const [bulkUpdateModal, setBulkUpdateModal] = useState(false); const utils = trpc.useUtils(); - const { data: availabilityData } = trpc.viewer.availability.list.useQuery(); - const meQuery = trpc.viewer.me.get.useQuery(); - const router = useRouter(); const deleteMutation = trpc.viewer.availability.schedule.delete.useMutation({ @@ -55,6 +53,7 @@ export function AvailabilityList() { utils.viewer.availability.list.invalidate(); }, onSuccess: () => { + revalidateAvailabilityList(); showToast(t("schedule_deleted_successfully"), "success"); }, }); @@ -62,6 +61,7 @@ export function AvailabilityList() { const updateMutation = trpc.viewer.availability.schedule.update.useMutation({ onSuccess: async ({ schedule }) => { await utils.viewer.availability.list.invalidate(); + revalidateAvailabilityList(); showToast( t("availability_updated_successfully", { scheduleName: schedule.name, @@ -92,6 +92,7 @@ export function AvailabilityList() { { onSuccess: () => { utils.viewer.availability.list.invalidate(); + revalidateAvailabilityList(); showToast(t("success"), "success"); callback(); }, @@ -120,13 +121,9 @@ export function AvailabilityList() { const [animationParentRef] = useAutoAnimate(); - if (!availabilityData) { - return ; - } - return ( <> - {availabilityData.schedules.length === 0 ? ( + {availabilities.schedules.length === 0 ? (
    - {availabilityData.schedules.map((schedule) => ( + {availabilities.schedules.map((schedule) => ( > | null; +type AvailabilityCTAProps = { + toggleGroupOptions: { + value: string; + label: string; + }[]; }; - -export const AvailabilityCTA = () => { - const { t } = useLocale(); +export const AvailabilityCTA = ({ toggleGroupOptions }: AvailabilityCTAProps) => { const searchParams = useCompatSearchParams(); const router = useRouter(); const pathname = usePathname(); @@ -203,18 +201,6 @@ export const AvailabilityCTA = () => { [searchParams] ); - const { data } = trpc.viewer.organizations.listCurrent.useQuery(); - const isOrgAdminOrOwner = (data && checkAdminOrOwner(data.user.role)) ?? false; - const isOrgAndPrivate = data?.isOrganization && data.isPrivate; - - const canViewTeamAvailability = isOrgAdminOrOwner || !isOrgAndPrivate; - - const toggleGroupOptions = [{ value: "mine", label: t("my_availability") }]; - - if (canViewTeamAvailability) { - toggleGroupOptions.push({ value: "team", label: t("team_availability") }); - } - return (
    {
    ); }; - -export default function AvailabilityPage({ currentOrg }: PageProps) { - const { t } = useLocale(); - const searchParams = useCompatSearchParams(); - const me = useMeQuery(); - - const { data: _data } = trpc.viewer.organizations.listCurrent.useQuery(undefined, { enabled: !currentOrg }); - const data = currentOrg ?? _data; - - const isOrg = Boolean(data); - const isOrgAdminOrOwner = (data && checkAdminOrOwner(data.user.role)) ?? false; - const isOrgAndPrivate = data?.isOrganization && data.isPrivate; - - const canViewTeamAvailability = isOrgAdminOrOwner || !isOrgAndPrivate; - - const toggleGroupOptions = [{ value: "mine", label: t("my_availability") }]; - - if (canViewTeamAvailability) { - toggleGroupOptions.push({ value: "team", label: t("team_availability") }); - } - - return searchParams?.get("type") === "team" && canViewTeamAvailability ? ( - - ) : ( - - ); -} diff --git a/packages/features/availability/components/SkeletonLoader.tsx b/packages/features/availability/components/SkeletonLoader.tsx index 263ecc6b93..45c87e2a1f 100644 --- a/packages/features/availability/components/SkeletonLoader.tsx +++ b/packages/features/availability/components/SkeletonLoader.tsx @@ -1,6 +1,8 @@ -import { SkeletonText } from "@calcom/ui/components/skeleton"; -import { Button } from "@calcom/ui/components/button"; +"use client"; + import classNames from "@calcom/ui/classNames"; +import { Button } from "@calcom/ui/components/button"; +import { SkeletonText } from "@calcom/ui/components/skeleton"; function SkeletonLoader() { return ( diff --git a/packages/features/schedules/components/ScheduleListItem.tsx b/packages/features/schedules/components/ScheduleListItem.tsx index 9b5bad0b0e..11e499fca3 100644 --- a/packages/features/schedules/components/ScheduleListItem.tsx +++ b/packages/features/schedules/components/ScheduleListItem.tsx @@ -1,3 +1,5 @@ +"use client"; + import Link from "next/link"; import { Fragment } from "react"; @@ -5,6 +7,8 @@ import { availabilityAsString } from "@calcom/lib/availability"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { sortAvailabilityStrings } from "@calcom/lib/weekstart"; import type { RouterOutputs } from "@calcom/trpc/react"; +import { Badge } from "@calcom/ui/components/badge"; +import { Button } from "@calcom/ui/components/button"; import { Dropdown, DropdownItem, @@ -12,8 +16,6 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@calcom/ui/components/dropdown"; -import { Badge } from "@calcom/ui/components/badge"; -import { Button } from "@calcom/ui/components/button"; import { Icon } from "@calcom/ui/components/icon"; import { showToast } from "@calcom/ui/components/toast"; diff --git a/packages/lib/server/repository/organization.ts b/packages/lib/server/repository/organization.ts index 07fe565891..db97e700a3 100644 --- a/packages/lib/server/repository/organization.ts +++ b/packages/lib/server/repository/organization.ts @@ -456,4 +456,18 @@ export class OrganizationRepository { }, }); } + + static async checkIfPrivate({ orgId }: { orgId: number }) { + const team = await prisma.team.findUnique({ + where: { + id: orgId, + isOrganization: true, + }, + select: { + isPrivate: true, + }, + }); + + return team?.isPrivate ?? false; + } }