Manually reorder event types (#1142)

* Add event type reordering

* Add migration for position field

* hack on a hack

* can edit

* fix ordering

* Remove console.log

Co-authored-by: Alex Johansson <alexander@n1s.se>

Co-authored-by: KATT <alexander@n1s.se>
This commit is contained in:
Bailey Pumfleet 2021-11-15 12:25:49 +00:00 committed by GitHub
parent 6fa980f801
commit 6b171a6f87
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 184 additions and 7 deletions

View file

@ -124,6 +124,14 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
},
],
},
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
select: {
id: true,
slug: true,

View file

@ -1,13 +1,20 @@
// TODO: replace headlessui with radix-ui
import { Menu, Transition } from "@headlessui/react";
import { UsersIcon } from "@heroicons/react/solid";
import { ChevronDownIcon, PlusIcon } from "@heroicons/react/solid";
import { DotsHorizontalIcon, ExternalLinkIcon, LinkIcon } from "@heroicons/react/solid";
import {
DotsHorizontalIcon,
ExternalLinkIcon,
LinkIcon,
ArrowDownIcon,
ChevronDownIcon,
PlusIcon,
ArrowUpIcon,
UsersIcon,
} from "@heroicons/react/solid";
import { SchedulingType } from "@prisma/client";
import Head from "next/head";
import Link from "next/link";
import { useRouter } from "next/router";
import React, { Fragment, useRef } from "react";
import React, { Fragment, useRef, useState, useEffect } from "react";
import { useMutation } from "react-query";
import { QueryCell } from "@lib/QueryCell";
@ -72,10 +79,40 @@ interface EventTypeListProps {
}
const EventTypeList = ({ readOnly, types, profile }: EventTypeListProps): JSX.Element => {
const { t } = useLocale();
const utils = trpc.useContext();
const mutation = trpc.useMutation("viewer.eventTypeOrder", {
onError: (err) => {
console.error(err.message);
},
async onSettled() {
await utils.cancelQuery(["viewer.eventTypes"]);
await utils.invalidateQueries(["viewer.eventTypes"]);
},
});
const [sortableTypes, setSortableTypes] = useState(types);
useEffect(() => {
setSortableTypes(types);
}, [types]);
function moveEventType(index: number, increment: 1 | -1) {
const newList = [...sortableTypes];
const type = sortableTypes[index];
const tmp = sortableTypes[index + increment];
if (tmp) {
newList[index] = tmp;
newList[index + increment] = type;
}
setSortableTypes(newList);
mutation.mutate({
ids: newList.map((type) => type.id),
});
}
return (
<div className="mb-16 -mx-4 overflow-hidden bg-white border border-gray-200 rounded-sm sm:mx-0">
<ul className="divide-y divide-neutral-200" data-testid="event-types">
{types.map((type) => (
{sortableTypes.map((type, index) => (
<li
key={type.id}
className={classNames(
@ -87,7 +124,17 @@ const EventTypeList = ({ readOnly, types, profile }: EventTypeListProps): JSX.El
"hover:bg-neutral-50 flex justify-between items-center ",
type.$disabled && "pointer-events-none"
)}>
<div className="flex items-center justify-between w-full px-4 py-4 sm:px-6 hover:bg-neutral-50">
<div className="group flex items-center justify-between w-full px-4 py-4 sm:px-6 hover:bg-neutral-50">
<button
className="absolute mb-8 left-1/2 -ml-4 sm:ml-0 sm:left-[19px] border hover:border-transparent text-gray-400 transition-all hover:text-black hover:shadow group-hover:scale-100 scale-0 w-7 h-7 p-1 invisible group-hover:visible bg-white rounded-full"
onClick={() => moveEventType(index, -1)}>
<ArrowUpIcon />
</button>
<button
className="absolute mt-8 left-1/2 -ml-4 sm:ml-0 sm:left-[19px] border hover:border-transparent text-gray-400 transition-all hover:text-black hover:shadow group-hover:scale-100 scale-0 w-7 h-7 p-1 invisible group-hover:visible bg-white rounded-full"
onClick={() => moveEventType(index, 1)}>
<ArrowDownIcon />
</button>
<Link href={"/event-types/" + type.id}>
<a
className="flex-grow text-sm truncate"

View file

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventType" ADD COLUMN "position" INTEGER NOT NULL DEFAULT 0;

View file

@ -21,6 +21,7 @@ model EventType {
title String
slug String
description String?
position Int @default(0)
locations Json?
length Int
hidden Boolean @default(false)

View file

@ -517,6 +517,7 @@
"confirm_delete_event_type": "Yes, delete event type",
"integrations": "Integrations",
"settings": "Settings",
"event_type_moved_successfully": "Event type has been moved successfully",
"next_step": "Skip step",
"prev_step": "Prev step",
"installed": "Installed",

View file

@ -1,4 +1,5 @@
import { BookingStatus, Prisma } from "@prisma/client";
import _ from "lodash";
import { z } from "zod";
import { checkPremiumUsername } from "@ee/lib/core/checkPremiumUsername";
@ -85,6 +86,7 @@ const loggedInViewerRouter = createProtectedRouter()
hidden: true,
price: true,
currency: true,
position: true,
users: {
select: {
id: true,
@ -126,6 +128,14 @@ const loggedInViewerRouter = createProtectedRouter()
},
eventTypes: {
select: eventTypeSelect,
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
},
},
},
@ -136,6 +146,14 @@ const loggedInViewerRouter = createProtectedRouter()
team: null,
},
select: eventTypeSelect,
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
},
},
});
@ -150,6 +168,14 @@ const loggedInViewerRouter = createProtectedRouter()
userId: ctx.user.id,
},
select: eventTypeSelect,
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
});
type EventTypeGroup = {
@ -184,7 +210,7 @@ const loggedInViewerRouter = createProtectedRouter()
name: user.name,
image: user.avatar,
},
eventTypes: mergedEventTypes,
eventTypes: _.orderBy(mergedEventTypes, ["position", "id"], ["desc", "asc"]),
metadata: {
membershipCount: 1,
readOnly: false,
@ -449,6 +475,98 @@ const loggedInViewerRouter = createProtectedRouter()
data,
});
},
})
.mutation("eventTypeOrder", {
input: z.object({
ids: z.array(z.number()),
}),
async resolve({ input, ctx }) {
const { prisma, user } = ctx;
const allEventTypes = await ctx.prisma.eventType.findMany({
select: {
id: true,
},
where: {
id: {
in: input.ids,
},
OR: [
{
userId: user.id,
},
{
users: {
some: {
id: user.id,
},
},
},
{
team: {
members: {
some: {
userId: user.id,
},
},
},
},
],
},
});
const allEventTypeIds = new Set(allEventTypes.map((type) => type.id));
if (input.ids.some((id) => !allEventTypeIds.has(id))) {
throw new TRPCError({
code: "UNAUTHORIZED",
});
}
await Promise.all(
_.reverse(input.ids).map((id, position) => {
return prisma.eventType.update({
where: {
id,
},
data: {
position,
},
});
})
);
},
})
.mutation("eventTypePosition", {
input: z.object({
eventType: z.number(),
action: z.string(),
}),
async resolve({ input, ctx }) {
// This mutation is for the user to be able to order their event types by incrementing or decrementing the position number
const { prisma } = ctx;
if (input.eventType && input.action == "increment") {
await prisma.eventType.update({
where: {
id: input.eventType,
},
data: {
position: {
increment: 1,
},
},
});
}
if (input.eventType && input.action == "decrement") {
await prisma.eventType.update({
where: {
id: input.eventType,
},
data: {
position: {
decrement: 1,
},
},
});
}
},
});
export const viewerRouter = createRouter()