fix: other reported issues (#11015)
* fix: weird margin top in avatar * fix: pending users are shown on booking page * fix: avatar and naming issues * fix: toast alignment and removing unneeded titles * missing changes from toast improvements * feat: empty state for teams without event types * Removing console.log
This commit is contained in:
1 parent
1bf68d50e0
commit
356117feaf
21 files changed
+102
-53
No files matched your search
@@ -1,6 +1,5 @@
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { default as DatePickerComponent } from "@calcom/features/calendars/DatePicker";
|
||||
import { useNonEmptyScheduleDays } from "@calcom/features/schedules";
|
||||
|
||||
@@ -161,7 +161,7 @@ export const useBookerStore = create<BookerStore>((set, get) => ({
|
||||
|
||||
// Setting month make sure small calendar in fullscreen layouts also updates.
|
||||
// If selectedDate is null, prevents setting month to Invalid-Date
|
||||
if (selectedDate && newSelection.month() !== currentSelection.month() ) {
|
||||
if (selectedDate && newSelection.month() !== currentSelection.month()) {
|
||||
set({ month: newSelection.format("YYYY-MM") });
|
||||
updateQueryParam("month", newSelection.format("YYYY-MM"));
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export const EventMembers = ({ schedulingType, users, profile, entity }: EventMe
|
||||
(profile.name !== users[0].name && schedulingType === SchedulingType.COLLECTIVE);
|
||||
|
||||
const avatars: Avatar[] = shownUsers.map((user) => ({
|
||||
title: `${user.name}`,
|
||||
title: `${user.name || user.username}`,
|
||||
image: "image" in user ? `${user.image}` : `/${user.username}/avatar.png`,
|
||||
alt: user.name || undefined,
|
||||
href: `/${user.username}`,
|
||||
@@ -54,7 +54,7 @@ export const EventMembers = ({ schedulingType, users, profile, entity }: EventMe
|
||||
|
||||
// Add profile later since we don't want to force creating an avatar for this if it doesn't exist.
|
||||
avatars.unshift({
|
||||
title: `${profile.name}`,
|
||||
title: `${profile.name || profile.username}`,
|
||||
image: "logo" in profile && profile.logo ? `${profile.logo}` : undefined,
|
||||
alt: profile.name || undefined,
|
||||
href: profile.username ? `${CAL_URL}/${profile.username}` : undefined,
|
||||
|
||||
@@ -65,7 +65,6 @@ export const AboutOrganizationForm = () => {
|
||||
<Avatar
|
||||
alt=""
|
||||
fallback={<Plus className="text-subtle h-6 w-6" />}
|
||||
asChild
|
||||
className="items-center"
|
||||
imageSrc={image}
|
||||
size="lg"
|
||||
|
||||
@@ -8,7 +8,6 @@ import type { ControlProps } from "react-select";
|
||||
import { components } from "react-select";
|
||||
import { shallow } from "zustand/shallow";
|
||||
|
||||
import type { Dayjs } from "@calcom/dayjs";
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { AvailableTimes } from "@calcom/features/bookings";
|
||||
import { useBookerStore, useInitializeBookerStore } from "@calcom/features/bookings/Booker/store";
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import type { Props } from "react-select";
|
||||
|
||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||
import { getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -34,7 +36,7 @@ export const ChildrenEventTypeSelect = ({
|
||||
onChange: (value: readonly ChildrenEventType[]) => void;
|
||||
}) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const orgBranding = useOrgBranding();
|
||||
const [animationRef] = useAutoAnimate<HTMLUListElement>();
|
||||
|
||||
return (
|
||||
@@ -61,7 +63,9 @@ export const ChildrenEventTypeSelect = ({
|
||||
<Avatar
|
||||
size="mdLg"
|
||||
className="overflow-visible"
|
||||
imageSrc={`${CAL_URL}/${children.owner.username}/avatar.png`}
|
||||
imageSrc={`${orgBranding ? getOrgFullDomain(orgBranding.slug) : CAL_URL}/${
|
||||
children.owner.username
|
||||
}/avatar.png`}
|
||||
alt={children.owner.name || children.owner.email || ""}
|
||||
/>
|
||||
<div className="flex w-full flex-row justify-between">
|
||||
|
||||
@@ -117,7 +117,7 @@ export default function CreateEventTypeDialog({
|
||||
const createMutation = trpc.viewer.eventTypes.create.useMutation({
|
||||
onSuccess: async ({ eventType }) => {
|
||||
await router.replace("/event-types/" + eventType.id);
|
||||
showToast(t("event_type_created_successfully", { eventTypeTitle: eventType.title }), "success");
|
||||
showToast(t("event_type_created_successfully"), "success");
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err instanceof HttpError) {
|
||||
|
||||
@@ -66,7 +66,7 @@ const DuplicateDialog = () => {
|
||||
const duplicateMutation = trpc.viewer.eventTypes.duplicate.useMutation({
|
||||
onSuccess: async ({ eventType }) => {
|
||||
await router.replace("/event-types/" + eventType.id);
|
||||
showToast(t("event_type_created_successfully", { eventTypeTitle: eventType.title }), "success");
|
||||
showToast(t("event_type_created_successfully"), "success");
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err instanceof HttpError) {
|
||||
|
||||
@@ -285,7 +285,7 @@ function getProfileFromEvent(event: Event) {
|
||||
function getUsersFromEvent(event: Event) {
|
||||
const { team, hosts, owner } = event;
|
||||
if (team) {
|
||||
return (hosts || []).map(mapHostsToUsers);
|
||||
return (hosts || []).filter((host) => host.user.username).map(mapHostsToUsers);
|
||||
}
|
||||
if (!owner) {
|
||||
return null;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { getLocationGroupedOptions } from "@calcom/app-store/server";
|
||||
import type { StripeData } from "@calcom/app-store/stripepayment/lib/server";
|
||||
import { getEventTypeAppData } from "@calcom/app-store/utils";
|
||||
import type { LocationObject } from "@calcom/core/location";
|
||||
import { getOrgFullDomain } from "@calcom/ee/organizations/lib/orgDomains";
|
||||
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
|
||||
import { parseBookingLimit, parseDurationLimit, parseRecurringEvent } from "@calcom/lib";
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
@@ -113,6 +114,11 @@ export default async function getEventTypeById({
|
||||
name: true,
|
||||
slug: true,
|
||||
parentId: true,
|
||||
parent: {
|
||||
select: {
|
||||
slug: true,
|
||||
},
|
||||
},
|
||||
members: {
|
||||
select: {
|
||||
role: true,
|
||||
@@ -319,7 +325,9 @@ export default async function getEventTypeById({
|
||||
const eventTypeUsers: ((typeof eventType.users)[number] & { avatar: string })[] = eventType.users.map(
|
||||
(user) => ({
|
||||
...user,
|
||||
avatar: `${CAL_URL}/${user.username}/avatar.png`,
|
||||
avatar: `${eventType.team?.parent?.slug ? getOrgFullDomain(eventType.team?.parent?.slug) : CAL_URL}/${
|
||||
user.username
|
||||
}/avatar.png`,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -365,7 +373,11 @@ export default async function getEventTypeById({
|
||||
.map((member) => {
|
||||
const user: typeof member.user & { avatar: string } = {
|
||||
...member.user,
|
||||
avatar: `${CAL_URL}/${member.user.username}/avatar.png`,
|
||||
avatar: `${
|
||||
eventTypeObject.team?.parent?.slug
|
||||
? getOrgFullDomain(eventTypeObject.team?.parent?.slug)
|
||||
: CAL_URL
|
||||
}/${member.user.username}/avatar.png`,
|
||||
};
|
||||
return {
|
||||
...user,
|
||||
|
||||
@@ -633,4 +633,3 @@ export const ZVerifyCodeInputSchema = z.object({
|
||||
export type ZVerifyCodeInputSchema = z.infer<typeof ZVerifyCodeInputSchema>;
|
||||
|
||||
export const coerceToDate = z.coerce.date();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getOrgFullDomain } from "@calcom/ee/organizations/lib/orgDomains";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
@@ -24,7 +25,9 @@ export const meHandler = async ({ ctx }: MeOptions) => {
|
||||
locale: user.locale,
|
||||
timeFormat: user.timeFormat,
|
||||
timeZone: user.timeZone,
|
||||
avatar: `${WEBAPP_URL}/${user.username}/avatar.png`,
|
||||
avatar: `${user.organization?.slug ? getOrgFullDomain(user.organization.slug) : WEBAPP_URL}/${
|
||||
user.username
|
||||
}/avatar.png`,
|
||||
createdDate: user.createdDate,
|
||||
trialEndsAt: user.trialEndsAt,
|
||||
defaultScheduleId: user.defaultScheduleId,
|
||||
|
||||
@@ -165,6 +165,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
|
||||
|
||||
type EventTypeGroup = {
|
||||
teamId?: number | null;
|
||||
parentId?: number | null;
|
||||
membershipRole?: MembershipRole | null;
|
||||
profile: {
|
||||
slug: (typeof user)["username"];
|
||||
@@ -226,6 +227,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
|
||||
)?.membershipRole;
|
||||
return {
|
||||
teamId: membership.team.id,
|
||||
parentId: membership.team.parentId,
|
||||
membershipRole:
|
||||
orgMembership && compareMembership(orgMembership, membership.role)
|
||||
? orgMembership
|
||||
@@ -265,7 +267,7 @@ export const getByViewerHandler = async ({ ctx, input }: GetByViewerOptions) =>
|
||||
const bookerUrl = await getBookerUrl(user);
|
||||
return {
|
||||
// don't display event teams without event types,
|
||||
eventTypeGroups: eventTypeGroups.filter((groupBy) => !!groupBy.eventTypes?.length),
|
||||
eventTypeGroups: eventTypeGroups.filter((groupBy) => groupBy.parentId || !!groupBy.eventTypes?.length),
|
||||
// so we can show a dropdown when the user has teams
|
||||
profiles: eventTypeGroups.map((group) => ({
|
||||
...group.profile,
|
||||
|
||||
@@ -40,7 +40,7 @@ export function Avatar(props: AvatarProps) {
|
||||
let avatar = (
|
||||
<AvatarPrimitive.Root
|
||||
className={classNames(
|
||||
"bg-emphasis item-center relative inline-flex aspect-square justify-center overflow-hidden rounded-full",
|
||||
"bg-emphasis item-center relative aspect-square justify-center overflow-hidden rounded-full",
|
||||
props.className,
|
||||
sizesPropsBySize[size]
|
||||
)}>
|
||||
@@ -50,7 +50,10 @@ export function Avatar(props: AvatarProps) {
|
||||
alt={alt}
|
||||
className={classNames("aspect-square rounded-full", sizesPropsBySize[size])}
|
||||
/>
|
||||
<AvatarPrimitive.Fallback delayMs={600} asChild={props.asChild} className="flex items-center">
|
||||
<AvatarPrimitive.Fallback
|
||||
delayMs={600}
|
||||
asChild={props.asChild}
|
||||
className="flex h-full items-center justify-center">
|
||||
<>
|
||||
{props.fallback ? (
|
||||
props.fallback
|
||||
|
||||
@@ -22,7 +22,7 @@ export function EmptyScreen({
|
||||
Icon?: SVGComponent | IconType;
|
||||
avatar?: React.ReactElement;
|
||||
headline: string | React.ReactElement;
|
||||
description: string | React.ReactElement;
|
||||
description?: string | React.ReactElement;
|
||||
buttonText?: string;
|
||||
buttonOnClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
||||
buttonRaw?: ReactNode; // Used incase you want to provide your own button.
|
||||
@@ -49,9 +49,11 @@ export function EmptyScreen({
|
||||
)}
|
||||
<div className="flex max-w-[420px] flex-col items-center">
|
||||
<h2 className="text-semibold font-cal text-emphasis mt-6 text-center text-xl">{headline}</h2>
|
||||
<div className="text-default mb-8 mt-3 text-center text-sm font-normal leading-6">
|
||||
{description}
|
||||
</div>
|
||||
{description && (
|
||||
<div className="text-default mb-8 mt-3 text-center text-sm font-normal leading-6">
|
||||
{description}
|
||||
</div>
|
||||
)}
|
||||
{buttonOnClick && buttonText && <Button onClick={(e) => buttonOnClick(e)}>{buttonText}</Button>}
|
||||
{buttonRaw}
|
||||
</div>
|
||||
|
||||
@@ -13,56 +13,64 @@ type IToast = {
|
||||
export const SuccessToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"data-testid-toast-success bg-brand-default text-inverted mb-2 flex h-auto items-center space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
"data-testid-toast-success bg-brand-default text-inverted mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span>
|
||||
<span className="mt-0.5">
|
||||
<Check className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-success">{message}</p>
|
||||
<p data-testid="toast-success" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const ErrorToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-error text-error mb-2 flex h-auto items-center space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
"animate-fade-in-up bg-error text-error mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span>
|
||||
<span className="mt-0.5">
|
||||
<Info className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-error">{message}</p>
|
||||
<p data-testid="toast-error" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const WarningToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-brand-default text-brand mb-2 flex h-auto items-center space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
"animate-fade-in-up bg-brand-default text-brand mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span>
|
||||
<span className="mt-0.5">
|
||||
<Info className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-warning">{message}</p>
|
||||
<p data-testid="toast-warning" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
export const DefaultToast = ({ message, toastVisible, onClose, toastId }: IToast) => (
|
||||
<button
|
||||
className={classNames(
|
||||
"animate-fade-in-up bg-brand-default text-inverted mb-2 flex h-auto items-center space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
"animate-fade-in-up bg-brand-default text-inverted mb-2 flex h-auto space-x-2 rounded-md p-3 text-sm font-semibold shadow-md rtl:space-x-reverse md:max-w-sm",
|
||||
toastVisible && "animate-fade-in-up cursor-pointer"
|
||||
)}
|
||||
onClick={() => onClose(toastId)}>
|
||||
<span>
|
||||
<span className="mt-0.5">
|
||||
<Check className="h-4 w-4" />
|
||||
</span>
|
||||
<p data-testid="toast-default">{message}</p>
|
||||
<p data-testid="toast-default" className="text-left">
|
||||
{message}
|
||||
</p>
|
||||
</button>
|
||||
);
|
||||
|
||||
|
||||
Reference in new issue
Block a user