├── .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 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
43 |
--------------------------------------------------------------------------------
/src/components/Navigation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |

5 |
6 |
7 |
8 |
9 |
14 |
15 |
43 |
--------------------------------------------------------------------------------
/src/components/Modal.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Are you sure you want to exit? Your changes will not be saved?
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
37 |
38 |
67 |
--------------------------------------------------------------------------------
/src/components/Invoice.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #{{ invoice.invoiceId }}
5 | {{ invoice.paymentDueDate }}
6 | {{ invoice.clientName }}
7 |
8 |
9 |
${{ invoice.invoiceTotal }}
10 |
14 | Paid
15 | Draft
16 | Pending
17 |
18 |
19 |

20 |
21 |
22 |
23 |
24 |
25 |
31 |
32 |
75 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Sorry, this app is not supported on Mobile Devices
15 |
To use this app, please use a computer or Tablet
16 |
17 |
18 |
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 |
2 |
3 |
4 |
30 |
31 |
32 |
33 |
34 |
35 |

36 |
There is nothing here
37 |
Create a new invoice by clicking the New Invoice button and get started
38 |
39 |
40 |
41 |
42 |
94 |
95 |
197 |
--------------------------------------------------------------------------------
/src/views/InvoiceView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Go Back
5 |
6 |
7 |
38 |
39 |
40 |
41 |
42 |
43 |
#{{ currentInvoice.invoiceId }}
44 |
{{ currentInvoice.productDescription }}
45 |
46 |
47 |
{{ currentInvoice.billerStreetAddress }}
48 |
{{ currentInvoice.billerCity }}
49 |
{{ currentInvoice.billerZipCode }}
50 |
{{ currentInvoice.billerCountry }}
51 |
52 |
53 |
54 |
55 |
Invoice Date
56 |
57 | {{ currentInvoice.invoiceDate }}
58 |
59 |
Payment Date
60 |
61 | {{ currentInvoice.paymentDueDate }}
62 |
63 |
64 |
65 |
Bill To
66 |
{{ currentInvoice.clientName }}
67 |
{{ currentInvoice.clientStreetAddress }}
68 |
{{ currentInvoice.clientCity }}
69 |
{{ currentInvoice.clientZipCode }}
70 |
{{ currentInvoice.clientCountry }}
71 |
72 |
73 |
Sent To
74 |
{{ currentInvoice.clientEmail }}
75 |
76 |
77 |
78 |
79 |
80 |
Item Name
81 |
QTY
82 |
Price
83 |
Total
84 |
85 |
86 |
{{ item.itemName }}
87 |
{{ item.qty }}
88 |
{{ item.price }}
89 |
{{ item.total }}
90 |
91 |
92 |
93 |
Amount Due
94 |
{{ currentInvoice.invoiceTotal }}
95 |
96 |
97 |
98 |
99 |
100 |
101 |
153 |
154 |
346 |
--------------------------------------------------------------------------------
/src/components/InvoiceModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
123 |
124 |
125 |
346 |
347 |
512 |
--------------------------------------------------------------------------------
/src/assets/illustration-empty.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------