Skip to content

Commit

Permalink
VxDesign: Render ballot previews on the backend (#4556)
Browse files Browse the repository at this point in the history
  • Loading branch information
jonahkagan authored Feb 12, 2024
1 parent 47f1713 commit c8fe63a
Show file tree
Hide file tree
Showing 21 changed files with 691 additions and 648 deletions.
10 changes: 4 additions & 6 deletions apps/admin/frontend/src/screens/manual_data_entry_screen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ import {
LabelledText,
H1,
Font,
TaskContent,
TaskControls,
TaskHeader,
TaskScreen,
} from '@votingworks/ui';
import {
isElectionManagerAuth,
Expand All @@ -47,12 +51,6 @@ import {
setManualResults,
} from '../api';
import { normalizeWriteInName } from '../utils/write_ins';
import {
TaskContent,
TaskControls,
TaskHeader,
TaskScreen,
} from './task_screen';

export const TITLE = 'Edit Tallies';

Expand Down
2 changes: 1 addition & 1 deletion apps/design/backend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = {
coverageThreshold: {
global: {
branches: -20,
lines: -78,
lines: -79,
},
},
setupFilesAfterEnv: ['<rootDir>/test/setupTests.ts'],
Expand Down
37 changes: 21 additions & 16 deletions apps/design/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {
BallotPaperSize,
SystemSettings,
BallotType,
getBallotStyle,
} from '@votingworks/types';
import express, { Application } from 'express';
import { assertDefined, find, groupBy, ok, Result } from '@votingworks/basics';
import { assertDefined, groupBy, ok, Result } from '@votingworks/basics';
import {
BallotMode,
BALLOT_MODES,
layOutAllBallotStyles,
LayoutOptions,
layOutBallot,
} from '@votingworks/hmpb-layout';
import JsZip from 'jszip';
import { renderDocumentToPdf } from '@votingworks/hmpb-render-backend';
Expand Down Expand Up @@ -213,35 +215,38 @@ function buildApi({ translator, workspace }: AppContext) {
};
},

async exportBallot(input: {
async getBallotPreviewPdf(input: {
electionId: Id;
precinctId: string;
ballotStyleId: string;
ballotType: BallotType;
ballotMode: BallotMode;
}): Promise<Buffer> {
}): Promise<Result<Buffer, Error>> {
const { election, layoutOptions, nhCustomContent } = store.getElection(
input.electionId
);
const { ballots } = layOutAllBallotStyles({
const precinct = assertDefined(
getPrecinctById({ election, precinctId: input.precinctId })
);
const ballotStyle = assertDefined(
getBallotStyle({
election,
ballotStyleId: input.ballotStyleId,
})
);
const ballotResult = layOutBallot({
election,
precinct,
ballotStyle,
ballotType: input.ballotType,
ballotMode: input.ballotMode,
layoutOptions,
nhCustomContent,
translatedElectionStrings: (
await extractAndTranslateElectionStrings(translator, election)
).electionStrings,
}).unsafeUnwrap();
const { document } = find(
ballots,
({ precinctId, gridLayout }) =>
precinctId === input.precinctId &&
gridLayout.ballotStyleId === input.ballotStyleId
);
const pdf = renderDocumentToPdf(document);
});
if (ballotResult.isErr()) return ballotResult;
const pdf = renderDocumentToPdf(ballotResult.ok().document);
pdf.end();
return streamToBuffer(pdf);
return ok(await streamToBuffer(pdf));
},

getElectionPackage({ electionId }: { electionId: Id }): ElectionPackage {
Expand Down
1 change: 1 addition & 0 deletions apps/design/frontend/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
*.d.ts
*.config.js
*.config.ts
/public
4 changes: 2 additions & 2 deletions apps/design/frontend/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ module.exports = {
],
coverageThreshold: {
global: {
branches: -119,
lines: -173,
branches: -83,
lines: -93,
},
},
setupFiles: ['react-app-polyfill/jsdom'],
Expand Down
1 change: 1 addition & 0 deletions apps/design/frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"react-flip-toolkit": "^7.1.0",
"react-pdf": "7.7.0",
"react-router-dom": "^5.3.4",
"styled-components": "^5.3.11",
"util": "^0.12.4"
Expand Down
22 changes: 22 additions & 0 deletions apps/design/frontend/public/pdf.worker.min.js

Large diffs are not rendered by default.

25 changes: 21 additions & 4 deletions apps/design/frontend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
useQuery,
useQueryClient,
} from '@tanstack/react-query';
import { Id } from '@votingworks/types';
import { BallotType, Id } from '@votingworks/types';
import { BallotMode } from '@votingworks/hmpb-layout';

export type ApiClient = grout.Client<Api>;

Expand Down Expand Up @@ -157,10 +158,26 @@ export const exportAllBallots = {
},
} as const;

export const exportBallot = {
useMutation() {
interface GetBallotPreviewInput {
electionId: Id;
precinctId: string;
ballotStyleId: string;
ballotType: BallotType;
ballotMode: BallotMode;
}

export const getBallotPreviewPdf = {
queryKey(input: GetBallotPreviewInput): QueryKey {
return ['getBallotPreviewPdf', input];
},
useQuery(input: GetBallotPreviewInput) {
const apiClient = useApiClient();
return useMutation(apiClient.exportBallot);
return useQuery(
this.queryKey(input),
() => apiClient.getBallotPreviewPdf(input),
// Never cache PDFs, that way we don't have to worry about invalidating them
{ staleTime: 0, cacheTime: 0 }
);
},
} as const;

Expand Down
6 changes: 5 additions & 1 deletion apps/design/frontend/src/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ export function App({
apiClient?: ApiClient;
}): JSX.Element {
return (
<AppBase defaultColorMode="desktop" defaultSizeMode="desktop">
<AppBase
defaultColorMode="desktop"
defaultSizeMode="desktop"
showScrollBars
>
<ErrorBoundary errorMessage={<ErrorScreen />}>
<ApiClientContext.Provider value={apiClient}>
<QueryClientProvider client={createQueryClient()}>
Expand Down
Loading

0 comments on commit c8fe63a

Please sign in to comment.