Skip to content
Draft
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
8 changes: 3 additions & 5 deletions components/modals/ApplyToGrantModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import AnimatedProposal from '@/components/Proposal/AnimatedProposal';
import { NoteService } from '@/services/note.service';
import { setPendingGrant } from '@/components/Editor/lib/utils/publishingFormStorage';
import { useUser } from '@/contexts/UserContext';
import { useOrganizationContext } from '@/contexts/OrganizationContext';
import { useRouter } from 'next/navigation';
import { cn } from '@/utils/styles';
import type { GrantApplicationVisibility } from '@/types/grant';
Expand Down Expand Up @@ -63,7 +62,6 @@ export const ApplyToGrantModal: React.FC<ApplyToGrantModalProps> = ({
const [draftNewSelected, setDraftNewSelected] = useState(false);
const [loading, setLoading] = useState(false);
const { user } = useUser();
const { selectedOrg } = useOrganizationContext();
const router = useRouter();

const selectedDraftNote = draftNotes.find((n) => n.id.toString() === selectedDraftNoteId);
Expand Down Expand Up @@ -121,17 +119,17 @@ export const ApplyToGrantModal: React.FC<ApplyToGrantModalProps> = ({
fetchDraftNotes();
}
}
}, [isOpen, user?.id, selectedOrg?.slug]);
}, [isOpen, user?.id]);

const fetchDraftNotes = async () => {
if (!user?.id || !selectedOrg?.slug) {
if (!user?.id) {
setDraftNotes([]);
return;
}

setLoading(true);
try {
const response = await NoteService.getOrganizationNotes(selectedOrg.slug, {
const response = await NoteService.getAccessibleNotes({
status: 'DRAFT',
documentType: 'PREREGISTRATION',
});
Expand Down
35 changes: 12 additions & 23 deletions contexts/NotebookContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,17 +85,12 @@ export function NotebookProvider({ children, noteId: explicitNoteId }: NotebookP
// Editor state
const [editor, setEditor] = useState<Editor | null>(null);

const fetchNotes = useCallback(async (slug?: string) => {
if (!slug) {
setNotesError(new Error('No organization slug provided'));
return;
}

const fetchNotes = useCallback(async () => {
setIsLoadingNotes(true);
setNotesError(null);

try {
const data = await NoteService.getOrganizationNotes(slug);
const data = await NoteService.getAccessibleNotes();

setNotes(data.results);
setTotalCount(data.count);
Expand Down Expand Up @@ -142,12 +137,8 @@ export function NotebookProvider({ children, noteId: explicitNoteId }: NotebookP
);

const refreshNotes = useCallback(async () => {
if (!selectedOrg?.slug) {
setNotesError(new Error('No organization slug provided'));
return;
}
await fetchNotes(selectedOrg.slug);
}, [selectedOrg?.slug, fetchNotes]);
await fetchNotes();
}, [fetchNotes]);

const loadNote = useCallback(async (noteId: string) => {
if (noteId === lastLoadedNoteIdRef.current) {
Expand Down Expand Up @@ -193,15 +184,16 @@ export function NotebookProvider({ children, noteId: explicitNoteId }: NotebookP
);

const refreshAll = useCallback(async () => {
if (!selectedOrg?.slug || !selectedOrg?.id) return;

const promises = [fetchNotes(selectedOrg.slug), fetchUsers(selectedOrg.id.toString())];
const promises = [fetchNotes()];
if (selectedOrg?.id) {
promises.push(fetchUsers(selectedOrg.id.toString()));
}
if (activeNoteId) {
promises.push(loadNote(activeNoteId));
}

await Promise.all(promises);
}, [selectedOrg?.slug, selectedOrg?.id, activeNoteId, fetchNotes, fetchUsers, loadNote]);
}, [selectedOrg?.id, activeNoteId, fetchNotes, fetchUsers, loadNote]);

// Initial data loading when organization changes
useEffect(() => {
Expand All @@ -211,20 +203,17 @@ export function NotebookProvider({ children, noteId: explicitNoteId }: NotebookP
return;
}

fetchNotes();

if (!selectedOrg) {
setNotes([]);
setTotalCount(0);
setUsers(null);
setNotesError(null);
setUsersError(null);
setIsLoadingNotes(false);
setIsLoadingUsers(false);
return;
}

fetchNotes(selectedOrg.slug);
fetchUsers(selectedOrg.id.toString());
}, [selectedOrg?.slug, selectedOrg?.id, isLoadingOrg, fetchNotes, fetchUsers]);
}, [selectedOrg?.id, isLoadingOrg, fetchNotes, fetchUsers]);

useEffect(() => {
if (activeNoteId) {
Expand Down
41 changes: 39 additions & 2 deletions services/note.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
title: string;
}

export interface GetOrganizationNotesParams {
export interface GetNotesParams {
status?: 'DRAFT' | 'PUBLISHED';
documentType?: 'PREREGISTRATION' | 'GRANT' | 'DISCUSSION';
}
Expand Down Expand Up @@ -141,7 +141,7 @@
*/
static async getOrganizationNotes(
orgSlug: string,
params?: GetOrganizationNotesParams
params?: GetNotesParams
): Promise<NoteListResponse> {
if (!orgSlug) {
throw new NoteError('Missing organization slug', 'INVALID_PARAMS');
Expand Down Expand Up @@ -178,6 +178,43 @@
}
}

/**
* Fetches all notes the current user can access.
*
* @throws {NoteError} When the request fails or parameters are invalid
*/
static async getAccessibleNotes(params?: GetNotesParams): Promise<NoteListResponse> {
try {
const queryParams = new URLSearchParams();
if (params?.status) queryParams.append('status', params.status);
if (params?.documentType) queryParams.append('type', params.documentType);
const qs = queryParams.toString();

const response = await ApiClient.get<any>(
`${this.BASE_PATH}/note/accessible/${qs ? `?${qs}` : ''}`

Check warning on line 194 in services/note.service.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this code to not use nested template literals.

See more on https://sonarcloud.io/project/issues?id=ResearchHub_web&issues=AZ89KlSl264LgDBIy53a&open=AZ89KlSl264LgDBIy53a&pullRequest=923
);

if (!response || !Array.isArray(response.results)) {
throw new NoteError('Invalid response format', 'INVALID_RESPONSE');
}

return {
count: response.count || 0,
next: response.next || null,
previous: response.previous || null,
results: response.results.map(transformNote),
};
} catch (error) {
if (error instanceof NoteError) {
throw error;
}
throw new NoteError(
'Failed to fetch accessible notes',
error instanceof Error ? error.message : 'UNKNOWN_ERROR'
);
}
}

/**
* Creates a new note
* @param params - The note creation parameters
Expand Down