Skip to content

Commit 3709fb3

Browse files
committed
timesheet rendering
1 parent 7419da0 commit 3709fb3

26 files changed

+1533
-167
lines changed

notebooks/walkthrough/03-timetracking.ipynb

Lines changed: 148 additions & 156 deletions
Large diffs are not rendered by default.

notebooks/walkthrough/04-invoicing.ipynb

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,15 +300,19 @@
300300
{
301301
"cell_type": "markdown",
302302
"id": "910771d5-2f8d-4f7a-ad26-b180eddca0c4",
303-
"metadata": {},
303+
"metadata": {
304+
"tags": []
305+
},
304306
"source": [
305307
"... or render it to a file:"
306308
]
307309
},
308310
{
309311
"cell_type": "markdown",
310312
"id": "4247beb1-b36c-4bad-8cb5-9655a83583f4",
311-
"metadata": {},
313+
"metadata": {
314+
"tags": []
315+
},
312316
"source": [
313317
"_Set the path to a folder where you want your invoices to appear_:"
314318
]
@@ -357,19 +361,17 @@
357361
]
358362
},
359363
{
360-
"cell_type": "code",
361-
"execution_count": null,
362-
"id": "3b5bacc7-c361-45e3-821c-affb6a79975d",
364+
"cell_type": "markdown",
365+
"id": "5f2c80aa-5ad4-4662-be17-44813b0c67ef",
363366
"metadata": {},
364-
"outputs": [],
365367
"source": [
366-
"!qlmanage -p {invoice_path}"
368+
"_Your turn:_"
367369
]
368370
},
369371
{
370372
"cell_type": "code",
371373
"execution_count": null,
372-
"id": "688a97b8-cd72-4211-8e7a-6e6cd4ab99b7",
374+
"id": "9042bde9-bc0e-4957-9c55-a3e7ba42cefc",
373375
"metadata": {},
374376
"outputs": [],
375377
"source": []

templates/timesheet-anvil/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# HTML invoice template
2+
3+
This repo contains an HTML invoice template you can customize to fit your business needs.
4+
5+
The template is meant to be used either in a browser, or rendered as a PDF with Anvil's [HTML to PDF API](https://www.useanvil.com/docs/api/generate-pdf#html--css-to-pdf).
6+
7+
Here it is rendered in a browser:
8+
9+
<img width="772" alt="HTML invoice template" src="https://user-images.githubusercontent.com/69169/115467239-0ac27c00-a1e6-11eb-836b-190bf0ab264d.png" />
10+
11+
And rendered as a PDF via the HTML to PDF API:
12+
13+
<img width="744" alt="HTML to PDF invoice" src="https://user-images.githubusercontent.com/69169/115467145-e5357280-a1e5-11eb-942b-2e1a0361252b.png" />
14+
15+
## Rendering as a PDF
16+
17+
You can render the invoice with vanilla HTML and CSS or with React and styled-components. For use with React, see the [react-pdf](./react-pdf/README.md) directory.
18+
19+
First [sign up](https://app.useanvil.com/signup) for Anvil and get your [API key](https://www.useanvil.com/docs/api/getting-started#api-key).
20+
21+
There is an [example node script](./generate-pdf.js) you can use to generate the PDF from vanilla HTML and CSS. Run the following command at the root of this repo
22+
23+
```sh
24+
$ ANVIL_API_TOKEN=<YOURKEY> node ./generate-pdf.js && open ./generate.output.pdf
25+
```
26+
27+
Vanilla HTML and CSS for the invoice template is in the root of this repo. Feel free to view and edit these files to change the output PDF:
28+
29+
* [invoice.html](./invoice.html) - the invoice's HTML
30+
* [invoice.css](./invoice.css) - the invoice's CSS
31+
* [invoice-pdf.css](./invoice-pdf.css) - the invoice's PDF-specific CSS
32+
33+
The script simply reads the HTML and CSS from this repo, then generates a PDF.
34+
35+
```js
36+
function buildHTMLToPDFPayload () {
37+
const html = fs.readFileSync('./invoice.html').toString()
38+
const css =
39+
fs.readFileSync('./invoice.css').toString() +
40+
fs.readFileSync('./invoice-pdf.css').toString()
41+
return {
42+
type: 'html',
43+
title: 'HTML Invoice',
44+
data: {
45+
html,
46+
css,
47+
},
48+
}
49+
}
50+
const exampleData = buildHTMLToPDFPayload()
51+
const { statusCode, data, errors } = await client.generatePDF(exampleData)
52+
```
53+
54+
## Running in a browser
55+
56+
Just get a server running at the root of this repo, and visit `index.html`. e.g.
57+
58+
```sh
59+
python -m SimpleHTTPServer 8080
60+
```
61+
62+
Visit http://localhost:8080
63+
64+
See [index.html](https://github.com/anvilco/html-pdf-invoice-template/blob/main/index.html) for more information
65+
66+
## License
67+
68+
MIT
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Generate a PDF from the HTML code in this repo.
2+
//
3+
// Usage:
4+
// ANVIL_API_TOKEN=<YOURKEY> node ./generate-pdf.js && open ./generate.output.pdf
5+
6+
const fs = require('fs')
7+
const path = require('path')
8+
const Anvil = require('@anvilco/anvil')
9+
10+
const apiKey = process.env.ANVIL_API_TOKEN
11+
12+
function buildHTMLToPDFPayload () {
13+
const html = fs.readFileSync('./invoice.html').toString()
14+
const css =
15+
fs.readFileSync('./invoice.css').toString() +
16+
fs.readFileSync('./invoice-pdf.css').toString()
17+
return {
18+
type: 'html',
19+
title: 'HTML Invoice',
20+
data: {
21+
html,
22+
css,
23+
},
24+
page: {
25+
marginLeft: '60px',
26+
marginRight: '60px',
27+
},
28+
}
29+
}
30+
31+
async function main () {
32+
const client = new Anvil({
33+
apiKey,
34+
})
35+
36+
const exampleData = buildHTMLToPDFPayload()
37+
const { statusCode, data, errors } = await client.generatePDF(exampleData)
38+
39+
if (statusCode === 200) {
40+
const scriptDir = __dirname
41+
const outputFilePath = path.join(scriptDir, 'generate.output.pdf')
42+
fs.writeFileSync(outputFilePath, data, { encoding: null })
43+
} else {
44+
console.log(statusCode, JSON.stringify(errors || data, null, 2))
45+
}
46+
}
47+
48+
main()
49+
.then(() => {
50+
process.exit(0)
51+
})
52+
.catch((err) => {
53+
console.log(err.stack || err.message)
54+
process.exit(1)
55+
})
612 Bytes
Loading
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"name": "html-pdf-invoice-template",
3+
"version": "1.0.0",
4+
"description": "An HTML invoice template for use in a browser and with HTML to PDF generation.",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1",
8+
"react:generate-pdf": "yarn babel-node ./react-pdf/generate-pdf.js"
9+
},
10+
"repository": {
11+
"type": "git",
12+
"url": "git+https://github.com/anvilco/html-pdf-invoice-template.git"
13+
},
14+
"keywords": [
15+
"pdf",
16+
"html",
17+
"api",
18+
"invoice",
19+
"template"
20+
],
21+
"author": "Anvil Inc.",
22+
"license": "MIT",
23+
"bugs": {
24+
"url": "https://github.com/anvilco/html-pdf-invoice-template/issues"
25+
},
26+
"homepage": "https://github.com/anvilco/html-pdf-invoice-template#readme",
27+
"dependencies": {
28+
"@anvilco/anvil": "^2.8.1",
29+
"prop-types": "^15.7.2",
30+
"react": "^17.0.2",
31+
"react-dom": "^17.0.2",
32+
"styled-components": "^5.3.0"
33+
},
34+
"devDependencies": {
35+
"@babel/core": "^7.15.0",
36+
"@babel/node": "^7.14.9",
37+
"@babel/preset-env": "^7.15.0",
38+
"@babel/preset-react": "^7.14.5"
39+
}
40+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"presets": [
3+
"@babel/preset-env",
4+
"@babel/preset-react",
5+
]
6+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# React to PDF Invoice
2+
3+
This example uses React and styled-components to generate an invoice PDF. You can use this as a jumping off point to generate a PDF with your own custom React / styled-components code.
4+
5+
To run it, `yarn install` at the root of this repo, then run:
6+
7+
```sh
8+
ANVIL_API_TOKEN=your_token yarn react:generate-pdf && open ./generate-react.output.pdf
9+
```
10+
11+
See the [generate-pdf.js](./generate-pdf.js) script, and `components` directory here for the code.
12+
13+
<img width="754" alt="React to HTML to PDF" src="https://user-images.githubusercontent.com/69169/129096427-c32ed4f1-bb7b-4bda-86df-830f9f18a690.png">
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react'
2+
import styled, { createGlobalStyle } from 'styled-components'
3+
4+
import GlobalStyle from './GlobalStyle'
5+
6+
const FooterContainer = styled.div`
7+
margin-top: 30px;
8+
`
9+
10+
const Thanks = styled.div`
11+
font-size: 1.125em;
12+
13+
img {
14+
display: inline-block;
15+
position: relative;
16+
top: 1px;
17+
width: 16px;
18+
margin-right: 4px;
19+
}
20+
`
21+
22+
const Info = styled.div`
23+
position: running(footer);
24+
margin-top: -25px;
25+
font-size: 0.75em;
26+
color: #ccc;
27+
28+
span {
29+
padding: 0 5px;
30+
color: black;
31+
32+
&:last-child {
33+
padding-right: 0;
34+
}
35+
}
36+
`
37+
38+
// The `content` here references `position` from the FooterContainer
39+
const FooterPlacement = createGlobalStyle`
40+
@page {
41+
@bottom-left {
42+
content: element(footer);
43+
}
44+
}
45+
`
46+
47+
const Footer = () => (
48+
<FooterContainer>
49+
<FooterPlacement />
50+
<Info>
51+
<span>[email protected]</span>|{' '}
52+
<span>555 444 6666</span> |{' '}
53+
<span>useanvil.com</span>
54+
</Info>
55+
<Thanks>
56+
<img src="https://github.com/anvilco/html-pdf-invoice-template/raw/main/img/heart.png" alt="heart" />
57+
<span>Thank you!</span>
58+
</Thanks>
59+
</FooterContainer>
60+
)
61+
62+
export default Footer
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { createGlobalStyle } from 'styled-components'
2+
3+
const GlobalStyle = createGlobalStyle`
4+
body {
5+
font-size: 16px;
6+
}
7+
8+
table {
9+
width: 100%;
10+
border-collapse: collapse;
11+
}
12+
13+
table tr td {
14+
padding: 0;
15+
}
16+
17+
table tr td:last-child {
18+
text-align: right;
19+
}
20+
`
21+
22+
export default GlobalStyle

0 commit comments

Comments
 (0)