git ssb

0+

Rômulo Alves / plainbudget-next



Commit 546a29ac502f64ff85e2f6faae88604083593767

Initial commit

Rômulo Alves committed on 8/29/2018, 3:05:02 AM

Files changed

.dockerignoreadded
.gitignoreadded
Dockerfileadded
components/textarea.jsadded
next.config.jsadded
now.jsonadded
package-lock.jsonadded
package.jsonadded
pages/_app.jsadded
pages/_document.jsadded
pages/index.jsadded
server.jsadded
static/images/icons/icon-128x128.pngadded
static/images/icons/icon-144x144.pngadded
static/images/icons/icon-152x152.pngadded
static/images/icons/icon-192x192.pngadded
static/images/icons/icon-32x32.pngadded
static/images/icons/icon-384x384.pngadded
static/images/icons/icon-512x512.pngadded
static/images/icons/icon-72x72.pngadded
static/images/icons/icon-96x96.pngadded
static/manifest.jsonadded
utils/mobile.jsadded
utils/request.jsadded
.dockerignoreView
@@ -1,0 +1,8 @@
1 +*
2 +!components/
3 +!pages/
4 +!utils/
5 +!static/
6 +!next.config.js
7 +!server.js
8 +!package*.json
.gitignoreView
@@ -1,0 +1,3 @@
1 +node_modules/
2 +.next/
3 +.DS_Store
DockerfileView
@@ -1,0 +1,13 @@
1 +FROM mhart/alpine-node:10 as base
2 +WORKDIR /usr/src
3 +COPY package*.json /usr/src/
4 +RUN npm ci
5 +COPY . .
6 +RUN npm run build && npm install --production
7 +
8 +FROM mhart/alpine-node:base-10
9 +WORKDIR /usr/src
10 +ENV NODE_ENV="production"
11 +COPY --from=base /usr/src .
12 +EXPOSE 3000
13 +CMD ["node", "server.js"]
components/textarea.jsView
@@ -1,0 +1,94 @@
1 +import { Component, Fragment } from 'react';
2 +import { func, string } from 'prop-types';
3 +
4 +export default class Textarea extends Component {
5 + static propTypes = {
6 + value: string,
7 + onChange: func,
8 + onKeyDown: func,
9 + onClick: func
10 + }
11 +
12 + static defaultProps = {
13 + value: ''
14 + }
15 +
16 + constructor (props) {
17 + super(props);
18 +
19 + this.onChangeText = this.onChangeText.bind(this);
20 + this.onKeyDown = this.onKeyDown.bind(this);
21 + this.onClick = this.onClick.bind(this);
22 + }
23 +
24 + onChangeText (event) {
25 + event.preventDefault();
26 +
27 + const { onChange } = this.props;
28 +
29 + if (!onChange) {
30 + return;
31 + }
32 +
33 + const { value } = event.target;
34 +
35 + return onChange(value);
36 + }
37 +
38 + onKeyDown (event) {
39 + const { onKeyDown: onPropKeyDown } = this.props;
40 +
41 + if (!onPropKeyDown) {
42 + return;
43 + }
44 +
45 + return onPropKeyDown(event);
46 + }
47 +
48 + onClick (event) {
49 + event.preventDefault()
50 +
51 + const { onClick: onPropClick } = this.props;
52 +
53 + if (!onPropClick) {
54 + return;
55 + }
56 +
57 + return onPropClick(event);
58 + }
59 +
60 + render () {
61 + const { value } = this.props;
62 +
63 + return (
64 + <Fragment>
65 + <textarea onChange={ this.onChangeText }
66 + onKeyDown={ this.onKeyDown }
67 + onClick={ this.onClick }
68 + value={ value }>
69 + </textarea>
70 + <style jsx>{`
71 + textarea {
72 + background: #f6f6f6;
73 + border: none;
74 + font-family: Fira Mono, monospace;
75 + font-size: 17px;
76 + height: 100vh;
77 + margin: 0;
78 + outline: none;
79 + padding: 20px;
80 + padding-left: 100px;
81 + resize: none;
82 + width: 100%;
83 + }
84 +
85 + @media (max-width: 680px) {
86 + textarea {
87 + padding-left: 40px;
88 + }
89 + }
90 + `}</style>
91 + </Fragment>
92 + );
93 + }
94 +}
next.config.jsView
@@ -1,0 +1,3 @@
1 +const withOffline = require('next-offline');
2 +
3 +module.exports = withOffline();
now.jsonView
@@ -1,0 +1,10 @@
1 +{
2 + "name": "plainbudget",
3 + "type": "docker",
4 + "features": {
5 + "cloud": "v2"
6 + },
7 + "alias": [
8 + "plainbudget.now.sh"
9 + ]
10 +}
package-lock.jsonView
The diff is too large to show. Use a local git client to view these changes.
Old file size: 0 bytes
New file size: 259667 bytes
package.jsonView
@@ -1,0 +1,31 @@
1 +{
2 + "name": "plainbudget-next",
3 + "version": "1.0.0",
4 + "description": "",
5 + "main": "index.js",
6 + "scripts": {
7 + "dev": "IS_DEVELOPMENT=true node server.js",
8 + "build": "next build",
9 + "start": "node server.js"
10 + },
11 + "repository": {
12 + "type": "git",
13 + "url": "git+https://github.com/romuloalves/plainbudget-next.git"
14 + },
15 + "author": "",
16 + "license": "ISC",
17 + "bugs": {
18 + "url": "https://github.com/romuloalves/plainbudget-next/issues"
19 + },
20 + "homepage": "https://github.com/romuloalves/plainbudget-next#readme",
21 + "dependencies": {
22 + "compression": "^1.7.3",
23 + "express": "^4.16.3",
24 + "next": "^6.1.1",
25 + "next-offline": "^2.11.0",
26 + "pbudget": "^0.2.8",
27 + "prop-types": "^15.6.2",
28 + "react": "^16.4.2",
29 + "react-dom": "^16.4.2"
30 + }
31 +}
pages/_app.jsView
@@ -1,0 +1,31 @@
1 +import App, { Container } from 'next/app';
2 +import React from 'react';
3 +
4 +import { isMobile } from '../utils/mobile';
5 +
6 +export default class PlainBudgetApp extends App {
7 + static async getInitialProps ({ Component, ctx }) {
8 + const { req } = ctx;
9 + const isMobileDevice = isMobile(req);
10 +
11 + ctx.isMobile = isMobileDevice;
12 +
13 + let pageProps = {};
14 +
15 + if (Component.getInitialProps) {
16 + pageProps = await Component.getInitialProps(ctx);
17 + }
18 +
19 + return { pageProps };
20 + }
21 +
22 + render () {
23 + const { Component, pageProps } = this.props;
24 +
25 + return (
26 + <Container>
27 + <Component { ...pageProps } />
28 + </Container>
29 + );
30 + }
31 +}
pages/_document.jsView
@@ -1,0 +1,49 @@
1 +import Document, { Head, Main, NextScript } from 'next/document';
2 +
3 +export default class MyDocument extends Document {
4 + static async getInitialProps(ctx) {
5 + const initialProps = await Document.getInitialProps(ctx);
6 +
7 + return { ...initialProps };
8 + }
9 +
10 + render() {
11 + return (
12 + <html lang="en">
13 + <Head>
14 + <title>plainbudget</title>
15 + <meta name="viewport" content="initial-scale=1.0, width=device-width" />
16 + <link rel="manifest" href="/static/manifest.json" />
17 +
18 + <meta name="mobile-web-app-capable" content="yes" />
19 + <meta name="apple-mobile-web-app-capable" content="yes" />
20 + <meta name="application-name" content="PlainBudget" />
21 + <meta name="apple-mobile-web-app-title" content="PlainBudget" />
22 + <meta name="theme-color" content="#49cf8b" />
23 + <meta name="msapplication-navbutton-color" content="#49cf8b" />
24 + <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
25 + <meta name="msapplication-starturl" content="/" />
26 + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
27 +
28 + <link rel="icon" href="/static/images/icons/icon-32x32.png" />
29 + <link rel="apple-touch-icon" href="/static/images/icons/icon-32x32.png" />
30 +
31 + <style>{`
32 + body {
33 + background: #fff;
34 + color: #000;
35 + font-family: Sorts Mill Goudy, serif;
36 + font-size: 20px;
37 + margin: 0;
38 + padding: 0;
39 + }
40 + `}</style>
41 + </Head>
42 + <body>
43 + <Main />
44 + <NextScript />
45 + </body>
46 + </html>
47 + )
48 + }
49 +}
pages/index.jsView
@@ -1,0 +1,58 @@
1 +import { Component } from 'react';
2 +import { Plainbudget } from 'pbudget';
3 +
4 +import Textarea from '../components/textarea';
5 +
6 +export default class Index extends Component {
7 + static getInitialProps (ctx) {
8 + return { isMobile: ctx.isMobile };
9 + }
10 +
11 + constructor (props) {
12 + super(props);
13 +
14 + this.compute = this.compute.bind(this);
15 +
16 + this.onTextareaKeyDown = this.onTextareaKeyDown.bind(this);
17 + this.onTextareaChange = this.onTextareaChange.bind(this);
18 + }
19 +
20 + state = {
21 + value: ''
22 + }
23 +
24 + compute () {
25 + const { value } = this.state;
26 + const calculated = Plainbudget.computeSheet(value);
27 +
28 + return this.setState({
29 + value: calculated
30 + });
31 + }
32 +
33 + onTextareaKeyDown (event) {
34 + if ((event.metaKey || event.ctrlKey) && event.keyCode === 13) {
35 + this.compute();
36 + }
37 + }
38 +
39 + onTextareaClick () {
40 + this.compute();
41 + }
42 +
43 + onTextareaChange (value) {
44 + return this.setState({ value });
45 + }
46 +
47 + render () {
48 + const { value } = this.state;
49 +
50 + return (
51 + <div>
52 + <Textarea value={ value }
53 + onKeyDown={ this.onTextareaKeyDown }
54 + onChange={ this.onTextareaChange } />
55 + </div>
56 + );
57 + }
58 +}
server.jsView
@@ -1,0 +1,33 @@
1 +const next = require('next');
2 +const express = require('express');
3 +const compression = require('compression');
4 +const { join } = require('path');
5 +
6 +const dev = process.env.IS_DEVELOPMENT || false;
7 +const port = process.env.PORT || 3000;
8 +
9 +const app = next({ dev });
10 +const handle = app.getRequestHandler();
11 +
12 +app.prepare()
13 + .then(function() {
14 + const server = express();
15 +
16 + if (!dev) {
17 + server.use(compression());
18 + }
19 +
20 + server.get('/service-worker.js', function(request, response) {
21 + const filePath = join(__dirname, '.next', 'service-worker.js');
22 +
23 + return app.serveStatic(request, response, filePath);
24 + });
25 +
26 + server.get('*', function(request, response) {
27 + return handle(request, response);
28 + });
29 +
30 + server.listen(port, function() {
31 + console.log(`> Ready on http://localhost:${port}`);
32 + });
33 + });
static/images/icons/icon-128x128.png
static/images/icons/icon-128x128.png
static/images/icons/icon-144x144.png
static/images/icons/icon-144x144.png
static/images/icons/icon-152x152.png
static/images/icons/icon-152x152.png
static/images/icons/icon-192x192.png
static/images/icons/icon-192x192.png
static/images/icons/icon-32x32.png
static/images/icons/icon-32x32.png
static/images/icons/icon-384x384.png
static/images/icons/icon-384x384.png
static/images/icons/icon-512x512.png
static/images/icons/icon-512x512.png
static/images/icons/icon-72x72.png
static/images/icons/icon-72x72.png
static/images/icons/icon-96x96.png
static/images/icons/icon-96x96.png
static/manifest.jsonView
@@ -1,0 +1,52 @@
1 +{
2 + "name": "PlainBudget",
3 + "short_name": "PlainBudget",
4 + "theme_color": "#49cf8b",
5 + "background_color": "#49cf8b",
6 + "display": "standalone",
7 + "Scope": "/",
8 + "start_url": "/",
9 + "icons": [
10 + {
11 + "src": "/static/images/icons/icon-72x72.png",
12 + "sizes": "72x72",
13 + "type": "image/png"
14 + },
15 + {
16 + "src": "/static/images/icons/icon-96x96.png",
17 + "sizes": "96x96",
18 + "type": "image/png"
19 + },
20 + {
21 + "src": "/static/images/icons/icon-128x128.png",
22 + "sizes": "128x128",
23 + "type": "image/png"
24 + },
25 + {
26 + "src": "/static/images/icons/icon-144x144.png",
27 + "sizes": "144x144",
28 + "type": "image/png"
29 + },
30 + {
31 + "src": "/static/images/icons/icon-152x152.png",
32 + "sizes": "152x152",
33 + "type": "image/png"
34 + },
35 + {
36 + "src": "/static/images/icons/icon-192x192.png",
37 + "sizes": "192x192",
38 + "type": "image/png"
39 + },
40 + {
41 + "src": "/static/images/icons/icon-384x384.png",
42 + "sizes": "384x384",
43 + "type": "image/png"
44 + },
45 + {
46 + "src": "/static/images/icons/icon-512x512.png",
47 + "sizes": "512x512",
48 + "type": "image/png"
49 + }
50 + ],
51 + "splash_pages": null
52 +}
utils/mobile.jsView
@@ -1,0 +1,17 @@
1 +import { getUserAgent } from '../utils/request';
2 +
3 +/**
4 + * @function isMobile
5 + * @description Returns if the current device is mobile
6 + * @param {Object} request Http request
7 + * @return {Boolean} Is mobile or not
8 + */
9 +export const isMobile = (request) => {
10 + const userAgent = getUserAgent(request);
11 +
12 + if (/Mobi/.test(userAgent)) {
13 + return true;
14 + }
15 +
16 + return false;
17 +};
utils/request.jsView
@@ -1,0 +1,15 @@
1 +/**
2 + * @function getUserAgent
3 + * @description Returns the user agent of the request or from the navigator
4 + * @param {Object} request Http request
5 + * @return {String} User Agent
6 + */
7 +export const getUserAgent = request => {
8 + if (request) {
9 + return request.headers['user-agent'];
10 + } else if (typeof window !== 'undefined' && window.navigator) {
11 + return window.navigator.userAgent;
12 + }
13 +
14 + return '';
15 +};

Built with git-ssb-web