add prettier and format all

This commit is contained in:
Thomas Ruoff
2021-02-08 22:51:35 +01:00
parent d7044f5f78
commit 78af180567
24 changed files with 266 additions and 297 deletions

12
.editorconfig Normal file
View File

@@ -0,0 +1,12 @@
; EditorConfig file: http://EditorConfig.org
; Install the "EditorConfig" plugin into Sublime Text to use
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

1
.prettierignore Normal file
View File

@@ -0,0 +1 @@
.next

7
.prettierrc Normal file
View File

@@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false,
"singleQuote": true,
"printWidth": 120
}

View File

@@ -1,8 +1,8 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import App from './App'; import App from './App'
it('renders without crashing', () => { it('renders without crashing', () => {
const div = document.createElement('div'); const div = document.createElement('div')
ReactDOM.render(<App />, div); ReactDOM.render(<App />, div)
}); })

View File

@@ -1,25 +1,27 @@
import React, { Component } from 'react'; import React, { Component } from 'react'
import LetterOptions from './LetterOptions'; import LetterOptions from './LetterOptions'
import Button from './Button'; import Button from './Button'
import Preview from './Preview'; import Preview from './Preview'
import LatestList from './LatestList'; import LatestList from './LatestList'
import {generatePdf, getLatest, removeLatest} from './apiHelper'; import { generatePdf, getLatest, removeLatest } from './apiHelper'
//import './App.css'; //import './App.css';
class App extends Component { class App extends Component {
componentDidMount() { componentDidMount() {
this._getLatest(); this._getLatest()
} }
render() { render() {
const state = this.state || {}; const state = this.state || {}
return ( return (
<div className="home"> <div className="home">
<div> <div>
<LetterOptions onChange={this._onChange.bind(this)} {...state.options}/> <LetterOptions onChange={this._onChange.bind(this)} {...state.options} />
<LatestList latest={state.latest} onSelect={this._onSelectLatest.bind(this)} <LatestList
latest={state.latest}
onSelect={this._onSelectLatest.bind(this)}
onRemove={this._onRemoveLatest.bind(this)} onRemove={this._onRemoveLatest.bind(this)}
/> />
</div> </div>
@@ -28,73 +30,67 @@ class App extends Component {
<Button onClick={this._onGenerate.bind(this)}>Backe PDF</Button> <Button onClick={this._onGenerate.bind(this)}>Backe PDF</Button>
<Button onClick={this._onClear.bind(this)}>Alles Löschen</Button> <Button onClick={this._onClear.bind(this)}>Alles Löschen</Button>
</div> </div>
<Preview <Preview pdfUrl={state.pdfUrl} pdfIsLoading={state.pdfIsLoading} pdfError={state.pdfError} />
pdfUrl={state.pdfUrl}
pdfIsLoading={state.pdfIsLoading}
pdfError={state.pdfError}
/>
</div> </div>
</div> </div>
); )
} }
_onSelectLatest(selectedOptions) { _onSelectLatest(selectedOptions) {
this.setState({options: Object.assign({}, selectedOptions)}); this.setState({ options: Object.assign({}, selectedOptions) })
} }
_onRemoveLatest(item) { _onRemoveLatest(item) {
removeLatest(item) removeLatest(item).then(() => {
.then(() => { const latest = this.state.latest.filter((curr) => curr.id !== item.id)
const latest = this.state.latest
.filter(curr => curr.id !== item.id);
this.setState({ latest }); this.setState({ latest })
}); })
} }
_onClear() { _onClear() {
window.location.reload(); window.location.reload()
} }
_onGenerate() { _onGenerate() {
const state = this.state || {}; const state = this.state || {}
this.setState({ this.setState({
pdfIsLoading: true, pdfIsLoading: true,
pdfError: null pdfError: null,
}) })
generatePdf(state.options) generatePdf(state.options)
.then((data) => { .then((data) => {
const {id} = data; const { id } = data
this.setState({ this.setState({
pdfIsLoading: false, pdfIsLoading: false,
pdfUrl: `/api/pdf/${id}` pdfUrl: `/api/pdf/${id}`,
}); })
}) })
.catch((error) => { .catch((error) => {
this.setState({ this.setState({
pdfIsLoading: false, pdfIsLoading: false,
pdfError: error.message, pdfError: error.message,
pdfUrl: null pdfUrl: null,
}); })
}) })
.then(() => this._getLatest()) .then(() => this._getLatest())
} }
_getLatest() { _getLatest() {
getLatest() getLatest()
.then(latest => this.setState({latest})) .then((latest) => this.setState({ latest }))
.catch(err => console.error('Unable to get latest:', err)); .catch((err) => console.error('Unable to get latest:', err))
} }
_onChange(name, event) { _onChange(name, event) {
if (!name) { if (!name) {
return; return
} }
const value = event && event.target && event.target.value; const value = event && event.target && event.target.value
const options = Object.assign({}, this.state.options, {[name]: value || undefined}); const options = Object.assign({}, this.state.options, { [name]: value || undefined })
this.setState({options}); this.setState({ options })
} }
} }
export default App; export default App

View File

@@ -1,7 +1,9 @@
import React from 'react'; import React from 'react'
export default function(props) { export default function (props) {
return ( return (
<button className="p-button" onClick={props.onClick}>{props.children}</button> <button className="p-button" onClick={props.onClick}>
); {props.children}
</button>
)
} }

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react'
export default function() { export default function () {
return ( return (
<header className="header"> <header className="header">
<h1>PDFer</h1> <h1>PDFer</h1>

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react'
export default function(props) { export default function (props) {
return ( return (
<label> <label>
{props.text} {props.text}
@@ -12,5 +12,5 @@ export default function(props) {
value={props.value} value={props.value}
/> />
</label> </label>
); )
} }

View File

@@ -1,24 +1,26 @@
import React from 'react'; import React from 'react'
import Button from './Button'; import Button from './Button'
export default function(props) { export default function (props) {
const latest = props.latest || []; const latest = props.latest || []
const latestElements = latest.map(item => { const latestElements = latest.map((item) => {
const created = new Date(item.created); const created = new Date(item.created)
const hrefId = `#item-${item.id}`; const hrefId = `#item-${item.id}`
return ( return (
<li key={item.id}> <li key={item.id}>
<a href={hrefId} onClick={() => props.onSelect(item)}>{created.toLocaleString()}</a> <a href={hrefId} onClick={() => props.onSelect(item)}>
<span> </span> {created.toLocaleString()}
</a>
<span> </span>
<Button onClick={() => props.onRemove(item)}>Remove</Button> <Button onClick={() => props.onRemove(item)}>Remove</Button>
</li> </li>
); )
}); })
return ( return (
<div> <div>
<h4>Vergangene Briefe:</h4> <h4>Vergangene Briefe:</h4>
<ul>{latestElements}</ul> <ul>{latestElements}</ul>
</div> </div>
); )
} }

View File

@@ -1,14 +1,12 @@
import React from 'react'; import React from 'react'
import Header from './Header'; import Header from './Header'
export default function(props) { export default function (props) {
return ( return (
<div id="app"> <div id="app">
<Header /> <Header />
<div id="content"> <div id="content">{props.children}</div>
{ props.children }
</div>
</div> </div>
); )
} }

View File

@@ -1,33 +1,33 @@
import React from 'react'; import React from 'react'
import Collapsible from 'react-collapsible'; import Collapsible from 'react-collapsible'
import Input from './Input'; import Input from './Input'
import TextAreaInput from './TextAreaInput'; import TextAreaInput from './TextAreaInput'
import Select from './Select'; import Select from './Select'
//import './LetterOptions.css'; //import './LetterOptions.css';
const EXAMPLE_ADDRESS = 'Max Mustermann\nMusterstr. 73\n12345 Musterstadt'; const EXAMPLE_ADDRESS = 'Max Mustermann\nMusterstr. 73\n12345 Musterstadt'
export default function(props) { export default function (props) {
const templateTypeOptions = [ const templateTypeOptions = [
{ {
value: 'brief-fam', value: 'brief-fam',
name: 'Familie' name: 'Familie',
}, },
{ {
value: 'brief-valerie', value: 'brief-valerie',
name: 'Valerie' name: 'Valerie',
}, },
{ {
value: 'brief-rain', value: 'brief-rain',
name: 'Rain Baumeister' name: 'Rain Baumeister',
}, },
{ {
value: 'brief-thomas', value: 'brief-thomas',
name: 'Thomas' name: 'Thomas',
} },
]; ]
return ( return (
<div className="letter-options"> <div className="letter-options">
@@ -46,53 +46,16 @@ export default function(props) {
onchange={props.onChange} onchange={props.onChange}
value={props.address} value={props.address}
/> />
<Input <Input name="subject" text="Betreff" placeholder="Betreffzeile" onchange={props.onChange} value={props.subject} />
name="subject"
text="Betreff"
placeholder="Betreffzeile"
onchange={props.onChange}
value={props.subject}
/>
<Collapsible trigger="Bezugszeichenzeile"> <Collapsible trigger="Bezugszeichenzeile">
<Input <Input name="yourRef" text="Ihr Zeichen" onchange={props.onChange} value={props.yourRef} />
name="yourRef" <Input name="yourMail" text="Ihr Schreiben vom" onchange={props.onChange} value={props.yourMail} />
text="Ihr Zeichen" <Input name="myRef" text="Unser Zeichen" onchange={props.onChange} value={props.myRef} />
onchange={props.onChange} <Input name="customer" text="Kundernummer" onchange={props.onChange} value={props.customer} />
value={props.yourRef} <Input name="invoice" text="Rechnung" onchange={props.onChange} value={props.invoice} />
/>
<Input
name="yourMail"
text="Ihr Schreiben vom"
onchange={props.onChange}
value={props.yourMail}
/>
<Input
name="myRef"
text="Unser Zeichen"
onchange={props.onChange}
value={props.myRef}
/>
<Input
name="customer"
text="Kundernummer"
onchange={props.onChange}
value={props.customer}
/>
<Input
name="invoice"
text="Rechnung"
onchange={props.onChange}
value={props.invoice}
/>
</Collapsible> </Collapsible>
<Collapsible trigger="Sonstige Einstellungen"> <Collapsible trigger="Sonstige Einstellungen">
<Input <Input name="date" text="Datum" placeholder="Heute" onchange={props.onChange} value={props.date} />
name="date"
text="Datum"
placeholder="Heute"
onchange={props.onChange}
value={props.date}
/>
<Input <Input
name="opening" name="opening"
text="Eröffnung" text="Eröffnung"
@@ -107,12 +70,7 @@ export default function(props) {
onchange={props.onChange} onchange={props.onChange}
value={props.closing} value={props.closing}
/> />
<Input <Input name="specialMail" text="Versandhinweis" onchange={props.onChange} value={props.specialMail} />
name="specialMail"
text="Versandhinweis"
onchange={props.onChange}
value={props.specialMail}
/>
</Collapsible> </Collapsible>
<TextAreaInput <TextAreaInput
name="body" name="body"
@@ -122,5 +80,5 @@ export default function(props) {
value={props.body} value={props.body}
/> />
</div> </div>
); )
}; }

View File

@@ -1,10 +1,8 @@
import React from 'react'; import React from 'react'
export default props => { export default (props) => {
if (props.pdfIsLoading) { if (props.pdfIsLoading) {
return ( return <div>Lade&hellip;</div>
<div>Lade&hellip;</div>
);
} }
const errorStyles = { const errorStyles = {
@@ -12,26 +10,27 @@ export default props => {
color: 'white', color: 'white',
backgroundColor: '#d81e1e', backgroundColor: '#d81e1e',
borderRadius: '3px', borderRadius: '3px',
}; }
if (props.pdfError) { if (props.pdfError) {
return ( return (
<div style={errorStyles}><span role="img" aria-label="Crying Man">😢</span> {props.pdfError}</div> <div style={errorStyles}>
); <span role="img" aria-label="Crying Man">
😢
</span>{' '}
{props.pdfError}
</div>
)
} }
if (!props.pdfUrl) { if (!props.pdfUrl) {
return ( return <div>Knopf drücken dann gibts hier was zu sehen!</div>
<div>Knopf drücken dann gibts hier was zu sehen!</div>
);
} }
const styles = { const styles = {
width: '700px', width: '700px',
height: '1050px' height: '1050px',
}; }
return ( return <embed src={props.pdfUrl} style={styles} type="application/pdf" />
<embed src={props.pdfUrl} style={styles} type="application/pdf" /> }
);
};

View File

@@ -1,18 +1,18 @@
import React from 'react'; import React from 'react'
export default (props) => { export default (props) => {
const { options = [] } = props; const { options = [] } = props
return ( return (
<label> <label>
{props.text} {props.text}
<select <select className={props.name} onChange={props.onchange.bind(null, props.name)} value={props.value}>
className={props.name} {options.map((option) => (
onChange={props.onchange.bind(null, props.name)} <option value={option.value} key={option.value}>
value={props.value} {option.name}
> </option>
{options.map((option) => <option value={option.value} key={option.value}>{option.name}</option>)} ))}
</select> </select>
</label> </label>
); )
} }

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react'
export default function(props) { export default function (props) {
return ( return (
<label> <label>
{props.text} {props.text}
@@ -11,5 +11,5 @@ export default function(props) {
value={props.value} value={props.value}
/> />
</label> </label>
); )
} }

View File

@@ -1,32 +1,31 @@
function checkStatus(res) { function checkStatus(res) {
if (res.status < 200 || res.status >= 400) { if (res.status < 200 || res.status >= 400) {
throw new Error(`Something went wrong (Status ${res.status}) - I do feel very sorry!`); throw new Error(`Something went wrong (Status ${res.status}) - I do feel very sorry!`)
} }
return res; return res
} }
export function generatePdf(state){ export function generatePdf(state) {
const options = { const options = {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json',
}, },
body: JSON.stringify(state) body: JSON.stringify(state),
}; }
return fetch('/api/pdf/generate/brief', options) return fetch('/api/pdf/generate/brief', options)
.then(checkStatus) .then(checkStatus)
.then(res => res.json()); .then((res) => res.json())
} }
export function getLatest() { export function getLatest() {
return fetch('/api/pdf/latest') return fetch('/api/pdf/latest')
.then(checkStatus) .then(checkStatus)
.then(res => res.json()); .then((res) => res.json())
} }
export function removeLatest(item) { export function removeLatest(item) {
return fetch(`/api/pdf/latest/${item.id}`, {method: 'DELETE'}) return fetch(`/api/pdf/latest/${item.id}`, { method: 'DELETE' }).then(checkStatus)
.then(checkStatus);
} }

View File

@@ -1,9 +1,6 @@
import React from 'react'; import React from 'react'
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom'
import App from './App'; import App from './App'
import './index.css'; import './index.css'
ReactDOM.render( ReactDOM.render(<App />, document.getElementById('root'))
<App />,
document.getElementById('root')
);

View File

@@ -2,50 +2,49 @@ import { mkdir, writeFile } from 'fs'
import { spawn } from 'child_process' import { spawn } from 'child_process'
import { v1 as uuidv1 } from 'uuid' import { v1 as uuidv1 } from 'uuid'
import { getDirPath, getDocPath } from './utils'; import { getDirPath, getDocPath } from './utils'
function copyToTemp(id: string, texDocument: string): Promise<string> { function copyToTemp(id: string, texDocument: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const dirPath = getDirPath(id); const dirPath = getDirPath(id)
mkdir(dirPath, (err) => { mkdir(dirPath, (err) => {
if (err) { if (err) {
reject(err); reject(err)
return; return
} }
const docPath = getDocPath(id); const docPath = getDocPath(id)
writeFile(docPath, texDocument, (err) => { writeFile(docPath, texDocument, (err) => {
if (err) { if (err) {
reject(err); reject(err)
} }
resolve(id); resolve(id)
}); })
}); })
}); })
} }
function generateDoc(id: string) { function generateDoc(id: string) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const pdflatex = spawn('pdflatex', ['-interaction', 'nonstopmode', getDocPath(id)], { cwd: getDirPath(id) }); const pdflatex = spawn('pdflatex', ['-interaction', 'nonstopmode', getDocPath(id)], { cwd: getDirPath(id) })
pdflatex.stderr.on('data', (data) => { pdflatex.stderr.on('data', (data) => {
console.error('onData', data); console.error('onData', data)
}); })
pdflatex.on('close', (code) => { pdflatex.on('close', (code) => {
if (code > 0) { if (code > 0) {
reject(`pdflatex returned with code ${code}`); reject(`pdflatex returned with code ${code}`)
return; return
} }
console.log(`PDF ${id} generated`); console.log(`PDF ${id} generated`)
resolve(id); resolve(id)
}); })
}); })
} }
export default function(texDocument: string) { export default async function (texDocument: string) {
const id = uuidv1(); const id = uuidv1()
return copyToTemp(id, texDocument) const id_2 = await copyToTemp(id, texDocument)
.then(generateDoc); return generateDoc(id_2)
}; }

View File

@@ -1,14 +1,14 @@
const storeDir = process.env.JSON_STORE || '/tmp/pdfer-store/'; const storeDir = process.env.JSON_STORE || '/tmp/pdfer-store/'
import { promisify } from 'util'; import { promisify } from 'util'
import JsonStore from 'json-fs-store' import JsonStore from 'json-fs-store'
const store = JsonStore(storeDir); const store = JsonStore(storeDir)
console.log(`using json-store at ${storeDir}`); console.log(`using json-store at ${storeDir}`)
export const list = promisify(store.list); export const list = promisify(store.list)
export const load = promisify(store.load); export const load = promisify(store.load)
export const add = promisify(store.add); export const add = promisify(store.add)
export const remove = promisify(store.remove); export const remove = promisify(store.remove)

View File

@@ -1,30 +1,29 @@
function convertLineBreaks(string) { function convertLineBreaks(string) {
return string.replace(/\n/g, '\\\\'); return string.replace(/\n/g, '\\\\')
}; }
export function brief(options) { export function brief(options) {
const {
template = 'brief-fam',
subject = '',
yourRef = '',
yourRefName = 'Ihr Zeichen',
yourMail = '',
myRef = '',
customer = '',
invoice = '',
date = '\\today',
signature = '',
specialMail = '',
address = 'Max Mustermann\\\\Musterstrasse\\\\12345 Musterstadt',
opening = 'Sehr geehrte Damen und Herren',
body = '',
closing = 'Mit freundlichen Grüßen',
ps = '',
enclosing = '',
} = options
const { return `% brief document
template = 'brief-fam',
subject = '',
yourRef = '',
yourRefName = 'Ihr Zeichen',
yourMail = '',
myRef = '',
customer = '',
invoice = '',
date = '\\today',
signature = '',
specialMail = '',
address = 'Max Mustermann\\\\Musterstrasse\\\\12345 Musterstadt',
opening = 'Sehr geehrte Damen und Herren',
body = '',
closing = 'Mit freundlichen Grüßen',
ps = '',
enclosing = '',
} = options;
return `% brief document
\\documentclass{scrlttr2} \\documentclass{scrlttr2}
\\LoadLetterOption{${template}} \\LoadLetterOption{${template}}
@@ -56,5 +55,5 @@ export function brief(options) {
%\\encl{${enclosing}} %\\encl{${enclosing}}
\\end{letter} \\end{letter}
\\end{document}`; \\end{document}`
}; }

View File

@@ -1,13 +1,13 @@
import path from 'path'; import path from 'path'
export function getDirPath(id) { export function getDirPath(id) {
return `/tmp/pdfer-${id}`; return `/tmp/pdfer-${id}`
} }
export function getDocPath(id) { export function getDocPath(id) {
return path.join(getDirPath(id), 'doc.tex'); return path.join(getDirPath(id), 'doc.tex')
} }
export function getPdfPath(id) { export function getPdfPath(id) {
return path.join(getDirPath(id), 'doc.pdf'); return path.join(getDirPath(id), 'doc.pdf')
} }

4
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "with-typescript", "name": "pdfer",
"version": "1.0.0", "version": "2.0.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -5,7 +5,8 @@
"dev": "next", "dev": "next",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"type-check": "tsc" "type-check": "tsc",
"format": "prettier --write **/*.{ts,tsx,json}"
}, },
"dependencies": { "dependencies": {
"json-fs-store": "^1.0.1", "json-fs-store": "^1.0.1",

View File

@@ -3,7 +3,7 @@ import App from '../components/App'
const IndexPage = () => ( const IndexPage = () => (
<Layout title="Home | Next.js + TypeScript Example"> <Layout title="Home | Next.js + TypeScript Example">
<App/> <App />
</Layout> </Layout>
) )

View File

@@ -1,30 +1,29 @@
function convertLineBreaks(string) { function convertLineBreaks(string) {
return string.replace(/\n/g, '\\\\'); return string.replace(/\n/g, '\\\\')
}; }
export default (options) => { export default (options) => {
const {
template = 'brief-fam',
subject = '',
yourRef = '',
yourRefName = 'Ihr Zeichen',
yourMail = '',
myRef = '',
customer = '',
invoice = '',
date = '\\today',
signature = '',
specialMail = '',
address = 'Max Mustermann\\\\Musterstrasse\\\\12345 Musterstadt',
opening = 'Sehr geehrte Damen und Herren',
body = '',
closing = 'Mit freundlichen Grüßen',
ps = '',
enclosing = '',
} = options
const { return `% brief document
template = 'brief-fam',
subject = '',
yourRef = '',
yourRefName = 'Ihr Zeichen',
yourMail = '',
myRef = '',
customer = '',
invoice = '',
date = '\\today',
signature = '',
specialMail = '',
address = 'Max Mustermann\\\\Musterstrasse\\\\12345 Musterstadt',
opening = 'Sehr geehrte Damen und Herren',
body = '',
closing = 'Mit freundlichen Grüßen',
ps = '',
enclosing = '',
} = options;
return `% brief document
\\documentclass{scrlttr2} \\documentclass{scrlttr2}
\\LoadLetterOption{${template}} \\LoadLetterOption{${template}}
@@ -56,5 +55,5 @@ export default (options) => {
%\\encl{${enclosing}} %\\encl{${enclosing}}
\\end{letter} \\end{letter}
\\end{document}`; \\end{document}`
}; }