Skip to content

Commit 5bda38c

Browse files
toddaheathclaude
andcommitted
Add PDF and STL export buttons to toolbar
When a design is active, show PDF and STL download buttons in the toolbar for quick access to exports. Includes divider separator and tests verifying button visibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8854257 commit 5bda38c

File tree

2 files changed

+80
-0
lines changed

2 files changed

+80
-0
lines changed

src/shed-builder-ui/src/components/App.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import Brightness4Icon from '@mui/icons-material/Brightness4';
2626
import Brightness7Icon from '@mui/icons-material/Brightness7';
2727
import LogoutIcon from '@mui/icons-material/Logout';
2828
import LockIcon from '@mui/icons-material/Lock';
29+
import DownloadIcon from '@mui/icons-material/Download';
30+
import PictureAsPdfIcon from '@mui/icons-material/PictureAsPdf';
2931
import CircularProgress from '@mui/material/CircularProgress';
3032
import { ThemeProvider, createTheme } from '@mui/material/styles';
3133
import useMediaQuery from '@mui/material/useMediaQuery';
@@ -472,6 +474,25 @@ function AuthenticatedApp({ mode, toggleDarkMode, onSignOut }: AuthenticatedAppP
472474
</IconButton>
473475
</span>
474476
</Tooltip>
477+
<Box sx={{ mx: 0.5, height: 24, borderLeft: '1px solid', borderColor: 'rgba(255,255,255,0.3)' }} />
478+
<Tooltip title="Download PDF report">
479+
<IconButton
480+
color="inherit"
481+
aria-label="Download PDF"
482+
onClick={() => api.downloadPdf(activeDesign.id, activeDesign.name)}
483+
>
484+
<PictureAsPdfIcon />
485+
</IconButton>
486+
</Tooltip>
487+
<Tooltip title="Download STL model">
488+
<IconButton
489+
color="inherit"
490+
aria-label="Download STL"
491+
onClick={() => api.downloadStl(activeDesign.id, activeDesign.name)}
492+
>
493+
<DownloadIcon />
494+
</IconButton>
495+
</Tooltip>
475496
</>
476497
)}
477498
<Tooltip title={mode === 'dark' ? 'Switch to light mode' : 'Switch to dark mode'}>

src/shed-builder-ui/src/components/__tests__/App.test.tsx

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,65 @@ describe('App', () => {
371371
unmount();
372372
});
373373

374+
it('shows export buttons when a design is active', async () => {
375+
const { getStoredToken, api: mockApi } = await getApi();
376+
vi.mocked(getStoredToken).mockReturnValue('valid-token');
377+
378+
const newDesign = {
379+
id: 'new-id',
380+
name: 'Export Test',
381+
widthFeet: 10,
382+
widthInches: 0,
383+
depthFeet: 12,
384+
depthInches: 0,
385+
heightFeet: 8,
386+
heightInches: 0,
387+
roofPitch: 4,
388+
roofType: 'Gable' as const,
389+
openings: [],
390+
createdAt: '2024-01-01T00:00:00Z',
391+
updatedAt: '2024-01-01T00:00:00Z',
392+
};
393+
vi.mocked(mockApi.createDesign).mockResolvedValue(newDesign);
394+
vi.mocked(mockApi.listDesigns)
395+
.mockResolvedValueOnce({ items: [], totalCount: 0 })
396+
.mockResolvedValue({ items: [newDesign], totalCount: 1 });
397+
398+
const { unmount } = render(<App />);
399+
400+
await waitFor(() => {
401+
expect(screen.getByText('Select or create a design to begin')).toBeInTheDocument();
402+
});
403+
404+
// Create a design to make export buttons appear
405+
await userEvent.click(screen.getByLabelText('Create new design'));
406+
await userEvent.type(screen.getByLabelText('Design Name'), 'Export Test');
407+
await userEvent.click(screen.getByRole('button', { name: 'Create' }));
408+
409+
await waitFor(() => {
410+
expect(screen.getByLabelText('Download PDF')).toBeInTheDocument();
411+
});
412+
expect(screen.getByLabelText('Download STL')).toBeInTheDocument();
413+
414+
unmount();
415+
});
416+
417+
it('does not show export buttons when no design is selected', async () => {
418+
const { getStoredToken } = await getApi();
419+
vi.mocked(getStoredToken).mockReturnValue('valid-token');
420+
421+
const { unmount } = render(<App />);
422+
423+
await waitFor(() => {
424+
expect(screen.getByText('Select or create a design to begin')).toBeInTheDocument();
425+
});
426+
427+
expect(screen.queryByLabelText('Download PDF')).not.toBeInTheDocument();
428+
expect(screen.queryByLabelText('Download STL')).not.toBeInTheDocument();
429+
430+
unmount();
431+
});
432+
374433
it('shows tabs when a design is selected', async () => {
375434
const { getStoredToken, api: mockApi } = await getApi();
376435
vi.mocked(getStoredToken).mockReturnValue('valid-token');

0 commit comments

Comments
 (0)