| 'use client'; |
| import 'reflect-metadata'; |
|
|
| import React, { FC, Fragment, useCallback, useMemo, useState } from 'react'; |
| import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; |
| import useSWR from 'swr'; |
| import { useUser } from '@gitroom/frontend/components/layout/user.context'; |
| import { Button } from '@gitroom/react/form/button'; |
| import { Input } from '@gitroom/react/form/input'; |
| import { useToaster } from '@gitroom/react/toaster/toaster'; |
| import clsx from 'clsx'; |
| import { deleteDialog } from '@gitroom/react/helpers/delete.dialog'; |
| import { useT } from '@gitroom/react/translation/get.transation.service.client'; |
| import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; |
| import { newDayjs } from '@gitroom/frontend/components/layout/set.timezone'; |
| import { useModals } from '@gitroom/frontend/components/layout/new-modal'; |
|
|
| const SaveSetModal: FC<{ |
| postData: any; |
| initialValue?: string; |
| onSave: (name: string) => void; |
| onCancel: () => void; |
| }> = ({ postData, onSave, onCancel, initialValue }) => { |
| const [name, setName] = useState(initialValue); |
| const t = useT(); |
|
|
| const handleSubmit = (e: React.FormEvent) => { |
| e.preventDefault(); |
| if (name.trim()) { |
| onSave(name.trim()); |
| } |
| }; |
|
|
| return ( |
| <form onSubmit={handleSubmit} className="flex flex-col gap-4"> |
| <div> |
| <Input |
| label="Set Name" |
| translationKey="label_set_name" |
| name="setName" |
| value={name} |
| disableForm={true} |
| onChange={(e) => setName(e.target.value)} |
| placeholder="Enter a name for this set" |
| autoFocus |
| /> |
| </div> |
| <div className="flex gap-2 justify-end"> |
| <Button type="button" secondary onClick={onCancel}> |
| {t('cancel', 'Cancel')} |
| </Button> |
| <Button type="submit" disabled={!name.trim()}> |
| {t('save', 'Save')} |
| </Button> |
| </div> |
| </form> |
| ); |
| }; |
|
|
| export const Sets: FC = () => { |
| const fetch = useFetch(); |
| const user = useUser(); |
| const modal = useModals(); |
| const toaster = useToaster(); |
|
|
| const load = useCallback(async (path: string) => { |
| return (await (await fetch(path)).json()).integrations; |
| }, []); |
|
|
| const { isLoading, data: integrations } = useSWR('/integrations/list', load, { |
| revalidateOnFocus: false, |
| revalidateOnReconnect: false, |
| revalidateIfStale: false, |
| revalidateOnMount: true, |
| refreshWhenHidden: false, |
| refreshWhenOffline: false, |
| fallbackData: [], |
| }); |
|
|
| const list = useCallback(async () => { |
| return (await fetch('/sets')).json(); |
| }, []); |
|
|
| const { data, mutate } = useSWR('sets', list, { |
| revalidateOnFocus: false, |
| revalidateOnReconnect: false, |
| revalidateIfStale: false, |
| revalidateOnMount: true, |
| refreshWhenHidden: false, |
| refreshWhenOffline: false, |
| }); |
|
|
| const addSet = useCallback( |
| (params?: { id?: string; name?: string; content?: string }) => () => { |
| modal.openModal({ |
| id: 'add-edit-modal', |
| closeOnClickOutside: false, |
| removeLayout: true, |
| closeOnEscape: false, |
| withCloseButton: false, |
| askClose: true, |
| fullScreen: true, |
| classNames: { |
| modal: 'w-[100%] max-w-[1400px] text-textColor', |
| }, |
| children: ( |
| <AddEditModal |
| allIntegrations={integrations.map((p: any) => ({ |
| ...p, |
| }))} |
| {...(params?.id ? { set: JSON.parse(params.content) } : {})} |
| addEditSets={(data) => { |
| modal.openModal({ |
| title: 'Save as Set', |
| children: ( |
| <SaveSetModal |
| initialValue={params?.name || ''} |
| postData={data} |
| onSave={async (name: string) => { |
| 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()} |
| /> |
| ), |
| }); |
| }} |
| reopenModal={() => {}} |
| mutate={() => {}} |
| integrations={integrations} |
| date={newDayjs()} |
| /> |
| ), |
| title: ``, |
| }); |
| }, |
| [integrations] |
| ); |
|
|
| const deleteSet = useCallback( |
| (data: any) => async () => { |
| if (await deleteDialog(`Are you sure you want to delete ${data.name}?`)) { |
| await fetch(`/sets/${data.id}`, { |
| method: 'DELETE', |
| }); |
| mutate(); |
| toaster.show('Set deleted successfully', 'success'); |
| } |
| }, |
| [] |
| ); |
|
|
| const t = useT(); |
|
|
| return ( |
| <div className="flex flex-col"> |
| <h3 className="text-[20px]">Sets ({data?.length || 0})</h3> |
| <div className="text-customColor18 mt-[4px]"> |
| Manage your content sets for easy reuse across posts. |
| </div> |
| <div className="my-[16px] mt-[16px] bg-sixth border-fifth items-center border rounded-[4px] p-[24px] flex gap-[24px]"> |
| <div className="flex flex-col w-full"> |
| {!!data?.length && ( |
| <div className="grid grid-cols-[2fr,1fr,1fr] w-full gap-y-[10px]"> |
| <div>{t('name', 'Name')}</div> |
| <div>{t('edit', 'Edit')}</div> |
| <div>{t('delete', 'Delete')}</div> |
| {data?.map((p: any) => ( |
| <Fragment key={p.id}> |
| <div className="flex flex-col justify-center">{p.name}</div> |
| <div className="flex flex-col justify-center"> |
| <div> |
| <Button onClick={addSet(p)}>{t('edit', 'Edit')}</Button> |
| </div> |
| </div> |
| <div className="flex flex-col justify-center"> |
| <div> |
| <Button onClick={deleteSet(p)}> |
| {t('delete', 'Delete')} |
| </Button> |
| </div> |
| </div> |
| </Fragment> |
| ))} |
| </div> |
| )} |
| <div> |
| <Button |
| onClick={addSet()} |
| className={clsx((data?.length || 0) > 0 && 'my-[16px]')} |
| > |
| Add a set |
| </Button> |
| </div> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |
|
|