feat: tabs for teams in mobile (#6685)

* fix: padding

* feat: add teams tab component

* fix: add image to type

* fix: current path check

* fix: add avatar prop

* feat: add teams tab for mobile

* fix: padding

* fix: background and padding x

* fix: empty logic

* fix: conditions

* fix: bg

---------

Co-authored-by: Alan <alannnc@gmail.com>
This commit is contained in:
Nafees Nazik 2023-02-01 01:24:40 +05:30 committed by GitHub
parent 77d45a6ec2
commit 3b8f436bdf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 27 deletions

View File

@ -2,7 +2,8 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
import { GetServerSidePropsContext } from "next";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { Fragment, useEffect, useState } from "react";
import { FC, useEffect, useState, memo } from "react";
import { z } from "zod";
import {
CreateEventTypeButton,
@ -11,6 +12,8 @@ import {
import Shell from "@calcom/features/shell/Shell";
import { APP_NAME, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useMediaQuery from "@calcom/lib/hooks/useMediaQuery";
import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery";
import { RouterOutputs, trpc, TRPCClientError } from "@calcom/trpc/react";
import {
Badge,
@ -31,6 +34,7 @@ import {
Avatar,
AvatarGroup,
Tooltip,
HorizontalTabs,
} from "@calcom/ui";
import {
FiArrowDown,
@ -74,6 +78,40 @@ interface EventTypeListProps {
types: EventType[];
}
interface MobileTeamsTabProps {
eventTypeGroups: EventTypeGroups;
}
const querySchema = z.object({
teamId: z.nullable(z.coerce.number()).optional().default(null),
});
const MobileTeamsTab: FC<MobileTeamsTabProps> = (props) => {
const { eventTypeGroups } = props;
const tabs = eventTypeGroups.map((item) => ({
name: item.profile.name ?? "",
href: item.teamId ? `/event-types?teamId=${item.teamId}` : "/event-types",
avatar: item.profile.image ?? `${WEBAPP_URL}/${item.profile.slug}/avatar.png`,
}));
const { data } = useTypedQuery(querySchema);
const events = eventTypeGroups.filter((item) => item.teamId === data.teamId);
return (
<div>
<HorizontalTabs tabs={tabs} />
{events.length && (
<EventTypeList
types={events[0].eventTypes}
group={events[0]}
groupIndex={0}
readOnly={events[0].metadata.readOnly}
/>
)}
</div>
);
};
const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGroup; readOnly: boolean }) => {
const { t } = useLocale();
@ -105,7 +143,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
);
};
const MemoizedItem = React.memo(Item);
const MemoizedItem = memo(Item);
export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => {
const { t } = useLocale();
@ -589,6 +627,8 @@ const WithQuery = withQuery(trpc.viewer.eventTypes.getByViewer);
const EventTypesPage = () => {
const { t } = useLocale();
const isMobile = useMediaQuery("(max-width: 768px)");
return (
<div>
<Shell heading={t("event_types_page_title")} subtitle={t("event_types_page_subtitle")} CTA={<CTA />}>
@ -596,26 +636,40 @@ const EventTypesPage = () => {
customLoader={<SkeletonLoader />}
success={({ data }) => (
<>
{data.eventTypeGroups.map((group, index) => (
<Fragment key={group.profile.slug}>
{/* hide list heading when there is only one (current user) */}
{(data.eventTypeGroups.length !== 1 || group.teamId) && (
<EventTypeListHeading
profile={group.profile}
membershipCount={group.metadata.membershipCount}
teamId={group.teamId}
/>
)}
<EventTypeList
types={group.eventTypes}
group={group}
groupIndex={index}
readOnly={group.metadata.readOnly}
/>
</Fragment>
))}
{data.eventTypeGroups.length > 1 ? (
<>
{isMobile ? (
<MobileTeamsTab eventTypeGroups={data.eventTypeGroups} />
) : (
data.eventTypeGroups.map((group, index) => (
<div className="flex flex-col" key={group.profile.slug}>
<EventTypeListHeading
profile={group.profile}
membershipCount={group.metadata.membershipCount}
teamId={group.teamId}
/>
<EventTypeList
types={group.eventTypes}
group={group}
groupIndex={index}
readOnly={group.metadata.readOnly}
/>
</div>
))
)}
</>
) : data.eventTypeGroups.length === 1 ? (
<EventTypeList
types={data.eventTypeGroups[0].eventTypes}
group={data.eventTypeGroups[0]}
groupIndex={0}
readOnly={data.eventTypeGroups[0].metadata.readOnly}
/>
) : (
<CreateFirstEventTypeView />
)}
{data.eventTypeGroups.length === 0 && <CreateFirstEventTypeView />}
<EmbedDialog />
</>
)}

View File

@ -846,7 +846,7 @@ function MainContainer({
<main className="relative z-0 flex-1 bg-white focus:outline-none">
{/* show top navigation for md and smaller (tablet and phones) */}
{TopNavContainerProp}
<div className="max-w-full px-4 py-8 lg:px-12">
<div className="max-w-full px-4 py-4 md:py-8 lg:px-12">
<ErrorBoundary>
{!props.withoutMain ? <ShellMain {...props}>{props.children}</ShellMain> : props.children}
</ErrorBoundary>

View File

@ -299,6 +299,7 @@ export const eventTypesRouter = router({
profile: {
slug: typeof user["username"];
name: typeof user["name"];
image?: string;
};
metadata: {
membershipCount: number;

View File

@ -6,6 +6,7 @@ import classNames from "@calcom/lib/classNames";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SVGComponent } from "@calcom/types/SVGComponent";
import { Avatar } from "../../avatar";
import { SkeletonText } from "../../skeleton";
export type HorizontalTabItemProps = {
@ -15,12 +16,14 @@ export type HorizontalTabItemProps = {
href: string;
linkProps?: Omit<ComponentProps<typeof Link>, "href">;
icon?: SVGComponent;
avatar?: string;
};
const HorizontalTabItem = function ({ name, href, linkProps, ...props }: HorizontalTabItemProps) {
const HorizontalTabItem = function ({ name, href, linkProps, avatar, ...props }: HorizontalTabItemProps) {
const { t, isLocaleReady } = useLocale();
const { asPath } = useRouter();
const isCurrent = asPath.startsWith(href);
const isCurrent = asPath === href;
return (
<Link
@ -28,8 +31,8 @@ const HorizontalTabItem = function ({ name, href, linkProps, ...props }: Horizon
href={href}
{...linkProps}
className={classNames(
isCurrent ? "bg-gray-200 text-gray-900" : " text-gray-600 hover:bg-gray-100 hover:text-gray-900 ",
"inline-flex items-center justify-center whitespace-nowrap rounded-[6px] py-[10px] px-4 text-sm font-medium leading-4 md:mb-0",
isCurrent ? "bg-gray-100 text-gray-900" : " text-gray-600 hover:bg-gray-100 hover:text-gray-900 ",
"inline-flex items-center justify-center whitespace-nowrap rounded-[6px] py-[10px] px-2 text-sm font-medium leading-4 md:mb-0",
props.disabled && "pointer-events-none !opacity-30",
props.className
)}
@ -45,7 +48,13 @@ const HorizontalTabItem = function ({ name, href, linkProps, ...props }: Horizon
aria-hidden="true"
/>
)}
{isLocaleReady ? t(name) : <SkeletonText className="h-4 w-24" />}
{isLocaleReady ? (
<div className="flex items-center gap-x-2">
{avatar && <Avatar size="sm" imageSrc={avatar} alt="avatar" />} {t(name)}
</div>
) : (
<SkeletonText className="h-4 w-24" />
)}
</Link>
);
};