rafmacalaba commited on
Commit
e715c5d
·
1 Parent(s): 58e23bb

feat: show all pages with jump-to-mention navigation

Browse files

- documents/route.js now includes ALL pages, not just those with datasets
- Added pages_with_mentions array to document data
- PageNavigator has ⏮/⏭ jump buttons to skip to pages with mentions
- Green ● dot indicator on pages that have data mentions
- Users can browse full document context while quickly jumping to mentions

app/api/documents/route.js CHANGED
@@ -106,11 +106,13 @@ export async function GET(request) {
106
  if (!docRes.ok) return null;
107
 
108
  const pagesData = await docRes.json();
109
- const annotatablePages = pagesData
 
 
110
  .filter(page => page.datasets && page.datasets.length > 0)
111
  .map(page => page.document.pages[0]);
112
 
113
- if (annotatablePages.length === 0) return null;
114
 
115
  const pdfUrl = link.direct_pdf_url;
116
  if (!pdfUrl) return null;
@@ -121,7 +123,8 @@ export async function GET(request) {
121
  index: link.index,
122
  pdf_url: pdfUrl,
123
  landing_page: link.landing_page_url,
124
- annotatable_pages: annotatablePages
 
125
  };
126
  })
127
  );
 
106
  if (!docRes.ok) return null;
107
 
108
  const pagesData = await docRes.json();
109
+ const allPages = pagesData
110
+ .map(page => page.document.pages[0]);
111
+ const pagesWithMentions = pagesData
112
  .filter(page => page.datasets && page.datasets.length > 0)
113
  .map(page => page.document.pages[0]);
114
 
115
+ if (allPages.length === 0) return null;
116
 
117
  const pdfUrl = link.direct_pdf_url;
118
  if (!pdfUrl) return null;
 
123
  index: link.index,
124
  pdf_url: pdfUrl,
125
  landing_page: link.landing_page_url,
126
+ annotatable_pages: allPages,
127
+ pages_with_mentions: pagesWithMentions
128
  };
129
  })
130
  );
app/components/PageNavigator.js CHANGED
@@ -4,33 +4,60 @@ export default function PageNavigator({
4
  currentIndex,
5
  totalPages,
6
  currentPageNumber,
 
7
  onPrevious,
8
  onNext,
 
 
 
9
  }) {
10
  return (
11
  <div className="page-navigator">
12
- <button
13
- className="btn btn-nav"
14
- onClick={onPrevious}
15
- disabled={currentIndex <= 0}
16
- aria-label="Previous page"
17
- >
18
- Prev
19
- </button>
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  <span className="page-indicator">
22
  Page <strong>{currentPageNumber}</strong>
 
23
  <span className="page-count">{currentIndex + 1} / {totalPages}</span>
24
  </span>
25
 
26
- <button
27
- className="btn btn-nav"
28
- onClick={onNext}
29
- disabled={currentIndex >= totalPages - 1}
30
- aria-label="Next page"
31
- >
32
- Next →
33
- </button>
 
 
 
 
 
 
 
 
 
 
 
34
  </div>
35
  );
36
  }
 
4
  currentIndex,
5
  totalPages,
6
  currentPageNumber,
7
+ hasMentions,
8
  onPrevious,
9
  onNext,
10
+ onJumpToMention,
11
+ hasNextMention,
12
+ hasPrevMention,
13
  }) {
14
  return (
15
  <div className="page-navigator">
16
+ <div className="page-nav-group">
17
+ <button
18
+ className="btn btn-nav btn-jump"
19
+ onClick={() => onJumpToMention(-1)}
20
+ disabled={!hasPrevMention}
21
+ aria-label="Previous page with mentions"
22
+ title="Jump to previous page with data mentions"
23
+ >
24
+
25
+ </button>
26
+ <button
27
+ className="btn btn-nav"
28
+ onClick={onPrevious}
29
+ disabled={currentIndex <= 0}
30
+ aria-label="Previous page"
31
+ >
32
+ ← Prev
33
+ </button>
34
+ </div>
35
 
36
  <span className="page-indicator">
37
  Page <strong>{currentPageNumber}</strong>
38
+ {hasMentions && <span className="mention-dot" title="This page has data mentions">●</span>}
39
  <span className="page-count">{currentIndex + 1} / {totalPages}</span>
40
  </span>
41
 
42
+ <div className="page-nav-group">
43
+ <button
44
+ className="btn btn-nav"
45
+ onClick={onNext}
46
+ disabled={currentIndex >= totalPages - 1}
47
+ aria-label="Next page"
48
+ >
49
+ Next →
50
+ </button>
51
+ <button
52
+ className="btn btn-nav btn-jump"
53
+ onClick={() => onJumpToMention(1)}
54
+ disabled={!hasNextMention}
55
+ aria-label="Next page with mentions"
56
+ title="Jump to next page with data mentions"
57
+ >
58
+
59
+ </button>
60
+ </div>
61
  </div>
62
  );
63
  }
app/globals.css CHANGED
@@ -527,7 +527,12 @@ h4 {
527
  .page-navigator {
528
  display: flex;
529
  align-items: center;
530
- gap: 24px;
 
 
 
 
 
531
  }
532
 
533
  .page-indicator {
@@ -544,6 +549,26 @@ h4 {
544
  color: #f8fafc;
545
  }
546
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
547
  .page-count {
548
  font-size: 0.75rem;
549
  color: #64748b;
@@ -560,6 +585,11 @@ h4 {
560
  transition: all 0.2s;
561
  }
562
 
 
 
 
 
 
563
  .btn-nav:hover:not(:disabled) {
564
  background-color: var(--accent);
565
  border-color: var(--accent);
 
527
  .page-navigator {
528
  display: flex;
529
  align-items: center;
530
+ gap: 16px;
531
+ }
532
+
533
+ .page-nav-group {
534
+ display: flex;
535
+ gap: 4px;
536
  }
537
 
538
  .page-indicator {
 
549
  color: #f8fafc;
550
  }
551
 
552
+ .mention-dot {
553
+ color: #10b981;
554
+ font-size: 0.6rem;
555
+ margin-left: 4px;
556
+ vertical-align: middle;
557
+ animation: pulse-dot 2s ease-in-out infinite;
558
+ }
559
+
560
+ @keyframes pulse-dot {
561
+
562
+ 0%,
563
+ 100% {
564
+ opacity: 1;
565
+ }
566
+
567
+ 50% {
568
+ opacity: 0.4;
569
+ }
570
+ }
571
+
572
  .page-count {
573
  font-size: 0.75rem;
574
  color: #64748b;
 
585
  transition: all 0.2s;
586
  }
587
 
588
+ .btn-nav.btn-jump {
589
+ padding: 10px 12px;
590
+ font-size: 0.8rem;
591
+ }
592
+
593
  .btn-nav:hover:not(:disabled) {
594
  background-color: var(--accent);
595
  border-color: var(--accent);
app/page.js CHANGED
@@ -47,6 +47,7 @@ export default function Home() {
47
 
48
  // Derived: current page number from the annotatable_pages array
49
  const annotatablePages = currentDoc?.annotatable_pages ?? [];
 
50
  const currentPageNumber = annotatablePages[pageIdx] ?? null;
51
 
52
  // Load documents (re-fetches when annotatorName changes to get user-specific assignment)
@@ -204,6 +205,27 @@ export default function Home() {
204
  setPageIdx(prev => Math.min(annotatablePages.length - 1, prev + 1));
205
  };
206
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207
  const handleAnnotate = (text, domOffset) => {
208
  setSelectedText(text);
209
  setSelectionOffset(domOffset || 0);
@@ -527,8 +549,12 @@ export default function Home() {
527
  currentIndex={pageIdx}
528
  totalPages={annotatablePages.length}
529
  currentPageNumber={currentPageNumber}
 
530
  onPrevious={handlePrevPage}
531
  onNext={handleNextPage}
 
 
 
532
  />
533
  </div>
534
 
 
47
 
48
  // Derived: current page number from the annotatable_pages array
49
  const annotatablePages = currentDoc?.annotatable_pages ?? [];
50
+ const pagesWithMentions = new Set(currentDoc?.pages_with_mentions ?? []);
51
  const currentPageNumber = annotatablePages[pageIdx] ?? null;
52
 
53
  // Load documents (re-fetches when annotatorName changes to get user-specific assignment)
 
205
  setPageIdx(prev => Math.min(annotatablePages.length - 1, prev + 1));
206
  };
207
 
208
+ const handleJumpToMention = (direction = 1) => {
209
+ const currentPage = annotatablePages[pageIdx];
210
+ if (direction === 1) {
211
+ // Find next page index (after current) that has mentions
212
+ for (let i = pageIdx + 1; i < annotatablePages.length; i++) {
213
+ if (pagesWithMentions.has(annotatablePages[i])) {
214
+ setPageIdx(i);
215
+ return;
216
+ }
217
+ }
218
+ } else {
219
+ // Find previous page index that has mentions
220
+ for (let i = pageIdx - 1; i >= 0; i--) {
221
+ if (pagesWithMentions.has(annotatablePages[i])) {
222
+ setPageIdx(i);
223
+ return;
224
+ }
225
+ }
226
+ }
227
+ };
228
+
229
  const handleAnnotate = (text, domOffset) => {
230
  setSelectedText(text);
231
  setSelectionOffset(domOffset || 0);
 
549
  currentIndex={pageIdx}
550
  totalPages={annotatablePages.length}
551
  currentPageNumber={currentPageNumber}
552
+ hasMentions={pagesWithMentions.has(currentPageNumber)}
553
  onPrevious={handlePrevPage}
554
  onNext={handleNextPage}
555
+ onJumpToMention={handleJumpToMention}
556
+ hasNextMention={annotatablePages.slice(pageIdx + 1).some(p => pagesWithMentions.has(p))}
557
+ hasPrevMention={annotatablePages.slice(0, pageIdx).some(p => pagesWithMentions.has(p))}
558
  />
559
  </div>
560