Rename the Export stage to Finish

It now holds files, sharing, handouts, slides, importing notes, schedule,
co-teachers and earlier versions, not just exports.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
nmemmertandClaude Sonnet 5.5 committed 2026-10-14 13:17:08 -04:00
1 parent 2c7c7145c4
commit 0f1a181d87
12 files changed
+18 -18

No files matched your search

+1 -1
View File
@@ -167,7 +167,7 @@ const AppInner = () => {
// The browser tab and history show where you are ("Romans 8 · Bible Study", "Luke — Outline · Bible Study") // The browser tab and history show where you are ("Romans 8 · Bible Study", "Luke — Outline · Bible Study")
useEffect(() => { useEffect(() => {
const names = { const names = {
home: 'My Studies', setup: 'Chunks', study: 'Study', outline: 'Outline', script: 'Script', export: 'Export', home: 'My Studies', setup: 'Chunks', study: 'Study', outline: 'Outline', script: 'Script', export: 'Finish',
import: 'Import', pronunciation: 'Pronunciation Guide', settings: 'Settings', admin: 'Admin', 'reading-plans': 'Reading Plans', prayer: 'Prayer journal', insights: 'Insights', import: 'Import', pronunciation: 'Pronunciation Guide', settings: 'Settings', admin: 'Admin', 'reading-plans': 'Reading Plans', prayer: 'Prayer journal', insights: 'Insights',
}; };
let page = names[currentPage] ?? ''; let page = names[currentPage] ?? '';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Co-teachers: let other accounts edit this study with you. You make a single-use invite link; // Finish → Co-teachers: let other accounts edit this study with you. You make a single-use invite link;
// whoever opens it (signed in) can edit the study, and you can remove them any time. Edits from both of you // whoever opens it (signed in) can edit the study, and you can remove them any time. Edits from both of you
// sync through the account; if you both change the same study, "Combine both" keeps everything. // sync through the account; if you both change the same study, "Combine both" keeps everything.
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Lesson handout: a printable page for each session (or one session), for a group to follow along. // Finish → Lesson handout: a printable page for each session (or one session), for a group to follow along.
import { useState } from 'react'; import { useState } from 'react';
import { HANDOUT_LAYOUTS, printHandout, sessionsFor } from '../utils/handout.js'; import { HANDOUT_LAYOUTS, printHandout, sessionsFor } from '../utils/handout.js';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Import notes: bring notes from a Word or Markdown file into this study, matched to its chunks by passage. // Finish → Import notes: bring notes from a Word or Markdown file into this study, matched to its chunks by passage.
import { useState } from 'react'; import { useState } from 'react';
import { useApp } from '../context/AppContext.js'; import { useApp } from '../context/AppContext.js';
import { useToast } from './Toasts.jsx'; import { useToast } from './Toasts.jsx';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Schedule: the study's sessions (one per chunk) as a calendar file, starting on a date you pick. // Finish → Schedule: the study's sessions (one per chunk) as a calendar file, starting on a date you pick.
import { useState } from 'react'; import { useState } from 'react';
import { formatChunkReference } from '../utils/bibleUtils.jsx'; import { formatChunkReference } from '../utils/bibleUtils.jsx';
import { buildIcs, downloadIcs, sessionDates } from '../utils/icsFile.js'; import { buildIcs, downloadIcs, sessionDates } from '../utils/icsFile.js';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Slides: a printable slide deck for a session or the whole study (Save as PDF in the print dialog). // Finish → Slides: a printable slide deck for a session or the whole study (Save as PDF in the print dialog).
import { useState } from 'react'; import { useState } from 'react';
import { sessionsFor } from '../utils/handout.js'; import { sessionsFor } from '../utils/handout.js';
import { printSlides } from '../utils/slides.js'; import { printSlides } from '../utils/slides.js';
+1 -1
View File
@@ -1,4 +1,4 @@
// Export → Earlier versions: the copies the server kept of this study as you worked. Look at what is different // Finish → Earlier versions: the copies the server kept of this study as you worked. Look at what is different
// from now, and put one back. The version you replace is kept, so putting one back can be undone too. // from now, and put one back. The version you replace is kept, so putting one back can be undone too.
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import { getStudyVersion, listStudyVersions } from '../syncService.js'; import { getStudyVersion, listStudyVersions } from '../syncService.js';
+1 -1
View File
@@ -10,7 +10,7 @@ export const STAGES = [
{ id: 'study', label: 'Study' }, { id: 'study', label: 'Study' },
{ id: 'outline', label: 'Outline' }, { id: 'outline', label: 'Outline' },
{ id: 'script', label: 'Script' }, { id: 'script', label: 'Script' },
{ id: 'export', label: 'Export' }, { id: 'export', label: 'Finish' },
]; ];
const THEME_ORDER = ['paper', 'sepia', 'dark']; const THEME_ORDER = ['paper', 'sepia', 'dark'];
+2 -2
View File
@@ -1,4 +1,4 @@
// The Export stage: take the finished study out as a file, or share it read-only. // The Finish stage: everything for when a study is ready to use: files, sharing, handouts, slides, importing notes, schedule, co-teachers and earlier versions.
import { useApp } from '../context/AppContext.js'; import { useApp } from '../context/AppContext.js';
import { useShareLink } from '../hooks/useShareLink.js'; import { useShareLink } from '../hooks/useShareLink.js';
import SlidesExport from '../components/SlidesExport.jsx'; import SlidesExport from '../components/SlidesExport.jsx';
@@ -32,7 +32,7 @@ export default function ExportPage() {
<StudioShell stage="export"> <StudioShell stage="export">
<main className="mx-auto max-w-2xl space-y-5 px-4 py-8"> <main className="mx-auto max-w-2xl space-y-5 px-4 py-8">
<div> <div>
<h1 className="text-xl font-medium">Export</h1> <h1 className="text-xl font-medium">Finish</h1>
<p className="mt-1 text-sm text-[var(--r-sub)]">{allChunks.length} chunk{allChunks.length === 1 ? '' : 's'} · {filled} script{filled === 1 ? '' : 's'} written</p> <p className="mt-1 text-sm text-[var(--r-sub)]">{allChunks.length} chunk{allChunks.length === 1 ? '' : 's'} · {filled} script{filled === 1 ? '' : 's'} written</p>
</div> </div>
+1 -1
View File
@@ -1,5 +1,5 @@
// Every Greek and Hebrew word in the study with its transliteration, so a // Every Greek and Hebrew word in the study with its transliteration, so a
// speaker can rehearse them. Reached from the Script and Export stages. // speaker can rehearse them. Reached from the Script and Finish stages.
import { useState } from 'react'; import { useState } from 'react';
import { useApp } from '../context/AppContext.js'; import { useApp } from '../context/AppContext.js';
import StudioShell from '../components/study/StudioShell.jsx'; import StudioShell from '../components/study/StudioShell.jsx';
+6 -6
View File
@@ -190,16 +190,16 @@ describe('Home page', () => {
}); });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Export stage (useShareLink) and the shared read-only view // Finish stage (useShareLink) and the shared read-only view
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
describe('Project header and sharing', () => { describe('Project header and sharing', () => {
const json = (status, data) => Promise.resolve({ ok: status < 400, status, json: () => Promise.resolve(data) }); const json = (status, data) => Promise.resolve({ ok: status < 400, status, json: () => Promise.resolve(data) });
test('Export stage → Markdown downloads a file', async () => { test('Finish stage → Markdown downloads a file', async () => {
const user = await loadChapter(); const user = await loadChapter();
addChunk('Paul, a servant', 'Paul, a servant'); addChunk('Paul, a servant', 'Paul, a servant');
await screen.findByText('1 created'); await screen.findByText('1 created');
await user.click(screen.getByRole('button', { name: /^export$/i })); await user.click(screen.getByRole('button', { name: /^finish$/i }));
await user.click(await screen.findByRole('button', { name: /export markdown/i })); await user.click(await screen.findByRole('button', { name: /export markdown/i }));
expect(URL.createObjectURL).toHaveBeenCalled(); expect(URL.createObjectURL).toHaveBeenCalled();
}); });
@@ -216,7 +216,7 @@ describe('Project header and sharing', () => {
const user = await loadChapter(); const user = await loadChapter();
addChunk('Paul, a servant', 'Paul, a servant'); addChunk('Paul, a servant', 'Paul, a servant');
await screen.findByText('1 created'); await screen.findByText('1 created');
await user.click(screen.getByRole('button', { name: /^export$/i })); await user.click(screen.getByRole('button', { name: /^finish$/i }));
await user.click(await screen.findByRole('button', { name: /enable sharing/i })); await user.click(await screen.findByRole('button', { name: /enable sharing/i }));
expect(await screen.findByDisplayValue(/\?share=tok123$/)).toBeInTheDocument(); expect(await screen.findByDisplayValue(/\?share=tok123$/)).toBeInTheDocument();
}); });
@@ -310,12 +310,12 @@ describe('Chunk builder', () => {
}); });
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Stage bar (Chunks / Study / Outline / Script / Export) // Stage bar (Chunks / Study / Outline / Script / Finish)
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
describe('Stage bar', () => { describe('Stage bar', () => {
test('later stages are disabled until a chunk exists', async () => { test('later stages are disabled until a chunk exists', async () => {
await loadChapter(); await loadChapter();
for (const name of [/^study$/i, /^outline$/i, /^script$/i, /^export$/i]) { for (const name of [/^study$/i, /^outline$/i, /^script$/i, /^finish$/i]) {
expect(screen.getByRole('button', { name })).toBeDisabled(); expect(screen.getByRole('button', { name })).toBeDisabled();
} }
expect(screen.getByRole('button', { name: /^chunks$/i })).not.toBeDisabled(); expect(screen.getByRole('button', { name: /^chunks$/i })).not.toBeDisabled();
+1 -1
View File
@@ -26,7 +26,7 @@ describe('Back to the dashboard', () => {
await dashboard(); await dashboard();
}); });
test.each(['Chunks', 'Study', 'Outline', 'Script', 'Export'])('the %s stage of a study', async (stage) => { test.each(['Chunks', 'Study', 'Outline', 'Script', 'Finish'])('the %s stage of a study', async (stage) => {
const u = await loadChapter(); const u = await loadChapter();
addChunk('Paul, a servant', 'In hope of eternal'); addChunk('Paul, a servant', 'In hope of eternal');
await screen.findByText('1 created'); await screen.findByText('1 created');