diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateDoc.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateDoc.tsx index ed03cf087..ab90fac3e 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateDoc.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/api/useCreateDoc.tsx @@ -6,9 +6,14 @@ import { Doc } from '../types'; import { KEY_LIST_DOC } from './useDocs'; -export const createDoc = async (): Promise => { +type CreateDocParams = { + title?: string; +} | void; + +export const createDoc = async (params: CreateDocParams): Promise => { const response = await fetchAPI(`documents/`, { method: 'POST', + body: JSON.stringify({ title: params?.title }), }); if (!response.ok) { @@ -25,7 +30,7 @@ interface CreateDocProps { export function useCreateDoc({ onSuccess, onError }: CreateDocProps) { const queryClient = useQueryClient(); - return useMutation({ + return useMutation({ mutationFn: createDoc, onSuccess: (data) => { void queryClient.resetQueries({ diff --git a/src/frontend/apps/impress/src/features/docs/doc-share/api/useUpdateDocLink.tsx b/src/frontend/apps/impress/src/features/docs/doc-share/api/useUpdateDocLink.tsx index 19fb33eb2..29dc34cb4 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-share/api/useUpdateDocLink.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-share/api/useUpdateDocLink.tsx @@ -1,17 +1,23 @@ import { VariantType, useToastProvider } from '@openfun/cunningham-react'; -import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + UseMutationOptions, + useMutation, + useQueryClient, +} from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { APIError, errorCauses, fetchAPI } from '@/api'; -import { Doc } from '@/docs/doc-management'; +import { Doc, LinkReach, LinkRole } from '@/docs/doc-management'; export type UpdateDocLinkParams = Pick & Partial>; +type UpdateDocLinkResponse = { link_role: LinkRole; link_reach: LinkReach }; + export const updateDocLink = async ({ id, ...params -}: UpdateDocLinkParams): Promise => { +}: UpdateDocLinkParams): Promise => { const response = await fetchAPI(`documents/${id}/link-configuration/`, { method: 'PUT', body: JSON.stringify({ @@ -26,26 +32,27 @@ export const updateDocLink = async ({ ); } - return response.json() as Promise; + return response.json() as Promise; }; -interface UpdateDocLinkProps { - onSuccess?: (data: Doc) => void; +type UseUpdateDocLinkOptions = UseMutationOptions< + UpdateDocLinkResponse, + APIError, + UpdateDocLinkParams +> & { listInvalidQueries?: string[]; -} +}; -export function useUpdateDocLink({ - onSuccess, - listInvalidQueries, -}: UpdateDocLinkProps = {}) { +export function useUpdateDocLink(options?: UseUpdateDocLinkOptions) { const queryClient = useQueryClient(); const { toast } = useToastProvider(); const { t } = useTranslation(); - return useMutation({ + return useMutation({ mutationFn: updateDocLink, - onSuccess: (data) => { - listInvalidQueries?.forEach((queryKey) => { + ...options, + onSuccess: (data, variables, onMutateResult, context) => { + options?.listInvalidQueries?.forEach((queryKey) => { void queryClient.invalidateQueries({ queryKey: [queryKey], }); @@ -59,7 +66,7 @@ export function useUpdateDocLink({ }, ); - onSuccess?.(data); + options?.onSuccess?.(data, variables, onMutateResult, context); }, }); } diff --git a/src/frontend/apps/impress/src/pages/docs/new/index.tsx b/src/frontend/apps/impress/src/pages/docs/new/index.tsx new file mode 100644 index 000000000..d7b87c07c --- /dev/null +++ b/src/frontend/apps/impress/src/pages/docs/new/index.tsx @@ -0,0 +1,243 @@ +import { captureException } from '@sentry/nextjs'; +import Head from 'next/head'; +import { useSearchParams } from 'next/navigation'; +import { useRouter } from 'next/router'; +import { ReactElement, useCallback, useEffect } from 'react'; + +import { Loading } from '@/components'; +import { + LinkReach, + LinkRole, + Role, + useCreateDoc, +} from '@/features/docs/doc-management'; +import { + KEY_LIST_USER, + getUsers, + useCreateDocAccess, + useCreateDocInvitation, + useDocAccesses, + useUsers, +} from '@/features/docs/doc-share'; +import { useUpdateDocLink } from '@/features/docs/doc-share/api/useUpdateDocLink'; +import { useSkeletonStore } from '@/features/skeletons'; +import { MainLayout } from '@/layouts'; +import { NextPageWithLayout } from '@/types/next'; + +const Page: NextPageWithLayout = () => { + const { setIsSkeletonVisible } = useSkeletonStore(); + const router = useRouter(); + const searchParams = useSearchParams(); + const linkReach = searchParams.get('link-reach'); + const linkRole = searchParams.get('link-role'); + const title = searchParams.get('title'); + const members = searchParams.get('members'); + const { mutateAsync: createInvitation } = useCreateDocInvitation(); + const { mutateAsync: createDocAccess } = useCreateDocAccess(); + + const { + mutate: createDoc, + //isSuccess: isDocCreated, + data: doc, + } = useCreateDoc({ + onSuccess: (doc) => { + if ((linkReach && linkRole) || linkReach || members) { + return; + } + + redirectToDoc(doc.id); + }, + onError: () => {}, + }); + + const { mutate: updateDocLink } = useUpdateDocLink({ + onSuccess: (_, params) => { + if (members || !params.id) { + return; + } + + redirectToDoc(params.id); + }, + onError: (error, params) => { + captureException(error, { + extra: { + docId: params.id, + linkReach, + linkRole, + }, + }); + + if (params.id) { + redirectToDoc(params.id); + } + }, + }); + + const redirectToDoc = useCallback( + (docId: string) => { + void router.push(`/docs/${docId}`); + }, + [router], + ); + + useEffect(() => { + setIsSkeletonVisible(true); + }, [setIsSkeletonVisible]); + + // Doc creation effect + useEffect(() => { + if (doc) { + return; + } + + createDoc({ + title: title || undefined, + }); + }, [createDoc, doc, title]); + + // Doc link update effect + useEffect(() => { + if (!linkReach || !doc) { + return; + } + + updateDocLink({ + id: doc.id, + link_reach: linkReach as LinkReach, + link_role: (linkRole as LinkRole | undefined) || undefined, + }); + }, [linkReach, doc, updateDocLink, redirectToDoc, linkRole]); + + // const onInvite = async () => { + // setIsLoading(true); + // const promises = selectedUsers.map((user) => { + // const isInvitationMode = user.id === user.email; + + // const payload = { + // role: invitationRole, + // docId: doc.id, + // }; + + // return isInvitationMode + // ? createInvitation({ + // ...payload, + // email: user.email.toLowerCase(), + // }) + // : createDocAccess({ + // ...payload, + // memberId: user.id, + // }); + // }); + + // const settledPromises = await Promise.allSettled(promises); + // settledPromises.forEach((settledPromise) => { + // if (settledPromise.status === 'rejected') { + // onError(settledPromise.reason as APIErrorUser); + // } + // }); + // afterInvite?.(); + // setIsLoading(false); + // }; + + // members=user%40example.org%2Ceditor%7Cuser2%40example.org%2Creader + // members=user@example.org,editor|user2@example.org,reader + useEffect(() => { + if (!members || !doc) { + return; + } + + console.log('members', members); + const membersList = members.split('|').map((memberStr) => { + const [email, role] = memberStr.split(','); + return { email, role: role as Role }; + }); + + console.log('membersList', membersList); + + for (const member of membersList) { + getUsers({ + query: member.email, + docId: doc.id, + }) + .then((users) => { + if (users.length > 0) { + console.log('User exists:', users); + // User exists, create doc access + // createDocAccess({ + // role: member.role, + // docId: doc.id, + // memberId: users[0].id, + // }).catch(() => { + // // Ignore errors + // }); + } else { + console.log('User does not exist:', { + role: member.role, + docId: doc.id, + email: member.email.toLowerCase(), + }); + // User does not exist, create invitation + // createInvitation({ + // role: member.role, + // docId: doc.id, + // email: member.email.toLowerCase(), + // }).catch(() => { + // // Ignore errors + // }); + } + }) + .catch(() => { + // Ignore errors + }); + + // const isInvitationMode = !member.email.match( + // /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/, + // ); + + // const payload = { + // role: member.role, + // docId: doc.id, + // }; + + // if (isInvitationMode) { + // createInvitation({ + // ...payload, + // email: member.email.toLowerCase(), + // }).catch(() => { + // // Ignore errors + // }); + // } else { + // createDocAccess({ + // ...payload, + // memberId: member.email, + // }).catch(() => { + // // Ignore errors + // }); + // } + } + + //redirectToDoc(doc.id); + + //getUsers + }, [createDocAccess, createInvitation, doc, members]); + + if (!linkReach && linkRole) { + console.warn('link-reach parameter is missing'); + } + + return ; +}; + +Page.getLayout = function getLayout(page: ReactElement) { + return ( + <> + + + + + {page} + + ); +}; + +export default Page;