fix: RTL issues on booking pages + email confirmation (#10526)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
pull/10538/head^2
Anik Dhabal Babu 2023-08-12 05:09:25 +05:30 committed by GitHub
parent 471b2687c4
commit 99e9425257
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 115 additions and 32 deletions

View File

@ -314,7 +314,7 @@ export default function Success(props: SuccessProps) {
<Link
href={allRemainingBookings ? "/bookings/recurring" : "/bookings/upcoming"}
className="hover:bg-subtle text-subtle hover:text-default mt-2 inline-flex px-1 py-2 text-sm dark:hover:bg-transparent">
<ChevronLeft className="h-5 w-5" /> {t("back_to_bookings")}
<ChevronLeft className="h-5 w-5 rtl:rotate-180" /> {t("back_to_bookings")}
</Link>
</div>
)}
@ -394,7 +394,7 @@ export default function Success(props: SuccessProps) {
</h4>
)}
<div className="border-subtle text-default mt-8 grid grid-cols-3 border-t pt-8 text-left">
<div className="border-subtle text-default mt-8 grid grid-cols-3 border-t pt-8 text-left rtl:text-right">
{(isCancelled || reschedule) && cancellationReason && (
<>
<div className="font-medium">
@ -715,7 +715,7 @@ export default function Success(props: SuccessProps) {
</div>
{isGmail && (
<Alert
className="main -mb-20 mt-4 inline-block text-left sm:-mt-4 sm:mb-4 sm:w-full sm:max-w-xl sm:align-middle"
className="main -mb-20 mt-4 inline-block ltr:text-right rtl:text-right sm:-mt-4 sm:mb-4 sm:w-full sm:max-w-xl sm:align-middle"
severity="warning"
message={
<div>

View File

@ -497,6 +497,70 @@
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
<td style="direction: rtl; font-size: 0px; padding: 0px; text-align: center">
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
<div
class="mj-column-per-100 mj-outlook-group-fix"
style="
font-size: 0px;
text-align: right;
direction: rtl;
display: inline-block;
vertical-align: top;
width: 100%;
">
<table
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="vertical-align: top"
width="100%">
<tbody>
<tr>
<td
align="center"
style="
font-size: 0px;
padding: 10px 25px;
padding-top: 32px;
word-break: break-word;
">
<table
border="0"
cellpadding="0"
cellspacing="0"
role="presentation"
style="border-collapse: collapse; border-spacing: 0px">
<tbody>
<tr>
<td style="width: 89px">
<a href="{{base_url}}" target="_blank">
<img
height="19"
src="https://app.cal.com/emails/logo.png"
style="
border: 0;
display: block;
outline: none;
text-decoration: none;
height: 19px;
width: 100%;
font-size: 13px;
"
width="89" />
</a>
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
<!--[if mso | IE]></td></tr></table><![endif]-->
</td>
</tr>
</tbody>
</table>

View File

@ -207,7 +207,7 @@ const BookerComponent = ({
<BookerSection
area="header"
className={classNames(
layout === BookerLayouts.MONTH_VIEW && "fixed right-4 top-4 z-10",
layout === BookerLayouts.MONTH_VIEW && "fixed top-4 z-10 ltr:right-4 rtl:left-4",
(layout === BookerLayouts.COLUMN_VIEW || layout === BookerLayouts.WEEK_VIEW) &&
"bg-default dark:bg-muted sticky top-0 z-10"
)}>
@ -280,7 +280,7 @@ const BookerComponent = ({
layout === BookerLayouts.COLUMN_VIEW
}
className={classNames(
"border-subtle flex h-full w-full flex-col px-5 py-3 pb-0 md:border-l",
"border-subtle rtl:border-default flex h-full w-full flex-col px-5 py-3 pb-0 rtl:border-r ltr:md:border-l",
layout === BookerLayouts.MONTH_VIEW &&
"scroll-bar h-full overflow-auto md:w-[var(--booker-timeslots-width)]",
layout !== BookerLayouts.MONTH_VIEW && "sticky top-0"

View File

@ -71,7 +71,7 @@ export const EventMeta = () => {
<div dangerouslySetInnerHTML={{ __html: markdownToSafeHTML(event.description) }} />
</EventMetaBlock>
)}
<div className="space-y-4 font-medium">
<div className="space-y-4 font-medium rtl:-mr-2">
{rescheduleUid && bookingData && (
<EventMetaBlock icon={Calendar}>
{t("former_time")}

View File

@ -21,7 +21,7 @@ export function Header({
isMobile: boolean;
enabledLayouts: BookerLayouts[];
}) {
const { t } = useLocale();
const { t, i18n } = useLocale();
const [layout, setLayout] = useBookerStore((state) => [state.layout, state.setLayout], shallow);
const selectedDateString = useBookerStore((state) => state.selectedDate);
const setSelectedDate = useBookerStore((state) => state.setSelectedDate);
@ -66,13 +66,13 @@ export function Header({
const isSameYear = () => {
return selectedDate.format("YYYY") === endDate.format("YYYY");
};
const formattedMonth = new Intl.DateTimeFormat(i18n.language, { month: "short" });
const FormattedSelectedDateRange = () => {
return (
<h3 className="min-w-[150px] text-base font-semibold leading-4">
{selectedDate.format("MMM D")}
{formattedMonth.format(selectedDate.toDate())} {selectedDate.format("D")}
{!isSameYear() && <span className="text-subtle">, {selectedDate.format("YYYY")} </span>}-{" "}
{isSameMonth() ? endDate.format("D") : endDate.format("MMM D")},{" "}
{!isSameMonth() && formattedMonth.format(endDate.toDate())} {endDate.format("D")},{" "}
<span className="text-subtle">
{isSameYear() ? selectedDate.format("YYYY") : endDate.format("YYYY")}
</span>
@ -81,11 +81,12 @@ export function Header({
};
return (
<div className="border-default relative z-10 flex border-b border-l px-5 py-4">
<div className="flex items-center gap-3">
<div className="border-default relative z-10 flex border-b px-5 py-4 ltr:border-l rtl:border-r">
<div className="flex items-center gap-5 rtl:flex-grow">
<FormattedSelectedDateRange />
<ButtonGroup>
<Button
className="group rtl:ml-1 rtl:rotate-180"
variant="icon"
color="minimal"
StartIcon={ChevronLeft}
@ -93,6 +94,7 @@ export function Header({
onClick={() => addToSelectedDate(-extraDays - 1)}
/>
<Button
className="group rtl:mr-1 rtl:rotate-180"
variant="icon"
color="minimal"
StartIcon={ChevronRight}
@ -101,7 +103,7 @@ export function Header({
/>
{selectedDateMin3DaysDifference && (
<Button
className="capitalize"
className="capitalize ltr:ml-2 rtl:mr-2"
color="secondary"
onClick={() => setSelectedDate(today.format("YYYY-MM-DD"))}>
{t("today")}
@ -111,15 +113,15 @@ export function Header({
</div>
<div className="ml-auto flex gap-2">
<TimeFormatToggle />
<div className="fixed right-4 top-4">
<div className="fixed top-4 ltr:right-4 rtl:left-4">
<LayoutToggleWithData />
</div>
{/*
This second layout toggle is hidden, but needed to reserve the correct spot in the DIV
for the fixed toggle above to fit into. If we wouldn't make it fixed in this view, the transition
would be really weird, because the element is positioned fixed in the month view, and then
when switching layouts wouldn't anymmore, causing it to animate from the center to the top right,
while it actuall already was on place. That's why we have this element twice.
when switching layouts wouldn't anymore, causing it to animate from the center to the top right,
while it actually already was on place. That's why we have this element twice.
*/}
<div className="pointer-events-none opacity-0" aria-hidden>
<LayoutToggleWithData />

View File

@ -69,7 +69,7 @@ export const AvailableTimes = ({
</span>
{showTimeFormatToggle && (
<div className="ml-auto">
<div className="ml-auto rtl:mr-auto">
<TimeFormatToggle />
</div>
)}

View File

@ -112,7 +112,7 @@ export function AvailableEventLocations({ locations }: { locations: LocationObje
</Tooltip>
</div>
) : filteredLocations.length === 1 ? (
<div className="text-default mr-6 flex w-full flex-col space-y-4 break-words text-sm">
<div className="text-default mr-6 flex w-full flex-col space-y-4 break-words text-sm rtl:mr-2">
{filteredLocations}
</div>
) : null;

View File

@ -86,7 +86,7 @@ export const EventMetaBlock = ({
)}
/>
) : (
<>{!!Icon && <Icon className="relative z-20 mr-2 mt-[2px] h-4 w-4 flex-shrink-0" />}</>
<>{!!Icon && <Icon className="relative z-20 mr-2 mt-[2px] h-4 w-4 flex-shrink-0 rtl:ml-2" />}</>
)}
<div className={classNames("relative z-10 max-w-full break-words", contentClassName)}>{children}</div>
</div>
@ -128,9 +128,9 @@ export const EventDetails = ({ event, blocks = defaultEventDetailsBlocks }: Even
case EventDetailBlocks.LOCATION:
if (!event?.locations?.length) return null;
return (
<React.Fragment key={block}>
<EventMetaBlock key={block}>
<AvailableEventLocations locations={event.locations} />
</React.Fragment>
</EventMetaBlock>
);
case EventDetailBlocks.REQUIRES_CONFIRMATION:

View File

@ -241,7 +241,7 @@ const DatePicker = ({
<div className="flex">
<Button
className={classNames(
"group p-1 opacity-70 hover:opacity-100",
"group p-1 opacity-70 hover:opacity-100 rtl:rotate-180",
!browsingDate.isAfter(dayjs()) &&
"disabled:text-bookinglighter hover:bg-background hover:opacity-70"
)}
@ -253,7 +253,7 @@ const DatePicker = ({
StartIcon={ChevronLeft}
/>
<Button
className="group p-1 opacity-70 hover:opacity-100"
className="group p-1 opacity-70 hover:opacity-100 rtl:rotate-180"
onClick={() => changeMonth(+1)}
data-testid="incrementMonth"
color="minimal"

View File

@ -2,6 +2,7 @@ import React from "react";
import dayjs from "@calcom/dayjs";
import { classNames } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
type Props = {
days: dayjs.Dayjs[];
@ -9,10 +10,14 @@ type Props = {
};
export function DateValues({ days, containerNavRef }: Props) {
const { i18n } = useLocale();
const formatDate = (date: dayjs.Dayjs): string => {
return new Intl.DateTimeFormat(i18n.language, { weekday: "short" }).format(date.toDate());
};
return (
<div
ref={containerNavRef}
className="bg-default dark:bg-muted border-b-subtle sticky top-[var(--calendar-dates-sticky-offset,0px)] z-[80] flex-none border-b sm:pr-8">
className="bg-default dark:bg-muted border-b-subtle rtl:border-r-default sticky top-[var(--calendar-dates-sticky-offset,0px)] z-[80] flex-none border-b border-r sm:pr-8">
<div className="text-subtle flex text-sm leading-6 sm:hidden" data-dayslength={days.length}>
{days.map((day) => {
const isToday = dayjs().isSame(day, "day");
@ -34,7 +39,7 @@ export function DateValues({ days, containerNavRef }: Props) {
})}
</div>
<div className="text-subtle -mr-px hidden auto-cols-fr text-sm leading-6 sm:flex ">
<div className="border-default col-end-1 w-14 border-l" />
<div className="border-default col-end-1 w-14 ltr:border-l" />
{days.map((day) => {
const isToday = dayjs().isSame(day, "day");
return (
@ -45,7 +50,7 @@ export function DateValues({ days, containerNavRef }: Props) {
isToday && "font-bold"
)}>
<span>
{day.format("ddd")}{" "}
{formatDate(day)}{" "}
<span
className={classNames(
"items-center justify-center p-1",

View File

@ -26,14 +26,14 @@ export const HorizontalLines = ({
<div className="row-end-1 h-[--calendar-offset-top] " ref={containerOffsetRef} />
{hours.map((hour) => (
<div key={`${id}-${hour.get("hour")}`}>
<div className="text-muted sticky left-0 z-20 -ml-14 -mt-2.5 w-14 pr-2 text-right text-xs leading-5">
<div className="text-muted sticky left-0 z-20 -ml-14 -mt-2.5 w-14 pr-2 text-right text-xs leading-5 rtl:-mr-14">
{/* We need to force the minute to zero, because otherwise in ex GMT+5.5, it would show :30 minute times (but at the positino of :00) */}
{hour.minute(0).format(timeFormat)}
</div>
</div>
))}
<div key={`${id}-${finalHour}`}>
<div className="text-muted sticky left-0 z-20 -ml-14 -mt-2.5 w-14 pr-2 text-right text-xs leading-5">
<div className="text-muted sticky left-0 z-20 -ml-14 -mt-2.5 w-14 pr-2 text-right text-xs leading-5 rtl:-mr-14">
{finalHour}
</div>
</div>

View File

@ -1,16 +1,28 @@
import type dayjs from "@calcom/dayjs";
export const VeritcalLines = ({ days }: { days: dayjs.Dayjs[] }) => {
const isRTL = () => {
const userLocale = navigator.language;
const userLanguage = new Intl.Locale(userLocale).language;
return ["ar", "he", "fa", "ur"].includes(userLanguage);
};
const direction = isRTL() ? "rtl" : "ltr";
return (
<div
className="divide-default pointer-events-none relative z-[60] col-start-1 col-end-2 row-start-1 grid
auto-cols-auto grid-rows-1 divide-x sm:pr-8">
auto-cols-auto grid-rows-1 divide-x sm:pr-8"
dir={direction}
style={{
direction: direction,
}}>
{days.map((_, i) => (
<div
key={`Key_vertical_${i}`}
className="row-span-full"
style={{
gridColumnStart: i + 1,
gridColumnStart: isRTL() ? days.length - i : i + 1,
}}
/>
))}

View File

@ -83,7 +83,7 @@ export const Alert = forwardRef<HTMLDivElement, AlertProps>((props, ref) => {
</div>
)}
<div className="flex flex-grow flex-col sm:flex-row">
<div className="ml-3 ">
<div className="ltr:ml-3 rtl:mr-3">
<h3 className="text-sm font-medium">{props.title}</h3>
<div className="text-sm">{props.message}</div>
</div>

View File

@ -40,7 +40,7 @@ export const ToggleGroup = ({ options, onValueChange, isFullWidth, ...props }: T
{...props}
onValueChange={onValueChange}
className={classNames(
"min-h-9 border-default bg-default relative inline-flex gap-0.5 rounded-md border p-1",
"min-h-9 border-default bg-default relative inline-flex gap-0.5 rounded-md border p-1 rtl:flex-row-reverse",
props.className,
isFullWidth && "w-full"
)}>