From 722460ce9555a7975048bf67a202e32efe34530d Mon Sep 17 00:00:00 2001 From: Nevo David Date: Fri, 4 Jul 2025 19:47:47 +0700 Subject: [PATCH] feat: generate api modal --- .../app/(app)/auth/login-required/page.tsx | 7 + .../src/components/media/media.component.tsx | 78 ++++--- .../components/new-launch/add.edit.modal.tsx | 5 +- .../new-launch/dummy.code.component.tsx | 48 +++++ .../src/components/new-launch/editor.tsx | 6 + .../components/new-launch/manage.modal.tsx | 191 +++++++++++------- .../providers/high.order.provider.tsx | 4 +- .../src/components/new-launch/store.ts | 7 + .../standalone-modal/standalone.modal.tsx | 11 +- apps/frontend/src/middleware.ts | 4 + 10 files changed, 246 insertions(+), 115 deletions(-) create mode 100644 apps/frontend/src/app/(app)/auth/login-required/page.tsx create mode 100644 apps/frontend/src/components/new-launch/dummy.code.component.tsx diff --git a/apps/frontend/src/app/(app)/auth/login-required/page.tsx b/apps/frontend/src/app/(app)/auth/login-required/page.tsx new file mode 100644 index 00000000..9e167388 --- /dev/null +++ b/apps/frontend/src/app/(app)/auth/login-required/page.tsx @@ -0,0 +1,7 @@ +export default async function LoginRequiredPage() { + return ( +
+ Login to use the wizard to generate API code +
+ ); +} diff --git a/apps/frontend/src/components/media/media.component.tsx b/apps/frontend/src/components/media/media.component.tsx index bbc4ccbe..865f64dc 100644 --- a/apps/frontend/src/components/media/media.component.tsx +++ b/apps/frontend/src/components/media/media.component.tsx @@ -463,6 +463,7 @@ export const MediaBox: FC<{ export const MultiMediaComponent: FC<{ label: string; description: string; + dummy: boolean; allData: { content: string; id?: string; @@ -492,8 +493,17 @@ export const MultiMediaComponent: FC<{ }; }) => void; }> = (props) => { - const { onOpen, onClose, name, error, text, onChange, value, allData } = - props; + const { + onOpen, + onClose, + name, + error, + text, + onChange, + value, + allData, + dummy, + } = props; const user = useUser(); useEffect(() => { if (value) { @@ -566,7 +576,7 @@ export const MultiMediaComponent: FC<{ <>
{modal && } - {mediaModal && !!user?.tier?.ai && ( + {mediaModal && !!user?.tier?.ai && !dummy && ( )} @@ -668,40 +678,42 @@ export const MultiMediaComponent: FC<{ )}
-
-
-
- + - + - {!!user?.tier?.ai && ( - - )} + {!!user?.tier?.ai && ( + + )} +
- + )}
{error}
diff --git a/apps/frontend/src/components/new-launch/add.edit.modal.tsx b/apps/frontend/src/components/new-launch/add.edit.modal.tsx index aae6811a..151a24b2 100644 --- a/apps/frontend/src/components/new-launch/add.edit.modal.tsx +++ b/apps/frontend/src/components/new-launch/add.edit.modal.tsx @@ -11,6 +11,7 @@ import { useShallow } from 'zustand/react/shallow'; import { useExistingData } from '@gitroom/frontend/components/launches/helpers/use.existing.data'; export interface AddEditModalProps { + dummy?: boolean; date: dayjs.Dayjs; integrations: Integrations[]; allIntegrations?: Integrations[]; @@ -33,16 +34,18 @@ export interface AddEditModalProps { } export const AddEditModal: FC = (props) => { - const { setAllIntegrations, setDate, setIsCreateSet } = useLaunchStore( + const { setAllIntegrations, setDate, setIsCreateSet, setDummy } = useLaunchStore( useShallow((state) => ({ setAllIntegrations: state.setAllIntegrations, setDate: state.setDate, setIsCreateSet: state.setIsCreateSet, + setDummy: state.setDummy, })) ); const integrations = useLaunchStore((state) => state.integrations); useEffect(() => { + setDummy(!!props.dummy); setDate(props.date || dayjs()); setAllIntegrations(props.allIntegrations || []); setIsCreateSet(!!props.addEditSets); diff --git a/apps/frontend/src/components/new-launch/dummy.code.component.tsx b/apps/frontend/src/components/new-launch/dummy.code.component.tsx new file mode 100644 index 00000000..13cdef0e --- /dev/null +++ b/apps/frontend/src/components/new-launch/dummy.code.component.tsx @@ -0,0 +1,48 @@ +import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; +import { useModals } from '@mantine/modals'; +import React, { FC } from 'react'; +import { Button } from '@gitroom/react/form/button'; +import copy from 'copy-to-clipboard'; +import { useToaster } from '@gitroom/react/toaster/toaster'; + +export const DummyCodeComponent: FC<{ code: any }> = ({ code }) => { + const modal = useModals(); + const toaster = useToaster(); + + return ( +
+ + + + +
{JSON.stringify(code, null, 2)}
+
+ ); +}; diff --git a/apps/frontend/src/components/new-launch/editor.tsx b/apps/frontend/src/components/new-launch/editor.tsx index f3a9fd39..01f8c6e0 100644 --- a/apps/frontend/src/components/new-launch/editor.tsx +++ b/apps/frontend/src/components/new-launch/editor.tsx @@ -59,6 +59,7 @@ export const EditorWrapper: FC<{ internalFromAll, totalChars, postComment, + dummy, } = useLaunchStore( useShallow((state) => ({ internal: state.internal.find((p) => p.integration.id === state.current), @@ -66,6 +67,7 @@ export const EditorWrapper: FC<{ global: state.global, current: state.current, addRemoveInternal: state.addRemoveInternal, + dummy: state.dummy, setInternalValueText: state.setInternalValueText, setGlobalValueText: state.setGlobalValueText, addInternalValue: state.addInternalValue, @@ -295,6 +297,7 @@ export const EditorWrapper: FC<{ identifier={internalFromAll?.identifier || 'global'} totalChars={totalChars} appendImages={appendImages(index)} + dummy={dummy} />
@@ -373,6 +376,7 @@ export const Editor: FC<{ validateChars?: boolean; identifier?: string; totalChars?: number; + dummy: boolean; }> = (props) => { const { allValues, @@ -383,6 +387,7 @@ export const Editor: FC<{ validateChars, identifier, appendImages, + dummy, } = props; const user = useUser(); const [id] = useState(makeId(10)); @@ -536,6 +541,7 @@ export const Editor: FC<{ label={t('attachments', 'Attachments')} description="" value={props.pictures} + dummy={dummy} name="image" onChange={(value) => { setImages(value.target.value); diff --git a/apps/frontend/src/components/new-launch/manage.modal.tsx b/apps/frontend/src/components/new-launch/manage.modal.tsx index c6d2b899..542c68b7 100644 --- a/apps/frontend/src/components/new-launch/manage.modal.tsx +++ b/apps/frontend/src/components/new-launch/manage.modal.tsx @@ -28,6 +28,7 @@ import useKeypress from 'react-use-keypress'; import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component'; import { SelectCustomer } from '@gitroom/frontend/components/launches/select.customer'; import { CopilotPopup } from '@copilotkit/react-ui'; +import { DummyCodeComponent } from '@gitroom/frontend/components/new-launch/dummy.code.component'; function countCharacters(text: string, type: string): number { if (type !== 'x') { @@ -46,7 +47,7 @@ export const ManageModal: FC = (props) => { const modal = useModals(); usePreventWindowUnload(true); - const { addEditSets, mutate, customClose } = props; + const { addEditSets, mutate, customClose, dummy } = props; const { selectedIntegrations, @@ -98,7 +99,7 @@ export const ManageModal: FC = (props) => { }, [existingData, mutate, modal]); const askClose = useCallback(async () => { - if (!activateExitButton) { + if (!activateExitButton || dummy) { return; } @@ -117,7 +118,7 @@ export const ManageModal: FC = (props) => { } modal.closeAll(); } - }, [activateExitButton]); + }, [activateExitButton, dummy]); const changeCustomer = useCallback( (customer: string) => { @@ -212,16 +213,18 @@ export const ManageModal: FC = (props) => { } } - const shortLinkUrl = await ( - await fetch('/posts/should-shortlink', { - method: 'POST', - body: JSON.stringify({ - messages: checkAllValid.flatMap((p: any) => - p.values.flatMap((a: any) => a.content) - ), - }), - }) - ).json(); + const shortLinkUrl = dummy + ? { ask: false } + : await ( + await fetch('/posts/should-shortlink', { + method: 'POST', + body: JSON.stringify({ + messages: checkAllValid.flatMap((p: any) => + p.values.flatMap((a: any) => a.content) + ), + }), + }) + ).json(); const shortLink = !shortLinkUrl.ask ? false @@ -233,48 +236,68 @@ export const ManageModal: FC = (props) => { const group = existingData.group || makeId(10); const data = { type, - inter: repeater || undefined, + ...(repeater ? { inter: repeater } : {}), tags, shortLink, date: date.utc().format('YYYY-MM-DDTHH:mm:ss'), - posts: checkAllValid.map((p: any) => ({ - integration: p.integration, + posts: checkAllValid.map((post: any) => ({ + integration: { + id: post.integration.id, + }, group, - settings: { ...(p.settings || {}) }, - value: p.values.map((a: any) => ({ - ...a, - image: a.media || [], - content: a.content.slice(0, p.maximumCharacters || 1000000), + settings: { ...(post.settings || {}) }, + value: post.values.map((value: any) => ({ + ...(value.id ? { id: value.id } : {}), + content: value.content.slice(0, post.maximumCharacters || 1000000), + image: value.media.map(({ id, path }: any) => ({ id, path })) || [], })), })), }; - addEditSets - ? addEditSets(data) - : await fetch('/posts', { - method: 'POST', - body: JSON.stringify(data), - }); + if (dummy) { + modal.openModal({ + title: '', + children: , + classNames: { + modal: 'w-[100%] bg-transparent text-textColor', + }, + size: '100%', + withCloseButton: false, + closeOnEscape: true, + closeOnClickOutside: true, + }); - if (!addEditSets) { - mutate(); - toaster.show( - !existingData.integration - ? 'Added successfully' - : 'Updated successfully' - ); - } - if (customClose) { - setTimeout(() => { - customClose(); - }, 2000); + setLoading(false); } - if (!addEditSets) { - modal.closeAll(); + if (!dummy) { + addEditSets + ? addEditSets(data) + : await fetch('/posts', { + method: 'POST', + body: JSON.stringify(data), + }); + + if (!addEditSets) { + mutate(); + toaster.show( + !existingData.integration + ? 'Added successfully' + : 'Updated successfully' + ); + } + if (customClose) { + setTimeout(() => { + customClose(); + }, 2000); + } + + if (!addEditSets) { + modal.closeAll(); + } } }, - [ref, repeater, tags, date, addEditSets] + [ref, repeater, tags, date, addEditSets, dummy] ); return ( @@ -292,13 +315,17 @@ export const ManageModal: FC = (props) => {
- + {!dummy && ( + + )}
@@ -331,7 +358,7 @@ export const ManageModal: FC = (props) => { )} - {!addEditSets && ( + {!addEditSets && !dummy && (
-
- - - -
- {t('post_now', 'Post now')} + {!dummy && ( +
+ + + +
+ {t('post_now', 'Post now')} +
-
+ )}
)} @@ -417,17 +448,21 @@ export const ManageModal: FC = (props) => {
- setTags(e.target.value)} - /> + {!dummy && ( + setTags(e.target.value)} + /> + )}
- + {!dummy && ( + + )}
(params: { justCurrent, allIntegrations, setPostComment, + dummy, } = useLaunchStore( useShallow((state) => ({ date: state.date, tab: state.tab, setTab: state.setTab, global: state.global, + dummy: state.dummy, internal: state.internal.find((p) => p.integration.id === props.id), integrations: state.selectedIntegrations, allIntegrations: state.integrations, @@ -304,7 +306,7 @@ export const withProvider = function (params: { {(SettingsComponent || !!data?.internalPlugs?.length) && (
- {!!data?.internalPlugs?.length && ( + {!!data?.internalPlugs?.length && !dummy && ( )}
diff --git a/apps/frontend/src/components/new-launch/store.ts b/apps/frontend/src/components/new-launch/store.ts index d00b22a7..c229f517 100644 --- a/apps/frontend/src/components/new-launch/store.ts +++ b/apps/frontend/src/components/new-launch/store.ts @@ -27,6 +27,7 @@ interface SelectedIntegrations { interface StoreState { date: dayjs.Dayjs; postComment: PostComment; + dummy: boolean; repeater?: number; isCreateSet: boolean; totalChars: number; @@ -116,9 +117,11 @@ interface StoreState { ) => void; setPostComment: (postComment: PostComment) => void; setActivateExitButton?: (activateExitButton: boolean) => void; + setDummy: (dummy: boolean) => void; } const initialState = { + dummy: false, activateExitButton: true, date: dayjs(), postComment: PostComment.ALL, @@ -505,4 +508,8 @@ export const useLaunchStore = create()((set) => ({ set((state) => ({ activateExitButton, })), + setDummy: (dummy: boolean) => + set((state) => ({ + dummy, + })), })); diff --git a/apps/frontend/src/components/standalone-modal/standalone.modal.tsx b/apps/frontend/src/components/standalone-modal/standalone.modal.tsx index c3037fbb..da781301 100644 --- a/apps/frontend/src/components/standalone-modal/standalone.modal.tsx +++ b/apps/frontend/src/components/standalone-modal/standalone.modal.tsx @@ -5,17 +5,23 @@ import { FC, useCallback } from 'react'; import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import dayjs from 'dayjs'; -import { usePathname } from 'next/navigation'; +import { useParams } from 'next/navigation'; import { AddEditModal } from '@gitroom/frontend/components/new-launch/add.edit.modal'; export const StandaloneModal: FC = () => { const fetch = useFetch(); - const params = usePathname(); + const params = useParams<{ platform: string }>(); + const load = useCallback(async (path: string) => { return (await (await fetch(path)).json()).integrations; }, []); + const loadDate = useCallback(async () => { + if (params.platform === 'all') { + return dayjs().utc().format('YYYY-MM-DDTHH:mm:ss'); + } return (await (await fetch('/posts/find-slot')).json()).date; }, []); + const { isLoading, data: integrations, @@ -31,6 +37,7 @@ export const StandaloneModal: FC = () => { } return ( { window.parent.postMessage( { diff --git a/apps/frontend/src/middleware.ts b/apps/frontend/src/middleware.ts index de3ad3e3..9f07b1ef 100644 --- a/apps/frontend/src/middleware.ts +++ b/apps/frontend/src/middleware.ts @@ -31,6 +31,10 @@ export async function middleware(request: NextRequest) { headers.set(headerName, lng); } + if (nextUrl.pathname.startsWith('/modal/') && !authCookie) { + return NextResponse.redirect(new URL(`/auth/login-required`, nextUrl.href)); + } + if ( nextUrl.pathname.startsWith('/uploads/') || nextUrl.pathname.startsWith('/p/') ||