Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AddStudiesForm, type AddStudiesFormState } from '../add-studies/AddStud
import type { MergedStudy } from '@/hooks/useAddStudies/deduplication';
import { GoogleDrivePickerModal } from '../google-drive/GoogleDrivePickerModal';
import { StudyCard } from './study-card/StudyCard';
import { StudiesExplainer } from './StudiesExplainer';
import { useProjectStore, selectConnectionPhase } from '@/stores/projectStore';
import { useAllStudies } from '@/project/workspace-data';
import { useAddStudies } from '@/hooks/useAddStudies';
Expand All @@ -19,7 +20,8 @@ import { useProjectContext } from '../ProjectContext';
import { saveFormState } from '@/lib/formStatePersistence.js';

export function AllStudiesTab() {
const { projectId, getMember, isOwner, openAssignSheet } = useProjectContext();
const { projectId, getMember, isOwner, openAssignSheet, setAddStudiesSheetOpen } =
useProjectContext();

const [showGoogleDriveModal, setShowGoogleDriveModal] = useState(false);
const [googleDriveTargetStudyId, setGoogleDriveTargetStudyId] = useState<string | null>(null);
Expand Down Expand Up @@ -96,6 +98,8 @@ export function AllStudiesTab() {
</div>
)}

{studies.length > 0 && <StudiesExplainer onAddStudies={() => setAddStudiesSheetOpen(true)} />}

{studies.length > 0 && (
<div className='mb-3 flex items-center justify-between'>
<span className='text-muted-foreground text-sm'>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/**
* StudiesExplainer - One-time orientation card above the study list that
* names the ways studies get into a project. Dismissal is per user, not
* per project, so a lead with several projects sees it once.
*/

import { useState } from 'react';
import { CloudUploadIcon, FileTextIcon, PaperclipIcon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useAuthStore, selectUser } from '@/stores/authStore';

const STORAGE_KEY_PREFIX = 'studiesExplainerDismissed:';

function readDismissed(key: string) {
try {
return localStorage.getItem(key) === 'true';
} catch {
return false;
}
}

export function StudiesExplainer({ onAddStudies }: { onAddStudies: () => void }) {
const user = useAuthStore(selectUser);
const storageKey = `${STORAGE_KEY_PREFIX}${user?.id ?? 'anonymous'}`;
const [dismissed, setDismissed] = useState(() => readDismissed(storageKey));

if (dismissed) return null;

const dismiss = () => {
setDismissed(true);
try {
localStorage.setItem(storageKey, 'true');
} catch {
// Private browsing can refuse writes; the card simply returns next visit.
}
};

return (
<div className='border-border bg-card mb-4 overflow-hidden rounded-xl border'>
<div className='bg-muted/50 flex h-10 items-center gap-2.5 border-b px-3.5'>
<span className='text-sm font-semibold'>Three ways to add studies</span>
<span className='text-muted-foreground hidden text-xs sm:inline'>
Shown once. Everything here is also behind the Add studies button.
</span>
<Button variant='outline' size='xs' className='ml-auto' onClick={dismiss}>
Got it
</Button>
</div>

<div className='grid grid-cols-1 sm:grid-cols-3'>
<Way
icon={<CloudUploadIcon />}
title='Drop PDFs anywhere on this page'
body='Drag one or many PDFs from your desktop onto the list. Each becomes a study, with the title, first author, year, and DOI read from the file for you to check before adding.'
/>
<Way
icon={<FileTextIcon />}
title='Import from your reference manager'
body={
<>
Use{' '}
<button type='button' onClick={onAddStudies} className='text-primary font-medium'>
Add studies
</button>{' '}
to bring in a RIS, EndNote, or BibTeX export, paste a list of DOIs or PubMed IDs, or
pick PDFs from Google Drive.
</>
}
/>
<Way
icon={<PaperclipIcon />}
title='Attach a PDF to a study later'
body='Expand a study to upload its PDF or import one from Google Drive. Reviewers read it beside the checklist while they appraise.'
/>
</div>
</div>
);
}

function Way({
icon,
title,
body,
}: {
icon: React.ReactNode;
title: string;
body: React.ReactNode;
}) {
return (
<div className='border-border flex min-w-0 flex-col gap-2 border-b p-3.5 last:border-b-0 sm:border-r sm:border-b-0 sm:last:border-r-0'>
<span className='bg-primary/10 text-primary flex size-7 items-center justify-center rounded-lg [&_svg]:size-4'>
{icon}
</span>
<p className='text-foreground text-xs font-medium'>{title}</p>
<p className='text-muted-foreground text-xs'>{body}</p>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { useProjectSetup, type SetupStep } from './useProjectSetup';
export function ProjectSetupCard() {
const setup = useProjectSetup();
const { steps, activeKey, doneCount, dismiss, isDismissing } = setup;
const allDone = doneCount === steps.length;

return (
<div className='border-border bg-card overflow-hidden rounded-xl border'>
Expand All @@ -39,13 +40,13 @@ export function ProjectSetupCard() {
))}
</div>
<Button
variant='ghost'
variant={allDone ? 'outline' : 'ghost'}
size='xs'
className='text-muted-foreground ml-auto'
className={cn('ml-auto', !allDone && 'text-muted-foreground')}
onClick={dismiss}
disabled={isDismissing}
>
Finish later
{allDone ? 'Done' : 'Finish later'}
</Button>
</div>

Expand Down Expand Up @@ -112,7 +113,7 @@ function StudiesBody({ setup }: { setup: Setup }) {
<p className='text-muted-foreground text-xs'>
{count} {count === 1 ? 'study' : 'studies'} added, {withPdf} with PDFs
</p>
<Button variant='outline' size='xs' className='self-start' onClick={setup.steps[0].onOpen}>
<Button size='xs' className='self-start' onClick={setup.steps[0].onOpen}>
<PlusIcon />
Add more
</Button>
Expand Down
Loading