chore: preserve non-org dashboard for platform users (#16181)
This commit is contained in:
1 parent
a11242e93d
commit
84606543ae
10 files changed
+107
-25
No files matched your search
@@ -51,6 +51,10 @@
|
||||
<path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9" ></path>
|
||||
<path d="M10.3 21a1.94 1.94 0 0 0 3.4 0" ></path>
|
||||
</symbol>
|
||||
<symbol class="lucide lucide-blocks" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="blocks">
|
||||
<rect width="7" height="7" x="14" y="3" rx="1" ></rect>
|
||||
<path d="M10 21V8a1 1 0 0 0-1-1H4a1 1 0 0 0-1 1v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-5a1 1 0 0 0-1-1H3" ></path>
|
||||
</symbol>
|
||||
<symbol class="lucide lucide-bold" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="bold">
|
||||
<path d="M6 12h9a4 4 0 0 1 0 8H7a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h7a4 4 0 0 1 0 8" ></path>
|
||||
</symbol>
|
||||
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
@@ -531,17 +531,19 @@ export const AUTH_OPTIONS: AuthOptions = {
|
||||
belongsToActiveTeam,
|
||||
// All organizations in the token would be too big to store. It breaks the sessions request.
|
||||
// So, we just set the currently switched organization only here.
|
||||
org: profileOrg
|
||||
? {
|
||||
id: profileOrg.id,
|
||||
name: profileOrg.name,
|
||||
slug: profileOrg.slug ?? profileOrg.requestedSlug ?? "",
|
||||
logoUrl: profileOrg.logoUrl,
|
||||
fullDomain: getOrgFullOrigin(profileOrg.slug ?? profileOrg.requestedSlug ?? ""),
|
||||
domainSuffix: subdomainSuffix(),
|
||||
role: orgRole as MembershipRole, // It can't be undefined if we have a profileOrg
|
||||
}
|
||||
: null,
|
||||
// platform org user don't need profiles nor domains
|
||||
org:
|
||||
profileOrg && !profileOrg.isPlatform
|
||||
? {
|
||||
id: profileOrg.id,
|
||||
name: profileOrg.name,
|
||||
slug: profileOrg.slug ?? profileOrg.requestedSlug ?? "",
|
||||
logoUrl: profileOrg.logoUrl,
|
||||
fullDomain: getOrgFullOrigin(profileOrg.slug ?? profileOrg.requestedSlug ?? ""),
|
||||
domainSuffix: subdomainSuffix(),
|
||||
role: orgRole as MembershipRole, // It can't be undefined if we have a profileOrg
|
||||
}
|
||||
: null,
|
||||
} as JWT;
|
||||
};
|
||||
if (!user) {
|
||||
|
||||
@@ -248,7 +248,7 @@ export const getPublicEvent = async (
|
||||
};
|
||||
|
||||
// In case it's not a group event, it's either a single user or a team, and we query that data.
|
||||
const event = await prisma.eventType.findFirst({
|
||||
let event = await prisma.eventType.findFirst({
|
||||
where: {
|
||||
slug: eventSlug,
|
||||
...usersOrTeamQuery,
|
||||
@@ -256,6 +256,27 @@ export const getPublicEvent = async (
|
||||
select: publicEventSelect,
|
||||
});
|
||||
|
||||
// If no event was found, check for platform org user event
|
||||
if (!event && !orgQuery) {
|
||||
event = await prisma.eventType.findFirst({
|
||||
where: {
|
||||
slug: eventSlug,
|
||||
users: {
|
||||
some: {
|
||||
username,
|
||||
isPlatformManaged: false,
|
||||
movedToProfile: {
|
||||
organization: {
|
||||
isPlatform: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
select: publicEventSelect,
|
||||
});
|
||||
}
|
||||
|
||||
if (!event) return null;
|
||||
|
||||
const eventMetaData = EventTypeMetaDataSchema.parse(event.metadata || {});
|
||||
|
||||
@@ -384,7 +384,8 @@ function UserDropdown({ small }: UserDropdownProps) {
|
||||
const { data: user } = useMeQuery();
|
||||
const utils = trpc.useUtils();
|
||||
const bookerUrl = useBookerUrl();
|
||||
|
||||
const pathname = usePathname();
|
||||
const isPlatformPages = pathname?.startsWith("/settings/platform");
|
||||
useEffect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
@@ -468,7 +469,7 @@ function UserDropdown({ small }: UserDropdownProps) {
|
||||
<HelpMenuItem onHelpItemSelect={() => onHelpItemSelect()} />
|
||||
) : (
|
||||
<>
|
||||
{!isPlatformUser && (
|
||||
{!isPlatformPages && (
|
||||
<>
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem
|
||||
@@ -527,7 +528,7 @@ function UserDropdown({ small }: UserDropdownProps) {
|
||||
{t("help")}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
{!isPlatformUser && (
|
||||
{!isPlatformPages && (
|
||||
<DropdownMenuItem className="todesktop:hidden hidden lg:flex">
|
||||
<DropdownItem
|
||||
StartIcon="download"
|
||||
@@ -539,6 +540,17 @@ function UserDropdown({ small }: UserDropdownProps) {
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{!isPlatformPages && (
|
||||
<DropdownMenuItem className="todesktop:hidden hidden lg:flex">
|
||||
<DropdownItem
|
||||
StartIcon="blocks"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
href="/settings/platform">
|
||||
Platform
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuItem>
|
||||
@@ -912,9 +924,11 @@ function SideBarContainer({ bannersHeight, isPlatformUser = false }: SideBarCont
|
||||
return <SideBar isPlatformUser={isPlatformUser} bannersHeight={bannersHeight} user={data?.user} />;
|
||||
}
|
||||
|
||||
function SideBar({ bannersHeight, user, isPlatformUser = false }: SideBarProps) {
|
||||
function SideBar({ bannersHeight, user }: SideBarProps) {
|
||||
const { t, isLocaleReady } = useLocale();
|
||||
const orgBranding = useOrgBranding();
|
||||
const pathname = usePathname();
|
||||
const isPlatformPages = pathname?.startsWith("/settings/platform");
|
||||
|
||||
const publicPageUrl = useMemo(() => {
|
||||
if (!user?.org?.id) return `${process.env.NEXT_PUBLIC_WEBSITE_URL}/${user?.username}`;
|
||||
@@ -953,10 +967,10 @@ function SideBar({ bannersHeight, user, isPlatformUser = false }: SideBarProps)
|
||||
return (
|
||||
<div className="relative">
|
||||
<aside
|
||||
style={!isPlatformUser ? sidebarStylingAttributes : {}}
|
||||
style={!isPlatformPages ? sidebarStylingAttributes : {}}
|
||||
className={classNames(
|
||||
"bg-muted border-muted fixed left-0 hidden h-full w-14 flex-col overflow-y-auto overflow-x-hidden border-r md:sticky md:flex lg:w-56 lg:px-3",
|
||||
!isPlatformUser && "max-h-screen"
|
||||
!isPlatformPages && "max-h-screen"
|
||||
)}>
|
||||
<div className="flex h-full flex-col justify-between py-3 lg:pt-4">
|
||||
<header className="todesktop:-mt-3 todesktop:flex-col-reverse todesktop:[-webkit-app-region:drag] items-center justify-between md:hidden lg:flex">
|
||||
@@ -1018,10 +1032,10 @@ function SideBar({ bannersHeight, user, isPlatformUser = false }: SideBarProps)
|
||||
<Link href="/event-types" className="text-center md:inline lg:hidden">
|
||||
<Logo small icon />
|
||||
</Link>
|
||||
<Navigation isPlatformNavigation={isPlatformUser} />
|
||||
<Navigation isPlatformNavigation={isPlatformPages} />
|
||||
</div>
|
||||
|
||||
{!isPlatformUser && (
|
||||
{!isPlatformPages && (
|
||||
<div>
|
||||
<Tips />
|
||||
{bottomNavItems.map((item, index) => (
|
||||
|
||||
@@ -15,11 +15,18 @@ export const getBrand = async (orgId: number | null) => {
|
||||
name: true,
|
||||
slug: true,
|
||||
metadata: true,
|
||||
isPlatform: true,
|
||||
},
|
||||
});
|
||||
if (!org) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// platform orgs don't have a brand nor a domain
|
||||
if (org.isPlatform) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const metadata = teamMetadataSchema.parse(org.metadata);
|
||||
const slug = (org.slug || metadata?.requestedSlug) as string;
|
||||
const fullDomain = getOrgFullOrigin(slug);
|
||||
|
||||
@@ -23,6 +23,7 @@ const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
startTime: true,
|
||||
endTime: true,
|
||||
bufferTime: true,
|
||||
isPlatformManaged: true,
|
||||
});
|
||||
|
||||
const membershipSelect = Prisma.validator<Prisma.MembershipSelect>()({
|
||||
@@ -43,6 +44,7 @@ const organizationSelect = {
|
||||
logoUrl: true,
|
||||
calVideoLogo: true,
|
||||
bannerUrl: true,
|
||||
isPlatform: true,
|
||||
};
|
||||
|
||||
export enum LookupTarget {
|
||||
@@ -329,6 +331,12 @@ export class ProfileRepository {
|
||||
return null;
|
||||
}
|
||||
const user = profile.user;
|
||||
if (profile.organization?.isPlatform && !user.isPlatformManaged) {
|
||||
return {
|
||||
...this.buildPersonalProfileFromUser({ user }),
|
||||
...ProfileRepository.getInheritedDataFromUser({ user }),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...profile,
|
||||
...ProfileRepository.getInheritedDataFromUser({ user }),
|
||||
|
||||
@@ -30,6 +30,7 @@ const teamSelect = Prisma.validator<Prisma.TeamSelect>()({
|
||||
logoUrl: true,
|
||||
organizationSettings: true,
|
||||
isOrganization: true,
|
||||
isPlatform: true,
|
||||
});
|
||||
|
||||
const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
@@ -67,6 +68,7 @@ const userSelect = Prisma.validator<Prisma.UserSelect>()({
|
||||
locked: true,
|
||||
movedToProfileId: true,
|
||||
metadata: true,
|
||||
isPlatformManaged: true,
|
||||
});
|
||||
|
||||
export class UserRepository {
|
||||
@@ -330,6 +332,15 @@ export class UserRepository {
|
||||
const profiles = await ProfileRepository.findManyForUser({ id: user.id });
|
||||
if (profiles.length) {
|
||||
const profile = profiles[0];
|
||||
// platform org user doesn't need org profile
|
||||
if (profile?.organization?.isPlatform) {
|
||||
return {
|
||||
...user,
|
||||
nonProfileUsername: user.username,
|
||||
profile: ProfileRepository.buildPersonalProfileFromUser({ user }),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...user,
|
||||
username: profile.username,
|
||||
|
||||
@@ -106,6 +106,23 @@ export const meHandler = async ({ ctx, input }: MeOptions) => {
|
||||
|
||||
// Destructuring here only makes it more illegible
|
||||
// pick only the part we want to expose in the API
|
||||
|
||||
const profileData = user.organization?.isPlatform
|
||||
? {
|
||||
organizationId: null,
|
||||
organization: { id: -1, isPlatform: true, slug: "", isOrgAdmin: false },
|
||||
username: user.username ?? null,
|
||||
profile: ProfileRepository.buildPersonalProfileFromUser({ user }),
|
||||
profiles: [],
|
||||
}
|
||||
: {
|
||||
organizationId: user.profile?.organizationId ?? null,
|
||||
organization: user.organization,
|
||||
username: user.profile?.username ?? user.username ?? null,
|
||||
profile: user.profile ?? null,
|
||||
profiles: allUserEnrichedProfiles,
|
||||
};
|
||||
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
@@ -140,11 +157,7 @@ export const meHandler = async ({ ctx, input }: MeOptions) => {
|
||||
allowDynamicBooking: user.allowDynamicBooking,
|
||||
allowSEOIndexing: user.allowSEOIndexing,
|
||||
receiveMonthlyDigestEmail: user.receiveMonthlyDigestEmail,
|
||||
organizationId: user.profile?.organizationId ?? null,
|
||||
organization: user.organization,
|
||||
username: user.profile?.username ?? user.username ?? null,
|
||||
profile: user.profile ?? null,
|
||||
profiles: allUserEnrichedProfiles,
|
||||
...profileData,
|
||||
secondaryEmails,
|
||||
sumOfBookings: additionalUserInfo?.bookings.length,
|
||||
sumOfCalendars: additionalUserInfo?.selectedCalendars.length,
|
||||
|
||||
@@ -136,4 +136,5 @@ export const lucideIconList = new Set([
|
||||
"webhook",
|
||||
"x",
|
||||
"zap",
|
||||
"blocks",
|
||||
]);
|
||||
@@ -13,6 +13,7 @@ export type IconName =
|
||||
| "badge-check"
|
||||
| "ban"
|
||||
| "bell"
|
||||
| "blocks"
|
||||
| "bold"
|
||||
| "book-open-check"
|
||||
| "book-open"
|
||||
|
||||
Reference in new issue
Block a user