├── .browserslistrc ├── public ├── favicon.ico └── index.html ├── babel.config.js ├── src ├── assets │ ├── file-invoice-dollar-solid.png │ ├── icon-arrow-right.svg │ ├── icon-check.svg │ ├── icon-arrow-down.svg │ ├── icon-arrow-left.svg │ ├── icon-plus.svg │ ├── icon-delete.svg │ ├── icon-moon.svg │ └── illustration-empty.svg ├── main.js ├── firebase │ └── firebaseInit.js ├── router │ └── index.js ├── components │ ├── Loading.vue │ ├── Navigation.vue │ ├── Modal.vue │ ├── Invoice.vue │ └── InvoiceModal.vue ├── App.vue ├── store │ └── index.js └── views │ ├── Home.vue │ └── InvoiceView.vue ├── .gitignore ├── README.md ├── .eslintrc.js └── package.json /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnkomarnicki/vue-invoice-app/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/file-invoice-dollar-solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnkomarnicki/vue-invoice-app/HEAD/src/assets/file-invoice-dollar-solid.png -------------------------------------------------------------------------------- /src/assets/icon-arrow-right.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-check.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-arrow-down.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | import store from './store' 5 | 6 | createApp(App).use(store).use(router).mount('#app') 7 | -------------------------------------------------------------------------------- /src/assets/icon-arrow-left.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-plus.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/icon-delete.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # invoice 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended' 9 | ], 10 | parserOptions: { 11 | parser: 'babel-eslint' 12 | }, 13 | rules: { 14 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off', 15 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off' 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/firebase/firebaseInit.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app"; 2 | import "firebase/firestore"; 3 | 4 | var firebaseConfig = { 5 | apiKey: "AIzaSyBtX0zbq8OWFoczz-D55IEQ-zQxcmjZ6BU", 6 | authDomain: "invoice-app-yt.firebaseapp.com", 7 | projectId: "invoice-app-yt", 8 | storageBucket: "invoice-app-yt.appspot.com", 9 | messagingSenderId: "638958069840", 10 | appId: "1:638958069840:web:2a359ba0cf4b8c7b648955", 11 | }; 12 | 13 | const firebaseApp = firebase.initializeApp(firebaseConfig); 14 | 15 | export default firebaseApp.firestore(); 16 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import { createRouter, createWebHistory } from "vue-router"; 2 | import Home from "../views/Home.vue"; 3 | import InvoiceView from "../views/InvoiceView.vue"; 4 | 5 | const routes = [ 6 | { 7 | path: "/", 8 | name: "Home", 9 | component: Home, 10 | }, 11 | { 12 | path: "/invoice/:invoiceId", 13 | name: "Invoice", 14 | component: InvoiceView, 15 | }, 16 | ]; 17 | 18 | const router = createRouter({ 19 | history: createWebHistory(process.env.BASE_URL), 20 | routes, 21 | }); 22 | 23 | export default router; 24 | -------------------------------------------------------------------------------- /src/assets/icon-moon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "invoice", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "uid": "^2.0.0", 13 | "vue": "^3.0.11", 14 | "vue-router": "^4.0.0-0", 15 | "vuex": "^4.0.0-0" 16 | }, 17 | "devDependencies": { 18 | "@vue/cli-plugin-babel": "~4.5.0", 19 | "@vue/cli-plugin-eslint": "~4.5.0", 20 | "@vue/cli-plugin-router": "~4.5.0", 21 | "@vue/cli-plugin-vuex": "~4.5.0", 22 | "@vue/cli-service": "~4.5.0", 23 | "@vue/compiler-sfc": "^3.0.0", 24 | "babel-eslint": "^10.1.0", 25 | "eslint": "^6.7.2", 26 | "eslint-plugin-vue": "^7.0.0", 27 | "node-sass": "^4.12.0", 28 | "sass-loader": "^8.0.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/Loading.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 43 | -------------------------------------------------------------------------------- /src/components/Navigation.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 14 | 15 | 43 | -------------------------------------------------------------------------------- /src/components/Modal.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 37 | 38 | 67 | -------------------------------------------------------------------------------- /src/components/Invoice.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 31 | 32 | 75 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 58 | 59 | 206 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { createStore } from "vuex"; 2 | import db from "../firebase/firebaseInit"; 3 | 4 | export default createStore({ 5 | state: { 6 | invoiceData: [], 7 | invoiceModal: null, 8 | modalActive: null, 9 | invoicesLoaded: null, 10 | currentInvoiceArray: null, 11 | editInvoice: null, 12 | }, 13 | mutations: { 14 | TOGGLE_INVOICE(state) { 15 | state.invoiceModal = !state.invoiceModal; 16 | }, 17 | TOGGLE_MODAL(state) { 18 | state.modalActive = !state.modalActive; 19 | }, 20 | SET_INVOICE_DATA(state, payload) { 21 | state.invoiceData.push(payload); 22 | }, 23 | INVOICES_LOADED(state) { 24 | state.invoicesLoaded = true; 25 | }, 26 | SET_CURRENT_INVOICE(state, payload) { 27 | state.currentInvoiceArray = state.invoiceData.filter((invoice) => { 28 | return invoice.invoiceId === payload; 29 | }); 30 | }, 31 | TOGGLE_EDIT_INVOICE(state) { 32 | state.editInvoice = !state.editInvoice; 33 | }, 34 | DELETE_INVOICE(state, payload) { 35 | state.invoiceData = state.invoiceData.filter((invoice) => invoice.docId !== payload); 36 | }, 37 | UPDATE_STATUS_TO_PAID(state, payload) { 38 | state.invoiceData.forEach((invoice) => { 39 | if (invoice.docId === payload) { 40 | invoice.invoicePaid = true; 41 | invoice.invoicePending = false; 42 | } 43 | }); 44 | }, 45 | UPDATE_STATUS_TO_PENDING(state, payload) { 46 | state.invoiceData.forEach((invoice) => { 47 | if (invoice.docId === payload) { 48 | invoice.invoicePaid = false; 49 | invoice.invoicePending = true; 50 | invoice.invoiceDraft = false; 51 | } 52 | }); 53 | }, 54 | }, 55 | actions: { 56 | async GET_INVOICES({ commit, state }) { 57 | const getData = db.collection("invoices"); 58 | const results = await getData.get(); 59 | results.forEach((doc) => { 60 | if (!state.invoiceData.some((invoice) => invoice.docId === doc.id)) { 61 | const data = { 62 | docId: doc.id, 63 | invoiceId: doc.data().invoiceId, 64 | billerStreetAddress: doc.data().billerStreetAddress, 65 | billerCity: doc.data().billerCity, 66 | billerZipCode: doc.data().billerZipCode, 67 | billerCountry: doc.data().billerCountry, 68 | clientName: doc.data().clientName, 69 | clientEmail: doc.data().clientEmail, 70 | clientStreetAddress: doc.data().clientStreetAddress, 71 | clientCity: doc.data().clientCity, 72 | clientZipCode: doc.data().clientZipCode, 73 | clientCountry: doc.data().clientCountry, 74 | invoiceDateUnix: doc.data().invoiceDateUnix, 75 | invoiceDate: doc.data().invoiceDate, 76 | paymentTerms: doc.data().paymentTerms, 77 | paymentDueDateUnix: doc.data().paymentDueDateUnix, 78 | paymentDueDate: doc.data().paymentDueDate, 79 | productDescription: doc.data().productDescription, 80 | invoiceItemList: doc.data().invoiceItemList, 81 | invoiceTotal: doc.data().invoiceTotal, 82 | invoicePending: doc.data().invoicePending, 83 | invoiceDraft: doc.data().invoiceDraft, 84 | invoicePaid: doc.data().invoicePaid, 85 | }; 86 | commit("SET_INVOICE_DATA", data); 87 | } 88 | }); 89 | commit("INVOICES_LOADED"); 90 | }, 91 | async UPDATE_INVOICE({ commit, dispatch }, { docId, routeId }) { 92 | commit("DELETE_INVOICE", docId); 93 | await dispatch("GET_INVOICES"); 94 | commit("TOGGLE_INVOICE"); 95 | commit("TOGGLE_EDIT_INVOICE"); 96 | commit("SET_CURRENT_INVOICE", routeId); 97 | }, 98 | async DELETE_INVOICE({ commit }, docId) { 99 | const getInvoice = db.collection("invoices").doc(docId); 100 | await getInvoice.delete(); 101 | commit("DELETE_INVOICE", docId); 102 | }, 103 | async UPDATE_STATUS_TO_PAID({ commit }, docId) { 104 | const getInvoice = db.collection("invoices").doc(docId); 105 | await getInvoice.update({ 106 | invoicePaid: true, 107 | invoicePending: false, 108 | }); 109 | commit("UPDATE_STATUS_TO_PAID", docId); 110 | }, 111 | async UPDATE_STATUS_TO_PENDING({ commit }, docId) { 112 | const getInvoice = db.collection("invoices").doc(docId); 113 | await getInvoice.update({ 114 | invoicePaid: false, 115 | invoicePending: true, 116 | invoiceDraft: false, 117 | }); 118 | commit("UPDATE_STATUS_TO_PENDING", docId); 119 | }, 120 | }, 121 | modules: {}, 122 | }); 123 | -------------------------------------------------------------------------------- /src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 41 | 42 | 94 | 95 | 197 | -------------------------------------------------------------------------------- /src/views/InvoiceView.vue: -------------------------------------------------------------------------------- 1 | 100 | 101 | 153 | 154 | 346 | -------------------------------------------------------------------------------- /src/components/InvoiceModal.vue: -------------------------------------------------------------------------------- 1 | 124 | 125 | 346 | 347 | 512 | -------------------------------------------------------------------------------- /src/assets/illustration-empty.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------