diff --git a/apps/frontend/src/components/launches/add.edit.model.tsx b/apps/frontend/src/components/launches/add.edit.model.tsx index 299b50a5..feb430d9 100644 --- a/apps/frontend/src/components/launches/add.edit.model.tsx +++ b/apps/frontend/src/components/launches/add.edit.model.tsx @@ -61,17 +61,21 @@ import { TagsComponent } from './tags.component'; import { RepeatComponent } from '@gitroom/frontend/components/launches/repeat.component'; import { MergePost } from '@gitroom/frontend/components/launches/merge.post'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { CreatePostDto } from '@gitroom/nestjs-libraries/dtos/posts/create.post.dto'; +import { uniq } from 'lodash'; +import { SetContext } from '@gitroom/frontend/components/launches/set.context'; function countCharacters(text: string, type: string): number { if (type !== 'x') { return text.length; } return weightedLength(text); } + export const AddEditModal: FC<{ date: dayjs.Dayjs; integrations: Integrations[]; allIntegrations?: Integrations[]; - setId?: string; + set?: CreatePostDto; addEditSets?: (data: any) => void; reopenModal: () => void; mutate: () => void; @@ -95,7 +99,9 @@ export const AddEditModal: FC<{ padding, customClose, addEditSets, + set, } = props; + const [customer, setCustomer] = useState(''); const [loading, setLoading] = useState(false); const [uploading, setUploading] = useState(false); @@ -105,17 +111,24 @@ export const AddEditModal: FC<{ // selected integrations to allow edit const [selectedIntegrations, setSelectedIntegrations] = useStateCallback< Integrations[] - >([]); + >( + set + ? ints.filter( + (f) => + uniq(set.posts.flatMap((p) => p.integration.id)).indexOf(f.id) > -1 + ) + : [] + ); const integrations = useMemo(() => { if (!customer) { return ints; } const list = ints.filter((f) => f?.customer?.id === customer); - if (list.length === 1) { + if (list.length === 1 && !set) { setSelectedIntegrations([list[0]]); } return list; - }, [customer, ints]); + }, [customer, ints, set]); const [dateState, setDateState] = useState(date); // hook to open a new modal @@ -134,6 +147,12 @@ export const AddEditModal: FC<{ >( onlyValues ? onlyValues + : set + ? set?.posts?.[0].value || [ + { + content: '', + }, + ] : [ { content: '', @@ -467,18 +486,24 @@ export const AddEditModal: FC<{ body: JSON.stringify(data), }); existingData.group = makeId(10); - mutate(); - toaster.show( - !existingData.integration - ? 'Added successfully' - : 'Updated successfully' - ); + + if (!addEditSets) { + mutate(); + toaster.show( + !existingData.integration + ? 'Added successfully' + : 'Updated successfully' + ); + } if (customClose) { setTimeout(() => { customClose(); }, 2000); } - modal.closeAll(); + + if (!addEditSets) { + modal.closeAll(); + } }, [ inter, @@ -578,7 +603,7 @@ export const AddEditModal: FC<{ }, [data, postFor, selectedIntegrations]); useClickOutside(askClose); return ( - <> + {user?.tier?.ai && ( !f.disabled)} - selectedIntegrations={[]} + selectedIntegrations={set ? selectedIntegrations : []} singleSelect={false} onChange={setSelectedIntegrations} isMain={true} @@ -938,6 +963,7 @@ Here are the things you can do: {!!selectedIntegrations.length && (
- +
); }); diff --git a/apps/frontend/src/components/launches/calendar.context.tsx b/apps/frontend/src/components/launches/calendar.context.tsx index 445a9c37..f054d22b 100644 --- a/apps/frontend/src/components/launches/calendar.context.tsx +++ b/apps/frontend/src/components/launches/calendar.context.tsx @@ -27,6 +27,7 @@ export const CalendarContext = createContext({ currentYear: dayjs().year(), currentMonth: dayjs().month(), customer: null as string | null, + sets: [] as { name: string; id: string; content: string[] }[], comments: [] as Array<{ date: string; total: number; @@ -142,6 +143,13 @@ export const CalendarWeekProvider: FC<{ refreshWhenHidden: false, revalidateOnFocus: false, }); + + const setList = useCallback(async () => { + return (await fetch('/sets')).json(); + }, []); + + const { data: sets, mutate } = useSWR('sets', setList); + const setFiltersWrapper = useCallback( (filters: { currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6; @@ -202,6 +210,7 @@ export const CalendarWeekProvider: FC<{ setFilters: setFiltersWrapper, changeDate, comments, + sets: sets || [], }} > {children} diff --git a/apps/frontend/src/components/launches/calendar.tsx b/apps/frontend/src/components/launches/calendar.tsx index 2b6e4e15..bfe6a718 100644 --- a/apps/frontend/src/components/launches/calendar.tsx +++ b/apps/frontend/src/components/launches/calendar.tsx @@ -336,6 +336,7 @@ export const CalendarColumn: FC<{ changeDate, display, reloadCalendarView, + sets, } = useCalendar(); const toaster = useToaster(); const modal = useModals(); @@ -547,8 +548,13 @@ export const CalendarColumn: FC<{ }, [integrations] ); + const addModal = useCallback(async () => { const signature = await (await fetch('/signatures/default')).json(); + const set = !sets.length ? undefined : await new Promise(() => { + + }); + modal.openModal({ closeOnClickOutside: false, closeOnEscape: false, @@ -583,7 +589,7 @@ export const CalendarColumn: FC<{ size: '80%', // title: `Adding posts for ${getDate.format('DD/MM/YYYY HH:mm')}`, }); - }, [integrations, getDate]); + }, [integrations, getDate, sets]); const openStatistics = useCallback( (id: string) => () => { modal.openModal({ diff --git a/apps/frontend/src/components/launches/helpers/use.values.ts b/apps/frontend/src/components/launches/helpers/use.values.ts index efb834ff..ec8ebf02 100644 --- a/apps/frontend/src/components/launches/helpers/use.values.ts +++ b/apps/frontend/src/components/launches/helpers/use.values.ts @@ -57,7 +57,6 @@ export const useValues = ( criteriaMode: 'all', }); - console.log(form.formState.errors); const getValues = useMemo(() => { return () => ({ ...form.getValues(), diff --git a/apps/frontend/src/components/launches/providers.options.tsx b/apps/frontend/src/components/launches/providers.options.tsx index 9393148d..8d4bc337 100644 --- a/apps/frontend/src/components/launches/providers.options.tsx +++ b/apps/frontend/src/components/launches/providers.options.tsx @@ -6,6 +6,7 @@ import { ShowAllProviders } from '@gitroom/frontend/components/launches/provider import dayjs from 'dayjs'; import { useStateCallback } from '@gitroom/react/helpers/use.state.callback'; export const ProvidersOptions: FC<{ + hideEditOnlyThis: boolean; integrations: Integrations[]; allIntegrations: Integrations[]; editorValue: Array<{ @@ -14,7 +15,7 @@ export const ProvidersOptions: FC<{ }>; date: dayjs.Dayjs; }> = (props) => { - const { integrations, editorValue, date } = props; + const { integrations, editorValue, date, hideEditOnlyThis } = props; const [selectedIntegrations, setSelectedIntegrations] = useStateCallback([ integrations[0], ]); @@ -42,6 +43,7 @@ export const ProvidersOptions: FC<{ }} > ( }>; hideMenu?: boolean; show: boolean; + hideEditOnlyThis?: boolean; }) => { const existingData = useExistingData(); const t = useT(); @@ -170,9 +172,13 @@ export const withProvider = function ( } ); + const set = useSet(); + // this is a smart function, it updates the global value without updating the states (too heavy) and set the settings validation const form = useValues( - existingData.settings, + set?.set + ? set.set.posts.find((p) => p.integration.id === props.id).settings + : existingData.settings, props.id, props.identifier, editInPlace ? InPlaceValue : props.value, @@ -451,7 +457,7 @@ export const withProvider = function ( )} - {!existingData.integration && ( + {!existingData.integration && !props.hideEditOnlyThis && (
+ +
+ + ); +}; + export const Sets: FC = () => { const fetch = useFetch(); const user = useUser(); @@ -41,7 +78,7 @@ export const Sets: FC = () => { const { data, mutate } = useSWR('sets', list); const addSet = useCallback( - (data?: any) => () => { + (params?: { id?: string; name?: string; content?: string }) => () => { modal.openModal({ closeOnClickOutside: false, closeOnEscape: false, @@ -54,8 +91,40 @@ export const Sets: FC = () => { allIntegrations={integrations.map((p: any) => ({ ...p, }))} + {...(params?.id ? { set: JSON.parse(params.content) } : {})} addEditSets={(data) => { - console.log('save', data); + modal.openModal({ + title: 'Save as Set', + classNames: { + modal: 'bg-sixth text-textColor', + title: 'text-textColor', + }, + children: ( + { + try { + await fetch('/sets', { + method: 'POST', + body: JSON.stringify({ + ...(params?.id ? { id: params.id } : {}), + name, + content: JSON.stringify(data), + }), + }); + modal.closeAll(); + mutate(); + toaster.show('Set saved successfully', 'success'); + } catch (error) { + toaster.show('Failed to save set', 'warning'); + } + }} + onCancel={() => modal.closeAll()} + /> + ), + size: 'md', + }); }} reopenModal={() => {}} mutate={() => {}} @@ -130,90 +199,3 @@ export const Sets: FC = () => { ); }; - -const details = object().shape({ - name: string().required(), - content: string().required(), -}); - -export const AddOrEditSet: FC<{ - data?: any; - reload: () => void; -}> = (props) => { - const { data, reload } = props; - const fetch = useFetch(); - const modal = useModals(); - const toast = useToaster(); - - const form = useForm({ - resolver: yupResolver(details), - values: { - name: data?.name || '', - content: data?.content || '', - }, - }); - - const callBack = useCallback( - async (values: any) => { - // TODO: Implement save functionality - console.log('Save set functionality to be implemented', values); - modal.closeAll(); - reload(); - }, - [data] - ); - - const t = useT(); - - return ( - -
-
- - - -
- -