diff --git a/app/journal-pioneers/page.tsx b/app/journal-pioneers/page.tsx index 36a3ae585..494390de6 100644 --- a/app/journal-pioneers/page.tsx +++ b/app/journal-pioneers/page.tsx @@ -15,7 +15,7 @@ export const metadata: Metadata = buildOpenGraphMetadata({ export default function PioneersPage() { return ( - }> + }>
-

No journal entries yet

+

No Registered Reports yet

} renderEntry={({ diff --git a/app/journal/page.tsx b/app/journal/page.tsx index fb353bc1c..61ff3e5fb 100644 --- a/app/journal/page.tsx +++ b/app/journal/page.tsx @@ -202,7 +202,7 @@ export default function JournalNewPage() { rightSidebar={
- +
} > diff --git a/app/layouts/components/RightSidebarContainer.tsx b/app/layouts/components/RightSidebarContainer.tsx index 79a67d315..868cac5f8 100644 --- a/app/layouts/components/RightSidebarContainer.tsx +++ b/app/layouts/components/RightSidebarContainer.tsx @@ -2,7 +2,6 @@ import { ReactNode, Suspense } from 'react'; import { usePathname } from 'next/navigation'; -import { RHJRightSidebar } from '@/components/Journal/RHJRightSidebar'; import { cn } from '@/lib/utils'; import { useWorkTab } from '@/components/work/WorkHeader/WorkTabContext'; import { SwipeableDrawer } from '@/components/ui/SwipeableDrawer'; @@ -14,20 +13,12 @@ function getSidebarInstanceKey(pathname: string, rightSidebar: boolean | ReactNo return `custom:${pathname}`; } - if (pathname.startsWith('/paper/create')) { - return 'rhj-create'; - } - return 'default'; } function RightSidebarContent({ rightSidebar }: { rightSidebar: boolean | ReactNode }) { const pathname = usePathname(); - if (pathname.startsWith('/paper/create')) { - return ; - } - if (typeof rightSidebar === 'boolean') { return ; } diff --git a/app/layouts/topbar/pageRoutes.tsx b/app/layouts/topbar/pageRoutes.tsx index 961c236ac..cbac70e37 100644 --- a/app/layouts/topbar/pageRoutes.tsx +++ b/app/layouts/topbar/pageRoutes.tsx @@ -88,13 +88,6 @@ const ROUTE_RULES: RouteRule[] = [ icon: , }), }, - { - match: (p) => p.startsWith('/paper/create'), - getInfo: () => ({ - title: 'Submit your paper', - icon: , - }), - }, { match: (p) => p.startsWith('/earn'), getInfo: () => ({ diff --git a/app/paper/create/PaperActionCard.tsx b/app/paper/create/PaperActionCard.tsx deleted file mode 100644 index 4b82e933c..000000000 --- a/app/paper/create/PaperActionCard.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { LucideIcon } from 'lucide-react'; - -interface PaperActionCardProps { - icon: LucideIcon; - title: string; - description: string; - onClick: () => void; - badge?: string; -} - -export function PaperActionCard({ - icon: Icon, - title, - description, - onClick, - badge, -}: PaperActionCardProps) { - return ( - - ); -} diff --git a/app/paper/create/components/DeclarationCheckbox.tsx b/app/paper/create/components/DeclarationCheckbox.tsx deleted file mode 100644 index 3997c1b1c..000000000 --- a/app/paper/create/components/DeclarationCheckbox.tsx +++ /dev/null @@ -1,30 +0,0 @@ -'use client'; - -import { Checkbox } from '@/components/ui/form/Checkbox'; -import { ReactNode } from 'react'; - -interface DeclarationCheckboxProps { - id: string; - checked: boolean; - onChange: (checked: boolean) => void; - label: string; - description?: ReactNode; - error?: string | null; -} - -export function DeclarationCheckbox({ - id, - checked, - onChange, - label, - description, - error, -}: DeclarationCheckboxProps) { - return ( -
- - {description &&
{description}
} - {error &&
{error}
} -
- ); -} diff --git a/app/paper/create/page.tsx b/app/paper/create/page.tsx deleted file mode 100644 index f31077623..000000000 --- a/app/paper/create/page.tsx +++ /dev/null @@ -1,211 +0,0 @@ -'use client'; - -import { useState, useTransition } from 'react'; -import { useRouter } from 'next/navigation'; -import { Search } from '@/components/Search/Search'; -import { Button } from '@/components/ui/Button'; -import { PageLayout } from '@/app/layouts/PageLayout'; -import { PageHeader } from '@/components/ui/PageHeader'; -import { RadioGroup } from '@headlessui/react'; -import { FileUp, Eye, BadgeCheck, BookOpen, Loader2 } from 'lucide-react'; -import type { SearchSuggestion } from '@/types/search'; -import { PaperActionCard } from './PaperActionCard'; -import { Alert } from '@/components/ui/Alert'; -import { Callout } from '@/components/ui/Callout'; - -type PublishOption = 'pdf' | null; - -interface SelectedPaper { - id: string; - title: string; - authors: string[]; - abstract?: string; - doi?: string; - slug?: string; - displayName: string; - openalexId: string; -} - -export default function WorkCreatePage() { - const router = useRouter(); - const [isPending, startTransition] = useTransition(); - const [selectedPaper, setSelectedPaper] = useState(null); - const [showSuggestions, setShowSuggestions] = useState(true); - const showNewBadge = true; // This can be controlled by a feature flag or other logic - - const handlePaperSelect = (paper: SearchSuggestion) => { - // Only handle paper suggestions - if (paper.entityType === 'paper') { - setSelectedPaper({ - id: paper.id?.toString() || paper.openalexId, - title: paper.displayName, - authors: paper.authors, - abstract: undefined, - slug: paper.slug || paper.id?.toString() || paper.openalexId, - displayName: paper.displayName, - openalexId: paper.openalexId, - }); - setShowSuggestions(false); - } else { - console.log('User suggestion selected:', paper); - } - }; - - const publishOptions = [ - { - id: 'pdf', - title: 'Upload your manuscript', - description: 'Upload your existing manuscript', - icon: FileUp, - }, - ]; - - const handleOptionClick = (optionId: PublishOption) => { - startTransition(() => { - if (optionId === 'pdf') { - router.push('/paper/create/pdf'); - } - }); - }; - - return ( - -
- {/* Header */} -
-
- -
-

Submit your paper

-

- Publish your original work as a preprint natively on ResearchHub or as a publication in - the ResearchHub Journal. -

-
- - {selectedPaper ? ( -
- {/* Selected Paper Preview */} -
-
Selected Paper
-

{selectedPaper.title}

- {selectedPaper.authors && selectedPaper.authors.length > 0 && ( -

- {selectedPaper.authors.slice(0, 3).join(', ')} - {selectedPaper.authors.length > 3 ? ', et al.' : ''} -

- )} - {selectedPaper.doi && ( -

DOI: {selectedPaper.doi}

- )} -
- -
- router.push(`/paper/${selectedPaper.id}/${selectedPaper.slug}`)} - /> - - - router.push(`/paper/${selectedPaper.id}/${selectedPaper.slug}/claim`) - } - /> - - - router.push(`/paper/${selectedPaper.id}/${selectedPaper.slug}/publish`) - } - badge={showNewBadge ? 'NEW' : undefined} - /> -
- -
- -
-
- ) : ( -
- {/* -
-

- Do you have a preprint published? -

-

- Search by DOI or title to find your published preprint -

- -
- -
- -
-
-
-
-
- or -
-
-
- */} - -
-

Choose one:

-

- Select how you would like to submit your research to ResearchHub. -

-
- {publishOptions.map((option) => { - const Icon = option.icon; - return ( -
- - {option.id === 'pdf' && ( -
- -
- )} -
- ); - })} -
-
-
- )} -
- - ); -} diff --git a/app/paper/create/pdf/page.tsx b/app/paper/create/pdf/page.tsx deleted file mode 100644 index 5f272e25f..000000000 --- a/app/paper/create/pdf/page.tsx +++ /dev/null @@ -1,702 +0,0 @@ -'use client'; - -import { useState } from 'react'; -import { useRouter } from 'next/navigation'; -import { PageLayout } from '@/app/layouts/PageLayout'; -import { PageHeader } from '@/components/ui/PageHeader'; -import { Button } from '@/components/ui/Button'; -import { Textarea } from '@/components/ui/form/Textarea'; -import { Input } from '@/components/ui/form/Input'; -import { FileUpload } from '@/components/ui/form/FileUpload'; -import { SimpleStepProgress, SimpleStep } from '@/components/ui/SimpleStepProgress'; -import { AuthorsAndAffiliations, SelectedAuthor } from '../components/AuthorsAndAffiliations'; -import { HubsSelector, Hub } from '../components/HubsSelector'; -import { DeclarationCheckbox } from '../components/DeclarationCheckbox'; -import { - ArrowLeft, - ArrowRight, - Check, - BookOpen, - FileText, - FileUp, - Users, - Tags, - X, -} from 'lucide-react'; -import { UploadFileResult } from '@/services/file.service'; -import { PaperService, CreatePaperPayload } from '@/services/paper.service'; -import { ApiError } from '@/services/types'; -import toast from 'react-hot-toast'; -import { Switch } from '@/components/ui/Switch'; -import { AvatarStack } from '@/components/ui/AvatarStack'; -import { useScreenSize } from '@/hooks/useScreenSize'; -import { Callout } from '@/components/ui/Callout'; - -// Define the steps of our flow -const steps: SimpleStep[] = [ - { - id: 'content', - name: 'Content & Authors', - description: 'Upload paper, add details and authors', - }, - { id: 'declaration', name: 'Declaration', description: 'Legal requirements and permissions' }, - { id: 'preview', name: 'Submission', description: 'Review your submission' }, -]; - -export default function UploadPDFPage() { - const router = useRouter(); - const [currentStepIndex, setCurrentStepIndex] = useState(0); - const { smAndDown } = useScreenSize(); - - // Form state - const [title, setTitle] = useState(''); - const [abstract, setAbstract] = useState(''); - const [selectedFile, setSelectedFile] = useState(null); - const [authors, setAuthors] = useState([]); - const [selectedHubs, setSelectedHubs] = useState([]); - const [changeDescription, setChangeDescription] = useState('Initial submission'); - const [fileUploadResult, setFileUploadResult] = useState(null); - - // Declarations - const [acceptedTerms, setAcceptedTerms] = useState(false); - const [acceptedLicense, setAcceptedLicense] = useState(false); - const [acceptedAuthorship, setAcceptedAuthorship] = useState(false); - const [acceptedOriginality, setAcceptedOriginality] = useState(false); - - // Validation errors - const [errors, setErrors] = useState>({}); - - // Submission state - const [isSubmitting, setIsSubmitting] = useState(false); - - // Journal submission state - const [submitToJournal, setSubmitToJournal] = useState(true); - - // Journal editors avatars - const journalEditors = [ - { - src: 'https://www.researchhub.com/static/editorial-board/MaulikDhandha.jpeg', - alt: 'Maulik Dhandha', - tooltip: 'Maulik Dhandha, Editor', - }, - { - src: 'https://www.researchhub.com/static/editorial-board/EmilioMerheb.jpeg', - alt: 'Emilio Merheb', - tooltip: 'Emilio Merheb, Editor', - }, - { - src: 'https://storage.prod.researchhub.com/uploads/author_profile_images/2024/05/07/blob_48esqmw', - alt: 'Journal Editor', - tooltip: 'Editorial Board Member', - }, - { - src: 'https://storage.prod.researchhub.com/uploads/author_profile_images/2025/03/04/blob_pxj9rsH', - alt: 'Journal Editor', - tooltip: 'Editorial Board Member', - }, - { - src: 'https://storage.prod.researchhub.com/uploads/author_profile_images/2024/04/01/blob_Ut50nMY', - alt: 'Journal Editor', - tooltip: 'Editorial Board Member', - }, - { - src: 'https://storage.prod.researchhub.com/uploads/author_profile_images/2024/12/23/blob_oVmwyhP', - alt: 'Journal Editor', - tooltip: 'Editorial Board Member', - }, - { - src: 'https://storage.prod.researchhub.com/uploads/author_profile_images/2023/06/25/blob', - alt: 'Journal Editor', - tooltip: 'Editorial Board Member', - }, - ]; - - // Handlers with error clearing - const handleTitleChange = (e: React.ChangeEvent) => { - setTitle(e.target.value); - if (errors.title) { - setErrors({ ...errors, title: null }); - } - }; - - const handleAbstractChange = (e: React.ChangeEvent) => { - setAbstract(e.target.value); - if (errors.abstract) { - setErrors({ ...errors, abstract: null }); - } - }; - - const handleFileSelect = (file: File) => { - setSelectedFile(file); - if (errors.file) { - setErrors({ ...errors, file: null }); - } - }; - - const handleFileUpload = (result: UploadFileResult) => { - setFileUploadResult(result); - }; - - const handleFileRemove = () => { - setSelectedFile(null); - setFileUploadResult(null); - if (errors.file) { - setErrors({ ...errors, file: null }); - } - }; - - const handleAuthorsChange = (newAuthors: SelectedAuthor[]) => { - setAuthors(newAuthors); - if (errors.authors) { - setErrors({ ...errors, authors: null }); - } - }; - - const handleHubsChange = (newHubs: Hub[]) => { - setSelectedHubs(newHubs); - if (errors.hubs) { - setErrors({ ...errors, hubs: null }); - } - }; - - const handleTermsChange = (checked: boolean) => { - setAcceptedTerms(checked); - if (errors.terms) { - setErrors({ ...errors, terms: null }); - } - }; - - const handleLicenseChange = (checked: boolean) => { - setAcceptedLicense(checked); - if (errors.license) { - setErrors({ ...errors, license: null }); - } - }; - - const handleAuthorshipChange = (checked: boolean) => { - setAcceptedAuthorship(checked); - if (errors.authorship) { - setErrors({ ...errors, authorship: null }); - } - }; - - const handleOriginalityChange = (checked: boolean) => { - setAcceptedOriginality(checked); - if (errors.originality) { - setErrors({ ...errors, originality: null }); - } - }; - - const handleFileUploadError = (error: Error) => { - console.error('File upload error:', error); - setErrors({ - ...errors, - file: error.message || 'Failed to upload file. Please try again.', - }); - }; - - const handleSubmitToJournalChange = (checked: boolean) => { - setSubmitToJournal(checked); - }; - - const validateCurrentStep = () => { - const newErrors: Record = {}; - - switch (steps[currentStepIndex].id) { - case 'content': - if (!title.trim()) newErrors.title = 'Title is required'; - if (!abstract.trim()) newErrors.abstract = 'Abstract is required'; - if (!selectedFile) newErrors.file = 'Please upload a PDF file'; - if (selectedFile && !fileUploadResult) - newErrors.file = 'Please wait for the file to finish uploading'; - if (authors.length === 0) newErrors.authors = 'Please add at least one author'; - else if (!authors.some((author) => author.isCorrespondingAuthor)) { - newErrors.authors = 'Please designate at least one corresponding author'; - } - if (selectedHubs.length === 0) newErrors.hubs = 'Please select at least one topic'; - break; - - case 'declaration': - if (!acceptedTerms) newErrors.terms = 'You must accept the terms and conditions'; - if (!acceptedLicense) newErrors.license = 'You must accept the license agreement'; - if (!acceptedAuthorship) newErrors.authorship = 'You must confirm authorship'; - if (!acceptedOriginality) newErrors.originality = 'You must confirm originality'; - break; - } - - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; - - const handleNext = () => { - if (validateCurrentStep()) { - if (currentStepIndex < steps.length - 1) { - setCurrentStepIndex(currentStepIndex + 1); - globalThis.scrollTo(0, 0); - } else { - handleSubmit(); - } - } - }; - - const handleBack = () => { - if (currentStepIndex > 0) { - setCurrentStepIndex(currentStepIndex - 1); - globalThis.scrollTo(0, 0); - } else { - router.back(); - } - }; - - const handleSubmit = async () => { - if (!validateCurrentStep() && currentStepIndex === steps.length - 1) { - toast.error('Please fix the errors before submitting.'); - return; - } - if (!fileUploadResult?.absoluteUrl) { - toast.error('File upload is not complete or failed.'); - setErrors({ ...errors, file: 'File upload is not complete or failed.' }); - return; - } - - const loadingToast = toast.loading('Submitting your paper...'); - setIsSubmitting(true); - - try { - const payload: CreatePaperPayload = { - title, - abstract, - fileUrl: fileUploadResult.absoluteUrl, - changeDescription, - authors: authors.map((selectedAuthor, index) => { - const id = - typeof selectedAuthor.author.id === 'number' - ? selectedAuthor.author.id - : parseInt(String(selectedAuthor.author.id), 10); - - return { - id, - author_position: - index === 0 ? 'first' : index === authors.length - 1 ? 'last' : 'middle', - institution_id: undefined, - isCorrespondingAuthor: selectedAuthor.isCorrespondingAuthor, - }; - }), - hubs: selectedHubs.map((hub) => - typeof hub.id === 'number' ? hub.id : parseInt(String(hub.id), 10) - ), - declarations: { - termsAccepted: acceptedTerms, - licenseAccepted: acceptedLicense, - authorshipConfirmed: acceptedAuthorship, - originalityConfirmed: acceptedOriginality, - }, - }; - - const response = await PaperService.create(payload); - - toast.dismiss(loadingToast); - const isPending = response.status === 'PENDING'; - toast.success( - isPending - ? 'Paper submitted and is pending moderator review.' - : 'Paper submitted successfully!' - ); - - const statusParam = `&status=${response.status ?? ''}`; - if (submitToJournal) { - try { - const successUrl = `${globalThis.location.origin}/paper/create/success?paperId=${response.id}&paperTitle=${encodeURIComponent(response.title)}&isJournal=true${statusParam}`; - const failureUrl = `${globalThis.location.origin}/`; - - const checkoutData = await PaperService.payForJournalSubmission( - response.id, - successUrl, - failureUrl - ); - - if (checkoutData.url) { - globalThis.location.href = checkoutData.url; - return; - } else { - throw new Error('No checkout URL received from server'); - } - } catch (error) { - console.error('Checkout Error:', error); - toast.error('Failed to initiate payment. Please try again.'); - setIsSubmitting(false); - return; - } - } else { - router.push( - `/paper/create/success?paperId=${response.id}&paperTitle=${encodeURIComponent(response.title)}&isJournal=${submitToJournal}${statusParam}` - ); - } - } catch (error) { - console.error('Submission error:', error); - toast.dismiss(loadingToast); - const fallback = 'Failed to submit paper. Please try again.'; - if (error instanceof ApiError) { - const errorData = error.errors as Record | undefined; - toast.error(errorData?.msg || errorData?.message || errorData?.detail || fallback); - } else { - toast.error(fallback); - } - setIsSubmitting(false); - } - }; - - const renderStep = () => { - switch (steps[currentStepIndex].id) { - case 'content': - return ( -
-
-
- -

Paper Details

-
-

- Provide basic information about your paper -

-
- -
-
-