From a041e6eb2717915eba5594b590bb0f14871905ba Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Tue, 7 Jul 2026 17:11:05 +0200 Subject: [PATCH 1/4] Add API client for fetching a user's accessible notes --- services/note.service.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/services/note.service.ts b/services/note.service.ts index 1cc4b4fa8..0ec314286 100644 --- a/services/note.service.ts +++ b/services/note.service.ts @@ -178,6 +178,43 @@ export class NoteService { } } + /** + * Fetches all notes the current user can access. + * + * @throws {NoteError} When the request fails or parameters are invalid + */ + static async getAccessibleNotes(params?: GetOrganizationNotesParams): Promise { + 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( + `${this.BASE_PATH}/note/accessible/${qs ? `?${qs}` : ''}` + ); + + 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 From 9b41d16bc78b5814113acc53755108b972cc8c43 Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Tue, 7 Jul 2026 17:12:38 +0200 Subject: [PATCH 2/4] Rename params interface for fetching notes --- services/note.service.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/note.service.ts b/services/note.service.ts index 0ec314286..729bfe047 100644 --- a/services/note.service.ts +++ b/services/note.service.ts @@ -46,7 +46,7 @@ export interface UpdateNoteTitleParams { title: string; } -export interface GetOrganizationNotesParams { +export interface GetNotesParams { status?: 'DRAFT' | 'PUBLISHED'; documentType?: 'PREREGISTRATION' | 'GRANT' | 'DISCUSSION'; } @@ -141,7 +141,7 @@ export class NoteService { */ static async getOrganizationNotes( orgSlug: string, - params?: GetOrganizationNotesParams + params?: GetNotesParams ): Promise { if (!orgSlug) { throw new NoteError('Missing organization slug', 'INVALID_PARAMS'); @@ -183,7 +183,7 @@ export class NoteService { * * @throws {NoteError} When the request fails or parameters are invalid */ - static async getAccessibleNotes(params?: GetOrganizationNotesParams): Promise { + static async getAccessibleNotes(params?: GetNotesParams): Promise { try { const queryParams = new URLSearchParams(); if (params?.status) queryParams.append('status', params.status); From 8b0e456729d9c5994826619558d13155a0a1c2ba Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Tue, 7 Jul 2026 17:18:20 +0200 Subject: [PATCH 3/4] Fetch all accessible notes when loading a user's notes --- contexts/NotebookContext.tsx | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/contexts/NotebookContext.tsx b/contexts/NotebookContext.tsx index ebea9a59a..169a129d8 100644 --- a/contexts/NotebookContext.tsx +++ b/contexts/NotebookContext.tsx @@ -85,17 +85,12 @@ export function NotebookProvider({ children, noteId: explicitNoteId }: NotebookP // Editor state const [editor, setEditor] = useState(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); @@ -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) { @@ -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(() => { @@ -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) { From bb2d2b112b63c0cfc60f09aa4d63f34eacfaae6f Mon Sep 17 00:00:00 2001 From: Gregor Zurowski Date: Tue, 7 Jul 2026 17:18:48 +0200 Subject: [PATCH 4/4] Fetch all accessible notes in grant draft picker --- components/modals/ApplyToGrantModal.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/components/modals/ApplyToGrantModal.tsx b/components/modals/ApplyToGrantModal.tsx index b46960fe9..8fb2b5739 100644 --- a/components/modals/ApplyToGrantModal.tsx +++ b/components/modals/ApplyToGrantModal.tsx @@ -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'; @@ -63,7 +62,6 @@ export const ApplyToGrantModal: React.FC = ({ 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); @@ -121,17 +119,17 @@ export const ApplyToGrantModal: React.FC = ({ 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', });