├── .gitignore
├── LICENSE
├── README.md
├── babel.config.js
├── cover.png
├── functions
├── getUserData.js
├── hello.js
└── serviceAccountKey.json
├── netlify.toml
├── package.json
├── public
├── _redirects
├── favicon.ico
└── index.html
├── src
├── App.vue
├── assets
│ ├── DCHQ-dark.svg
│ ├── DCHQ-small-dark.svg
│ ├── DCHQ-small.svg
│ ├── DCHQ.svg
│ ├── contentful.png
│ ├── dropbox.png
│ ├── figma.png
│ ├── logo.png
│ ├── logout.svg
│ ├── moon.svg
│ ├── notion.png
│ ├── slack.png
│ └── sun.svg
├── components
│ ├── Header.vue
│ ├── HelloWorld.vue
│ ├── Notification.vue
│ ├── RequestAccount.vue
│ └── ThemeSwitch.vue
├── firebase.js
├── functions
│ ├── getUserData.js
│ ├── hello.js
│ └── serviceAccountKey.json
├── global-styles
│ ├── colors.scss
│ └── typography.scss
├── main.js
├── router.js
├── store.js
└── views
│ ├── Home.vue
│ ├── Manage.vue
│ ├── SignInFlow
│ ├── Recover.vue
│ ├── Request.vue
│ └── SignIn.vue
│ └── Team.vue
├── vue.config.js
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | # local env files
6 | .env.local
7 | .env.*.local
8 |
9 | # Log files
10 | npm-debug.log*
11 | yarn-debug.log*
12 | yarn-error.log*
13 |
14 | # Editor directories and files
15 | .idea
16 | .vscode
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw*
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Thomas Wang
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vue.js for Designers
8 |
9 |
10 | [Vue.js](https://github.com/vuejs/vue) is a powerful, easy-to-use JavaScript framework that is component-based, just like React. This course will walk you through how to build a beautiful admin dashboard that you can use for your own data on the web. Create stunning charts, delightful animations, and powerful administrative commands. Full video tutorials: https://designcode.io/vue.
11 |
12 | ## ⚡️ Course Downloads
13 |
14 | 1. [Intro to Vue.js](https://github.com/thomaswangio/vue-admin-dashboard/tree/293b57a08ab745a3d42dfd99c098833541719772)
15 | 2. [Global Styling with Sass](https://github.com/thomaswangio/vue-admin-dashboard/tree/ab0f7dcfe575b338a443ca3680f8ba99111a52a8)
16 | 3. [Single File Components](https://github.com/thomaswangio/vue-admin-dashboard/tree/05dbe70a9745edcf42be96d02e238b83ae5143bf)
17 | 4. [Dark Mode Part 1](https://github.com/thomaswangio/vue-admin-dashboard/tree/a4ab92e0f5a6ad186d5a785c3f86228455651c4a)
18 | 5. [Dark Mode Part 2](https://github.com/thomaswangio/vue-admin-dashboard/tree/f3366f209d1388ce73a51a7bf70056eb78d52de4)
19 | 6. [Animations and Transitions](https://github.com/thomaswangio/vue-admin-dashboard/tree/ec9ac65ece01830d5d2060c610a210d3a2a71b3f)
20 | 7. [Accounts with Netlify](https://github.com/thomaswangio/vue-admin-dashboard/tree/5700810705c56fa8a4a8657cb00cee89a14d4ee3)
21 | 8. [Accounts with Slack](https://github.com/thomaswangio/vue-admin-dashboard/tree/ea78a10cac69f8c3053c1f9fa8538c63143197e2)
22 | 9. [Charts and Data Visualization](https://github.com/thomaswangio/vue-admin-dashboard/tree/71d8bb1e0f8763222554d85ab7b14541cb9bcd60)
23 | 10. [Database with Firestore](https://github.com/thomaswangio/vue-admin-dashboard/tree/bc61877bfda94bb112fd5afa362eb2d37275ab13)
24 | 11. [Lambda Functions with Netlify](https://github.com/thomaswangio/vue-admin-dashboard/tree/0df2be6068f9160e90fa2a4d46011b1d7717597d)
25 |
26 | ## ⚙️ Install Yarn and Vue CLI
27 |
28 | Make sure that you have the [Yarn](https://yarnpkg.com/en/) installed, then install the [Vue CLI](https://cli.vuejs.org/):
29 |
30 | ```sh
31 | yarn global add @vue/cli
32 | ```
33 |
34 | If you have trouble with getting this project to run, please run the version of `@vue/cli` used in the course:
35 |
36 | ```sh
37 | yarn global add @vue/cli@3.3.0
38 | ```
39 |
40 | ## ✏️ Manage your Project from the Vue CLI GUI
41 |
42 | After installing the Vue CLI, you can open the Vue Project Manager by using the following command:
43 |
44 | ```sh
45 | vue ui
46 | ```
47 |
48 | Note: If you are importing this project instead of installing a new project from scratch, first run `yarn` in your project to install the dependencies.
49 |
50 | ```sh
51 | yarn
52 | ```
53 |
54 | Then you can run the project with:
55 |
56 | ```sh
57 | yarn serve
58 | ```
59 |
60 | ## 🎓 Learning Vue.js
61 |
62 | The Design+Code Vue.js course lives [here](https://designcode.io/vue). Start learning by creating this Web App!
63 |
64 | Looking for more guidance? Full documentation for Vue is [on their website](https://vuejs.org/). To see the documentation for the Vue CLL, which was used to create and configure this project, go [here](https://cli.vuejs.org/guide/).
65 |
66 | ## 👾 Demo
67 |
68 | Feel free to play around with the finished website at: https://vue-hq-2.netlify.app. The username is `support@designcode.io` and password is `123456789`.
69 |
70 | ## 🚀 Deploy
71 |
72 | Launch this site to Netlify by clicking below! Or go to [Netlify](https://www.netlify.app/) to deploy your own project.
73 |
74 | [](https://app.netlify.app/start/deploy?repository=https://github.com/thomaswangio/vue-admin-dashboard)
75 |
76 | ## 💭 Q&A and Issues
77 |
78 | Feel free to [ask any questions or note issues](https://github.com/thomaswangio/vue-admin-dashboard/issues/new)!
79 |
80 | - [Fixing dark and light text](https://github.com/thomaswangio/vue-admin-dashboard/issues/2)
81 | - [Using rem units instead of px](https://github.com/thomaswangio/vue-admin-dashboard/issues/3)
82 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ["@vue/app"]
3 | };
4 |
--------------------------------------------------------------------------------
/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/cover.png
--------------------------------------------------------------------------------
/functions/getUserData.js:
--------------------------------------------------------------------------------
1 | const { zipFunctions } = require("@netlify/zip-it-and-ship-it");
2 |
3 | zipFunctions("functions", "functions-dist")
4 |
5 | var admin = require("firebase-admin");
6 |
7 | var serviceAccount = require("./serviceAccountKey.json");
8 |
9 | admin.initializeApp({
10 | credential: admin.credential.cert(serviceAccount),
11 | databaseURL: "https://vue-hq.firebaseio.com"
12 | });
13 |
14 |
15 | exports.handler = function (event, context, callback) {
16 | const { user } = context.clientContext;
17 |
18 | // Do the user check here
19 | // If not user, callback a status code of 401, meaning unauthorized
20 |
21 | const { email, subscriptionId } = event.queryStringParameters;
22 |
23 | console.log("Searched email: " + email);
24 | console.log("Searched subscription ID: " + subscriptionId);
25 |
26 | const searchKey = email.length > 0 ? "email" : "subscriptionId"
27 | const searchQuery = email.length > 0 ? email : subscriptionId
28 |
29 | const firestore = admin.firestore();
30 |
31 | firestore.collection('users').where(searchKey, '==', searchQuery).limit(1).get().then(response => {
32 | if (response.empty) { return Promise.reject() }
33 |
34 | const userInfo = response.docs[0].data()
35 |
36 | console.log(userInfo)
37 |
38 | callback(null, {
39 | statusCode: 200,
40 | headers: {
41 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
42 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
43 | },
44 | body: JSON.stringify(userInfo)
45 | })
46 | }).catch(error => {
47 | console.log(error)
48 |
49 | callback(null, {
50 | statusCode: 500,
51 | headers: {
52 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
53 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
54 | },
55 | body: error
56 | })
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/functions/hello.js:
--------------------------------------------------------------------------------
1 | exports.handler = function (event, context, callback) {
2 | callback(null, {
3 | statusCode: 200,
4 | headers: {
5 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
6 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
7 | },
8 | body: "Hello, World"
9 | });
10 | }
--------------------------------------------------------------------------------
/functions/serviceAccountKey.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "service_account",
3 | "project_id": "vue-hq",
4 | "private_key_id": "7f290e3e535486a33eca36efd6d642872bb93c54",
5 | "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRKX1OTPLUB/gZ\nJlU7VgffVTiPuD6F3WsEgRpJ1o0BK4crGP1+R4VjdKcXgFyG9Mlt2S6Kzy2j/vLZ\ntjl7QdP72btOCr28+SlOYKLfz3i+HmzXMb+T1koJtfOAU/WVCR02Bhmx6asNq1Cy\nsUvuqFOFyCpI6iv8TgAiKKOUqLW1EUE/1mPOGbv9ToFdPaUpQNVfcpBUdO4dt9WF\nJf9gshuO2GgU4gIm3vY6RQtQ/ymOEYYKpVmU6PH+EIIjB2m8xkoR9tpYDGAsrps2\nznVIP6XNfqNWqic00BUF9QswhTMu1ti2G198RxGsv74dc/c80kb84TwIpNg1Je93\nftCm4ZmtAgMBAAECggEAAyd+sJqaeDYdOLaKRy3Mn+1CFXDFiJvxNuKCNKYxVC4w\nM1HaKM2ypkWma2/6dT07IxaeYWYvHa+vloxCoP2Pe9cpwEs11065SmmiAIFkJGyD\nCa/CCtxgG3YMqQfFxqyaLda5C897+Tsh1BVhEW/6anm9TCzejZyDhgBaOdiy0iXc\ndv3bx7VIIfGsapcHoVreUizwWUjmLfrSOKV/H+DT6QK2tcrECWJMRpEtlue6aNwT\nXK9qG1F6bUxomDKRy+hWnnVGZBcceMI+QoJdV9w5AOSMUkX/vUy1HpaJV2dTvgqb\nLp9BRUFBV98P9yES1bbbmiQ7nKLDlHhizpGXbZQc5QKBgQD7KvdMCzwbY5XBtvOq\n8aUgK2O947KpS+A4y94EKMd04qrDVJOm1Rk+HRb/T0h50979OXZpuOIkkTuujiXI\n/bkhazX2Q/qF+WnH5v4jp8WF8b+VX5wzGRd7mIRUEclCx+7dC38Hfuct80MHxA3s\nr9QTwtICuLfHq+slVjbyQn3HawKBgQDVL6O/5G+rrg/b/MXn0Dc0dUOc8AwOXr/9\nN8HH69zkMGB79Cjt4xVTvBoPyLuc1tK5qVoTJ9rVr3FNrtPQoMwYIG9Wjs1pTOZH\nEoXYWKrxNFySHMnp7dhDnVyz2siX5MlyaXul/vcW+mWHaqgZ41OuhXpDiNBB3LrY\n5+8GaP+hRwKBgQDnsu4ox2Buf8KBzi0gcAR9FanQXYHOLUGuA1tPCPNev076HEaN\nK546r4ksgs4AHNRjUyQKSVTMmPGuiFERBgZHbuh7wqAUAU5064FcEQPlGWs799RX\nSvlfugWwrCu2oEuYvGc12fqXaAZY3qJdyqHgjONBQXfACbdFXTAY8f+n+wKBgQCM\n74ezzpM8zj/R0VQMoow8c+GVGOT/gwgtoPTM9FL90DWY/JsqWVu6FbqtAKl3UFXT\nQ/A2hbyYU/n1v0RswkDrGlZAxPtrR9lc1xQBKO0ptNIWDk42BnVYjenBRTwjnq7G\nopGJ9froB/WjBpHHO5AQ6lEf4Iy9Xe1whDRy6mtE9QKBgGW3amuZRaBfWRhYQZqN\ne6PMXQP9DFce+HCa/f5d96AU8nORyOn10Wkd/D4DZYFqqbE/DEE5BeEdtk1WXvWb\nNtFpCdiNaV12iDEPrJ2aLElqKRlYDD6Q/Ckp6vNuFHcyA8Twr+FwSeqlQvAoHBHF\nlhdRoJO8b7rb3Ij1iGEaU9qy\n-----END PRIVATE KEY-----\n",
6 | "client_email": "firebase-adminsdk-vgwbn@vue-hq.iam.gserviceaccount.com",
7 | "client_id": "106063469757469881133",
8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9 | "token_uri": "https://oauth2.googleapis.com/token",
10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-vgwbn%40vue-hq.iam.gserviceaccount.com"
12 | }
13 |
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | functions = "./functions"
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-admin-dashboard",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "serve": "vue-cli-service serve",
7 | "lint": "vue-cli-service lint",
8 | "start:lambda": "netlify-lambda serve src/functions",
9 | "build:lambda": "netlify-lambda build src/functions",
10 | "build:vue": "vue-cli-service build"
11 | },
12 | "dependencies": {
13 | "@firebase/app": "^0.6.5",
14 | "@firebase/firestore": "^1.15.0",
15 | "@netlify/zip-it-and-ship-it": "^0.2.0",
16 | "animate.css": "^3.7.0",
17 | "apexcharts": "^3.2.2",
18 | "firebase-admin": "^7.0.0",
19 | "gotrue-js": "^0.9.24",
20 | "netlify-identity-widget": "^1.5.2",
21 | "netlify-lambda": "^1.4.2",
22 | "vue": "^2.5.21",
23 | "vue-apexcharts": "^1.2.9",
24 | "vue-firestore": "^0.3.16",
25 | "vue-github-buttons": "^3.0.1",
26 | "vue-router": "^3.0.1",
27 | "vuex": "^3.0.1"
28 | },
29 | "devDependencies": {
30 | "@vue/cli-plugin-babel": "^3.3.0",
31 | "@vue/cli-plugin-eslint": "^3.3.0",
32 | "@vue/cli-service": "^3.3.0",
33 | "@vue/eslint-config-prettier": "^4.0.1",
34 | "babel-eslint": "^10.0.1",
35 | "eslint": "^5.8.0",
36 | "eslint-plugin-vue": "^5.0.0",
37 | "node-sass": "^4.9.0",
38 | "sass-loader": "^7.0.1",
39 | "vue-template-compiler": "^2.5.21"
40 | },
41 | "eslintConfig": {
42 | "root": true,
43 | "env": {
44 | "node": true
45 | },
46 | "extends": [
47 | "plugin:vue/essential"
48 | ],
49 | "rules": {},
50 | "parserOptions": {
51 | "parser": "babel-eslint"
52 | }
53 | },
54 | "postcss": {
55 | "plugins": {
56 | "autoprefixer": {}
57 | }
58 | },
59 | "browserslist": [
60 | "> 1%",
61 | "last 2 versions",
62 | "not ie <= 8"
63 | ]
64 | }
65 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | # Netlify settings for single-page application
2 | /* /index.html 200
3 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Vue HQ Demo Site by Design+Code
6 |
7 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
36 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | We're sorry but vue-admin-dashboard doesn't work properly without
48 | JavaScript enabled. Please enable it to continue.
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
23 |
24 |
175 |
--------------------------------------------------------------------------------
/src/assets/DCHQ-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/assets/DCHQ-small-dark.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/assets/DCHQ-small.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/assets/DCHQ.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/assets/contentful.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/contentful.png
--------------------------------------------------------------------------------
/src/assets/dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/dropbox.png
--------------------------------------------------------------------------------
/src/assets/figma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/figma.png
--------------------------------------------------------------------------------
/src/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/logo.png
--------------------------------------------------------------------------------
/src/assets/logout.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/assets/moon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/src/assets/notion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/notion.png
--------------------------------------------------------------------------------
/src/assets/slack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/thomaswang-archive/vue-admin-dashboard/2aecdd70b7cf7385b6a74daeed15a36a1fc60b50/src/assets/slack.png
--------------------------------------------------------------------------------
/src/assets/sun.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/src/components/Header.vue:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
48 |
49 |
105 |
--------------------------------------------------------------------------------
/src/components/HelloWorld.vue:
--------------------------------------------------------------------------------
1 |
2 | {{ msg }}
3 |
4 |
5 |
13 |
14 |
15 |
17 |
--------------------------------------------------------------------------------
/src/components/Notification.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
8 |
9 |
28 |
29 |
47 |
--------------------------------------------------------------------------------
/src/components/RequestAccount.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Don't have a Design+Code HQ account?
8 | Request an account
9 |
10 |
11 |
12 |
13 |
31 |
32 |
59 |
--------------------------------------------------------------------------------
/src/components/ThemeSwitch.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
30 |
70 |
--------------------------------------------------------------------------------
/src/firebase.js:
--------------------------------------------------------------------------------
1 | import { firebase } from "@firebase/app";
2 | import "@firebase/firestore";
3 |
4 | const firebaseApp = firebase.initializeApp({
5 | apiKey: "AIzaSyBZcqLLNNKG-Gpcdr-2he4QAX8q1yvgEww",
6 | authDomain: "vue-hq.firebaseapp.com",
7 | databaseURL: "https://vue-hq.firebaseio.com",
8 | projectId: "vue-hq",
9 | storageBucket: "vue-hq.appspot.com",
10 | messagingSenderId: "857669172723"
11 | });
12 |
13 | export const db = firebaseApp.firestore();
--------------------------------------------------------------------------------
/src/functions/getUserData.js:
--------------------------------------------------------------------------------
1 | const { zipFunctions } = require("@netlify/zip-it-and-ship-it");
2 |
3 | zipFunctions("functions", "functions-dist")
4 |
5 | var admin = require("firebase-admin");
6 |
7 | var serviceAccount = require("./serviceAccountKey.json");
8 |
9 | admin.initializeApp({
10 | credential: admin.credential.cert(serviceAccount),
11 | databaseURL: "https://vue-hq.firebaseio.com"
12 | });
13 |
14 |
15 | exports.handler = function (event, context, callback) {
16 | const { user } = context.clientContext;
17 |
18 | // Do the user check here
19 | // If not user, callback a status code of 401, meaning unauthorized
20 |
21 | const { email, subscriptionId } = event.queryStringParameters;
22 |
23 | console.log("Searched email: " + email);
24 | console.log("Searched subscription ID: " + subscriptionId);
25 |
26 | const searchKey = email.length > 0 ? "email" : "subscriptionId"
27 | const searchQuery = email.length > 0 ? email : subscriptionId
28 |
29 | const firestore = admin.firestore();
30 |
31 | firestore.collection('users').where(searchKey, '==', searchQuery).limit(1).get().then(response => {
32 | if (response.empty) { return Promise.reject() }
33 |
34 | const userInfo = response.docs[0].data()
35 |
36 | console.log(userInfo)
37 |
38 | callback(null, {
39 | statusCode: 200,
40 | headers: {
41 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
42 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
43 | },
44 | body: JSON.stringify(userInfo)
45 | })
46 | }).catch(error => {
47 | console.log(error)
48 |
49 | callback(null, {
50 | statusCode: 500,
51 | headers: {
52 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
53 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
54 | },
55 | body: error
56 | })
57 | })
58 | }
59 |
--------------------------------------------------------------------------------
/src/functions/hello.js:
--------------------------------------------------------------------------------
1 | exports.handler = function (event, context, callback) {
2 | callback(null, {
3 | statusCode: 200,
4 | headers: {
5 | "Access-Control-Allow-Origin": "*", // Required for CORS support to work
6 | "Access-Control-Allow-Credentials": true // Required for cookies, authorization headers with HTTPS
7 | },
8 | body: "Hello, World"
9 | });
10 | }
--------------------------------------------------------------------------------
/src/functions/serviceAccountKey.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "service_account",
3 | "project_id": "vue-hq",
4 | "private_key_id": "7f290e3e535486a33eca36efd6d642872bb93c54",
5 | "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDRKX1OTPLUB/gZ\nJlU7VgffVTiPuD6F3WsEgRpJ1o0BK4crGP1+R4VjdKcXgFyG9Mlt2S6Kzy2j/vLZ\ntjl7QdP72btOCr28+SlOYKLfz3i+HmzXMb+T1koJtfOAU/WVCR02Bhmx6asNq1Cy\nsUvuqFOFyCpI6iv8TgAiKKOUqLW1EUE/1mPOGbv9ToFdPaUpQNVfcpBUdO4dt9WF\nJf9gshuO2GgU4gIm3vY6RQtQ/ymOEYYKpVmU6PH+EIIjB2m8xkoR9tpYDGAsrps2\nznVIP6XNfqNWqic00BUF9QswhTMu1ti2G198RxGsv74dc/c80kb84TwIpNg1Je93\nftCm4ZmtAgMBAAECggEAAyd+sJqaeDYdOLaKRy3Mn+1CFXDFiJvxNuKCNKYxVC4w\nM1HaKM2ypkWma2/6dT07IxaeYWYvHa+vloxCoP2Pe9cpwEs11065SmmiAIFkJGyD\nCa/CCtxgG3YMqQfFxqyaLda5C897+Tsh1BVhEW/6anm9TCzejZyDhgBaOdiy0iXc\ndv3bx7VIIfGsapcHoVreUizwWUjmLfrSOKV/H+DT6QK2tcrECWJMRpEtlue6aNwT\nXK9qG1F6bUxomDKRy+hWnnVGZBcceMI+QoJdV9w5AOSMUkX/vUy1HpaJV2dTvgqb\nLp9BRUFBV98P9yES1bbbmiQ7nKLDlHhizpGXbZQc5QKBgQD7KvdMCzwbY5XBtvOq\n8aUgK2O947KpS+A4y94EKMd04qrDVJOm1Rk+HRb/T0h50979OXZpuOIkkTuujiXI\n/bkhazX2Q/qF+WnH5v4jp8WF8b+VX5wzGRd7mIRUEclCx+7dC38Hfuct80MHxA3s\nr9QTwtICuLfHq+slVjbyQn3HawKBgQDVL6O/5G+rrg/b/MXn0Dc0dUOc8AwOXr/9\nN8HH69zkMGB79Cjt4xVTvBoPyLuc1tK5qVoTJ9rVr3FNrtPQoMwYIG9Wjs1pTOZH\nEoXYWKrxNFySHMnp7dhDnVyz2siX5MlyaXul/vcW+mWHaqgZ41OuhXpDiNBB3LrY\n5+8GaP+hRwKBgQDnsu4ox2Buf8KBzi0gcAR9FanQXYHOLUGuA1tPCPNev076HEaN\nK546r4ksgs4AHNRjUyQKSVTMmPGuiFERBgZHbuh7wqAUAU5064FcEQPlGWs799RX\nSvlfugWwrCu2oEuYvGc12fqXaAZY3qJdyqHgjONBQXfACbdFXTAY8f+n+wKBgQCM\n74ezzpM8zj/R0VQMoow8c+GVGOT/gwgtoPTM9FL90DWY/JsqWVu6FbqtAKl3UFXT\nQ/A2hbyYU/n1v0RswkDrGlZAxPtrR9lc1xQBKO0ptNIWDk42BnVYjenBRTwjnq7G\nopGJ9froB/WjBpHHO5AQ6lEf4Iy9Xe1whDRy6mtE9QKBgGW3amuZRaBfWRhYQZqN\ne6PMXQP9DFce+HCa/f5d96AU8nORyOn10Wkd/D4DZYFqqbE/DEE5BeEdtk1WXvWb\nNtFpCdiNaV12iDEPrJ2aLElqKRlYDD6Q/Ckp6vNuFHcyA8Twr+FwSeqlQvAoHBHF\nlhdRoJO8b7rb3Ij1iGEaU9qy\n-----END PRIVATE KEY-----\n",
6 | "client_email": "firebase-adminsdk-vgwbn@vue-hq.iam.gserviceaccount.com",
7 | "client_id": "106063469757469881133",
8 | "auth_uri": "https://accounts.google.com/o/oauth2/auth",
9 | "token_uri": "https://oauth2.googleapis.com/token",
10 | "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
11 | "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-vgwbn%40vue-hq.iam.gserviceaccount.com"
12 | }
13 |
--------------------------------------------------------------------------------
/src/global-styles/colors.scss:
--------------------------------------------------------------------------------
1 | // Background Colors
2 | $dark-blue: #212c4f;
3 | $super-dark-blue: #1b233f;
4 | $light-gray: #f0f3f5;
5 |
6 | // Text Colors
7 | $dark-green: #205284;
8 | $dark-gray: #5b6175;
9 | $light-blue: #c6d0eb;
10 | $black: #000000;
11 | $white: #ffffff;
12 |
13 | // Button Colors
14 | $purple: #7b42f6;
15 | $violet: #b01eff;
16 | $pink: #e1467c;
17 | $blue: #3672f8;
18 | $middle-blue: #546bfb;
19 | $teal: #56ccf2;
20 | $green: #a6e1d5;
21 | $turquoise: #14f1d9;
22 |
--------------------------------------------------------------------------------
/src/global-styles/typography.scss:
--------------------------------------------------------------------------------
1 | // Font Family
2 | $system-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
3 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
4 |
5 | // Headings
6 | @mixin heading-1($color: $white) {
7 | font-size: 80px;
8 | line-height: 86px;
9 | font-weight: bold;
10 | color: $color;
11 | }
12 |
13 | @mixin heading-2($color: $white) {
14 | font-size: 60px;
15 | line-height: 86px;
16 | font-weight: bold;
17 | color: $color;
18 | }
19 |
20 | @mixin heading-3($color: $white) {
21 | font-size: 30px;
22 | line-height: 43px;
23 | font-weight: bold;
24 | color: $color;
25 | }
26 |
27 | @mixin heading-4($color: $white) {
28 | font-size: 24px;
29 | line-height: 34px;
30 | font-weight: bold;
31 | color: $color;
32 | }
33 |
34 | // Body Text
35 | %large {
36 | line-height: 37px;
37 | font-size: 24px;
38 | }
39 |
40 | @mixin large-text($color: $white) {
41 | @extend %large;
42 | color: $color;
43 | }
44 |
45 | @mixin large-text-bold($color: $white) {
46 | @extend %large;
47 | font-weight: bold;
48 | color: $color;
49 | }
50 |
51 | %medium {
52 | line-height: 31px;
53 | font-size: 20px;
54 | }
55 |
56 | @mixin medium-text($color: $white) {
57 | @extend %medium;
58 | color: $color;
59 | }
60 |
61 | @mixin medium-text-bold($color: $white) {
62 | @extend %medium;
63 | font-weight: bold;
64 | color: $color;
65 | }
66 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import App from "./App.vue";
3 | import router from "./router";
4 | import store from "./store";
5 | import * as netlifyIdentityWidget from "netlify-identity-widget";
6 | import GoTrue from "gotrue-js";
7 | import VueFirestore from "vue-firestore";
8 | import "vue-github-buttons/dist/vue-github-buttons.css"; // Stylesheet
9 | import VueGitHubButtons from "vue-github-buttons"; // Component plugin
10 |
11 | Vue.config.productionTip = false;
12 |
13 | // Initialize Netlify Identity
14 | netlifyIdentityWidget.init();
15 |
16 | // Initialize GoTrue JS for Netlify
17 | export const auth = new GoTrue({
18 | APIUrl: "https://vue-hq.netlify.app/.netlify/identity",
19 | setCookie: true,
20 | });
21 |
22 | // Initialize Vue Firestore
23 | Vue.use(VueFirestore);
24 |
25 | // Initialize Github Buttons
26 | Vue.use(VueGitHubButtons);
27 |
28 | new Vue({
29 | router,
30 | store,
31 | render: (h) => h(App),
32 | }).$mount("#app");
33 |
--------------------------------------------------------------------------------
/src/router.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Router from "vue-router";
3 | import Home from "./views/Home.vue";
4 | import Team from "./views/Team.vue";
5 | import Manage from "./views/Manage.vue";
6 | import SignIn from "./views/SignInFlow/SignIn";
7 | import Request from "./views/SignInFlow/Request";
8 | import Recover from "./views/SignInFlow/Recover";
9 | import * as netlifyIdentityWidget from "netlify-identity-widget";
10 |
11 | Vue.use(Router);
12 |
13 | const router = new Router({
14 | mode: "history",
15 | base: process.env.BASE_URL,
16 | routes: [
17 | {
18 | path: "/",
19 | name: "home",
20 | component: Home,
21 | meta: {
22 | requiresAuth: true
23 | }
24 | },
25 | {
26 | path: "/Manage",
27 | name: "manage",
28 | component: Manage,
29 | meta: {
30 | requiresAuth: true
31 | }
32 | },
33 | {
34 | path: "/team",
35 | name: "team",
36 | component: Team,
37 | meta: {
38 | requiresAuth: true
39 | }
40 | },
41 | {
42 | path: "/signin",
43 | name: "signin",
44 | component: SignIn
45 | },
46 | {
47 | path: "/request",
48 | name: "request",
49 | component: Request
50 | },
51 | {
52 | path: "/recover",
53 | name: "recover",
54 | component: Recover
55 | }
56 | ]
57 | });
58 |
59 | router.beforeEach((to, from, next) => {
60 | const currentUser = netlifyIdentityWidget.currentUser();
61 | const requiresAuth = to.matched.some(record => {
62 | return record.meta.requiresAuth
63 | })
64 |
65 | if (requiresAuth && !currentUser) {
66 | next("signin");
67 | } else {
68 | next();
69 | }
70 | })
71 |
72 | export default router;
73 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import Vue from "vue";
2 | import Vuex from "vuex";
3 |
4 | Vue.use(Vuex);
5 |
6 | // Initial State
7 | if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
8 | window.localStorage.setItem("isDarkMode", "true");
9 | }
10 |
11 | const userSelectedDarkMode = window.localStorage.getItem("isDarkMode") === "true";
12 |
13 | const state = {
14 | isDarkMode: userSelectedDarkMode
15 | }
16 |
17 | // Getters
18 | const getters = {
19 | isDarkMode(state) {
20 | return state.isDarkMode
21 | }
22 | }
23 |
24 | // Mutations
25 | const mutations = {
26 | toggleDarkMode(state) {
27 | if (state.isDarkMode === true) {
28 | state.isDarkMode = false;
29 | window.localStorage.setItem("isDarkMode", "false");
30 | } else {
31 | state.isDarkMode = true;
32 | window.localStorage.setItem("isDarkMode", "true");
33 | }
34 | }
35 | }
36 |
37 | // Actions
38 | const actions = {
39 | triggerDarkMode(context) {
40 | context.commit('toggleDarkMode');
41 | }
42 | }
43 |
44 | export default new Vuex.Store({
45 | state,
46 | getters,
47 | mutations,
48 | actions
49 | });
50 |
--------------------------------------------------------------------------------
/src/views/Home.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Traffic Overview
7 |
8 |
Days
9 |
Weeks
10 |
Months
11 |
12 |
13 |
21 |
22 |
31 |
32 |
41 |
42 |
51 |
52 |
61 |
62 |
63 |
64 |
65 |
66 |
343 |
344 |
422 |
--------------------------------------------------------------------------------
/src/views/Manage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Manage Users
6 |
Enter either customer email or subscription ID
9 |
37 |
38 |
Customer Details
39 |
40 |
41 |
Subscription State
45 |
{{subscriptionState}}
49 |
50 |
51 |
Seated
55 |
{{seated}}
59 |
60 |
61 |
On Trial
65 |
{{onTrial}}
69 |
70 |
71 |
Trial End Date
75 |
{{trialEndDate}}
79 |
80 |
81 |
82 |
83 |
84 |
85 |
146 |
147 |
216 |
--------------------------------------------------------------------------------
/src/views/SignInFlow/Recover.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
Recover Account
11 |
21 |
Already have an account? Sign in now.
25 |
26 |
27 |
28 |
29 |
30 |
73 |
74 |
89 |
--------------------------------------------------------------------------------
/src/views/SignInFlow/Request.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
Request Account
10 |
20 |
Already have an account? Sign in now.
24 |
25 |
26 |
27 |
28 |
29 |
80 |
81 |
97 |
--------------------------------------------------------------------------------
/src/views/SignInFlow/SignIn.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
Sign into Design+Code HQ [DEMO]
14 |
31 |
Forgot your password?
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
103 |
104 |
127 |
--------------------------------------------------------------------------------
/src/views/Team.vue:
--------------------------------------------------------------------------------
1 |
2 |
73 |
74 |
75 |
90 |
91 |
183 |
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | css: {
3 | loaderOptions: {
4 | sass: {
5 | data: `
6 | @import "@/global-styles/colors.scss";
7 | @import "@/global-styles/typography.scss";
8 | `
9 | }
10 | }
11 | }
12 | };
13 |
--------------------------------------------------------------------------------