diff --git a/apps/frontend/src/components/launches/customer.modal.tsx b/apps/frontend/src/components/launches/customer.modal.tsx
new file mode 100644
index 000000000..109632250
--- /dev/null
+++ b/apps/frontend/src/components/launches/customer.modal.tsx
@@ -0,0 +1,88 @@
+import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
+import React, { FC, useCallback, useEffect, useState } from 'react';
+import { useModals } from '@mantine/modals';
+import { Integration } from '@prisma/client';
+import { Autocomplete } from '@mantine/core';
+import useSWR from 'swr';
+import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
+import { Button } from '@gitroom/react/form/button';
+
+export const CustomerModal: FC<{
+ integration: Integration & { customer?: { id: string; name: string } };
+ onClose: () => void;
+}> = (props) => {
+ const fetch = useFetch();
+ const { onClose, integration } = props;
+ const [customer, setCustomer] = useState(
+ integration.customer?.name || undefined
+ );
+ const modal = useModals();
+
+ const loadCustomers = useCallback(async () => {
+ return (await fetch('/integrations/customers')).json();
+ }, []);
+
+ const removeFromCustomer = useCallback(async () => {
+ saveCustomer(true);
+ }, []);
+
+ const saveCustomer = useCallback(async (removeCustomer?: boolean) => {
+ if (!customer) {
+ return;
+ }
+
+ await fetch(`/integrations/${integration.id}/customer-name`, {
+ method: 'PUT',
+ body: JSON.stringify({ name: removeCustomer ? '' : customer }),
+ });
+
+ modal.closeAll();
+ onClose();
+ }, [customer]);
+
+ const { data } = useSWR('/customers', loadCustomers);
+
+ return (
+
+
+
+
+
+
+
+
+ {!!integration?.customer?.name && }
+
+
+ );
+};
diff --git a/apps/frontend/src/components/launches/launches.component.tsx b/apps/frontend/src/components/launches/launches.component.tsx
index b03566b34..60884a503 100644
--- a/apps/frontend/src/components/launches/launches.component.tsx
+++ b/apps/frontend/src/components/launches/launches.component.tsx
@@ -1,9 +1,9 @@
'use client';
import { AddProviderButton } from '@gitroom/frontend/components/launches/add.provider.component';
-import { useCallback, useEffect, useMemo, useState } from 'react';
+import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import Image from 'next/image';
-import { orderBy } from 'lodash';
+import { groupBy, orderBy } from 'lodash';
import { CalendarWeekProvider } from '@gitroom/frontend/components/launches/calendar.context';
import { Filters } from '@gitroom/frontend/components/launches/filters';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
@@ -18,7 +18,201 @@ import ImageWithFallback from '@gitroom/react/helpers/image.with.fallback';
import { useToaster } from '@gitroom/react/toaster/toaster';
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';
import { Calendar } from './calendar';
+import { useDrag, useDrop } from 'react-dnd';
+import { DNDProvider } from '@gitroom/frontend/components/launches/helpers/dnd.provider';
+interface MenuComponentInterface {
+ refreshChannel: (
+ integration: Integration & { identifier: string }
+ ) => () => void;
+ continueIntegration: (integration: Integration) => () => void;
+ totalNonDisabledChannels: number;
+ mutate: (shouldReload?: boolean) => void;
+ update: (shouldReload: boolean) => void;
+}
+
+export const MenuGroupComponent: FC<
+ MenuComponentInterface & {
+ changeItemGroup: (id: string, group: string) => void;
+ group: {
+ id: string;
+ name: string;
+ values: Array<
+ Integration & {
+ identifier: string;
+ changeProfilePicture: boolean;
+ changeNickName: boolean;
+ }
+ >;
+ };
+ }
+> = (props) => {
+ const {
+ group,
+ mutate,
+ update,
+ continueIntegration,
+ totalNonDisabledChannels,
+ refreshChannel,
+ changeItemGroup,
+ } = props;
+
+ const [collectedProps, drop] = useDrop(() => ({
+ accept: 'menu',
+ drop: (item: { id: string }, monitor) => {
+ changeItemGroup(item.id, group.id);
+ },
+ collect: (monitor) => ({
+ isOver: !!monitor.isOver(),
+ }),
+ }));
+
+ return (
+
+ {collectedProps.isOver && (
+
+ )}
+ {!!group.name &&
{group.name}
}
+ {group.values.map((integration) => (
+
+ ))}
+
+ );
+};
+export const MenuComponent: FC<
+ MenuComponentInterface & {
+ integration: Integration & {
+ identifier: string;
+ changeProfilePicture: boolean;
+ changeNickName: boolean;
+ };
+ }
+> = (props) => {
+ const {
+ totalNonDisabledChannels,
+ continueIntegration,
+ refreshChannel,
+ mutate,
+ update,
+ integration,
+ } = props;
+
+ const user = useUser();
+ const [collected, drag, dragPreview] = useDrag(() => ({
+ type: 'menu',
+ item: { id: integration.id },
+ }));
+
+ return (
+
+
+ {(integration.inBetweenSteps || integration.refreshNeeded) && (
+
+ )}
+
+ {integration.identifier === 'youtube' ? (
+
+ ) : (
+
+ )}
+
+
+ {integration.name}
+
+
+ );
+};
export const LaunchesComponent = () => {
const fetch = useFetch();
const router = useRouter();
@@ -30,7 +224,6 @@ export const LaunchesComponent = () => {
const load = useCallback(async (path: string) => {
return (await (await fetch(path)).json()).integrations;
}, []);
- const user = useUser();
const {
isLoading,
@@ -47,6 +240,28 @@ export const LaunchesComponent = () => {
);
}, [integrations]);
+ const changeItemGroup = useCallback(
+ async (id: string, group: string) => {
+ mutate(
+ integrations.map((integration: any) => {
+ if (integration.id === id) {
+ return { ...integration, customer: { id: group } };
+ }
+ return integration;
+ }),
+ false
+ );
+
+ await fetch(`/integrations/${id}/group`, {
+ method: 'PUT',
+ body: JSON.stringify({ group }),
+ });
+
+ mutate();
+ },
+ [integrations]
+ );
+
const sortedIntegrations = useMemo(() => {
return orderBy(
integrations,
@@ -55,6 +270,25 @@ export const LaunchesComponent = () => {
);
}, [integrations]);
+ const menuIntegrations = useMemo(() => {
+ return orderBy(
+ Object.values(
+ groupBy(sortedIntegrations, (o) => o?.customer?.id || '')
+ ).map((p) => ({
+ name: (p[0].customer?.name || '') as string,
+ id: (p[0].customer?.id || '') as string,
+ isEmpty: p.length === 0,
+ values: orderBy(
+ p,
+ ['type', 'disabled', 'identifier'],
+ ['desc', 'asc', 'asc']
+ ),
+ })),
+ ['isEmpty', 'name'],
+ ['desc', 'asc']
+ );
+ }, [sortedIntegrations]);
+
const update = useCallback(async (shouldReload: boolean) => {
if (shouldReload) {
setReload(true);
@@ -112,114 +346,41 @@ export const LaunchesComponent = () => {
// @ts-ignore
return (
-
-
-
-
-
-
Channels
-
- {sortedIntegrations.length === 0 && (
-
No channels
- )}
- {sortedIntegrations.map((integration) => (
-
-
- {(integration.inBetweenSteps ||
- integration.refreshNeeded) && (
-
- )}
-
- {integration.identifier === 'youtube' ? (
-
- ) : (
-
- )}
-
-
- {integration.name}
-
-
-
-
+
+
);
};
diff --git a/apps/frontend/src/components/launches/menu/menu.tsx b/apps/frontend/src/components/launches/menu/menu.tsx
index d6f6b2276..7995e5d2b 100644
--- a/apps/frontend/src/components/launches/menu/menu.tsx
+++ b/apps/frontend/src/components/launches/menu/menu.tsx
@@ -8,6 +8,7 @@ import { useModals } from '@mantine/modals';
import { TimeTable } from '@gitroom/frontend/components/launches/time.table';
import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context';
import { BotPicture } from '@gitroom/frontend/components/launches/bot.picture';
+import { CustomerModal } from '@gitroom/frontend/components/launches/customer.modal';
export const Menu: FC<{
canEnable: boolean;
@@ -36,10 +37,13 @@ export const Menu: FC<{
setShow(false);
});
- const changeShow: MouseEventHandler
= useCallback((e) => {
- e.stopPropagation();
- setShow(!show);
- }, [show]);
+ const changeShow: MouseEventHandler = useCallback(
+ (e) => {
+ e.stopPropagation();
+ setShow(!show);
+ },
+ [show]
+ );
const disableChannel = useCallback(async () => {
if (
@@ -139,6 +143,34 @@ export const Menu: FC<{
setShow(false);
}, [integrations]);
+ const addToCustomer = useCallback(() => {
+ const findIntegration = integrations.find(
+ (integration) => integration.id === id
+ );
+
+ modal.openModal({
+ classNames: {
+ modal: 'w-[100%] max-w-[600px] bg-transparent text-textColor',
+ },
+ size: '100%',
+ withCloseButton: false,
+ closeOnEscape: true,
+ closeOnClickOutside: true,
+ children: (
+ {
+ mutate();
+ toast.show('Customer Updated', 'success');
+ }}
+ />
+ ),
+ });
+
+ setShow(false);
+ }, [integrations]);
+
return (
)}
+
+
+
Move / add to customer
+