Merge branch 'main' into patch-1

This commit is contained in:
Enno Gelhaus 2025-09-14 15:17:35 +02:00 committed by GitHub
commit cdb5d5ccbb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 920 additions and 789 deletions

View File

@ -22,7 +22,8 @@
--new-col-color: #2c2b2b; --new-col-color: #2c2b2b;
--new-menu-dots: #696868; --new-menu-dots: #696868;
--new-menu-hover: #fff; --new-menu-hover: #fff;
--menu-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.50); --menu-shadow: 0 8px 30px 0 rgba(0, 0, 0, 0.5);
--popup-color: rgba(65, 64, 66, 0.3);
} }
.light { .light {
--new-bgColor: #f0f2f4; --new-bgColor: #f0f2f4;
@ -33,8 +34,8 @@
--new-boxFocused: #ebe8ff; --new-boxFocused: #ebe8ff;
--new-textColor: 14 14 14; --new-textColor: 14 14 14;
--new-blockSeparator: #f2f2f4; --new-blockSeparator: #f2f2f4;
--new-btn-simple: #ECEEF1; --new-btn-simple: #eceef1;
--new-btn-text: #0E0E0E; --new-btn-text: #0e0e0e;
--new-btn-primary: #612bd3; --new-btn-primary: #612bd3;
--new-ai-btn: #d82d7e; --new-ai-btn: #d82d7e;
--new-box-hover: #f4f6f8; --new-box-hover: #f4f6f8;
@ -43,15 +44,19 @@
--new-table-text: #777b7f; --new-table-text: #777b7f;
--new-table-text-focused: #fc69ff; --new-table-text-focused: #fc69ff;
--new-small-strips: #191818; --new-small-strips: #191818;
--new-big-strips: #F5F7F9; --new-big-strips: #f5f7f9;
--new-col-color: #EFF1F3; --new-col-color: #eff1f3;
--new-menu-dots: #696868; --new-menu-dots: #696868;
--new-menu-hover: #000; --new-menu-hover: #000;
--menu-shadow: -22px 83px 24px 0 rgba(55, 52, 75, 0.00), -14px 53px 22px 0 rgba(55, 52, 75, 0.01), -8px 30px 19px 0 rgba(55, 52, 75, 0.05), -3px 13px 14px 0 rgba(55, 52, 75, 0.09), -1px 3px 8px 0 rgba(55, 52, 75, 0.10); --menu-shadow: -22px 83px 24px 0 rgba(55, 52, 75, 0),
-14px 53px 22px 0 rgba(55, 52, 75, 0.01),
-8px 30px 19px 0 rgba(55, 52, 75, 0.05),
-3px 13px 14px 0 rgba(55, 52, 75, 0.09),
-1px 3px 8px 0 rgba(55, 52, 75, 0.1);
--popup-color: rgba(55, 37, 97, 0.2);
} }
} }
:root { :root {
.dark { .dark {
--color-primary: #0e0e0e; --color-primary: #0e0e0e;

View File

@ -357,6 +357,10 @@ body * {
// /*right: -5rem !important;*/ // /*right: -5rem !important;*/
//} //}
.trz {
transform: translateZ(0);
}
.uppy-FileInput-container { .uppy-FileInput-container {
@apply cursor-pointer font-[500] flex justify-center items-center gap-[4px] text-[12px] rounded-[4px] w-[107px] h-[25px] text-textColor border-[2px]; @apply cursor-pointer font-[500] flex justify-center items-center gap-[4px] text-[12px] rounded-[4px] w-[107px] h-[25px] text-textColor border-[2px];
@apply bg-customColor3 border-customColor21; @apply bg-customColor3 border-customColor21;
@ -672,3 +676,6 @@ html[dir='rtl'] [dir='ltr'] {
} }
} }
} }
.blur-xs {
filter: blur(4px);
}

View File

@ -2,7 +2,7 @@ import React, { FC, Fragment, useCallback, useMemo, useState } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr'; import useSWR from 'swr';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
@ -28,11 +28,8 @@ export const Autopost: FC = () => {
const addWebhook = useCallback( const addWebhook = useCallback(
(data?: any) => () => { (data?: any) => () => {
modal.openModal({ modal.openModal({
title: '', title: data ? 'Edit Autopost' : 'Add Autopost',
withCloseButton: false, withCloseButton: true,
classNames: {
modal: 'bg-transparent text-textColor',
},
children: <AddOrEditWebhook data={data} reload={mutate} />, children: <AddOrEditWebhook data={data} reload={mutate} />,
}); });
}, },
@ -296,29 +293,7 @@ export const AddOrEditWebhook: FC<{
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<form onSubmit={form.handleSubmit(callBack)}> <form onSubmit={form.handleSubmit(callBack)}>
<div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0 w-[500px]"> <div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 pt-0">
<TopTitle title={data ? 'Edit autopost' : 'Add autopost'} />
<button
className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
onClick={modal.closeAll}
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
<div> <div>
<Input <Input
label="Title" label="Title"

View File

@ -17,7 +17,7 @@ import { useSWRConfig } from 'swr';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useRouter, useSearchParams } from 'next/navigation'; import { useRouter, useSearchParams } from 'next/navigation';
import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useVariables } from '@gitroom/react/helpers/variable.context';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Textarea } from '@gitroom/react/form/textarea'; import { Textarea } from '@gitroom/react/form/textarea';
import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events'; import { useFireEvents } from '@gitroom/helpers/utils/use.fire.events';

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import React, { FC, useCallback, useEffect, useMemo } from 'react'; import React, { FC, useCallback, useEffect, useMemo } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
@ -24,17 +24,9 @@ export const useAddProvider = (update?: () => void) => {
return useCallback(async () => { return useCallback(async () => {
const data = await (await fetch('/integrations')).json(); const data = await (await fetch('/integrations')).json();
modal.openModal({ modal.openModal({
title: '', title: 'Add Channel',
withCloseButton: false, withCloseButton: true,
classNames: { children: <AddProviderComponent update={update} {...data} />,
modal: 'text-textColor',
},
size: 'auto',
children: (
<ModalWrapperComponent title="Add Channel">
<AddProviderComponent update={update} {...data} />
</ModalWrapperComponent>
),
}); });
}, []); }, []);
}; };
@ -349,20 +341,18 @@ export const AddProviderComponent: FC<{
await fetch(`/integrations/social/${identifier}`) await fetch(`/integrations/social/${identifier}`)
).json(); ).json();
modal.openModal({ modal.openModal({
title: '', title: 'Web3 provider',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'bg-transparent text-textColor', modal: 'bg-transparent text-textColor',
}, },
children: ( children: (
<ModalWrapperComponent title="Web3 provider"> <Web3Providers
<Web3Providers onComplete={(code, newState) => {
onComplete={(code, newState) => { window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}`;
window.location.href = `/integrations/social/${identifier}?code=${code}&state=${newState}`; }}
}} nonce={url}
nonce={url} />
/>
</ModalWrapperComponent>
), ),
}); });
return; return;
@ -388,35 +378,29 @@ export const AddProviderComponent: FC<{
if (isExternal) { if (isExternal) {
modal.closeAll(); modal.closeAll();
modal.openModal({ modal.openModal({
title: '', title: 'URL',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'bg-transparent text-textColor', modal: 'bg-transparent text-textColor',
}, },
children: ( children: <UrlModal gotoUrl={gotoIntegration} />,
<ModalWrapperComponent title="URL">
<UrlModal gotoUrl={gotoIntegration} />
</ModalWrapperComponent>
),
}); });
return; return;
} }
if (customFields) { if (customFields) {
modal.closeAll(); modal.closeAll();
modal.openModal({ modal.openModal({
title: '', title: 'Add Provider',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'bg-transparent text-textColor', modal: 'bg-transparent text-textColor',
}, },
children: ( children: (
<ModalWrapperComponent title="Add Provider"> <CustomVariables
<CustomVariables identifier={identifier}
identifier={identifier} gotoUrl={(url: string) => router.push(url)}
gotoUrl={(url: string) => router.push(url)} variables={customFields}
variables={customFields} />
/>
</ModalWrapperComponent>
), ),
}); });
return; return;
@ -425,9 +409,7 @@ export const AddProviderComponent: FC<{
}, },
[] []
); );
const close = useCallback(() => {
modal.closeAll();
}, []);
const showApiButton = useCallback( const showApiButton = useCallback(
(identifier: string, name: string) => async () => { (identifier: string, name: string) => async () => {
modal.openModal({ modal.openModal({
@ -449,7 +431,6 @@ export const AddProviderComponent: FC<{
return ( return (
<div className="w-full flex flex-col gap-[20px] rounded-[4px] relative"> <div className="w-full flex flex-col gap-[20px] rounded-[4px] relative">
<div className="flex flex-col"> <div className="flex flex-col">
<h2 className="pt-[16px] pb-[10px]">{t('social', 'Social')}</h2>
<div className="grid grid-cols-5 gap-[10px] justify-items-center justify-center"> <div className="grid grid-cols-5 gap-[10px] justify-items-center justify-center">
{social.map((item) => ( {social.map((item) => (
<div <div

View File

@ -1,7 +1,7 @@
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import React, { FC, FormEventHandler, useCallback, useState } from 'react'; import React, { FC, FormEventHandler, useCallback, useState } from 'react';
import { Integrations } from '@gitroom/frontend/components/launches/calendar.context'; import { Integrations } from '@gitroom/frontend/components/launches/calendar.context';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';

View File

@ -30,7 +30,7 @@ import 'dayjs/locale/ar';
import 'dayjs/locale/tr'; import 'dayjs/locale/tr';
import 'dayjs/locale/vi'; import 'dayjs/locale/vi';
import localizedFormat from 'dayjs/plugin/localizedFormat'; import localizedFormat from 'dayjs/plugin/localizedFormat';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import clsx from 'clsx'; import clsx from 'clsx';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { ExistingDataContextProvider } from '@gitroom/frontend/components/launches/helpers/use.existing.data'; import { ExistingDataContextProvider } from '@gitroom/frontend/components/launches/helpers/use.existing.data';
@ -215,7 +215,8 @@ export const WeekView = () => {
<div <div
className={clsx( className={clsx(
'text-[14px] font-[600] flex items-center justify-center gap-[6px]', 'text-[14px] font-[600] flex items-center justify-center gap-[6px]',
day.day === newDayjs().format('L') && 'text-newTableTextFocused' day.day === newDayjs().format('L') &&
'text-newTableTextFocused'
)} )}
> >
{day.day === newDayjs().format('L') && ( {day.day === newDayjs().format('L') && (
@ -391,7 +392,9 @@ export const CalendarColumn: FC<{
const isBeforeNow = useMemo(() => { const isBeforeNow = useMemo(() => {
const originalUtc = getDate.startOf('hour'); const originalUtc = getDate.startOf('hour');
return originalUtc.startOf('hour').isBefore(newDayjs().startOf('hour').utc()); return originalUtc
.startOf('hour')
.isBefore(newDayjs().startOf('hour').utc());
}, [getDate, num]); }, [getDate, num]);
const { start, stop } = useInterval( const { start, stop } = useInterval(
@ -462,8 +465,10 @@ export const CalendarColumn: FC<{
: Fragment; : Fragment;
modal.openModal({ modal.openModal({
closeOnClickOutside: false, closeOnClickOutside: false,
removeLayout: true,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
askClose: true,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px] text-textColor', modal: 'w-[100%] max-w-[1400px] text-textColor',
}, },
@ -515,28 +520,24 @@ export const CalendarColumn: FC<{
? undefined ? undefined
: await new Promise((resolve) => { : await new Promise((resolve) => {
modal.openModal({ modal.openModal({
title: '', title: t('select_set', 'Select a Set'),
closeOnClickOutside: true, closeOnClickOutside: true,
askClose: true,
closeOnEscape: true, closeOnEscape: true,
withCloseButton: false, withCloseButton: true,
onClose: () => resolve('exit'), onClose: () => resolve('exit'),
classNames: {
modal: 'text-textColor',
},
children: ( children: (
<ModalWrapperComponent title={t('select_set', 'Select a Set')}> <SetSelectionModal
<SetSelectionModal sets={sets}
sets={sets} onSelect={(selectedSet) => {
onSelect={(selectedSet) => { resolve(selectedSet);
resolve(selectedSet); modal.closeAll();
modal.closeAll(); }}
}} onContinueWithoutSet={() => {
onContinueWithoutSet={() => { resolve(undefined);
resolve(undefined); modal.closeAll();
modal.closeAll(); }}
}} />
/>
</ModalWrapperComponent>
), ),
}); });
}); });
@ -547,6 +548,8 @@ export const CalendarColumn: FC<{
closeOnClickOutside: false, closeOnClickOutside: false,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
removeLayout: true,
askClose: true,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px] text-textColor', modal: 'w-[100%] max-w-[1400px] text-textColor',
}, },
@ -586,17 +589,14 @@ export const CalendarColumn: FC<{
const openStatistics = useCallback( const openStatistics = useCallback(
(id: string) => () => { (id: string) => () => {
modal.openModal({ modal.openModal({
title: t('statistics', 'Statistics'),
closeOnClickOutside: true, closeOnClickOutside: true,
closeOnEscape: true, closeOnEscape: true,
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px]', modal: 'w-[100%] max-w-[1400px]',
}, },
children: ( children: <StatisticsModal postId={id} />,
<ModalWrapperComponent title={t('statistics', 'Statistics')}>
<StatisticsModal postId={id} />
</ModalWrapperComponent>
),
size: '80%', size: '80%',
// title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`, // title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`,
}); });

View File

@ -1,7 +1,7 @@
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Textarea } from '@gitroom/react/form/textarea'; import { Textarea } from '@gitroom/react/form/textarea';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import clsx from 'clsx'; import clsx from 'clsx';

View File

@ -1,6 +1,6 @@
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import React, { FC, useCallback, useEffect, useState } from 'react'; import React, { FC, useCallback, useEffect, useState } from 'react';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Integration } from '@prisma/client'; import { Integration } from '@prisma/client';
import { Autocomplete } from '@mantine/core'; import { Autocomplete } from '@mantine/core';
import useSWR from 'swr'; import useSWR from 'swr';

View File

@ -2,7 +2,7 @@ import React, { FC, useCallback, useMemo, useState } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';
import { classValidatorResolver } from '@hookform/resolvers/class-validator'; import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { GeneratorDto } from '@gitroom/nestjs-libraries/dtos/generator/generator.dto'; import { GeneratorDto } from '@gitroom/nestjs-libraries/dtos/generator/generator.dto';
@ -153,6 +153,8 @@ const FirstStep: FC = (props) => {
closeOnClickOutside: false, closeOnClickOutside: false,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
removeLayout: true,
askClose: true,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor',
}, },
@ -301,7 +303,7 @@ export const GeneratorComponent = () => {
return; return;
} }
modal.openModal({ modal.openModal({
title: '', title: 'Generate Posts',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'bg-transparent text-textColor', modal: 'bg-transparent text-textColor',
@ -309,9 +311,7 @@ export const GeneratorComponent = () => {
size: 'xl', size: 'xl',
children: ( children: (
<CalendarWeekProvider {...all}> <CalendarWeekProvider {...all}>
<ModalWrapperComponent title="Generate Posts"> <GeneratorPopup />
<GeneratorPopup />
</ModalWrapperComponent>
</CalendarWeekProvider> </CalendarWeekProvider>
), ),
}); });

View File

@ -213,7 +213,9 @@ export const CreateThumbnail: FC<{
<div className="relative bg-black rounded-lg overflow-hidden"> <div className="relative bg-black rounded-lg overflow-hidden">
<video <video
ref={videoRef} ref={videoRef}
src={backendUrl + '/public/stream?url=' + encodeURIComponent(media.path)} src={
backendUrl + '/public/stream?url=' + encodeURIComponent(media.path)
}
className="w-full h-[200px] object-contain" className="w-full h-[200px] object-contain"
onLoadedMetadata={handleLoadedMetadata} onLoadedMetadata={handleLoadedMetadata}
onTimeUpdate={handleTimeUpdate} onTimeUpdate={handleTimeUpdate}
@ -360,163 +362,132 @@ export const MediaComponentInner: FC<{
}, [altText, newThumbnail, thumbnail, thumbnailTimestamp]); }, [altText, newThumbnail, thumbnail, thumbnailTimestamp]);
return ( return (
<div className="text-textColor fixed start-0 top-0 bg-primary/80 z-[300] w-full h-full p-[60px] animate-fade justify-center flex bg-black/40"> <div className="mt-[10px] flex flex-col gap-[20px]">
<div className="w-full h-full relative"> <div className="flex flex-col space-y-2">
<div className="w-[500px] bg-sixth border-tableBorder border-2 rounded-xl pb-[20px] px-[20px] absolute left-[50%] top-[100px] -translate-x-[50%]"> <label className="text-sm text-textColor font-medium">
<div className="flex"> Alt Text (for accessibility)
<div className="flex-1"> </label>
<TopTitle title={'Media Setting'} /> <input
</div> type="text"
<button value={altText}
onClick={onClose} onChange={(e) => setAltText(e.target.value)}
className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root bg-primary hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" placeholder="Describe the image/video content..."
type="button" className="w-full px-3 py-2 bg-fifth border border-tableBorder rounded-lg text-textColor placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-forth focus:border-transparent"
> />
<svg </div>
viewBox="0 0 15 15" {media?.path.indexOf('mp4') > -1 && (
fill="none" <>
xmlns="http://www.w3.org/2000/svg" {/* Alt Text Input */}
width="16" <div>
height="16" {!isEditingThumbnail ? (
> <div className="flex flex-col">
<path {/* Show existing thumbnail if it exists */}
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" {(newThumbnail || thumbnail) && (
fill="currentColor" <div className="flex flex-col space-y-2">
fillRule="evenodd" <span className="text-sm text-textColor">
clipRule="evenodd" Current Thumbnail:
></path> </span>
</svg> <img
</button> src={newThumbnail || thumbnail}
</div> alt="Current thumbnail"
<div className="mt-[10px] flex flex-col gap-[20px]"> className="max-w-full max-h-[500px] object-contain rounded-lg border border-tableBorder"
<div className="flex flex-col space-y-2"> />
<label className="text-sm text-textColor font-medium"> </div>
Alt Text (for accessibility) )}
</label>
<input
type="text"
value={altText}
onChange={(e) => setAltText(e.target.value)}
placeholder="Describe the image/video content..."
className="w-full px-3 py-2 bg-fifth border border-tableBorder rounded-lg text-textColor placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-forth focus:border-transparent"
/>
</div>
{media?.path.indexOf('mp4') > -1 && (
<>
{/* Alt Text Input */}
<div>
{!isEditingThumbnail ? (
<div className="flex flex-col">
{/* Show existing thumbnail if it exists */}
{(newThumbnail || thumbnail) && (
<div className="flex flex-col space-y-2">
<span className="text-sm text-textColor">
Current Thumbnail:
</span>
<img
src={newThumbnail || thumbnail}
alt="Current thumbnail"
className="max-w-full max-h-[500px] object-contain rounded-lg border border-tableBorder"
/>
</div>
)}
{/* Action Buttons */} {/* Action Buttons */}
<div className="flex space-x-2"> <div className="flex space-x-2">
<button <button
disabled={loading} disabled={loading}
onClick={() => setIsEditingThumbnail(true)} onClick={() => setIsEditingThumbnail(true)}
className="bg-third text-textColor px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all flex-1 border border-tableBorder" className="bg-third text-textColor px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all flex-1 border border-tableBorder"
> >
{media.thumbnail || newThumbnail {media.thumbnail || newThumbnail
? 'Edit Thumbnail' ? 'Edit Thumbnail'
: 'Create Thumbnail'} : 'Create Thumbnail'}
</button> </button>
{(thumbnail || newThumbnail) && ( {(thumbnail || newThumbnail) && (
<button <button
disabled={loading} disabled={loading}
onClick={() => { onClick={() => {
setNewThumbnail(null); setNewThumbnail(null);
setThumbnail(null); setThumbnail(null);
}} }}
className="bg-red-600 text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all flex-1 border border-red-700" className="bg-red-600 text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all flex-1 border border-red-700"
> >
Clear Thumbnail Clear Thumbnail
</button> </button>
)}
</div>
</div>
) : (
<div>
{/* Back button */}
<div className="flex justify-start">
<button
onClick={() => setIsEditingThumbnail(false)}
className="text-textColor hover:text-white transition-colors flex items-center space-x-2"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 12H5M12 19L5 12L12 5"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<span>Back</span>
</button>
</div>
{/* Thumbnail Editor */}
<CreateThumbnail
onSelect={(blob: Blob, timestampMs: number) => {
// Convert blob to base64 or handle as needed
const reader = new FileReader();
reader.onload = () => {
// You can handle the result here - for now just call onSelect with the blob URL
const url = URL.createObjectURL(blob);
setNewThumbnail(url);
setThumbnailTimestamp(timestampMs);
setIsEditingThumbnail(false);
};
reader.readAsDataURL(blob);
}}
media={media}
altText={altText}
onAltTextChange={setAltText}
/>
</div>
)} )}
</div> </div>
</> </div>
)} ) : (
<div>
{/* Back button */}
<div className="flex justify-start">
<button
onClick={() => setIsEditingThumbnail(false)}
className="text-textColor hover:text-white transition-colors flex items-center space-x-2"
>
<svg
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 12H5M12 19L5 12L12 5"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
<span>Back</span>
</button>
</div>
{!isEditingThumbnail && ( {/* Thumbnail Editor */}
<div className="flex space-x-2 !mt-[20px]"> <CreateThumbnail
<button onSelect={(blob: Blob, timestampMs: number) => {
disabled={loading} // Convert blob to base64 or handle as needed
onClick={onClose} const reader = new FileReader();
className="flex-1 bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all" reader.onload = () => {
> // You can handle the result here - for now just call onSelect with the blob URL
Cancel const url = URL.createObjectURL(blob);
</button> setNewThumbnail(url);
<button setThumbnailTimestamp(timestampMs);
onClick={save} setIsEditingThumbnail(false);
className="flex-1 bg-forth text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all" };
> reader.readAsDataURL(blob);
Save Changes }}
</button> media={media}
altText={altText}
onAltTextChange={setAltText}
/>
</div> </div>
)} )}
</div> </div>
</>
)}
{!isEditingThumbnail && (
<div className="flex space-x-2 !mt-[20px]">
<button
disabled={loading}
onClick={onClose}
className="flex-1 bg-gray-600 text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all"
>
Cancel
</button>
<button
onClick={save}
className="flex-1 bg-forth text-white px-6 py-2 rounded-lg hover:bg-opacity-80 transition-all"
>
Save Changes
</button>
</div> </div>
</div> )}
</div> </div>
); );
}; };

View File

@ -11,7 +11,7 @@ import { useClickOutside } from '@mantine/hooks';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useToaster } from '@gitroom/react/toaster/toaster'; import { useToaster } from '@gitroom/react/toaster/toaster';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TimeTable } from '@gitroom/frontend/components/launches/time.table'; import { TimeTable } from '@gitroom/frontend/components/launches/time.table';
import { import {
Integrations, Integrations,
@ -137,18 +137,12 @@ export const Menu: FC<{
(integration) => integration.id === id (integration) => integration.id === id
); );
modal.openModal({ modal.openModal({
classNames: {
modal: 'w-[100%] max-w-[600px] bg-transparent text-textColor',
},
size: '100%',
withCloseButton: false, withCloseButton: false,
closeOnEscape: false, closeOnEscape: false,
closeOnClickOutside: false, closeOnClickOutside: false,
children: ( askClose: true,
<ModalWrapperComponent title="Time Table Slots" ask={true}> title: 'Time Table Slots',
<TimeTable integration={findIntegration!} mutate={mutate} /> children: <TimeTable integration={findIntegration!} mutate={mutate} />,
</ModalWrapperComponent>
),
}); });
setShow(false); setShow(false);
}, [integrations]); }, [integrations]);
@ -175,6 +169,8 @@ export const Menu: FC<{
closeOnClickOutside: false, closeOnClickOutside: false,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
removeLayout: true,
askClose: true,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor',
}, },
@ -254,40 +250,36 @@ export const Menu: FC<{
classNames: { classNames: {
modal: 'md', modal: 'md',
}, },
title: '', title: 'Move / Add to customer',
withCloseButton: false, withCloseButton: false,
closeOnEscape: true, closeOnEscape: true,
closeOnClickOutside: true, closeOnClickOutside: true,
children: ( children: (
<ModalWrapperComponent title="Move / Add to customer"> <CustomerModal
<CustomerModal // @ts-ignore
// @ts-ignore integration={findIntegration}
integration={findIntegration} onClose={() => {
onClose={() => { mutate();
mutate(); toast.show('Customer Updated', 'success');
toast.show('Customer Updated', 'success'); }}
}} />
/>
</ModalWrapperComponent>
), ),
}); });
setShow(false); setShow(false);
}, [integrations]); }, [integrations]);
const updateCredentials = useCallback(() => { const updateCredentials = useCallback(() => {
modal.openModal({ modal.openModal({
title: '', title: 'Custom URL',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'md', modal: 'md',
}, },
children: ( children: (
<ModalWrapperComponent title="Custom URL"> <CustomVariables
<CustomVariables identifier={findIntegration.identifier}
identifier={findIntegration.identifier} gotoUrl={(url: string) => router.push(url)}
gotoUrl={(url: string) => router.push(url)} variables={findIntegration.customFields}
variables={findIntegration.customFields} />
/>
</ModalWrapperComponent>
), ),
}); });
}, []); }, []);

View File

@ -1,5 +1,5 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context'; import { useCalendar } from '@gitroom/frontend/components/launches/calendar.context';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
@ -21,7 +21,7 @@ export const NewPost = () => {
? undefined ? undefined
: await new Promise((resolve) => { : await new Promise((resolve) => {
modal.openModal({ modal.openModal({
title: '', title: t('select_set', 'Select a Set'),
closeOnClickOutside: true, closeOnClickOutside: true,
closeOnEscape: true, closeOnEscape: true,
withCloseButton: false, withCloseButton: false,
@ -30,19 +30,17 @@ export const NewPost = () => {
modal: 'text-textColor', modal: 'text-textColor',
}, },
children: ( children: (
<ModalWrapperComponent title={t('select_set', 'Select a Set')}> <SetSelectionModal
<SetSelectionModal sets={sets}
sets={sets} onSelect={(selectedSet) => {
onSelect={(selectedSet) => { resolve(selectedSet);
resolve(selectedSet); modal.closeAll();
modal.closeAll(); }}
}} onContinueWithoutSet={() => {
onContinueWithoutSet={() => { resolve(undefined);
resolve(undefined); modal.closeAll();
modal.closeAll(); }}
}} />
/>
</ModalWrapperComponent>
), ),
}); });
}); });
@ -53,6 +51,8 @@ export const NewPost = () => {
closeOnClickOutside: false, closeOnClickOutside: false,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
removeLayout: true,
askClose: true,
classNames: { classNames: {
modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor',
}, },

View File

@ -56,10 +56,12 @@ const ActionControls = ({ store }: any) => {
body: formData, body: formData,
}) })
).json(); ).json();
close.setMedia([{ close.setMedia([
id: data.id, {
path: data.path, id: data.id,
}]); path: data.path,
},
]);
close.close(); close.close();
}} }}
> >
@ -102,63 +104,34 @@ const Polonto: FC<{
}; };
}, []); }, []);
return ( return (
<div className="fixed start-0 top-0 bg-primary/80 z-[300] w-full min-h-full px-[60px] animate-fade"> <div className="bg-white text-black relative z-[400] polonto">
<div className="w-full h-full bg-sixth border-tableBorder border-2 rounded-xl pb-[20px] px-[20px] relative"> <CloseContext.Provider
<div className="flex"> value={{
<div className="flex-1"> close: () => closeModal(),
<TopTitle title="Design Media" /> setMedia,
</div> }}
<button >
onClick={closeModal} <PolotnoContainer
className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root bg-primary hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" style={{
type="button" width: '100%',
> height: '700px',
<svg }}
viewBox="0 0 15 15" >
fill="none" <SidePanelWrap>
xmlns="http://www.w3.org/2000/svg" <SidePanel store={store} sections={features} />
width="16" </SidePanelWrap>
height="16" <WorkspaceWrap>
> <Toolbar
<path store={store}
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z" components={{
fill="currentColor" ActionControls,
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
</div>
<div className="bg-white text-black relative z-[400] polonto">
<CloseContext.Provider
value={{
close: () => closeModal(),
setMedia,
}}
>
<PolotnoContainer
style={{
width: '100%',
height: '700px',
}} }}
> />
<SidePanelWrap> <Workspace store={store} />
<SidePanel store={store} sections={features} /> <ZoomButtons store={store} />
</SidePanelWrap> </WorkspaceWrap>
<WorkspaceWrap> </PolotnoContainer>
<Toolbar </CloseContext.Provider>
store={store}
components={{
ActionControls,
}}
/>
<Workspace store={store} />
<ZoomButtons store={store} />
</WorkspaceWrap>
</PolotnoContainer>
</CloseContext.Provider>
</div>
</div>
</div> </div>
); );
}; };

View File

@ -1,6 +1,6 @@
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import React, { FC, useCallback, useState } from 'react'; import React, { FC, useCallback, useState } from 'react';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Integration } from '@prisma/client'; import { Integration } from '@prisma/client';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';

View File

@ -1,5 +1,5 @@
import React, { FC, Fragment, useCallback } from 'react'; import React, { FC, Fragment, useCallback } from 'react';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import useSWR from 'swr'; import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';

View File

@ -10,7 +10,7 @@ import timezone from 'dayjs/plugin/timezone';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
// @ts-ignore // @ts-ignore
import useKeypress from 'react-use-keypress'; import useKeypress from 'react-use-keypress';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { sortBy } from 'lodash'; import { sortBy } from 'lodash';
import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload'; import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';

View File

@ -5,7 +5,7 @@ import React, { FC, useMemo, useState, useCallback, useEffect } from 'react';
import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface';
import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useVariables } from '@gitroom/react/helpers/variable.context';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { ButtonCaster } from '@gitroom/frontend/components/auth/providers/farcaster.provider'; import { ButtonCaster } from '@gitroom/frontend/components/auth/providers/farcaster.provider';
export const WrapcasterProvider: FC<Web3ProviderInterface> = (props) => { export const WrapcasterProvider: FC<Web3ProviderInterface> = (props) => {

View File

@ -4,7 +4,7 @@ import '@neynar/react/dist/style.css';
import React, { FC, useCallback, useEffect, useRef, useState } from 'react'; import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { timer } from '@gitroom/helpers/utils/timer'; import { timer } from '@gitroom/helpers/utils/timer';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is';

View File

@ -5,7 +5,7 @@ import React, { FC, useMemo, useState, useCallback, useEffect } from 'react';
import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface'; import { Web3ProviderInterface } from '@gitroom/frontend/components/launches/web3/web3.provider.interface';
import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useVariables } from '@gitroom/react/helpers/variable.context';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import { import {
NeynarAuthButton, NeynarAuthButton,

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { import {
cookieName, cookieName,
fallbackLng, fallbackLng,
@ -70,7 +70,7 @@ export const ChangeLanguageComponent = () => {
const handleLanguageChange = (language: string) => { const handleLanguageChange = (language: string) => {
setCookie(language); setCookie(language);
i18next.changeLanguage(language); i18next.changeLanguage(language);
modals.closeModal('change-language'); modals.closeCurrent();
}; };
// Function to get language name in its native script // Function to get language name in its native script
@ -123,16 +123,9 @@ export const LanguageComponent = () => {
const t = useT(); const t = useT();
const openModal = () => { const openModal = () => {
modal.openModal({ modal.openModal({
title: '', title: t('change_language', 'Change Language'),
withCloseButton: false, withCloseButton: true,
modalId: 'change-language', children: <ChangeLanguageComponent />,
children: (
<ModalWrapperComponent title={t('change_language', 'Change Language')}>
<ChangeLanguageComponent />
</ModalWrapperComponent>
),
size: 'lg',
centered: true,
}); });
}; };
return ( return (

View File

@ -72,6 +72,7 @@ export const LayoutSettings = ({ children }: { children: ReactNode }) => {
<CopilotKit <CopilotKit
credentials="include" credentials="include"
runtimeUrl={backendUrl + '/copilot/chat'} runtimeUrl={backendUrl + '/copilot/chat'}
showDevConsole={false}
> >
<MantineWrapper> <MantineWrapper>
{user.tier === 'FREE' && searchParams.get('check') && ( {user.tier === 'FREE' && searchParams.get('check') && (

View File

@ -0,0 +1,371 @@
import { create } from 'zustand';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import { useShallow } from 'zustand/react/shallow';
import React, {
createContext,
FC,
memo,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
} from 'react';
import { Button } from '@gitroom/react/form/button';
import { useHotkeys } from 'react-hotkeys-hook';
import clsx from 'clsx';
import { EventEmitter } from 'events';
interface OpenModalInterface {
title?: string;
closeOnClickOutside?: boolean;
removeLayout?: boolean;
closeOnEscape?: boolean;
withCloseButton?: boolean;
askClose?: boolean;
onClose?: () => void;
children: ReactNode | ((close: () => void) => ReactNode);
classNames?: {
modal?: string;
};
size?: string | number;
height?: string | number;
id?: string;
}
interface ModalManagerStoreInterface {
closeById(id: string): void;
openModal(params: OpenModalInterface): void;
closeAll(): void;
}
interface State extends ModalManagerStoreInterface {
modalManager: Array<{ id: string } & OpenModalInterface>;
}
const useModalStore = create<State>((set) => ({
modalManager: [],
openModal: (params) =>
set((state) => ({
modalManager: [
...state.modalManager,
{ id: params.id || makeId(20), ...params },
],
})),
closeById: (id) =>
set((state) => ({
modalManager: state.modalManager.filter((modal) => modal.id !== id),
})),
closeAll: () => set({ modalManager: [] }),
}));
const CurrentModalContext = createContext({ id: '' });
interface ModalManagerInterface extends ModalManagerStoreInterface {
closeCurrent(): void;
}
export const useModals = () => {
const { closeAll, openModal, closeById } = useModalStore(
useShallow((state) => ({
openModal: state.openModal,
closeById: state.closeById,
closeAll: state.closeAll,
}))
);
const modalContext = useContext(CurrentModalContext);
return {
openModal,
closeAll,
closeById,
closeCurrent: () => {
if (modalContext.id) {
closeById(modalContext.id);
}
},
} satisfies ModalManagerInterface;
};
export const Component: FC<{
closeModal: (id: string) => void;
zIndex: number;
isLast: boolean;
modal: { id: string } & OpenModalInterface;
}> = memo(({ isLast, modal, closeModal, zIndex }) => {
const decision = useDecisionModal();
const closeModalFunction = useCallback(async () => {
if (modal.askClose) {
const open = await decision.open();
if (!open) {
return;
}
}
modal?.onClose?.();
closeModal(modal.id);
}, [modal.id, closeModal]);
const RenderComponent = useMemo(() => {
return typeof modal.children === 'function'
? modal.children(closeModalFunction)
: modal.children;
}, [modal, closeModalFunction]);
useHotkeys(
'Escape',
() => {
if (isLast) {
closeModalFunction();
}
},
[isLast, closeModalFunction]
);
if (modal.removeLayout) {
return (
<div
style={{ zIndex }}
className={clsx(
'fixed flex left-0 top-0 min-w-full min-h-full bg-popup transition-all animate-fadeIn overflow-y-auto pb-[50px] text-newTextColor',
!isLast && '!overflow-hidden'
)}
>
<div className="relative flex-1">
<div className="absolute top-0 left-0 min-w-full min-h-full">
<div
className="mx-auto py-[48px]"
{...(modal.size && { style: { width: modal.size } })}
>
{typeof modal.children === 'function'
? modal.children(closeModalFunction)
: modal.children}
</div>
</div>
</div>
</div>
);
}
return (
<CurrentModalContext.Provider value={{ id: modal.id }}>
<div
onClick={closeModalFunction}
style={{ zIndex }}
className="fixed flex left-0 top-0 min-w-full min-h-full bg-popup transition-all animate-fadeIn overflow-y-auto pb-[50px] text-newTextColor"
>
<div className="relative flex-1">
<div className="absolute top-0 left-0 min-w-full min-h-full pt-[100px] pb-[100px]">
<div
className={clsx(
!modal.removeLayout && 'gap-[40px] p-[32px]',
'bg-newBgColorInner mx-auto flex flex-col w-fit rounded-[24px] relative',
modal.size ? '' : 'min-w-[600px]'
)}
{...(modal.size && { style: { width: modal.size } })}
onClick={(e) => e.stopPropagation()}
>
<div className="flex items-center">
<div className="text-[24px] font-[600] flex-1">
{modal.title}
</div>
{typeof modal.withCloseButton === 'undefined' ||
modal.withCloseButton ? (
<div className="cursor-pointer">
<button
className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
onClick={closeModalFunction}
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
</div>
) : null}
</div>
<div className="whitespace-pre-line">{RenderComponent}</div>
</div>
</div>
</div>
</div>
</CurrentModalContext.Provider>
);
});
export const ModalManagerInner: FC = () => {
const { closeModal, modalManager } = useModalStore(
useShallow((state) => ({
closeModal: state.closeById,
modalManager: state.modalManager,
}))
);
useEffect(() => {
if (modalManager.length > 0) {
document.querySelector('body')?.classList.add('overflow-hidden');
Array.from(document.querySelectorAll('.blurMe') || []).map((p) =>
p.classList.add('blur-xs', 'pointer-events-none')
);
} else {
document.querySelector('body')?.classList.remove('overflow-hidden');
Array.from(document.querySelectorAll('.blurMe') || []).map((p) =>
p.classList.remove('blur-xs', 'pointer-events-none')
);
}
}, [modalManager]);
if (modalManager.length === 0) {
return null;
}
return (
<>
<style>{`body, html { overflow: hidden !important; }`}</style>
{modalManager.map((modal, index) => (
<Component
isLast={modalManager.length - 1 === index}
key={modal.id}
modal={modal}
zIndex={200 + index}
closeModal={closeModal}
/>
))}
</>
);
};
export const ModalManager: FC<{ children: ReactNode }> = ({ children }) => {
return (
<div>
<ModalManagerEmitter />
<ModalManagerInner />
<div className="transition-all w-full">{children}</div>
</div>
);
};
const emitter = new EventEmitter();
export const showModalEmitter = (params: ModalManagerInterface) => {
emitter.emit('show', params);
};
export const ModalManagerEmitter: FC = () => {
const { showModal } = useModalStore(
useShallow((state) => ({
showModal: state.openModal,
}))
);
useEffect(() => {
emitter.on('show', (params: OpenModalInterface) => {
showModal(params);
});
return () => {
emitter.removeAllListeners('show');
};
}, []);
return null;
};
export const DecisionModal: FC<{
description: string;
approveLabel: string;
cancelLabel: string;
resolution: (value: boolean) => void;
}> = ({ description, cancelLabel, approveLabel, resolution }) => {
const { closeCurrent } = useModals();
return (
<div className="flex flex-col">
<div>{description}</div>
<div className="flex gap-[12px] mt-[16px]">
<Button
onClick={() => {
resolution(true);
closeCurrent();
}}
>
{approveLabel}
</Button>
<Button
onClick={() => {
resolution(false);
closeCurrent();
}}
>
{cancelLabel}
</Button>
</div>
</div>
);
};
export const decisionModalEmitter = new EventEmitter();
export const areYouSure = ({
title = 'Are you sure?',
description = 'Are you sure you want to close this modal?' as any,
approveLabel = 'Yes',
cancelLabel = 'No',
} = {}): Promise<boolean> => {
return new Promise<boolean>((newRes) => {
decisionModalEmitter.emit('open', {
title,
description,
approveLabel,
cancelLabel,
newRes,
});
});
};
export const DecisionEverywhere: FC = () => {
const decision = useDecisionModal();
useEffect(() => {
decisionModalEmitter.on('open', decision.open);
}, []);
return null;
};
export const useDecisionModal = () => {
const modals = useModals();
const open = useCallback(
({
title = 'Are you sure?',
description = 'Are you sure you want to close this modal?' as any,
approveLabel = 'Yes',
cancelLabel = 'No',
newRes = undefined as any,
} = {}) => {
return new Promise<boolean>((res) => {
modals.openModal({
title,
askClose: false,
onClose: () => res(false),
children: (
<DecisionModal
resolution={(value) => (newRes ? newRes(value) : res(value))}
description={description}
approveLabel={approveLabel}
cancelLabel={cancelLabel}
/>
),
});
});
},
[modals]
);
return { open };
};

View File

@ -1,19 +1,26 @@
import React, { FC, useEffect } from 'react'; import React, { FC, useEffect } from 'react';
import { useSearchParams } from 'next/navigation'; import { useSearchParams } from 'next/navigation';
import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component'; import { ModalWrapperComponent } from '@gitroom/frontend/components/new-launch/modal.wrapper.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
export const PreConditionComponentModal: FC = () => { export const PreConditionComponentModal: FC = () => {
return ( return (
<div className="flex flex-col gap-[16px]"> <div className="flex flex-col gap-[16px]">
<div className="whitespace-pre-line"> <div className="whitespace-pre-line">
This social channel was connected previously to another Postiz account.{'\n'} This social channel was connected previously to another Postiz account.
To continue, please fast-track your trial for an immediate charge.{'\n'}{'\n'} {'\n'}
** Please be advised that the account will not eligible for a refund, and the charge is final. To continue, please fast-track your trial for an immediate charge.{'\n'}
{'\n'}
** Please be advised that the account will not eligible for a refund,
and the charge is final.
</div> </div>
<div className="flex gap-[2px] justify-center"> <div className="flex gap-[2px] justify-center">
<Button onClick={() => window.location.href='/billing?finishTrial=true'}>Fast track - Charge me now</Button> <Button
onClick={() => (window.location.href = '/billing?finishTrial=true')}
>
Fast track - Charge me now
</Button>
<Button secondary={true}>Cancel</Button> <Button secondary={true}>Cancel</Button>
</div> </div>
</div> </div>
@ -25,17 +32,13 @@ export const PreConditionComponent: FC = () => {
useEffect(() => { useEffect(() => {
if (query.get('precondition')) { if (query.get('precondition')) {
modal.openModal({ modal.openModal({
title: '', title: 'Suspicious activity detected',
withCloseButton: false, withCloseButton: false,
classNames: { classNames: {
modal: 'text-textColor', modal: 'text-textColor',
}, },
size: 'auto', size: 'auto',
children: ( children: <PreConditionComponentModal />,
<ModalWrapperComponent title="Suspicious activity detected">
<PreConditionComponentModal />
</ModalWrapperComponent>
),
}); });
} }
}, []); }, []);

View File

@ -1,6 +1,6 @@
'use client'; 'use client';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import React, { import React, {
FC, FC,
Ref, Ref,

View File

@ -128,39 +128,39 @@ export const useMenuItem = () => {
] satisfies MenuItemInterface[] as MenuItemInterface[]; ] satisfies MenuItemInterface[] as MenuItemInterface[];
const secondMenu = [ const secondMenu = [
{ // {
name: 'GrowChief', // name: 'GrowChief',
icon: ( // icon: (
<svg // <svg
width="20" // width="20"
height="21" // height="21"
viewBox="0 0 50 28" // viewBox="0 0 50 28"
fill="none" // fill="none"
xmlns="http://www.w3.org/2000/svg" // xmlns="http://www.w3.org/2000/svg"
data-tooltip-id="tooltip" // data-tooltip-id="tooltip"
data-tooltip-content="New! Automate your X and LinkedIn outreach with GrowChief" // data-tooltip-content="New! Automate your X and LinkedIn outreach with GrowChief"
> // >
<path // <path
d="M24.8789 0.191772C39.9967 0.198463 49.621 14.0845 49.6514 14.1283C49.6514 14.1283 40.0206 27.8931 24.8789 27.8998C9.73703 27.9062 0.189453 14.1283 0.189453 14.1283C0.235704 14.0609 9.77381 0.185332 24.8789 0.191772Z" // d="M24.8789 0.191772C39.9967 0.198463 49.621 14.0845 49.6514 14.1283C49.6514 14.1283 40.0206 27.8931 24.8789 27.8998C9.73703 27.9062 0.189453 14.1283 0.189453 14.1283C0.235704 14.0609 9.77381 0.185332 24.8789 0.191772Z"
fill="none" // fill="none"
stroke="currentColor" // stroke="currentColor"
strokeWidth="3" // strokeWidth="3"
/> // />
//
<circle // <circle
cx="24.9189" // cx="24.9189"
cy="14.2621" // cy="14.2621"
r="9.1328" // r="9.1328"
fill="none" // fill="none"
stroke="currentColor" // stroke="currentColor"
strokeWidth="3" // strokeWidth="3"
/> // />
</svg> // </svg>
), // ),
path: 'https://growchief.com', // path: 'https://growchief.com',
role: ['ADMIN', 'SUPERADMIN', 'USER'], // role: ['ADMIN', 'SUPERADMIN', 'USER'],
requireBilling: true, // requireBilling: true,
}, // },
{ {
name: t('affiliate', 'Affiliate'), name: t('affiliate', 'Affiliate'),
icon: ( icon: (
@ -286,7 +286,7 @@ export const TopMenu: FC = () => {
const { isGeneral, billingEnabled } = useVariables(); const { isGeneral, billingEnabled } = useVariables();
return ( return (
<> <>
<div className="flex flex-1 flex-col gap-[16px]"> <div className="flex flex-1 flex-col gap-[16px] blurMe">
{ {
// @ts-ignore // @ts-ignore
user?.orgId && user?.orgId &&
@ -318,7 +318,7 @@ export const TopMenu: FC = () => {
)) ))
} }
</div> </div>
<div className="flex flex-col gap-[16px]"> <div className="flex flex-col gap-[16px] blurMe">
{secondMenu {secondMenu
.filter((f) => { .filter((f) => {
if (f.hide) { if (f.hide) {

View File

@ -20,7 +20,7 @@ import {
import { capitalize, chunk, fill } from 'lodash'; import { capitalize, chunk, fill } from 'lodash';
import useSWR from 'swr'; import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Textarea } from '@gitroom/react/form/textarea'; import { Textarea } from '@gitroom/react/form/textarea';
import { FormProvider, SubmitHandler, useForm } from 'react-hook-form'; import { FormProvider, SubmitHandler, useForm } from 'react-hook-form';

View File

@ -1,7 +1,7 @@
import React, { FC, useCallback, useContext, useMemo, useState } from 'react'; import React, { FC, useCallback, useContext, useMemo, useState } from 'react';
import { MarketplaceProvider } from '@gitroom/frontend/components/marketplace/marketplace.provider'; import { MarketplaceProvider } from '@gitroom/frontend/components/marketplace/marketplace.provider';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { CustomSelect } from '@gitroom/react/form/custom.select'; import { CustomSelect } from '@gitroom/react/form/custom.select';

View File

@ -10,7 +10,7 @@ import useSWR from 'swr';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { OrderList } from '@gitroom/frontend/components/marketplace/order.list'; import { OrderList } from '@gitroom/frontend/components/marketplace/order.list';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Select } from '@gitroom/react/form/select'; import { Select } from '@gitroom/react/form/select';
import { countries } from '@gitroom/nestjs-libraries/services/stripe.country.list'; import { countries } from '@gitroom/nestjs-libraries/services/stripe.country.list';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';

View File

@ -9,7 +9,7 @@ import useSWR from 'swr';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import removeMd from 'remove-markdown'; import removeMd from 'remove-markdown';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Post as PrismaPost } from '@prisma/client'; import { Post as PrismaPost } from '@prisma/client';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration'; import { IntegrationContext } from '@gitroom/frontend/components/launches/helpers/use.integration';

View File

@ -30,9 +30,13 @@ import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { ThirdPartyMedia } from '@gitroom/frontend/components/third-parties/third-party.media'; import { ThirdPartyMedia } from '@gitroom/frontend/components/third-parties/third-party.media';
import { ReactSortable } from 'react-sortablejs'; import { ReactSortable } from 'react-sortablejs';
import { useMediaSettings } from '@gitroom/frontend/components/launches/helpers/media.settings.component'; import {
MediaComponentInner,
useMediaSettings,
} from '@gitroom/frontend/components/launches/helpers/media.settings.component';
import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
import { AiVideo } from '@gitroom/frontend/components/launches/ai.video'; import { AiVideo } from '@gitroom/frontend/components/launches/ai.video';
import { useModals } from '@gitroom/frontend/components/layout/new-modal';
const Polonto = dynamic( const Polonto = dynamic(
() => import('@gitroom/frontend/components/launches/polonto') () => import('@gitroom/frontend/components/launches/polonto')
); );
@ -246,7 +250,7 @@ export const MediaBox: FC<{
const dragAndDrop = useCallback( const dragAndDrop = useCallback(
async (event: ClipboardEvent<HTMLDivElement> | File[]) => { async (event: ClipboardEvent<HTMLDivElement> | File[]) => {
if (!ref?.current?.setOptions) { if (!ref?.current?.setOptions) {
return ; return;
} }
// @ts-ignore // @ts-ignore
@ -564,12 +568,12 @@ export const MultiMediaComponent: FC<{
dummy, dummy,
} = props; } = props;
const user = useUser(); const user = useUser();
const modals = useModals();
useEffect(() => { useEffect(() => {
if (value) { if (value) {
setCurrentMedia(value); setCurrentMedia(value);
} }
}, [value]); }, [value]);
const [modal, setShowModal] = useState(false);
const [mediaModal, setMediaModal] = useState(false); const [mediaModal, setMediaModal] = useState(false);
const [currentMedia, setCurrentMedia] = useState(value); const [currentMedia, setCurrentMedia] = useState(value);
const mediaDirectory = useMediaDirectory(); const mediaDirectory = useMediaDirectory();
@ -598,17 +602,14 @@ export const MultiMediaComponent: FC<{
[currentMedia] [currentMedia]
); );
const showModal = useCallback(() => { const showModal = useCallback(() => {
if (!modal) { modals.openModal({
onOpen?.(); askClose: false,
} else { children: (close) => (
onClose?.(); <MediaBox setMedia={changeMedia} closeModal={close} />
} ),
setShowModal(!modal); });
}, [modal, onOpen, onClose]); }, [changeMedia]);
const closeDesignModal = useCallback(() => {
onClose?.();
setMediaModal(false);
}, [modal]);
const clearMedia = useCallback( const clearMedia = useCallback(
(topIndex: number) => () => { (topIndex: number) => () => {
const newMedia = currentMedia?.filter((f, index) => index !== topIndex); const newMedia = currentMedia?.filter((f, index) => index !== topIndex);
@ -622,10 +623,19 @@ export const MultiMediaComponent: FC<{
}, },
[currentMedia] [currentMedia]
); );
const designMedia = useCallback(() => { const designMedia = useCallback(() => {
onOpen?.(); if (!!user?.tier?.ai && !dummy) {
setMediaModal(true); modals.openModal({
}, []); askClose: false,
title: 'Design Media',
size: '80%',
children: (close) => (
<Polonto setMedia={changeMedia} closeModal={close} />
),
});
}
}, [changeMedia]);
const mediaSettings = useMediaSettings(); const mediaSettings = useMediaSettings();
@ -634,11 +644,6 @@ export const MultiMediaComponent: FC<{
return ( return (
<> <>
<div className="flex flex-col gap-[8px] bg-bigStrip rounded-bl-[8px] select-none w-full"> <div className="flex flex-col gap-[8px] bg-bigStrip rounded-bl-[8px] select-none w-full">
{modal && <MediaBox setMedia={changeMedia} closeModal={showModal} />}
{mediaModal && !!user?.tier?.ai && !dummy && (
<Polonto setMedia={changeMedia} closeModal={closeDesignModal} />
)}
<div className="flex gap-[10px]"> <div className="flex gap-[10px]">
<Button <Button
onClick={showModal} onClick={showModal}
@ -687,17 +692,31 @@ export const MultiMediaComponent: FC<{
<div className="w-full h-full relative group"> <div className="w-full h-full relative group">
<div <div
onClick={async () => { onClick={async () => {
const data: any = await mediaSettings(media); modals.openModal({
console.log( title: 'Media Settings',
value?.map((p) => (p.id === data.id ? data : p)) children: (close) => (
); <MediaComponentInner
onChange({ media={media as any}
target: { onClose={close}
name: 'upload', onSelect={(value: any) => {
value: value?.map((p) => console.log(value);
p.id === data.id ? data : p onChange({
), target: {
}, name: 'upload',
value: currentMedia.map((p) => {
if (p.id === media.id) {
return {
...p,
...value,
};
}
return p;
}),
},
});
}}
/>
),
}); });
}} }}
className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] bg-black/80 rounded-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-[100]" className="absolute top-[50%] left-[50%] -translate-x-[50%] -translate-y-[50%] bg-black/80 rounded-[10px] opacity-0 group-hover:opacity-100 transition-opacity z-[100]"

View File

@ -1,5 +1,5 @@
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import React, { FC } from 'react'; import React, { FC } from 'react';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import copy from 'copy-to-clipboard'; import copy from 'copy-to-clipboard';

View File

@ -20,11 +20,8 @@ import { weightedLength } from '@gitroom/helpers/utils/count.length';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is'; import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { usePreventWindowUnload } from '@gitroom/react/helpers/use.prevent.window.unload';
// @ts-ignore
import useKeypress from 'react-use-keypress';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer';
import { CopilotPopup } from '@copilotkit/react-ui'; import { CopilotPopup } from '@copilotkit/react-ui';
@ -46,7 +43,6 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const toaster = useToaster(); const toaster = useToaster();
const modal = useModals(); const modal = useModals();
usePreventWindowUnload(true);
const { addEditSets, mutate, customClose, dummy } = props; const { addEditSets, mutate, customClose, dummy } = props;
@ -136,8 +132,6 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
[integrations] [integrations]
); );
useKeypress('Escape', askClose);
const schedule = useCallback( const schedule = useCallback(
(type: 'draft' | 'now' | 'schedule') => async () => { (type: 'draft' | 'now' | 'schedule') => async () => {
setLoading(true); setLoading(true);
@ -166,6 +160,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
return; return;
} }
console.log(checkAllValid);
for (const item of checkAllValid) { for (const item of checkAllValid) {
if (item.valid === false) { if (item.valid === false) {
toaster.show('Some fields are not valid', 'warning'); toaster.show('Some fields are not valid', 'warning');
@ -312,7 +307,7 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
<> <>
<div <div
className={clsx( className={clsx(
'flex flex-col md:flex-row bg-newBgLineColor gap-[1px] rounded-[24px]' 'flex flex-col md:flex-row bg-newBgLineColor gap-[1px] rounded-[24px] trz'
)} )}
> >
<div <div
@ -496,11 +491,10 @@ export const ManageModal: FC<AddEditModalProps> = (props) => {
<ShowAllProviders ref={ref} /> <ShowAllProviders ref={ref} />
</div> </div>
</div> </div>
</div> <CopilotPopup
<CopilotPopup hitEscapeToClose={false}
hitEscapeToClose={false} clickOutsideToClose={true}
clickOutsideToClose={true} instructions={`
instructions={`
You are an assistant that help the user to schedule their social media posts, You are an assistant that help the user to schedule their social media posts,
Here are the things you can do: Here are the things you can do:
- Add a new comment / post to the list of posts - Add a new comment / post to the list of posts
@ -511,11 +505,12 @@ Here are the things you can do:
Post content can be added using the addPostContentFor{num} function. Post content can be added using the addPostContentFor{num} function.
After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} function. After using the addPostFor{num} it will create a new addPostContentFor{num+ 1} function.
`} `}
labels={{ labels={{
title: 'Your Assistant', title: 'Your Assistant',
initial: 'Hi! 👋 How can I assist you today?', initial: 'Hi! 👋 How can I assist you today?',
}} }}
/> />
</div>
</> </>
); );
}; };

View File

@ -1,5 +1,5 @@
import { FC, ReactNode, useEffect, useRef } from 'react'; import { FC, ReactNode, useEffect, useRef } from 'react';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';

View File

@ -69,6 +69,7 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => {
<CopilotKit <CopilotKit
credentials="include" credentials="include"
runtimeUrl={backendUrl + '/copilot/chat'} runtimeUrl={backendUrl + '/copilot/chat'}
showDevConsole={false}
> >
<MantineWrapper> <MantineWrapper>
{user.tier === 'FREE' && searchParams.get('check') && ( {user.tier === 'FREE' && searchParams.get('check') && (
@ -103,7 +104,7 @@ export const LayoutComponent = ({ children }: { children: ReactNode }) => {
</div> </div>
</div> </div>
</div> </div>
<div className="flex-1 bg-newBgLineColor rounded-[12px] overflow-hidden flex flex-col gap-[1px]"> <div className="flex-1 bg-newBgLineColor rounded-[12px] overflow-hidden flex flex-col gap-[1px] blurMe">
<div className="flex bg-newBgColorInner h-[80px] px-[20px] items-center"> <div className="flex bg-newBgColorInner h-[80px] px-[20px] items-center">
<div className="text-[24px] font-[600] flex flex-1"> <div className="text-[24px] font-[600] flex flex-1">
<Title /> <Title />

View File

@ -2,7 +2,7 @@
import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { useRouter, useSearchParams } from 'next/navigation'; import { useRouter, useSearchParams } from 'next/navigation';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { ConnectChannels } from '@gitroom/frontend/components/onboarding/connect.channels'; import { ConnectChannels } from '@gitroom/frontend/components/onboarding/connect.channels';
import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useVariables } from '@gitroom/react/helpers/variable.context';
@ -46,15 +46,11 @@ export const Onboarding: FC = () => {
} }
modalOpen.current = true; modalOpen.current = true;
modal.openModal({ modal.openModal({
title: '', title: t('onboarding', 'Onboarding'),
withCloseButton: false, withCloseButton: false,
closeOnEscape: false, closeOnEscape: false,
size: '900px', size: '900px',
children: ( children: <Welcome />,
<ModalWrapperComponent title={t('onboarding', 'Onboarding')}>
<Welcome />
</ModalWrapperComponent>
),
}); });
}, [query]); }, [query]);
return null; return null;

View File

@ -9,7 +9,7 @@ import { Button } from '@gitroom/react/form/button';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react'; import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR, { mutate } from 'swr'; import useSWR, { mutate } from 'swr';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { import {
FormProvider, FormProvider,
@ -243,18 +243,17 @@ export const Plug = () => {
mutate(); mutate();
}, },
size: '500px', size: '500px',
title: `Auto Plug: ${p.title}`,
children: ( children: (
<ModalWrapperComponent title={`Auto Plug: ${p.title}`}> <PlugPop
<PlugPop plug={p}
plug={p} data={data}
data={data} settings={{
settings={{ identifier: plug.identifier,
identifier: plug.identifier, providerId: plug.providerId,
providerId: plug.providerId, name: plug.name,
name: plug.name, }}
}} />
/>
</ModalWrapperComponent>
), ),
}); });
}, },

View File

@ -26,6 +26,7 @@ export const PreviewWrapper = ({ children }: { children: ReactNode }) => {
<CopilotKit <CopilotKit
credentials="include" credentials="include"
runtimeUrl={backendUrl + '/copilot/chat'} runtimeUrl={backendUrl + '/copilot/chat'}
showDevConsole={false}
> >
<MantineWrapper> <MantineWrapper>
<Toaster /> <Toaster />

View File

@ -6,15 +6,14 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr'; import useSWR from 'swr';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { useModals } from '@mantine/modals';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { useToaster } from '@gitroom/react/toaster/toaster'; import { useToaster } from '@gitroom/react/toaster/toaster';
import clsx from 'clsx'; import clsx from 'clsx';
import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; import { deleteDialog } from '@gitroom/react/helpers/delete.dialog';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';
import dayjs from 'dayjs';
import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal';
import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone';
import { useModals } from '@gitroom/frontend/components/layout/new-modal';
const SaveSetModal: FC<{ const SaveSetModal: FC<{
postData: any; postData: any;
@ -97,9 +96,8 @@ export const Sets: FC = () => {
closeOnClickOutside: false, closeOnClickOutside: false,
closeOnEscape: false, closeOnEscape: false,
withCloseButton: false, withCloseButton: false,
classNames: { removeLayout: true,
modal: 'w-[100%] max-w-[1400px] bg-transparent text-textColor', askClose: true,
},
children: ( children: (
<AddEditModal <AddEditModal
allIntegrations={integrations.map((p: any) => ({ allIntegrations={integrations.map((p: any) => ({
@ -109,10 +107,6 @@ export const Sets: FC = () => {
addEditSets={(data) => { addEditSets={(data) => {
modal.openModal({ modal.openModal({
title: 'Save as Set', title: 'Save as Set',
classNames: {
modal: 'bg-sixth text-textColor',
title: 'text-textColor',
},
children: ( children: (
<SaveSetModal <SaveSetModal
initialValue={params?.name || ''} initialValue={params?.name || ''}
@ -137,7 +131,6 @@ export const Sets: FC = () => {
onCancel={() => modal.closeAll()} onCancel={() => modal.closeAll()}
/> />
), ),
size: 'md',
}); });
}} }}
reopenModal={() => {}} reopenModal={() => {}}
@ -146,7 +139,6 @@ export const Sets: FC = () => {
date={newDayjs()} date={newDayjs()}
/> />
), ),
size: '80%',
title: ``, title: ``,
}); });
}, },

View File

@ -3,7 +3,7 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr'; import useSWR from 'swr';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import clsx from 'clsx'; import clsx from 'clsx';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { array, boolean, object, string } from 'yup'; import { array, boolean, object, string } from 'yup';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
@ -27,11 +27,8 @@ export const SignaturesComponent: FC<{
const addSignature = useCallback( const addSignature = useCallback(
(data?: any) => () => { (data?: any) => () => {
modal.openModal({ modal.openModal({
title: '', title: data ? 'Edit Signature' : 'Add Signature',
withCloseButton: false, withCloseButton: true,
classNames: {
modal: 'bg-transparent text-textColor',
},
children: <AddOrRemoveSignature data={data} reload={mutate} />, children: <AddOrRemoveSignature data={data} reload={mutate} />,
}); });
}, },
@ -170,7 +167,7 @@ const AddOrRemoveSignature: FC<{
: 'Signature added successfully', : 'Signature added successfully',
'success' 'success'
); );
modal.closeModal(modal.modals[modal.modals.length - 1].id); modal.closeCurrent();
reload(); reload();
}, },
[data, modal] [data, modal]
@ -181,14 +178,11 @@ const AddOrRemoveSignature: FC<{
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<form onSubmit={form.handleSubmit(callBack)}> <form onSubmit={form.handleSubmit(callBack)}>
<div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0 w-[500px]"> <div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] pt-0">
<TopTitle title={data ? 'Edit Signature' : 'Add Signature'} />
<button <button
className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa" className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button" type="button"
onClick={() => onClick={() => modal.closeCurrent()}
modal.closeModal(modal.modals[modal.modals.length - 1].id)
}
> >
<svg <svg
viewBox="0 0 15 15" viewBox="0 0 15 15"

View File

@ -4,7 +4,7 @@ import useSWR from 'swr';
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { capitalize } from 'lodash'; import { capitalize } from 'lodash';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { useForm, FormProvider, useWatch } from 'react-hook-form'; import { useForm, FormProvider, useWatch } from 'react-hook-form';
@ -65,38 +65,13 @@ export const AddMember = () => {
}, },
[] []
); );
const closeModal = useCallback(() => {
return modals.closeAll();
}, []);
const t = useT(); const t = useT();
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<form onSubmit={form.handleSubmit(submit)}> <form onSubmit={form.handleSubmit(submit)}>
<div className="relative flex gap-[10px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0"> <div className="relative flex gap-[10px] flex-col flex-1 p-[16px] pt-0">
<TopTitle title="Add Member" />
<button
onClick={closeModal}
className="outline-none absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
{sendEmail && ( {sendEmail && (
<Input <Input
label="Email" label="Email"
@ -153,7 +128,8 @@ export const TeamsComponent = () => {
classNames: { classNames: {
modal: 'bg-transparent text-textColor', modal: 'bg-transparent text-textColor',
}, },
withCloseButton: false, title: 'Add Team Member',
withCloseButton: true,
children: <AddMember />, children: <AddMember />,
}); });
}, []); }, []);

View File

@ -1,27 +1,26 @@
import { FC, useCallback, useState } from 'react'; import { FC, useCallback } from 'react';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { SignaturesComponent } from '@gitroom/frontend/components/settings/signatures.component'; import { SignaturesComponent } from '@gitroom/frontend/components/settings/signatures.component';
import { Transforms } from 'slate'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
export const SignatureBox: FC<{ export const SignatureBox: FC<{
editor: any; editor: any;
}> = ({ editor }) => { }> = ({ editor }) => {
const [showModal, setShowModal] = useState<any>(false); const modals = useModals();
const addSignature = useCallback(() => {
setShowModal(true);
}, [showModal]);
const appendValue = (val: string) => { const appendValue = (val: string) => {
editor?.commands?.insertContent("\n\n" + val); editor?.commands?.insertContent('\n\n' + val);
editor?.commands?.focus(); editor?.commands?.focus();
setShowModal(false);
}; };
const addSignature = useCallback(() => {
modals.openModal({
title: 'Add Signature',
children: (close) => (
<SignatureModal appendSignature={appendValue} close={close} />
),
});
}, [appendValue]);
return ( return (
<> <>
{showModal && (
<SignatureModal
appendSignature={appendValue}
close={() => setShowModal(false)}
/>
)}
<div <div
onClick={addSignature} onClick={addSignature}
className="select-none cursor-pointer w-[40px] p-[5px] text-center" className="select-none cursor-pointer w-[40px] p-[5px] text-center"
@ -45,32 +44,10 @@ export const SignatureModal: FC<{
close: () => void; close: () => void;
appendSignature: (sign: string) => void; appendSignature: (sign: string) => void;
}> = (props) => { }> = (props) => {
const { close, appendSignature } = props; const { appendSignature } = props;
return ( return (
<div className="bg-black/40 fixed start-0 top-0 w-full h-full z-[500]"> <div className="bg-black/40 fixed start-0 top-0 w-full h-full z-[500]">
<div className="relative w-[900px] mx-auto flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0"> <div className="relative w-[900px] mx-auto flex gap-[20px] flex-col flex-1 rounded-[4px] pt-0">
<TopTitle title={`Add signature`} />
<button
className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
onClick={close}
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
<SignaturesComponent appendSignature={appendSignature} /> <SignaturesComponent appendSignature={appendSignature} />
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ import useSWR from 'swr';
import React, { FC, useCallback, useState } from 'react'; import React, { FC, useCallback, useState } from 'react';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { FieldValues, FormProvider, useForm } from 'react-hook-form'; import { FieldValues, FormProvider, useForm } from 'react-hook-form';
import { useT } from '@gitroom/react/translation/get.transation.service.client'; import { useT } from '@gitroom/react/translation/get.transation.service.client';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
@ -115,12 +115,10 @@ export const ThirdPartyListComponent: FC<{ reload: () => void }> = (props) => {
const addApiKey = useCallback( const addApiKey = useCallback(
(title: string, identifier: string) => () => { (title: string, identifier: string) => () => {
modals.openModal({ modals.openModal({
title: '', title: `Add API key for ${title}`,
withCloseButton: false, withCloseButton: false,
children: ( children: (
<ModalWrapperComponent title={`Add API key for ${title}`}> <ApiModal identifier={identifier} title={title} update={reload} />
<ApiModal identifier={identifier} title={title} update={reload} />
</ModalWrapperComponent>
), ),
}); });
}, },

View File

@ -18,6 +18,7 @@ import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.titl
import './providers/heygen.provider'; import './providers/heygen.provider';
import { thirdPartyList } from '@gitroom/frontend/components/third-parties/third-party.wrapper'; import { thirdPartyList } from '@gitroom/frontend/components/third-parties/third-party.wrapper';
import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store'; import { useLaunchStore } from '@gitroom/frontend/components/new-launch/store';
import { useModals } from '@gitroom/frontend/components/layout/new-modal';
const ThirdPartyContext = createContext({ const ThirdPartyContext = createContext({
id: '', id: '',
@ -92,93 +93,53 @@ export const ThirdPartyPopup: FC<{
}, []); }, []);
return ( return (
<div <div className={clsx('flex flex-wrap flex-col gap-[10px] pt-[20px]')}>
className="removeEditor fixed start-0 top-0 bg-primary/80 z-[300] w-full min-h-full animate-fade bg-black/30 rounded-[24px]" {!thirdParty && (
onClick={closeModal} <div className="grid grid-cols-4 gap-[10px] justify-items-center justify-center">
> {thirdParties.map((p: any) => (
<div className="relative"> <div
<div className="absolute -top-[50px] left-0" ref={refNew} /> onClick={() => {
</div> setThirdParty(p);
<div }}
className="max-w-[1000px] w-full h-full bg-sixth border-tableBorder border-2 rounded-xl relative mx-auto" key={p.identifier}
onClick={(e) => e.stopPropagation()} className="w-full h-full p-[20px] min-h-[100px] text-[14px] bg-third hover:bg-input transition-all text-textColor relative flex flex-col gap-[15px] cursor-pointer"
>
<div className="pb-[20px] px-[20px] w-full h-full">
<div className="flex flex-col">
<div className="flex-1">
<TopTitle title="Integrations" />
</div>
<button
onClick={closeModal}
className="outline-none z-[300] absolute end-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root bg-primary hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
> >
<svg <div>
viewBox="0 0 15 15" <img
fill="none" className="w-[32px] h-[32px]"
xmlns="http://www.w3.org/2000/svg" src={`/icons/third-party/${p.identifier}.png`}
width="16" />
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
</div>
<div className={clsx('flex flex-wrap flex-col gap-[10px] pt-[20px]')}>
{!thirdParty && (
<div className="grid grid-cols-4 gap-[10px] justify-items-center justify-center">
{thirdParties.map((p: any) => (
<div
onClick={() => {
setThirdParty(p);
}}
key={p.identifier}
className="w-full h-full p-[20px] min-h-[100px] text-[14px] bg-third hover:bg-input transition-all text-textColor relative flex flex-col gap-[15px] cursor-pointer"
>
<div>
<img
className="w-[32px] h-[32px]"
src={`/icons/third-party/${p.identifier}.png`}
/>
</div>
<div className="whitespace-pre-wrap text-left text-lg">
{p.title}: {p.name}
</div>
<div className="whitespace-pre-wrap text-left">
{p.description}
</div>
<div className="w-full flex">
<Button className="w-full">Use</Button>
</div>
</div>
))}
</div> </div>
)} <div className="whitespace-pre-wrap text-left text-lg">
{thirdParty && ( {p.title}: {p.name}
<> </div>
<div> <div className="whitespace-pre-wrap text-left">
<div {p.description}
className="cursor-pointer float-left" </div>
onClick={() => setThirdParty(null)} <div className="w-full flex">
> <Button className="w-full">Use</Button>
{'<'} Back </div>
</div> </div>
</div> ))}
<ThirdPartyContext.Provider
value={{ ...thirdParty, data: allData, close, onChange }}
>
<Component />
</ThirdPartyContext.Provider>
</>
)}
</div>
</div> </div>
</div> )}
{thirdParty && (
<>
<div>
<div
className="cursor-pointer float-left"
onClick={() => setThirdParty(null)}
>
{'<'} Back
</div>
</div>
<ThirdPartyContext.Provider
value={{ ...thirdParty, data: allData, close, onChange }}
>
<Component />
</ThirdPartyContext.Provider>
</>
)}
</div> </div>
); );
}; };
@ -197,7 +158,7 @@ export const ThirdPartyMedia: FC<{
const { allData, onChange } = props; const { allData, onChange } = props;
const t = useT(); const t = useT();
const fetch = useFetch(); const fetch = useFetch();
const [popup, setPopup] = useState(false); const modals = useModals();
const thirdParties = useCallback(async () => { const thirdParties = useCallback(async () => {
return (await (await fetch('/third-party')).json()).filter( return (await (await fetch('/third-party')).json()).filter(
@ -220,20 +181,25 @@ export const ThirdPartyMedia: FC<{
return ( return (
<> <>
{popup && (
<ThirdPartyPopup
thirdParties={data}
closeModal={() => setPopup(false)}
allData={allData}
onChange={onChange}
/>
)}
<div className="relative group"> <div className="relative group">
<Button <Button
className={clsx( className={clsx(
'relative ms-[10px] !px-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor' 'relative ms-[10px] !px-[10px] rounded-[4px] gap-[8px] !text-primary justify-center items-center flex border border-dashed border-newBgLineColor bg-newColColor'
)} )}
onClick={() => setPopup(true)} onClick={() => {
modals.openModal({
title: t('integrations', 'Integrations'),
size: '80%',
children: (close) => (
<ThirdPartyPopup
thirdParties={data}
closeModal={close}
allData={allData}
onChange={onChange}
/>
),
});
}}
> >
<div className={clsx('flex gap-[5px] items-center')}> <div className={clsx('flex gap-[5px] items-center')}>
<div> <div>

View File

@ -3,7 +3,7 @@ import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import useSWR from 'swr'; import useSWR from 'swr';
import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useUser } from '@gitroom/frontend/components/layout/user.context';
import { Button } from '@gitroom/react/form/button'; import { Button } from '@gitroom/react/form/button';
import { useModals } from '@mantine/modals'; import { useModals } from '@gitroom/frontend/components/layout/new-modal';
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
import { Input } from '@gitroom/react/form/input'; import { Input } from '@gitroom/react/form/input';
import { FormProvider, useForm } from 'react-hook-form'; import { FormProvider, useForm } from 'react-hook-form';
@ -27,11 +27,8 @@ export const Webhooks: FC = () => {
const addWebhook = useCallback( const addWebhook = useCallback(
(data?: any) => () => { (data?: any) => () => {
modal.openModal({ modal.openModal({
title: '', title: data ? 'Update webhook' : 'Add webhook',
withCloseButton: false, withCloseButton: true,
classNames: {
modal: 'bg-transparent text-textColor',
},
children: <AddOrEditWebhook data={data} reload={mutate} />, children: <AddOrEditWebhook data={data} reload={mutate} />,
}); });
}, },
@ -246,29 +243,7 @@ export const AddOrEditWebhook: FC<{
return ( return (
<FormProvider {...form}> <FormProvider {...form}>
<form onSubmit={form.handleSubmit(callBack)}> <form onSubmit={form.handleSubmit(callBack)}>
<div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] border border-customColor6 bg-sixth p-[16px] pt-0 w-[500px]"> <div className="relative flex gap-[20px] flex-col flex-1 rounded-[4px] pt-0">
<TopTitle title={data ? 'Edit webhook' : 'Add webhook'} />
<button
className="outline-none absolute end-[20px] top-[15px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
type="button"
onClick={modal.closeAll}
>
<svg
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
>
<path
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
></path>
</svg>
</button>
<div> <div>
<Input <Input
label="Name" label="Name"

View File

@ -73,8 +73,6 @@ module.exports = {
customColor55: 'var(--color-custom55)', customColor55: 'var(--color-custom55)',
modalCustom: 'var(--color-modalCustom)', modalCustom: 'var(--color-modalCustom)',
newBgColor: 'var(--new-bgColor)', newBgColor: 'var(--new-bgColor)',
newBgColorInner: 'var(--new-bgColorInner)', newBgColorInner: 'var(--new-bgColorInner)',
newBgLineColor: 'var(--new-bgLineColor)', newBgLineColor: 'var(--new-bgLineColor)',
@ -96,6 +94,7 @@ module.exports = {
menuDots: 'var(--new-menu-dots)', menuDots: 'var(--new-menu-dots)',
menuDotsHover: 'var(--new-menu-hover)', menuDotsHover: 'var(--new-menu-hover)',
bigStrip: 'var(--new-big-strips)', bigStrip: 'var(--new-big-strips)',
popup: 'var(--popup-color)',
}, },
gridTemplateColumns: { gridTemplateColumns: {
13: 'repeat(13, minmax(0, 1fr));', 13: 'repeat(13, minmax(0, 1fr));',
@ -110,6 +109,7 @@ module.exports = {
animation: { animation: {
fade: 'fadeOut 0.5s ease-in-out', fade: 'fadeOut 0.5s ease-in-out',
normalFadeIn: 'normalFadeIn 0.5s ease-in-out', normalFadeIn: 'normalFadeIn 0.5s ease-in-out',
fadeIn: 'normalFadeIn 0.2s ease-in-out forwards',
normalFadeOut: 'normalFadeOut 0.5s linear 5s forwards', normalFadeOut: 'normalFadeOut 0.5s linear 5s forwards',
overflow: 'overFlow 0.5s ease-in-out forwards', overflow: 'overFlow 0.5s ease-in-out forwards',
overflowReverse: 'overFlowReverse 0.5s ease-in-out forwards', overflowReverse: 'overFlowReverse 0.5s ease-in-out forwards',
@ -121,7 +121,7 @@ module.exports = {
yellow: '0 0 60px 20px #6b6237', yellow: '0 0 60px 20px #6b6237',
yellowToast: '0px 0px 50px rgba(252, 186, 3, 0.3)', yellowToast: '0px 0px 50px rgba(252, 186, 3, 0.3)',
greenToast: '0px 0px 50px rgba(60, 124, 90, 0.3)', greenToast: '0px 0px 50px rgba(60, 124, 90, 0.3)',
menu: 'var(--menu-shadow)' menu: 'var(--menu-shadow)',
}, },
// that is actual animation // that is actual animation
keyframes: (theme) => ({ keyframes: (theme) => ({

View File

@ -30,3 +30,4 @@ export const agentCategories = [
'Update', 'Update',
'Trend', 'Trend',
]; ];

View File

@ -55,6 +55,7 @@ export class RedditSettingsDtoInner {
@ValidateIf((e) => e.is_flair_required) @ValidateIf((e) => e.is_flair_required)
@IsDefined() @IsDefined()
@ValidateNested() @ValidateNested()
@Type(() => RedditFlairDto)
flair: RedditFlairDto; flair: RedditFlairDto;
} }

View File

@ -50,7 +50,7 @@ export const Checkbox = forwardRef<
{...disableForm ? {} : form.register(props.name!)} {...disableForm ? {} : form.register(props.name!)}
onClick={changeStatus} onClick={changeStatus}
className={clsx( className={clsx(
'cursor-pointer rounded-[4px] select-none w-[24px] h-[24px] justify-center items-center flex', 'cursor-pointer rounded-[4px] select-none w-[24px] h-[24px] justify-center items-center flex text-white',
variant === 'default' || !variant variant === 'default' || !variant
? 'bg-forth' ? 'bg-forth'
: 'border-customColor1 border-2 bg-customColor2', : 'border-customColor1 border-2 bg-customColor2',

View File

@ -1,33 +0,0 @@
'use client';
import { Slider } from '@mantine/core';
import { FC } from 'react';
export const Track: FC<{
value: number;
min: number;
max: number;
onChange: (value: number) => void;
}> = (props) => {
const { value, onChange, min, max } = props;
return (
<Slider
color="violet"
labelAlwaysOn={true}
value={value}
onChange={onChange}
size="xl"
classNames={{
track:
'before:bg-customColor3 before:border before:border-customColor6',
mark: 'border-customColor6',
markFilled: 'border-customColor7',
}}
min={min}
max={max}
// classNames={{
// track: 'h-[15px]',
// thumb: 'w-[24px] h-[24px]',
// }}
/>
);
};

View File

@ -1,5 +1,5 @@
import Swal from 'sweetalert2';
import i18next from '@gitroom/react/translation/i18next'; import i18next from '@gitroom/react/translation/i18next';
import { areYouSure } from '@gitroom/frontend/components/layout/new-modal';
export const deleteDialog = async ( export const deleteDialog = async (
message: string, message: string,
@ -7,14 +7,11 @@ export const deleteDialog = async (
title?: string, title?: string,
cancelButton?: string cancelButton?: string
) => { ) => {
const fire = await Swal.fire({ return areYouSure({
title: title || i18next.t('are_you_sure', 'Are you sure?'), title: title || i18next.t('are_you_sure', 'Are you sure?'),
text: message, description: message,
icon: 'warning', approveLabel:
showCancelButton: true,
confirmButtonText:
confirmButton || i18next.t('yes_delete_it', 'Yes, delete it!'), confirmButton || i18next.t('yes_delete_it', 'Yes, delete it!'),
cancelButtonText: cancelButton || i18next.t('no_cancel', 'No, cancel!'), cancelLabel: cancelButton || i18next.t('no_cancel', 'No, cancel!'),
}); });
return fire.isConfirmed;
}; };

View File

@ -1,26 +1,15 @@
'use client'; 'use client';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { MantineProvider } from '@mantine/core'; import {
import { ModalsProvider } from '@mantine/modals'; DecisionEverywhere,
import i18next from '@gitroom/react/translation/i18next'; ModalManager,
} from '@gitroom/frontend/components/layout/new-modal';
export const MantineWrapper = (props: { children: ReactNode }) => { export const MantineWrapper = (props: { children: ReactNode }) => {
const dir = i18next.dir();
return ( return (
// @ts-ignore <ModalManager>
<MantineProvider> <DecisionEverywhere />
<ModalsProvider {props.children}
modalProps={{ </ModalManager>
dir,
classNames: {
modal: 'bg-primary text-white',
close: 'bg-black hover:bg-black cursor-pointer',
},
}}
>
{props.children}
</ModalsProvider>
</MantineProvider>
); );
}; };

View File

@ -186,6 +186,7 @@
"react-dom": "18.3.1", "react-dom": "18.3.1",
"react-dropzone": "^14.3.5", "react-dropzone": "^14.3.5",
"react-hook-form": "^7.58.1", "react-hook-form": "^7.58.1",
"react-hotkeys-hook": "^5.1.0",
"react-i18next": "^15.5.2", "react-i18next": "^15.5.2",
"react-loading": "^2.0.3", "react-loading": "^2.0.3",
"react-sortablejs": "^6.1.4", "react-sortablejs": "^6.1.4",

View File

@ -438,6 +438,9 @@ importers:
react-hook-form: react-hook-form:
specifier: ^7.58.1 specifier: ^7.58.1
version: 7.62.0(react@18.3.1) version: 7.62.0(react@18.3.1)
react-hotkeys-hook:
specifier: ^5.1.0
version: 5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react-i18next: react-i18next:
specifier: ^15.5.2 specifier: ^15.5.2
version: 15.7.3(i18next@25.5.2(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4) version: 15.7.3(i18next@25.5.2(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4)
@ -12884,6 +12887,12 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19 react: ^16.8.0 || ^17 || ^18 || ^19
react-hotkeys-hook@5.1.0:
resolution: {integrity: sha512-GCNGXjBzV9buOS3REoQFmSmE4WTvBhYQ0YrAeeMZI83bhXg3dRWsLHXDutcVDdEjwJqJCxk5iewWYX5LtFUd7g==}
peerDependencies:
react: '>=16.8.0'
react-dom: '>=16.8.0'
react-i18next@15.7.3: react-i18next@15.7.3:
resolution: {integrity: sha512-AANws4tOE+QSq/IeMF/ncoHlMNZaVLxpa5uUGW1wjike68elVYr0018L9xYoqBr1OFO7G7boDPrbn0HpMCJxTw==} resolution: {integrity: sha512-AANws4tOE+QSq/IeMF/ncoHlMNZaVLxpa5uUGW1wjike68elVYr0018L9xYoqBr1OFO7G7boDPrbn0HpMCJxTw==}
peerDependencies: peerDependencies:
@ -31585,6 +31594,11 @@ snapshots:
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
react-hotkeys-hook@5.1.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-i18next@15.7.3(i18next@25.5.2(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4): react-i18next@15.7.3(i18next@25.5.2(typescript@5.5.4))(react-dom@18.3.1(react@18.3.1))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@18.3.1)(bufferutil@4.0.9)(react@18.3.1)(utf-8-validate@5.0.10))(react@18.3.1)(typescript@5.5.4):
dependencies: dependencies:
'@babel/runtime': 7.28.4 '@babel/runtime': 7.28.4