/ components / SlideNav.tsx
SlideNav.tsx
  1  import { MessageSquarePlus } from 'lucide-react';
  2  
  3  interface Props {
  4    current: number;
  5    total: number;
  6    reviewedCount?: number;
  7    prevTitle: string | null;
  8    nextTitle: string | null;
  9    onPrev: () => void;
 10    onNext: () => void;
 11    commentCount?: number;
 12    onSubmitReview?: () => void;
 13  }
 14  
 15  // Bottom navigation bar. The previous version was a thin row of muted
 16  // text controls — so quiet that users overlooked it and missed how to
 17  // advance through the deck. This rebuild keeps the editorial register
 18  // (no shadcn Button chrome, no rounded pills) but gives the bar real
 19  // presence: a subtle paper-tinted background, a solid border-top, more
 20  // vertical padding, and the actual section titles on the prev/next
 21  // buttons so the user can see what's coming next at a glance. The
 22  // reading-progress hairline has moved to the very top of the page in
 23  // ReviewPage; this bar no longer carries it.
 24  export function SlideNav({
 25    current,
 26    total,
 27    reviewedCount = 0,
 28    prevTitle,
 29    nextTitle,
 30    onPrev,
 31    onNext,
 32    commentCount = 0,
 33    onSubmitReview,
 34  }: Props) {
 35    const isOverview = current === 0;
 36    const atEnd = current >= total;
 37  
 38    // On the very last slide, the right-hand button transforms from
 39    // "Next" into the submit-review CTA. The user has just finished
 40    // reading; the most prominent affordance on the page should now
 41    // be the conclusion of the flow, not a dead disabled button. When
 42    // this transformation happens, the center submit-review link
 43    // hides so the right button is the unique submit affordance.
 44    const showSubmitAsRight = atEnd && !!onSubmitReview;
 45    const showSubmitInCenter = !showSubmitAsRight && !!onSubmitReview;
 46  
 47    const nextLeadIn = isOverview ? 'Start reading' : 'Next';
 48  
 49    return (
 50      <div className="border-t border-border bg-muted/30 shrink-0">
 51        <div className="flex items-stretch justify-between gap-6 px-10 py-5">
 52          {/* ── Previous ── Quieter weight than Next so the eye is
 53              pulled forward. Disabled (faded) when on the overview. */}
 54          <button
 55            onClick={onPrev}
 56            disabled={isOverview}
 57            className="group flex items-baseline gap-3 text-left max-w-[36%] min-w-0 disabled:opacity-30 disabled:cursor-default"
 58            title="Previous (←)"
 59          >
 60            <span className="slide-meta shrink-0 group-hover:text-foreground transition-colors">
 61 62            </span>
 63            <span className="flex flex-col gap-0.5 min-w-0">
 64              <span className="slide-meta">Previous</span>
 65              <span className="font-serif text-base text-foreground/60 group-hover:text-foreground transition-colors truncate">
 66                {prevTitle ?? 'Start of review'}
 67              </span>
 68            </span>
 69          </button>
 70  
 71          {/* ── Center ── Counter + (when not at the end) the submit
 72              review affordance. On the last slide the submit moves
 73              to the right column, so this column shrinks back to
 74              just the counter. */}
 75          <div className="flex flex-col items-center justify-center gap-1 shrink-0">
 76            <span className="slide-meta tabular-nums">
 77              {isOverview
 78                ? 'Overview'
 79                : `Section ${current.toString().padStart(2, '0')} of ${total
 80                    .toString()
 81                    .padStart(2, '0')}`}
 82              {reviewedCount > 0 && ` · ${reviewedCount} reviewed`}
 83            </span>
 84            {showSubmitInCenter && (
 85              <button
 86                onClick={onSubmitReview}
 87                className="inline-flex items-center gap-1.5 slide-meta hover:text-foreground transition-colors"
 88              >
 89                <MessageSquarePlus className="h-3 w-3" />
 90                Submit review
 91                {commentCount > 0 && (
 92                  <span className="text-[var(--ring)] tabular-nums">({commentCount})</span>
 93                )}
 94              </button>
 95            )}
 96          </div>
 97  
 98          {/* ── Right ── On the last slide, this becomes the
 99              submit-review CTA — closing the reading loop with a
100              clear "you're done, click here" guide. Otherwise it's
101              the standard Next button with the upcoming section
102              title in heavy foreground type. */}
103          {showSubmitAsRight ? (
104            <button
105              onClick={onSubmitReview}
106              className="group flex items-baseline gap-3 text-right max-w-[36%] min-w-0 justify-end"
107              title="Submit review"
108            >
109              <span className="flex flex-col gap-0.5 min-w-0 items-end">
110                <span className="slide-meta">End of review</span>
111                <span className="font-serif text-base text-foreground group-hover:opacity-80 transition-opacity truncate">
112                  Submit review
113                  {commentCount > 0 && (
114                    <span className="text-[var(--ring)] tabular-nums"> ({commentCount})</span>
115                  )}
116                </span>
117              </span>
118              <span className="slide-meta shrink-0 text-foreground group-hover:opacity-80 transition-opacity">
119120              </span>
121            </button>
122          ) : (
123            <button
124              onClick={onNext}
125              disabled={atEnd}
126              className="group flex items-baseline gap-3 text-right max-w-[36%] min-w-0 disabled:opacity-30 disabled:cursor-default justify-end"
127              title="Next (→)"
128            >
129              <span className="flex flex-col gap-0.5 min-w-0 items-end">
130                <span className="slide-meta">{nextLeadIn}</span>
131                <span className="font-serif text-base text-foreground group-hover:opacity-80 transition-opacity truncate">
132                  {nextTitle ?? '—'}
133                </span>
134              </span>
135              <span className="slide-meta shrink-0 text-foreground group-hover:opacity-80 transition-opacity">
136137              </span>
138            </button>
139          )}
140        </div>
141      </div>
142    );
143  }