Fix destination calendar overflow on installed and move DestinationCalendarSelector to feature package (#4778)

* Standardize destination calendar selector

* Move DestinationCalendarSelector to feature package

* Render integration name

* Add custom components to label and selected

* Render destinationCalendar on page load

* Change name to just Outlook

* Small fixes

* Merge branch 'main' into hotfix/install-calendar-overflow

* Merge branch 'main' into hotfix/install-calendar-overflow

* Fix type errors

* Fix type errors

* Update apps/web/pages/settings/my-account/calendars.tsx

* More type fixes

Co-authored-by: Omar López <zomars@me.com>
pull/4766/head^2
Joe Au-Yeung 2022-10-18 14:06:26 -04:00 committed by GitHub
parent 5c01467caa
commit 5ab5af753a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 73 additions and 51 deletions

View File

@ -3,6 +3,7 @@ import Link from "next/link";
import { Fragment } from "react";
import { InstallAppButton } from "@calcom/app-store/components";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import showToast from "@calcom/lib/notification";
import { trpc } from "@calcom/trpc/react";
@ -18,7 +19,6 @@ import { QueryCell } from "@lib/QueryCell";
import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections";
import AdditionalCalendarSelector from "@components/v2/apps/AdditionalCalendarSelector";
import DestinationCalendarSelector from "@components/v2/apps/DestinationCalendarSelector";
import IntegrationListItem from "@components/v2/apps/IntegrationListItem";
type Props = {
@ -287,7 +287,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
<h1 className="text-sm font-semibold">{t("create_events_on")}</h1>
<p className="text-sm font-normal">{t("set_calendar")}</p>
</div>
<div className="flex justify-end md:w-6/12">
<div className="justify-end md:w-6/12">
<DestinationCalendarSelector
onChange={mutation.mutate}
hidePlaceholder

View File

@ -7,6 +7,7 @@ import { Controller, useFormContext } from "react-hook-form";
import short from "short-uuid";
import { v5 as uuidv5 } from "uuid";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { CAL_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@ -14,7 +15,6 @@ import { Icon } from "@calcom/ui";
import {
Button,
CustomInputItem,
DestinationCalendarSelector,
Dialog,
DialogContent,
Label,

View File

@ -3,6 +3,7 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { Fragment } from "react";
import DestinationCalendarSelector from "@calcom/features/calendars/DestinationCalendarSelector";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@ -14,7 +15,6 @@ import Meta from "@calcom/ui/v2/core/Meta";
import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout";
import { SkeletonContainer, SkeletonText, SkeletonButton } from "@calcom/ui/v2/core/skeleton";
import { List, ListItem, ListItemText, ListItemTitle } from "@calcom/ui/v2/modules/List";
import DestinationCalendarSelector from "@calcom/ui/v2/modules/event-types/DestinationCalendarSelector";
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
import { QueryCell } from "@lib/QueryCell";
@ -63,7 +63,7 @@ const CalendarsView = () => {
<Icon.FiCalendar className="h-6 w-6" />
</div>
<div className="flex flex-col space-y-3">
<div className="flex w-full flex-col space-y-3">
<div>
<h4 className=" pb-2 text-base font-semibold leading-5 text-black">
{t("add_to_calendar")}

View File

@ -3,10 +3,10 @@ import type { AppMeta } from "@calcom/types/App";
import _package from "./package.json";
export const metadata = {
name: "Office 365 / Outlook.com Calendar",
name: "Outlook Calendar",
description: _package.description,
type: "office365_calendar",
title: "Office 365 / Outlook.com Calendar",
title: "Outlook Calendar",
imageSrc: "/api/app-store/office365calendar/icon.svg",
variant: "calendar",
category: "calendar",

View File

@ -1,5 +1,7 @@
import classNames from "classnames";
import React, { useEffect, useState } from "react";
import { components } from "react-select";
import { SingleValueProps, OptionProps, SingleValue, ActionMeta } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { DestinationCalendar } from "@calcom/prisma/client";
@ -16,6 +18,30 @@ interface Props {
maxWidth?: number;
}
interface Option {
label: string;
value: string;
subtitle: string;
}
const SingleValueComponent = ({ ...props }: SingleValueProps<Option>) => {
const { label, subtitle } = props.data;
return (
<components.SingleValue {...props} className="flex space-x-1">
<p>{label}</p> <p className=" text-neutral-500">{subtitle}</p>
</components.SingleValue>
);
};
const OptionComponent = ({ ...props }: OptionProps<Option>) => {
const { label } = props.data;
return (
<components.Option {...props}>
<span>{label}</span>
</components.Option>
);
};
const DestinationCalendarSelector = ({
onChange,
isLoading,
@ -26,13 +52,18 @@ const DestinationCalendarSelector = ({
}: Props): JSX.Element | null => {
const { t } = useLocale();
const query = trpc.useQuery(["viewer.connectedCalendars"]);
const [selectedOption, setSelectedOption] = useState<{ value: string; label: string } | null>(null);
const [selectedOption, setSelectedOption] = useState<{
value: string;
label: string;
subtitle: string;
} | null>(null);
// Extra styles to show prefixed text in react-select
const content = (hidePlaceholder = false) => {
if (!hidePlaceholder) {
return {
alignItems: "center",
width: "100%",
display: "flex",
":before": {
content: `'${t("select_destination_calendar")}:'`,
@ -51,12 +82,19 @@ const DestinationCalendarSelector = ({
.find((cal) => cal.externalId === value);
if (selected) {
const selectedIntegration = query.data?.connectedCalendars.find((integration) =>
integration.calendars?.some((calendar) => calendar.externalId === selected.externalId)
);
setSelectedOption({
value: `${selected.integration}:${selected.externalId}`,
label: selected.name || "",
label: `${selected.name} ` || "",
subtitle: `(${selectedIntegration?.integration.title?.replace(/calendar/i, "")} - ${
selectedIntegration?.primary?.name
})`,
});
}
}, [query.data?.connectedCalendars, value]);
}, [query.data?.connectedCalendars]);
if (!query.data?.connectedCalendars.length) {
return null;
@ -64,11 +102,16 @@ const DestinationCalendarSelector = ({
const options =
query.data.connectedCalendars.map((selectedCalendar) => ({
key: selectedCalendar.credentialId,
label: `${selectedCalendar.integration.title} (${selectedCalendar.primary?.name})`,
label: `${selectedCalendar.integration.title?.replace(/calendar/i, "")} (${
selectedCalendar.primary?.name
})`,
options: (selectedCalendar.calendars ?? [])
.filter((cal) => cal.readOnly === false)
.map((cal) => ({
label: cal.name || "",
label: ` ${cal.name} `,
subtitle: `(${selectedCalendar?.integration.title?.replace(/calendar/i, "")} - ${
selectedCalendar?.primary?.name
})`,
value: `${cal.integration}:${cal.externalId}`,
})),
})) ?? [];
@ -113,14 +156,14 @@ const DestinationCalendarSelector = ({
className={classNames(
"mt-1 mb-2 block w-full min-w-0 flex-1 rounded-none rounded-r-sm border-gray-300 text-sm"
)}
onChange={(option) => {
setSelectedOption(option);
if (!option) {
onChange={(newValue) => {
setSelectedOption(newValue);
if (!newValue) {
return;
}
/* Split only the first `:`, since Apple uses the full URL as externalId */
const [integration, externalId] = option.value.split(/:(.+)/);
const [integration, externalId] = newValue.value.split(/:(.+)/);
onChange({
integration,
@ -129,6 +172,8 @@ const DestinationCalendarSelector = ({
}}
isLoading={isLoading}
value={selectedOption}
components={{ SingleValue: SingleValueComponent, Option: OptionComponent }}
isMulti={false}
/>
</div>
);

View File

@ -2,89 +2,67 @@
{
"/*": "This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually",
"dirName": "routing-forms",
"categories": [
"other"
],
"categories": ["other"],
"slug": "routing-forms",
"type": "routing-forms_other"
},
{
"dirName": "whereby",
"categories": [
"video"
],
"categories": ["video"],
"slug": "whereby",
"type": "whereby_video"
},
{
"dirName": "around",
"categories": [
"video"
],
"categories": ["video"],
"slug": "around",
"type": "around_video"
},
{
"dirName": "riverside",
"categories": [
"video"
],
"categories": ["video"],
"slug": "riverside",
"type": "riverside_video"
},
{
"dirName": "typeform",
"categories": [
"other"
],
"categories": ["other"],
"slug": "typeform",
"type": "typeform_other"
},
{
"dirName": "ping",
"categories": [
"video"
],
"categories": ["video"],
"slug": "ping",
"type": "ping_video"
},
{
"dirName": "campfire",
"categories": [
"video"
],
"categories": ["video"],
"slug": "campfire",
"type": "campfire_video"
},
{
"dirName": "rainbow",
"categories": [
"web3"
],
"categories": ["web3"],
"slug": "rainbow",
"type": "rainbow_web3"
},
{
"dirName": "raycast",
"categories": [
"other"
],
"categories": ["other"],
"slug": "raycast",
"type": "raycast_other"
},
{
"dirName": "n8n",
"categories": [
"automation"
],
"categories": ["automation"],
"slug": "n8n",
"type": "n8n_automation"
},
{
"dirName": "exchangecalendar",
"categories": [
"calendar"
],
"categories": ["calendar"],
"slug": "exchange",
"type": "exchange_calendar"
},
@ -106,4 +84,4 @@
"slug": "fathom",
"type": "fathom_analytics"
}
]
]

View File

@ -1,4 +1,3 @@
export { default as CreateEventType } from "./CreateEventType";
export { default as DestinationCalendarSelector } from "./DestinationCalendarSelector";
export { default as CustomInputItem } from "./CustomInputItem";
export { default as CheckedTeamSelect } from "./CheckedTeamSelect";