Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Team Workflows #7038

Merged
merged 80 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
97fadcb
add teamId to workflows and add first basic query
Jan 18, 2023
2b247c5
create first version of new button (without any functionality)
Jan 23, 2023
d231e4c
Merge branch 'main' into feat/teams-workflows
Jan 25, 2023
0cffbd0
create reusable component for CreateButtons
Jan 25, 2023
1921bb3
fix dropdown design
Jan 25, 2023
46eb2fe
continued work on workflow create dialog
Jan 25, 2023
64bb412
add create function and fix wrong unauthorized error
Jan 25, 2023
d605411
create new isAuthorized function
Jan 26, 2023
ee6ffcf
clean up isAuthorized function
Jan 26, 2023
ba05926
create first filter for workflows
Jan 26, 2023
42f77e4
Merge branch 'main' into feat/teams-workflows
Feb 1, 2023
c9726b9
default check all fitlers
Feb 1, 2023
fc691f7
fix empty workflow page
Feb 1, 2023
f9ed27e
filter workflows in frontend
Feb 1, 2023
e0b98a2
fix result for all filters disabled
Feb 1, 2023
7f1dc4b
use different empty screen for filtered result
Feb 1, 2023
19856ce
improve UI of filter
Feb 1, 2023
327415c
fix updated workflows after deletion + small UI fixes
Feb 1, 2023
86bde36
add badge of team name in editing view
Feb 2, 2023
d4fa6ab
only show correct user or team event types
Feb 2, 2023
567b77d
fix deleting workflows + UI fixes
Feb 2, 2023
89adbba
add loading state to create button
Feb 2, 2023
259449f
Merge branch 'main' into feat/teams-workflows
Feb 2, 2023
7ef06ef
Merge branch 'main' into feat/teams-workflows
Feb 6, 2023
1945e5d
add badge with team name
Feb 6, 2023
8757998
handle existing mixed event types
Feb 7, 2023
3e51533
improve empty screen for filtered view
Feb 7, 2023
aaee6ae
fix subtitle for create event type
Feb 7, 2023
06b6b1d
fix position of chevron icon
Feb 7, 2023
5a25d31
Merge branch 'main' into feat/teams-workflows
Feb 7, 2023
be9999d
add new workflow button as cta button of shell
Feb 7, 2023
1568935
add missing authorization checks
Feb 7, 2023
736401a
first authorization steps for workflows in event type settings
Feb 7, 2023
8752812
show only authorized workflows in event types
Feb 7, 2023
695d02e
Merge branch 'main' into feat/teams-workflows
Feb 8, 2023
41a7ea7
fix create button position
Feb 8, 2023
7d27138
Merge branch 'main' into feat/teams-workflows
Feb 8, 2023
3262ab5
fix duplicate dialog
Feb 8, 2023
74220ba
only show workflow of team the event type belongs to
Feb 8, 2023
edec4d5
create workflows from event type settings for correct team/user
Feb 8, 2023
c8c5042
code clean up
Feb 8, 2023
ea19185
Merge branch 'main' into feat/teams-workflows
Feb 10, 2023
6218466
remove classname from createButton props
Feb 10, 2023
c50da51
code clean up
Feb 10, 2023
e6a0f45
code clean up
Feb 10, 2023
02a3dca
text alignment + code clean up
Feb 10, 2023
1dccd1d
add migration
Feb 10, 2023
b292844
code clean up
Feb 10, 2023
606966d
fix duplicate dialog
Feb 10, 2023
cf4fb8b
remove not needed useEffect
Feb 10, 2023
cad331c
only show filter if user has at least one team
Feb 10, 2023
b182778
fix create button without teams
Feb 10, 2023
e9dedd2
add teamId to verified numbers
Feb 10, 2023
368036a
Merge branch 'main' into feat/teams-workflows
Feb 13, 2023
fd2eae2
fix verified phone numbers for teams
Feb 13, 2023
87165b3
code clean up
Feb 13, 2023
f19d4a4
Merge branch 'main' into feat/teams-workflows
Feb 14, 2023
2e4348a
Merge branch 'main' into feat/teams-workflows
Feb 14, 2023
29557d9
fixing list update after deleting last workflow
Feb 14, 2023
8feb88b
Merge branch 'main' into feat/teams-workflows
zomars Feb 15, 2023
0325bd7
Update packages/features/ee/workflows/components/EmptyScreen.tsx
PeerRich Feb 19, 2023
59c4ac1
check if membership is accepted + type predicate feedback
Feb 20, 2023
b22d0ba
always save userId
Feb 20, 2023
97f0ed7
Update packages/ui/components/createButton/CreateButton.tsx
CarinaWolli Feb 20, 2023
a140b12
Merge branch 'main' into feat/teams-workflows
Feb 21, 2023
726629e
improve createButton props
Feb 21, 2023
9b77f89
make workflows read-only for members
Feb 21, 2023
a47161d
remove duplicateDialog from createButton component
Feb 21, 2023
597af9d
improve types
Feb 21, 2023
2983e6c
fix type error
Feb 21, 2023
82e7834
Merge branch 'main' into feat/teams-workflows
Feb 23, 2023
420d3ef
fix default value for location in create event type dialog
Feb 23, 2023
08fb190
remove duplicate verification input
Feb 23, 2023
f2b1a37
add readonly value in trpc list call
Feb 23, 2023
6025874
improve UI for readonly workflows
Feb 23, 2023
89efc74
improve readonly UI
Feb 23, 2023
d49b251
Merge branch 'main' into feat/teams-workflows
zomars Feb 23, 2023
a987482
Update packages/features/ee/workflows/components/EventWorkflowsTab.tsx
zomars Feb 23, 2023
c7803e1
Update WorkflowDetailsPage.tsx
zomars Feb 23, 2023
bcb7376
Merge branch 'main' into feat/teams-workflows
Feb 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions apps/web/pages/event-types/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import type { FC } from "react";
import { useEffect, useState, memo } from "react";
import { z } from "zod";

import {
CreateEventTypeButton,
EventTypeDescriptionLazy as EventTypeDescription,
} from "@calcom/features/eventtypes/components";
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
import { DuplicateDialog } from "@calcom/features/eventtypes/components/DuplicateDialog";
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";
Expand All @@ -35,6 +34,7 @@ import {
showToast,
Switch,
Tooltip,
CreateButton,
HorizontalTabs,
} from "@calcom/ui";
import {
Expand Down Expand Up @@ -134,9 +134,9 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
</small>
) : null}
{readOnly && (
<span className="items-center rounded-sm bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 ltr:ml-2 ltr:mr-2 rtl:ml-2">
<Badge variant="gray" className="ml-2">
{t("readonly")}
</span>
</Badge>
)}
</div>
<EventTypeDescription
Expand Down Expand Up @@ -240,7 +240,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
const openDuplicateModal = (eventType: EventType, group: EventTypeGroup) => {
const query = {
...router.query,
dialog: "duplicate-event-type",
dialog: "duplicate",
title: eventType.title,
description: eventType.description,
slug: eventType.slug,
Expand Down Expand Up @@ -627,17 +627,32 @@ const CreateFirstEventTypeView = () => {
};

const CTA = () => {
const { t } = useLocale();

const query = trpc.viewer.eventTypes.getByViewer.useQuery();

if (!query.data) return null;

return <CreateEventTypeButton canAddEvents={true} options={query.data.profiles} />;
const profileOptions = query.data.profiles
.filter((profile) => !profile.readOnly)
.map((profile) => {
return { teamId: profile.teamId, label: profile.name || profile.slug, image: profile.image };
});

return (
<CreateButton
subtitle={t("create_event_on").toUpperCase()}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentional to change this text from 'Create an event type under your name or a team.' to 'CREATE EVENT ON'.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is. I just tried to find the Figma design where I saw that but I can't find it anymore. @Jaibles what is the current most recent design for that? This is how it looks with my changes:
Screenshot 2023-02-20 at 13 35 09

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should use CSS to uppercase then.

options={profileOptions}
createDialog={CreateEventTypeDialog}
/>
);
};

const WithQuery = withQuery(trpc.viewer.eventTypes.getByViewer);

const EventTypesPage = () => {
const { t } = useLocale();
const router = useRouter();

const isMobile = useMediaQuery("(max-width: 768px)");

Expand Down Expand Up @@ -683,6 +698,7 @@ const EventTypesPage = () => {
)}

<EmbedDialog />
{router.query.dialog === "duplicate" && <DuplicateDialog />}
</>
)}
/>
Expand Down
11 changes: 8 additions & 3 deletions apps/web/public/static/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -598,7 +598,6 @@
"new_event_type_heading": "Create your first event type",
"new_event_type_description": "Event types enable you to share links that show available times on your calendar and allow people to make bookings with you.",
"new_event_title": "Add a new event type",
"new_event_subtitle": "Create an event type under your name or a team.",
"new_team_event": "Add a new team event type",
"new_event_description": "Create a new event type for people to book times with.",
"event_type_created_successfully": "{{eventTypeTitle}} event type created successfully",
Expand Down Expand Up @@ -1527,6 +1526,7 @@
"sender_name": "Sender name",
"already_invited": "Attendee already invited",
"no_recordings_found": "No recordings found",
"new_workflow_subtitle": "New workflow for...",
"reporting": "Reporting",
"reporting_feature": "See all incoming from data and download it as a CSV",
"teams_plan_required": "Teams plan required",
Expand Down Expand Up @@ -1581,14 +1581,17 @@
"email_no_user_step_four":"Join {{teamName}}",
"email_no_user_signoff":"Happy Scheduling from the {{appName}} team",
"impersonation_user_tip": "You are about to impersonate a user, which means you can make changes on their behalf. Please be careful.",
"scheduler": "{Scheduler}",
"available_variables": "Available variables",
"scheduler": "{Scheduler}",
"no_workflows": "No workflows",
"change_filter": "Change filter to see your personal and team workflows.",
"recommended_next_steps": "Recommended next steps",
"create_a_managed_event": "Create a managed event type",
"meetings_are_better_with_the_right": "Meetings are better with the right team members there. Invite them now.",
"create_a_one_one_template": "Create a one-one one template for an event type and distribute it to multiple members.",
"collective_or_roundrobin": "Collective or round-robin",
"book_your_team_members": "Book your team members together with collective events or cycle through to get the right person with round-robin.",
"create_event_on": "Create event on",
"default_app_link_title":"Set a default app link",
"default_app_link_description":"Setting a default app link allows all newly created event types to use the app link you set.",
"change_default_conferencing_app":"Set as default",
Expand Down Expand Up @@ -1619,5 +1622,7 @@
"select_a_router": "Select a router",
"add_a_new_route": "Add a new Route",
"no_responses_yet": "No responses yet",
"this_will_be_the_placeholder": "This will be the placeholder"
"this_will_be_the_placeholder": "This will be the placeholder",
"verification_code": "Verification code",
"verify": "Verify"
}
4 changes: 2 additions & 2 deletions packages/features/ee/teams/pages/team-profile-view.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { MembershipRole, Prisma } from "@prisma/client";
import type { Prisma } from "@prisma/client";
import { MembershipRole } from "@prisma/client";
import MarkdownIt from "markdown-it";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/router";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";

import { CAL_URL } from "@calcom/lib/constants";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import { useLocale } from "@calcom/lib/hooks/useLocale";
Expand Down
6 changes: 5 additions & 1 deletion packages/features/ee/workflows/components/DeleteDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Dispatch, SetStateAction } from "react";
import type { Dispatch, SetStateAction } from "react";

import { useLocale } from "@calcom/lib/hooks/useLocale";
import { HttpError } from "@calcom/lib/http-error";
Expand Down Expand Up @@ -30,6 +30,10 @@ export const DeleteDialog = (props: IDeleteDialog) => {
showToast(message, "error");
setIsOpenDialog(false);
}
if (err.data?.code === "UNAUTHORIZED") {
const message = `${err.data.code}: You are not authorized to delete this workflow`;
showToast(message, "error");
}
},
});

Expand Down
94 changes: 53 additions & 41 deletions packages/features/ee/workflows/components/EmptyScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React from "react";
import { useRouter } from "next/router";

import { useLocale } from "@calcom/lib/hooks/useLocale";
import { SVGComponent } from "@calcom/types/SVGComponent";
import { Button } from "@calcom/ui";
import { FiSmartphone, FiMail, FiPlus } from "@calcom/ui/components/icon";
import { HttpError } from "@calcom/lib/http-error";
import { trpc } from "@calcom/trpc/react";
import type { SVGComponent } from "@calcom/types/SVGComponent";
import { CreateButton, showToast, EmptyScreen as ClassicEmptyScreen } from "@calcom/ui";
import { FiSmartphone, FiMail, FiZap } from "@calcom/ui/components/icon";

type WorkflowExampleType = {
Icon: SVGComponent;
Expand Down Expand Up @@ -31,24 +33,33 @@ function WorkflowExample(props: WorkflowExampleType) {
);
}

export default function EmptyScreen({
IconHeading,
headline,
description,
buttonText,
buttonOnClick,
isLoading,
showExampleWorkflows,
}: {
IconHeading: SVGComponent;
headline: string;
description: string | React.ReactElement;
buttonText?: string;
buttonOnClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
isLoading: boolean;
showExampleWorkflows: boolean;
export default function EmptyScreen(props: {
profileOptions: {
label: string | null;
image?: string | null;
teamId: number | null | undefined;
}[];
isFilteredView: boolean;
}) {
const { t } = useLocale();
const router = useRouter();

const createMutation = trpc.viewer.workflows.create.useMutation({
onSuccess: async ({ workflow }) => {
await router.replace("/workflows/" + workflow.id);
},
onError: (err) => {
if (err instanceof HttpError) {
const message = `${err.statusCode}: ${err.message}`;
showToast(message, "error");
}

if (err.data?.code === "UNAUTHORIZED") {
const message = `${err.data.code}: You are not authorized to create this workflow`;
showToast(message, "error");
}
},
});

const workflowsExamples = [
{ icon: FiSmartphone, text: t("workflow_example_1") },
Expand All @@ -60,38 +71,39 @@ export default function EmptyScreen({
];
// new workflow example when 'after meetings ends' trigger is implemented: Send custom thank you email to attendee after event (FiSmile icon),

if (props.isFilteredView) {
return <ClassicEmptyScreen Icon={FiZap} headline={t("no_workflows")} description={t("change_filter")} />;
}

return (
<>
<div className="min-h-80 flex w-full flex-col items-center justify-center rounded-md ">
<div className="flex h-[72px] w-[72px] items-center justify-center rounded-full bg-gray-200 dark:bg-white">
<IconHeading className="inline-block h-10 w-10 stroke-[1.3px] dark:bg-gray-900 dark:text-gray-600" />
<FiZap className="inline-block h-10 w-10 stroke-[1.3px] dark:bg-gray-900 dark:text-gray-600" />
</div>
<div className="max-w-[420px] text-center">
<h2 className="text-semibold font-cal mt-6 text-xl dark:text-gray-300">{headline}</h2>
<h2 className="text-semibold font-cal mt-6 text-xl dark:text-gray-300">{t("workflows")}</h2>
<p className="line-clamp-2 mt-3 text-sm font-normal leading-6 text-gray-700 dark:text-gray-300">
{description}
{t("no_workflows_description")}
</p>
{buttonOnClick && buttonText && (
<Button
type="button"
StartIcon={FiPlus}
onClick={(e) => buttonOnClick(e)}
loading={isLoading}
className="mx-auto mt-8">
{buttonText}
</Button>
)}
<div className="mt-8 ">
<CreateButton
subtitle={t("new_workflow_subtitle").toUpperCase()}
options={props.profileOptions}
createFunction={(teamId?: number) => createMutation.mutate({ teamId })}
buttonText={t("create_workflow")}
isLoading={createMutation.isLoading}
/>
</div>
</div>
</div>
{showExampleWorkflows && (
<div className="flex flex-row items-center justify-center">
<div className="grid-cols-none items-center lg:grid lg:grid-cols-3 xl:mx-20">
{workflowsExamples.map((example, index) => (
<WorkflowExample key={index} Icon={example.icon} text={example.text} />
))}
</div>
<div className="flex flex-row items-center justify-center">
<div className="grid-cols-none items-center lg:grid lg:grid-cols-3 xl:mx-20">
{workflowsExamples.map((example, index) => (
<WorkflowExample key={index} Icon={example.icon} text={example.text} />
))}
</div>
)}
</div>
</>
);
}
22 changes: 17 additions & 5 deletions packages/features/ee/workflows/components/EventWorkflowsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { FiExternalLink, FiZap } from "@calcom/ui/components/icon";
import LicenseRequired from "../../common/components/v2/LicenseRequired";
import { getActionIcon } from "../lib/getActionIcon";
import SkeletonLoader from "./SkeletonLoaderEventWorkflowsTab";
import { WorkflowType } from "./WorkflowListPage";
import type { WorkflowType } from "./WorkflowListPage";

type ItemProps = {
workflow: WorkflowType;
Expand Down Expand Up @@ -65,6 +65,11 @@ const WorkflowListItem = (props: ItemProps) => {
const message = `${err.statusCode}: ${err.message}`;
showToast(message, "error");
}
if (err.data?.code === "UNAUTHORIZED") {
// TODO: Add missing translation
const message = `${err.data.code}: You are not authorized to enable or disable this workflow`;
zomars marked this conversation as resolved.
Show resolved Hide resolved
showToast(message, "error");
}
},
});

Expand Down Expand Up @@ -148,14 +153,21 @@ type Props = {
eventType: {
id: number;
title: string;
userId: number | null;
team: {
id?: number;
} | null;
};
workflows: WorkflowType[];
};

function EventWorkflowsTab(props: Props) {
const { workflows } = props;
const { workflows, eventType } = props;
const { t } = useLocale();
const { data, isLoading } = trpc.viewer.workflows.list.useQuery();
const { data, isLoading } = trpc.viewer.workflows.list.useQuery({
teamId: eventType.team?.id,
userId: eventType.userId || undefined,
});
const router = useRouter();
const [sortedWorkflows, setSortedWorkflows] = useState<Array<WorkflowType>>([]);

Expand All @@ -176,7 +188,7 @@ function EventWorkflowsTab(props: Props) {
}
}, [isLoading]);

const createMutation = trpc.viewer.workflows.createV2.useMutation({
const createMutation = trpc.viewer.workflows.create.useMutation({
onSuccess: async ({ workflow }) => {
await router.replace("/workflows/" + workflow.id);
},
Expand Down Expand Up @@ -212,7 +224,7 @@ function EventWorkflowsTab(props: Props) {
<Button
target="_blank"
color="secondary"
onClick={() => createMutation.mutate()}
onClick={() => createMutation.mutate({ teamId: eventType.team?.id })}
loading={createMutation.isLoading}>
{t("create_workflow")}
</Button>
Expand Down
Loading