a ton of mobile improvements to save space (#4336)
* made /booking more responsive friendly * hiding headline and description on mobile, moving CTA to bottom right fixed on mobile * more padding on full screen mode on iPhone * nit fixes to wipemycal * nit fixes to wipemycal * lint fix * Update apps/web/styles/globals.css Massive nit, lol. Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
Alex van Andel
parent
770c35039d
commit
5cd2ce9246
@@ -13,6 +13,7 @@ import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
import { TextArea } from "@calcom/ui/form/fields";
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
import useMeQuery from "@lib/hooks/useMeQuery";
|
||||
@@ -261,7 +262,7 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
</Dialog>
|
||||
|
||||
<tr className="flex hover:bg-neutral-50">
|
||||
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-44" onClick={onClick}>
|
||||
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-28" onClick={onClick}>
|
||||
<div className="cursor-pointer py-4">
|
||||
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
@@ -304,17 +305,21 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
</td>
|
||||
<td className={"flex-1 px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
|
||||
<div className="cursor-pointer py-4">
|
||||
<div className="sm:hidden">
|
||||
{isPending && <Tag className="mb-2 ltr:mr-2 rtl:ml-2">{t("unconfirmed")}</Tag>}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Tag className="mb-2 ltr:mr-2 rtl:ml-2">Pending payment</Tag>
|
||||
)}
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{startTime}:{" "}
|
||||
<small className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")}
|
||||
</small>
|
||||
</div>
|
||||
{isPending && (
|
||||
<Badge variant="orange" className="mb-2 ltr:mr-2 rtl:ml-2">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Badge variant="orange" className="mb-2 ltr:mr-2 rtl:ml-2">
|
||||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
<div className="text-sm font-medium text-gray-900">
|
||||
{startTime}:{" "}
|
||||
<small className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format("HH:mm")} - {dayjs(booking.endTime).format("HH:mm")}
|
||||
</small>
|
||||
</div>
|
||||
<div
|
||||
title={booking.title}
|
||||
@@ -324,10 +329,6 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
)}>
|
||||
{booking.eventType?.team && <strong>{booking.eventType.team.name}: </strong>}
|
||||
{booking.title}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">Pending payment</Tag>
|
||||
)}
|
||||
{isPending && <Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">{t("unconfirmed")}</Tag>}
|
||||
</div>
|
||||
{booking.description && (
|
||||
<div
|
||||
@@ -372,13 +373,4 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
);
|
||||
}
|
||||
|
||||
const Tag = ({ children, className = "" }: React.PropsWithChildren<{ className?: string }>) => {
|
||||
return (
|
||||
<span
|
||||
className={`inline-flex items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800 ${className}`}>
|
||||
{children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default BookingListItem;
|
||||
|
||||
@@ -38,7 +38,7 @@ const DropdownActions = ({
|
||||
<Dropdown>
|
||||
{!actionTrigger ? (
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
|
||||
<Button type="button" color="secondary" size="icon" StartIcon={Icon.FiMoreHorizontal} />
|
||||
</DropdownMenuTrigger>
|
||||
) : (
|
||||
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
|
||||
|
||||
@@ -7,7 +7,7 @@ export default function MorePage() {
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<Shell>
|
||||
<div className="max-w-screen-lg">
|
||||
<div className="mt-8 max-w-screen-lg">
|
||||
<MobileNavigationMoreItems />
|
||||
<div className="mt-6">
|
||||
<UserV2OptInBanner />
|
||||
|
||||
@@ -40,7 +40,7 @@ export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availab
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="-mx-4 mb-16 overflow-hidden rounded-md border border-gray-200 bg-white sm:mx-0">
|
||||
<div className="mb-16 overflow-hidden rounded-md border border-gray-200 bg-white">
|
||||
<ul className="divide-y divide-neutral-200" data-testid="schedules">
|
||||
{schedules.map((schedule) => (
|
||||
<ScheduleListItem
|
||||
|
||||
@@ -87,7 +87,7 @@ export default function Bookings() {
|
||||
)}
|
||||
{(query.status === "loading" || query.status === "idle") && <SkeletonLoader />}
|
||||
{query.status === "success" && !isEmpty && (
|
||||
<div className="pt-2 xl:mx-6 xl:pt-0">
|
||||
<div className="pt-2 xl:pt-0">
|
||||
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||
{/* TODO: add today only for the current day
|
||||
<p className="pb-3 text-xs font-medium uppercase leading-4 text-gray-500">{t("today")}</p>
|
||||
|
||||
@@ -1186,5 +1186,6 @@
|
||||
"routing_forms_description": "You can see all forms and routes you have created here.",
|
||||
"add_new_form": "Add new form",
|
||||
"form_description": "Create your form to route a booker",
|
||||
"copy_link_to_form": "Copy link to form"
|
||||
"copy_link_to_form": "Copy link to form",
|
||||
"pending_payment": "Pending payment"
|
||||
}
|
||||
|
||||
@@ -220,6 +220,10 @@ button[role="switch"][data-state="checked"] {
|
||||
.bottom-nav {
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.cta {
|
||||
bottom: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
/* hide chat bubble on mobile */
|
||||
|
||||
@@ -40,7 +40,7 @@ export default function RoutingForms({
|
||||
/>
|
||||
) : null}
|
||||
{forms.length ? (
|
||||
<div className="-mx-4 mb-16 overflow-hidden bg-white sm:mx-0">
|
||||
<div className="mb-16 overflow-hidden bg-white">
|
||||
<List data-testid="routing-forms-list">
|
||||
{forms.map((form, index) => {
|
||||
if (!form) {
|
||||
|
||||
@@ -7,8 +7,8 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
interface IConfirmDialogWipe {
|
||||
isOpenDialog: boolean;
|
||||
@@ -93,7 +93,7 @@ export const ConfirmDialog = (props: IConfirmDialogWipe) => {
|
||||
{initialDate.format(dateFormat)} - {endDate.format(dateFormat)}
|
||||
</strong>
|
||||
</p>
|
||||
<p className="mt-6 mb-2 text-sm font-bold text-black">Are you sure? This can't be undone</p>
|
||||
<p className="mt-6 mb-2 text-sm">Are you sure? This can't be undone</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -103,6 +103,7 @@ export const ConfirmDialog = (props: IConfirmDialogWipe) => {
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
color="primary"
|
||||
data-testid="send_request"
|
||||
disabled={isLoading}
|
||||
onClick={async () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
import { ConfirmDialog } from "./confirmDialog";
|
||||
|
||||
@@ -30,7 +30,7 @@ const WipeMyCalActionButton = (props: IWipeMyCalActionButtonProps) => {
|
||||
{data && isSuccess && !isLoading && credentialId && (
|
||||
<div className="mb-4">
|
||||
<ConfirmDialog isOpenDialog={openDialog} setIsOpenDialog={setOpenDialog} />
|
||||
<Button onClick={() => setOpenDialog(true)} data-testid="wipe-today-button">
|
||||
<Button color="primary" onClick={() => setOpenDialog(true)} data-testid="wipe-today-button">
|
||||
Wipe Today
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="md:flex">
|
||||
<div className="my-8 sm:my-0 md:flex">
|
||||
<div className="pl-2 pr-3 md:pl-0">
|
||||
<div className="mb-5">
|
||||
<TextField label={`${t("workflow_name")}:`} type="text" {...form.register("name")} />
|
||||
|
||||
@@ -33,7 +33,7 @@ export const Badge = function Badge(props: BadgeProps) {
|
||||
<div
|
||||
{...passThroughProps}
|
||||
className={classNames(
|
||||
"inline-flex items-center justify-center rounded py-0.5 px-[6px] text-sm",
|
||||
"inline-flex items-center justify-center rounded py-0.5 px-[6px] text-xs",
|
||||
bold ? "font-semibold" : "font-normal",
|
||||
!StartIcon ? classNameBySize[size] : "",
|
||||
badgeClassNameByVariant[variant],
|
||||
|
||||
@@ -557,7 +557,7 @@ const MobileNavigation = () => {
|
||||
<>
|
||||
<nav
|
||||
className={classNames(
|
||||
"bottom-nav fixed bottom-0 z-30 -mx-4 flex w-full border border-t border-gray-200 bg-gray-50 px-1 shadow md:hidden",
|
||||
"bottom-nav fixed bottom-0 z-30 -mx-4 flex w-full border border-t border-gray-200 bg-gray-50 bg-opacity-40 px-1 shadow backdrop-blur-md md:hidden",
|
||||
isEmbed && "hidden"
|
||||
)}>
|
||||
{mobileNavigationBottomItems.map((item) => (
|
||||
@@ -721,7 +721,7 @@ export function ShellMain(props: LayoutProps) {
|
||||
const { isLocaleReady } = useLocale();
|
||||
return (
|
||||
<>
|
||||
<div className="flex items-baseline">
|
||||
<div className="flex items-baseline sm:mt-0">
|
||||
{!!props.backPath && (
|
||||
<Icon.FiArrowLeft
|
||||
className="mr-3 hover:cursor-pointer"
|
||||
@@ -735,7 +735,7 @@ export function ShellMain(props: LayoutProps) {
|
||||
"mb-4 flex w-full items-center pt-4 md:p-0 lg:mb-10"
|
||||
)}>
|
||||
{props.HeadingLeftIcon && <div className="ltr:mr-4">{props.HeadingLeftIcon}</div>}
|
||||
<div className="w-full ltr:mr-4 rtl:ml-4">
|
||||
<div className="hidden w-full ltr:mr-4 rtl:ml-4 sm:block">
|
||||
{props.heading && (
|
||||
<h1 className="font-cal mb-1 text-xl font-bold capitalize tracking-wide text-black">
|
||||
{!isLocaleReady ? null : props.heading}
|
||||
@@ -747,11 +747,18 @@ export function ShellMain(props: LayoutProps) {
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{props.CTA && <div className="mb-4 flex-shrink-0">{props.CTA}</div>}
|
||||
{props.CTA && (
|
||||
<div className="cta fixed right-4 bottom-[75px] z-40 mb-4 flex-shrink-0 sm:relative sm:bottom-auto sm:right-auto sm:z-0">
|
||||
{props.CTA}
|
||||
</div>
|
||||
)}
|
||||
</header>
|
||||
)}
|
||||
</div>
|
||||
<div className={classNames(props.flexChildrenContainer && "flex flex-1 flex-col")}>
|
||||
{/* add padding to top for mobile when App Bar is fixed */}
|
||||
<div className="pt-8 sm:hidden" />
|
||||
|
||||
{props.children}
|
||||
</div>
|
||||
</>
|
||||
@@ -805,29 +812,31 @@ function TopNav() {
|
||||
const isEmbed = useIsEmbed();
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<nav
|
||||
style={isEmbed ? { display: "none" } : {}}
|
||||
className="flex items-center justify-between border-b border-gray-200 bg-gray-50 py-1.5 px-4 sm:p-4 md:hidden">
|
||||
<Link href="/event-types">
|
||||
<a>
|
||||
<Logo />
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex items-center gap-2 self-center">
|
||||
<span className="group flex items-center rounded-full text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-neutral-900 lg:hidden">
|
||||
<KBarTrigger />
|
||||
</span>
|
||||
<button className="rounded-full p-1 text-gray-400 hover:bg-gray-50 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
|
||||
<span className="sr-only">{t("settings")}</span>
|
||||
<Link href="/settings/profile">
|
||||
<a>
|
||||
<Icon.FiSettings className="h-4 w-4 text-gray-700" aria-hidden="true" />
|
||||
</a>
|
||||
</Link>
|
||||
</button>
|
||||
<UserDropdown small />
|
||||
</div>
|
||||
</nav>
|
||||
<>
|
||||
<nav
|
||||
style={isEmbed ? { display: "none" } : {}}
|
||||
className="fixed z-40 flex w-full items-center justify-between border-b border-gray-200 bg-gray-50 bg-opacity-50 py-1.5 px-4 backdrop-blur-lg sm:relative sm:p-4 md:hidden">
|
||||
<Link href="/event-types">
|
||||
<a>
|
||||
<Logo />
|
||||
</a>
|
||||
</Link>
|
||||
<div className="flex items-center gap-2 self-center">
|
||||
<span className="group flex items-center rounded-full text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-neutral-900 lg:hidden">
|
||||
<KBarTrigger />
|
||||
</span>
|
||||
<button className="rounded-full p-1 text-gray-400 hover:bg-gray-50 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
|
||||
<span className="sr-only">{t("settings")}</span>
|
||||
<Link href="/settings/profile">
|
||||
<a>
|
||||
<Icon.FiSettings className="h-4 w-4 text-gray-700" aria-hidden="true" />
|
||||
</a>
|
||||
</Link>
|
||||
</button>
|
||||
<UserDropdown small />
|
||||
</div>
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,14 +43,14 @@ export default function BookingLayout({
|
||||
}: { children: React.ReactNode } & ComponentProps<typeof Shell>) {
|
||||
return (
|
||||
<Shell {...rest}>
|
||||
<div className="flex flex-col xl:flex-row ">
|
||||
<div className="flex flex-col sm:space-x-2 xl:flex-row">
|
||||
<div className="hidden xl:block">
|
||||
<VerticalTabs tabs={tabs} sticky />
|
||||
</div>
|
||||
<div className="block xl:hidden">
|
||||
<HorizontalTabs tabs={tabs} />
|
||||
</div>
|
||||
<main className="w-full">{children}</main>
|
||||
<main className="w-full max-w-6xl">{children}</main>
|
||||
</div>
|
||||
</Shell>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user