├── public ├── favicon.ico ├── 404.html ├── index.html ├── chock.svg ├── laptop.svg ├── photo.svg ├── cherry.svg ├── orange.svg ├── cheese.svg ├── pie.svg ├── teddy.svg ├── balloon.svg ├── ice.svg ├── home.svg ├── present.svg └── bird.svg ├── src ├── assets │ ├── logo.png │ ├── img │ │ ├── heart.png │ │ ├── instagram.png │ │ ├── GitHub-Mark-64px.png │ │ ├── delete.svg │ │ ├── close.svg │ │ ├── arrow.svg │ │ ├── twitter.svg │ │ ├── photo.svg │ │ ├── gitlab.svg │ │ ├── pencil.svg │ │ ├── orange.svg │ │ ├── key.svg │ │ └── circle_blue.svg │ └── styles │ │ ├── plugins.css │ │ ├── styles.css │ │ ├── buttons.css │ │ └── reset.css ├── layouts │ ├── EmptyLayout.vue │ └── MainLayout.vue ├── store │ ├── index.js │ ├── auth.js │ ├── notes.js │ └── user.js ├── components │ ├── Toast.vue │ ├── Loader.vue │ ├── Modal.vue │ ├── Main │ │ ├── Footer.vue │ │ ├── Sidebar.vue │ │ └── Header.vue │ └── ProfileData.vue ├── filters │ └── date.filter.js ├── App.vue ├── router │ └── index.js ├── main.js └── views │ ├── Info.vue │ ├── Profile.vue │ ├── Stats.vue │ ├── Add.vue │ ├── Login.vue │ └── History.vue ├── babel.config.js ├── vue.config.js ├── .gitignore ├── license ├── package.json └── README.md /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrey-kudinov/sweethome/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrey-kudinov/sweethome/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/img/heart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrey-kudinov/sweethome/HEAD/src/assets/img/heart.png -------------------------------------------------------------------------------- /src/assets/img/instagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrey-kudinov/sweethome/HEAD/src/assets/img/instagram.png -------------------------------------------------------------------------------- /src/assets/img/GitHub-Mark-64px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrey-kudinov/sweethome/HEAD/src/assets/img/GitHub-Mark-64px.png -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | 3 | module.exports = { 4 | publicPath: process.env.NODE_ENV === 'production' 5 | ? '/sweethome/' 6 | : '/', 7 | } 8 | -------------------------------------------------------------------------------- /src/assets/styles/plugins.css: -------------------------------------------------------------------------------- 1 | .fade-enter-active, .fade-leave-active { 2 | transition: opacity .5s; 3 | } 4 | .fade-enter, .fade-leave-to /* .fade-leave-active до версии 2.1.8 */ { 5 | opacity: 0; 6 | } -------------------------------------------------------------------------------- /src/assets/styles/styles.css: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro); 2 | @import './buttons.css'; 3 | @import './plugins.css'; 4 | @import './reset.css'; 5 | 6 | .card { 7 | box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.1); 8 | } 9 | 10 | input, textarea { 11 | box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.1); 12 | } -------------------------------------------------------------------------------- /src/layouts/EmptyLayout.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | -------------------------------------------------------------------------------- /.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 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | # IDE - VSCode 25 | *.code-workspace 26 | .vscode/* 27 | !.vscode/settings.json 28 | !.vscode/tasks.json 29 | !.vscode/launch.json 30 | !.vscode/extensions.json -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Vuex from 'vuex' 3 | import auth from './auth' 4 | import user from './user' 5 | import notes from './notes' 6 | 7 | Vue.use(Vuex) 8 | 9 | export default new Vuex.Store({ 10 | state: { 11 | error: null, 12 | }, 13 | mutations: { 14 | setError(state, error) { 15 | state.error = error 16 | }, 17 | clearError(state) { 18 | state.error = null 19 | } 20 | }, 21 | actions: { 22 | }, 23 | getters: { 24 | error: s => s.error 25 | }, 26 | modules: { 27 | auth, notes, user 28 | } 29 | }) 30 | -------------------------------------------------------------------------------- /src/components/Toast.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | 36 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/filters/date.filter.js: -------------------------------------------------------------------------------- 1 | export default function dateFilter(value, format = 'date') { 2 | const options = {} 3 | 4 | if (format.includes('date')) { 5 | options.day = '2-digit' 6 | options.month = 'long' 7 | options.year = 'numeric' 8 | } 9 | 10 | if (format.includes('time')) { 11 | options.hour = '2-digit' 12 | options.minute = '2-digit' 13 | options.second = '2-digit' 14 | } 15 | 16 | if (format.includes('month')) { 17 | options.month = 'long' 18 | } 19 | 20 | if (format.includes('year')) { 21 | options.year = 'numeric' 22 | } 23 | 24 | if (format.includes('mShort')) { 25 | options.month = '2-digit' 26 | } 27 | 28 | return new Intl.DateTimeFormat('ru-RU', options).format(new Date(value)) 29 | } -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | 22 | 44 | -------------------------------------------------------------------------------- /src/assets/img/delete.svg: -------------------------------------------------------------------------------- 1 | x -------------------------------------------------------------------------------- /src/assets/img/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/assets/img/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 9 | 10 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2022 Scott Chacon and others 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /src/store/auth.js: -------------------------------------------------------------------------------- 1 | import firebase from 'firebase/app' 2 | 3 | export default { 4 | actions: { 5 | async logIn({ commit }, {email, password}) { 6 | try { 7 | await firebase.auth().signInWithEmailAndPassword(email, password) 8 | sessionStorage.setItem("user", email); 9 | } catch (e) { 10 | commit('setError', e) 11 | throw e 12 | } 13 | }, 14 | getUid() { 15 | const user = firebase.auth().currentUser 16 | return user ? user.uid : null 17 | }, 18 | async logout() { 19 | await firebase.auth().signOut() 20 | }, 21 | async register({dispatch, commit}, {email, password}) { 22 | try { 23 | await firebase.auth().createUserWithEmailAndPassword(email, password); 24 | const uid = await dispatch('getUid') 25 | await firebase.database().ref(`/users/${uid}/user_1`).set({ 26 | avatar: '', 27 | name: '', 28 | }) 29 | await firebase.database().ref(`/users/${uid}/user_2`).set({ 30 | avatar: '', 31 | name: '', 32 | }) 33 | sessionStorage.setItem("user", email); 34 | } catch (e) { 35 | commit('setError', e) 36 | throw e 37 | } 38 | }, 39 | } 40 | } -------------------------------------------------------------------------------- /src/assets/img/twitter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sweethome", 3 | "version": "0.1.0", 4 | "license": "MIT", 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 | "firebase": "^8.4.1", 13 | "vue": "^2.6.11", 14 | "vue-croppie": "^2.0.2", 15 | "vue-router": "^3.2.0", 16 | "vuetify": "^2.4.10", 17 | "vuex": "^3.4.0" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "~4.5.0", 21 | "@vue/cli-plugin-eslint": "~4.5.0", 22 | "@vue/cli-plugin-router": "~4.5.0", 23 | "@vue/cli-plugin-vuex": "~4.5.0", 24 | "@vue/cli-service": "~4.5.0", 25 | "babel-eslint": "^10.1.0", 26 | "eslint": "^6.7.2", 27 | "eslint-plugin-vue": "^6.2.2", 28 | "node-sass": "^4.12.0", 29 | "sass-loader": "^8.0.2", 30 | "vue-template-compiler": "^2.6.11" 31 | }, 32 | "eslintConfig": { 33 | "root": true, 34 | "env": { 35 | "node": true 36 | }, 37 | "extends": [ 38 | "plugin:vue/essential", 39 | "eslint:recommended" 40 | ], 41 | "parserOptions": { 42 | "parser": "babel-eslint" 43 | }, 44 | "rules": {} 45 | }, 46 | "browserslist": [ 47 | "> 1%", 48 | "last 2 versions", 49 | "not dead" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 13 | 17 | 18 | 19 | 26 |
27 | 28 | 29 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/components/Loader.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 62 | -------------------------------------------------------------------------------- /src/components/Modal.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | 73 | -------------------------------------------------------------------------------- /public/chock.svg: -------------------------------------------------------------------------------- 1 | chocolate 5 -------------------------------------------------------------------------------- /public/laptop.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 15 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /public/photo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/assets/img/photo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes = [ 7 | { 8 | path: "/", 9 | name: "Add", 10 | meta: { layout: "main", log: true }, 11 | component: () => import("@/views/Add.vue"), 12 | }, 13 | { 14 | path: "/history", 15 | name: "History", 16 | meta: { layout: "main", log: true }, 17 | component: () => 18 | import("@/views/History.vue"), 19 | }, 20 | { 21 | path: "/profile", 22 | name: "Profile", 23 | meta: { layout: "main", log: true }, 24 | component: () => 25 | import("@/views/Profile.vue"), 26 | }, 27 | { 28 | path: "/info", 29 | name: "Info", 30 | meta: { layout: "main", log: true }, 31 | component: () => 32 | import("@/views/Info.vue"), 33 | }, 34 | { 35 | path: "/stats", 36 | name: "Stats", 37 | meta: { layout: "main", log: true }, 38 | component: () => 39 | import("@/views/Stats.vue"), 40 | }, 41 | { 42 | path: "/login", 43 | name: "Login", 44 | meta: { layout: "empty" }, 45 | component: () => import("@/views/Login.vue"), 46 | }, 47 | ]; 48 | 49 | const router = new VueRouter({ 50 | mode: "history", 51 | base: process.env.BASE_URL, 52 | scrollBehavior (to, from, savedPosition) { 53 | if (savedPosition) { 54 | return savedPosition 55 | } else { 56 | return new Promise((resolve) => { 57 | setTimeout(() => { 58 | resolve({ x: 0, y: 0 }) 59 | }, 500) 60 | }) 61 | } 62 | }, 63 | routes, 64 | }); 65 | 66 | router.beforeEach(async (to, from, next) => { 67 | if (to.matched.some((record) => record.meta.log)) { 68 | if (!sessionStorage.getItem("user")) { 69 | return next({ path: "/login" }); 70 | } 71 | } 72 | return next(); 73 | }); 74 | 75 | 76 | export default router; 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sweet Home 2 | 3 | ## Проект для меня и моей девушки, где мы будем записывать выполненные нами домашние дела. 4 | 5 | ## Цель проекта 6 | Цель проекта (помимо улучшения навыков и удовольствия от его создания) - решить определенные проблемы в паре. Так как мы с девушкой работаем (а значит и отдыхаем) по разным графикам, домашние дела мы делаем отдельно друг от друга. А то редкое время, что нам удается побыть вместе, мы не тратим на готовку и уборку, а уделяем друг другу. И чтобы не возникало фраз "Ты отдыхал/отдыхала два дня подряд, ничего не сделал/сделала по дому, мог/могла хотя бы отмыть ванну!?" (утрировано), мы решили, что было бы неплохо где-нибудь записывать то, что мы делаем по дому, чтобы каждый из нас видел как много всего мы делаем и не было обесценивания. Мы успешно вели записи домашних дел в нашем закрытом телеграм канале, пока я не решил сделать этот сайт для меня и моей девушки :) 7 | 8 | ## Для кого пригодится 9 | Несмотря на то, что в проекте есть регистрация, он сделан в первую очередь для меня и моей девушки. Но, очевидно, он подойдет для любой пары, где есть схожая проблема и желание её решить :) 10 | 11 | ## Как использовать 12 | Вкладка "Добавить": добавить новые карточки с выполненными делами по дому. Например: вытер пыль, помыл унитаз, очистил плиту. Один день - одна карточка со списком дел. 13 | 14 | Вкладка "История": все ваши карточки с домашними делами. Здесь их можно редактировать или полностью удалять. 15 | 16 | Вкладка "Профиль": здесь вы можете заменить ваш ник и аватарку. Данные сохраняются на сервере. 17 | 18 | ## О проете 19 | Проект сделан на Vue 2.6.11 (в т.ч. vuex, vue-router). Обрезка фотографий сделана с помощью vue-croppie. На бэке firebase. 20 | 21 | 22 | ## Project setup 23 | ``` 24 | npm install 25 | ``` 26 | 27 | ### Compiles and hot-reloads for development 28 | ``` 29 | npm run serve 30 | ``` 31 | 32 | ### Compiles and minifies for production 33 | ``` 34 | npm run build 35 | ``` 36 | ### Project link 37 | ``` 38 | https://andreykudinov63.github.io/sweethome/ 39 | ``` 40 | ### License 41 | License: released under the MIT License 42 | -------------------------------------------------------------------------------- /src/store/notes.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app"; 2 | 3 | export default { 4 | actions: { 5 | async createNote( 6 | { commit, dispatch }, 7 | { name, text, date, month, year, enable = true } 8 | ) { 9 | try { 10 | const uid = await dispatch("getUid"); 11 | const note = await firebase 12 | .database() 13 | .ref(`/users/${uid}/notes/${year}/${month}`) 14 | .push({ name, text, date, enable }); 15 | return { name, text, date, enable, id: note.key }; 16 | } catch (e) { 17 | commit("setError", e); 18 | throw e; 19 | } 20 | }, 21 | async fetchNotes({ commit, dispatch }, { month, year }) { 22 | try { 23 | const uid = await dispatch("getUid"); 24 | const notes = 25 | ( 26 | await firebase 27 | .database() 28 | .ref(`/users/${uid}/notes/${year}/${month}`) 29 | .once("value") 30 | ).val() || {}; 31 | return Object.keys(notes).map((key) => ({ ...notes[key], id: key })); 32 | } catch (error) { 33 | commit("setError", error); 34 | throw error; 35 | } 36 | }, 37 | async updateNote({ commit, dispatch }, { text, date, id, month, year }) { 38 | try { 39 | const uid = await dispatch("getUid"); 40 | await firebase 41 | .database() 42 | .ref(`/users/${uid}/notes/${year}/${month}`) 43 | .child(id) 44 | .update({ text, date }); 45 | } catch (error) { 46 | commit("setError", error); 47 | throw error; 48 | } 49 | }, 50 | async disableNote( 51 | { commit, dispatch }, 52 | { enable = false, id, month, year } 53 | ) { 54 | try { 55 | const uid = await dispatch("getUid"); 56 | await firebase 57 | .database() 58 | .ref(`/users/${uid}/notes/${year}/${month}`) 59 | .child(id) 60 | .update({ enable }); 61 | } catch (error) { 62 | commit("setError", error); 63 | throw error; 64 | } 65 | }, 66 | }, 67 | }; 68 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import store from "./store"; 4 | import router from "./router"; 5 | import Vuetify from "vuetify"; 6 | import dateFilter from '@/filters/date.filter' 7 | import VueCroppie from "vue-croppie"; 8 | 9 | import "vuetify/dist/vuetify.min.css"; 10 | import "/src/assets/styles/styles.css"; 11 | import "croppie/croppie.css"; 12 | import firebase from "firebase/app"; 13 | import "firebase/auth"; 14 | import "firebase/database"; 15 | 16 | Vue.config.productionTip = false; 17 | 18 | Vue.use(VueCroppie); 19 | Vue.filter('date', dateFilter) 20 | 21 | const firebaseConfig = { 22 | apiKey: "AIzaSyBNnu_cP9p67yVXRatnH3WUC0nmfdOCjbY", 23 | authDomain: "sweethome-km12.firebaseapp.com", 24 | databaseURL: "https://sweethome-km12-default-rtdb.firebaseio.com", 25 | projectId: "sweethome-km12", 26 | storageBucket: "sweethome-km12.appspot.com", 27 | messagingSenderId: "562209642420", 28 | appId: "1:562209642420:web:d598a900cdebf6cacd04e1", 29 | measurementId: "G-82HRYSQ442", 30 | }; 31 | 32 | firebase.initializeApp(firebaseConfig); 33 | 34 | let app; 35 | let date = new Date(); 36 | 37 | firebase.auth().onAuthStateChanged(() => { 38 | if (!app) { 39 | app = new Vue({ 40 | data() { 41 | return { 42 | current: { 43 | month: { 44 | name: dateFilter(date, "month"), 45 | id: +dateFilter(date, "mShort") 46 | }, 47 | year: { 48 | name: dateFilter(date, "year"), 49 | id: +dateFilter(date, "year")-2020, 50 | }, 51 | }, 52 | user_1: { 53 | avatar: null, 54 | name: '', 55 | isShow: true, 56 | counter: 0, 57 | goal: 40 58 | }, 59 | user_2: { 60 | avatar: null, 61 | name: '', 62 | isShow: true, 63 | counter: 0, 64 | goal: 40 65 | }, 66 | 67 | } 68 | }, 69 | router, 70 | store, 71 | Vuetify, 72 | render: (h) => h(App), 73 | }).$mount("#app"); 74 | } 75 | }); 76 | -------------------------------------------------------------------------------- /src/assets/img/gitlab.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Group 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/store/user.js: -------------------------------------------------------------------------------- 1 | import firebase from "firebase/app"; 2 | import "firebase/storage"; 3 | 4 | export default { 5 | actions: { 6 | async fetchUserInfo({ commit, dispatch }, userName) { 7 | try { 8 | const uid = await dispatch("getUid"); 9 | const userInfo = 10 | ( 11 | await firebase 12 | .database() 13 | .ref(`/users/${uid}/${userName}`) 14 | .once("value") 15 | ).val() || {}; 16 | return Object.keys(userInfo).map((key) => ({ 17 | ...userInfo[key], 18 | id: key, 19 | })); 20 | } catch (error) { 21 | commit("setError", error); 22 | throw error; 23 | } 24 | }, 25 | async updateUserName({ commit, dispatch }, { user, name }) { 26 | try { 27 | const uid = await dispatch("getUid"); 28 | await firebase 29 | .database() 30 | .ref(`/users/${uid}/${user}/name`) 31 | .update({ name }); 32 | } catch (error) { 33 | commit("setError", error); 34 | throw error; 35 | } 36 | }, 37 | async loadUserAvatar({ commit, dispatch }, { user, avatar }) { 38 | try { 39 | const uid = await dispatch("getUid"); 40 | await firebase 41 | .storage() 42 | .ref(`${uid}`) 43 | .child(`${user}`) 44 | .putString(avatar, "data_url", { contentType: "image/jpg" }); 45 | } catch (error) { 46 | commit("setError", error); 47 | throw error; 48 | } 49 | }, 50 | async fetchUserAvatar({ commit, dispatch }, user) { 51 | try { 52 | const uid = await dispatch("getUid"); 53 | if ( 54 | ( 55 | await firebase 56 | .storage() 57 | .ref(`${uid}`) 58 | .listAll() 59 | ).items && 60 | ( 61 | await firebase 62 | .storage() 63 | .ref(`${uid}`) 64 | .listAll() 65 | ).items.length > 1 66 | ) { 67 | const res = await firebase 68 | .storage() 69 | .ref(`${uid}/${user}`) 70 | .getDownloadURL(); 71 | return res; 72 | } 73 | } catch (error) { 74 | commit("setError", error); 75 | throw error; 76 | } 77 | }, 78 | }, 79 | }; 80 | -------------------------------------------------------------------------------- /src/assets/img/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 9 | 11 | 12 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/views/Info.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 80 | -------------------------------------------------------------------------------- /public/cherry.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 28 | 29 | 31 | 32 | 33 | 37 | 38 | 39 | 41 | 42 | 43 | 45 | 46 | 47 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /public/orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/assets/img/orange.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 28 | 29 | 30 | 34 | 35 | 36 | 39 | 40 | 41 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /src/assets/styles/buttons.css: -------------------------------------------------------------------------------- 1 | .btn { 2 | font-family: "Source Sans Pro"; 3 | font-style: normal; 4 | font-weight: 600; 5 | font-size: 16px; 6 | line-height: 20px; 7 | 8 | border: 0; 9 | border-radius: 3px; 10 | 11 | width: 240px; 12 | height: 45px; 13 | 14 | box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, 0.1); 15 | } 16 | 17 | .btn_ligth-blue { 18 | background: #1690c3; 19 | color: #ffffff; 20 | transition: 0.3s; 21 | white-space: nowrap; 22 | position: relative; 23 | } 24 | .btn_ligth-blue:before { 25 | content: ""; 26 | position: absolute; 27 | z-index: 1; 28 | left: 51%; 29 | right: 51%; 30 | top: 0; 31 | background: #8ca6db; 32 | height: 45px; 33 | transition: .4s; 34 | opacity: .5; 35 | } 36 | @media (max-width: 768px) { 37 | .btn_ligth-blue:before { 38 | height: 38px; 39 | } 40 | } 41 | .btn_ligth-blue:hover:before{ 42 | left: 0; 43 | right: 0; 44 | } 45 | /* .btn_ligth-blue:hover { 46 | background: #168fc3b7; 47 | } */ 48 | .btn_ligth-blue:active { 49 | background: #1690c3; 50 | } 51 | 52 | .btn_dark-blue { 53 | background: #0a467e; 54 | color: #ffffff; 55 | transition: 0.3s; 56 | } 57 | .btn_dark-blue:hover { 58 | background: #0a467ed6; 59 | } 60 | .btn_dark-blue:active { 61 | background: #0a467e; 62 | } 63 | 64 | .btn_white { 65 | background: #ffffff; 66 | border: 1px solid #dadada; 67 | color: #0a467e; 68 | display: flex; 69 | justify-content: center; 70 | align-items: center; 71 | position: relative; 72 | } 73 | .btn_white:before { 74 | content: ""; 75 | position: absolute; 76 | z-index: 1; 77 | left: 51%; 78 | right: 51%; 79 | top: 0; 80 | background: linear-gradient(to right, #8ca6db, #b993d6); 81 | height: 45px; 82 | transition: .4s; 83 | opacity: .5; 84 | } 85 | @media (max-width: 768px) { 86 | .btn_white:before { 87 | height: 100%; 88 | } 89 | } 90 | .btn_white:hover:before{ 91 | left: 0; 92 | right: 0; 93 | } 94 | .btn_white:hover{ 95 | border: 1px solid #b993d6; 96 | } 97 | .btn_white:active { 98 | background: #ffffff; 99 | color: #b993d6; 100 | } 101 | .btn_white img { 102 | margin-right: 11px; 103 | margin-top: 1px; 104 | } 105 | 106 | .btn_transporent { 107 | position: relative; 108 | } 109 | .btn_transporent img { 110 | position: absolute; 111 | left: 0; 112 | bottom: 5px; 113 | transition: 0.3s; 114 | } 115 | .btn_transporent span { 116 | padding-left: 20px; 117 | } 118 | .btn_transporent:hover img { 119 | left: -3px; 120 | } 121 | .btn_transporent:hover { 122 | text-decoration: none; 123 | } 124 | .btn_transporent:active { 125 | text-decoration: underline; 126 | } 127 | 128 | @media (max-width: 768px) { 129 | .btn{ 130 | width: 100%; 131 | height: 38px; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/layouts/MainLayout.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 47 | 48 | 130 | -------------------------------------------------------------------------------- /src/views/Profile.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 75 | 76 | 123 | -------------------------------------------------------------------------------- /src/views/Stats.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 65 | 66 | 130 | -------------------------------------------------------------------------------- /public/cheese.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 14 | 15 | 16 | 18 | 19 | 20 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 52 | 54 | 55 | 56 | 59 | 60 | 61 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/components/Main/Footer.vue: -------------------------------------------------------------------------------- 1 | 68 | 69 | 158 | -------------------------------------------------------------------------------- /public/pie.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 11 | 13 | 16 | 17 | 20 | 23 | 26 | 28 | 31 | 34 | 37 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /public/teddy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 10 | 12 | 14 | 16 | 17 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/assets/img/key.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 38 | 44 | 50 | 56 | 59 | 61 | 63 | image/svg+xml 66 | 69 | 72 | 74 | 77 | Openclipart 80 | 82 | 84 | 86 | 89 | 92 | 95 | 98 | 100 | 102 | 104 | 106 | -------------------------------------------------------------------------------- /public/balloon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 9 | 11 | 12 | 14 | 15 | 17 | 21 | 24 | 25 | 26 | 29 | 31 | 32 | 34 | 35 | 37 | 41 | 44 | 45 | 46 | 49 | 51 | 52 | 54 | 55 | 57 | 60 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /public/ice.svg: -------------------------------------------------------------------------------- 1 | Asset 17 -------------------------------------------------------------------------------- /src/views/Add.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 112 | 113 | 213 | -------------------------------------------------------------------------------- /src/components/ProfileData.vue: -------------------------------------------------------------------------------- 1 | 71 | 72 | 130 | 131 | 215 | 216 | 234 | -------------------------------------------------------------------------------- /public/home.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 23 | 33 | 39 | 45 | 51 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /public/present.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 10 | 11 | 13 | 14 | 15 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 33 | 34 | 36 | 38 | 40 | 62 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /public/bird.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 8 | 9 | 11 | 13 | 15 | 16 | 20 | 21 | 23 | 25 | 26 | 30 | 31 | 33 | 34 | 36 | 38 | 42 | 44 | 45 | 51 | 54 | 55 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 72 | 75 | 78 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /src/assets/styles/reset.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, applet, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | a, abbr, acronym, address, big, cite, code, 4 | del, dfn, em, img, ins, kbd, q, s, samp, 5 | small, strike, strong, sub, sup, tt, var, 6 | b, u, i, center, 7 | dl, dt, dd, ol, ul, li, 8 | fieldset, form, label, legend, 9 | table, caption, tbody, tfoot, thead, tr, th, td, 10 | article, aside, canvas, details, embed, 11 | figure, figcaption, footer, header, hgroup, 12 | menu, nav, output, ruby, section, summary, 13 | time, mark, audio, video { 14 | margin: 0; 15 | padding: 0; 16 | border: 0; 17 | font-size: 100%; 18 | font: inherit; 19 | vertical-align: baseline; 20 | } 21 | 22 | /* make sure to set some focus styles for accessibility */ 23 | :focus { 24 | outline: 0; 25 | } 26 | 27 | /* HTML5 display-role reset for older browsers */ 28 | article, aside, details, figcaption, figure, 29 | footer, header, hgroup, menu, nav, section { 30 | display: block; 31 | } 32 | 33 | body { 34 | line-height: 1; 35 | } 36 | 37 | ol, ul { 38 | list-style: none; 39 | } 40 | 41 | blockquote, q { 42 | quotes: none; 43 | } 44 | 45 | blockquote:before, blockquote:after, 46 | q:before, q:after { 47 | content: ''; 48 | content: none; 49 | } 50 | 51 | table { 52 | border-collapse: collapse; 53 | border-spacing: 0; 54 | } 55 | 56 | input[type=search]::-webkit-search-cancel-button, 57 | input[type=search]::-webkit-search-decoration, 58 | input[type=search]::-webkit-search-results-button, 59 | input[type=search]::-webkit-search-results-decoration { 60 | -webkit-appearance: none; 61 | -moz-appearance: none; 62 | } 63 | 64 | input[type=search] { 65 | -webkit-appearance: none; 66 | -moz-appearance: none; 67 | -webkit-box-sizing: content-box; 68 | -moz-box-sizing: content-box; 69 | box-sizing: content-box; 70 | } 71 | 72 | textarea { 73 | overflow: auto; 74 | vertical-align: top; 75 | resize: vertical; 76 | } 77 | 78 | /** 79 | * Correct `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. 80 | */ 81 | 82 | audio, 83 | canvas, 84 | video { 85 | display: inline-block; 86 | *display: inline; 87 | *zoom: 1; 88 | max-width: 100%; 89 | } 90 | 91 | /** 92 | * Prevent modern browsers from displaying `audio` without controls. 93 | * Remove excess height in iOS 5 devices. 94 | */ 95 | 96 | audio:not([controls]) { 97 | display: none; 98 | height: 0; 99 | } 100 | 101 | /** 102 | * Address styling not present in IE 7/8/9, Firefox 3, and Safari 4. 103 | * Known issue: no IE 6 support. 104 | */ 105 | 106 | [hidden] { 107 | display: none; 108 | } 109 | 110 | /** 111 | * 1. Correct text resizing oddly in IE 6/7 when body `font-size` is set using 112 | * `em` units. 113 | * 2. Prevent iOS text size adjust after orientation change, without disabling 114 | * user zoom. 115 | */ 116 | 117 | html { 118 | font-size: 100%; /* 1 */ 119 | -webkit-text-size-adjust: 100%; /* 2 */ 120 | -ms-text-size-adjust: 100%; /* 2 */ 121 | } 122 | 123 | /** 124 | * Address `outline` inconsistency between Chrome and other browsers. 125 | */ 126 | 127 | a:focus { 128 | /* outline: thin dotted; */ 129 | } 130 | 131 | /** 132 | * Improve readability when focused and also mouse hovered in all browsers. 133 | */ 134 | 135 | a:active, 136 | a:hover { 137 | outline: 0; 138 | } 139 | 140 | /** 141 | * 1. Remove border when inside `a` element in IE 6/7/8/9 and Firefox 3. 142 | * 2. Improve image quality when scaled in IE 7. 143 | */ 144 | 145 | img { 146 | border: 0; /* 1 */ 147 | -ms-interpolation-mode: bicubic; /* 2 */ 148 | } 149 | 150 | /** 151 | * Address margin not present in IE 6/7/8/9, Safari 5, and Opera 11. 152 | */ 153 | 154 | figure { 155 | margin: 0; 156 | } 157 | 158 | /** 159 | * Correct margin displayed oddly in IE 6/7. 160 | */ 161 | 162 | form { 163 | margin: 0; 164 | } 165 | 166 | /** 167 | * Define consistent border, margin, and padding. 168 | */ 169 | 170 | fieldset { 171 | border: 1px solid #c0c0c0; 172 | margin: 0 2px; 173 | padding: 0.35em 0.625em 0.75em; 174 | } 175 | 176 | /** 177 | * 1. Correct color not being inherited in IE 6/7/8/9. 178 | * 2. Correct text not wrapping in Firefox 3. 179 | * 3. Correct alignment displayed oddly in IE 6/7. 180 | */ 181 | 182 | legend { 183 | border: 0; /* 1 */ 184 | padding: 0; 185 | white-space: normal; /* 2 */ 186 | *margin-left: -7px; /* 3 */ 187 | } 188 | 189 | /** 190 | * 1. Correct font size not being inherited in all browsers. 191 | * 2. Address margins set differently in IE 6/7, Firefox 3+, Safari 5, 192 | * and Chrome. 193 | * 3. Improve appearance and consistency in all browsers. 194 | */ 195 | 196 | button, 197 | input, 198 | select, 199 | textarea { 200 | font-size: 100%; /* 1 */ 201 | margin: 0; /* 2 */ 202 | vertical-align: baseline; /* 3 */ 203 | *vertical-align: middle; /* 3 */ 204 | } 205 | 206 | /** 207 | * Address Firefox 3+ setting `line-height` on `input` using `!important` in 208 | * the UA stylesheet. 209 | */ 210 | 211 | button, 212 | input { 213 | line-height: normal; 214 | } 215 | 216 | /** 217 | * Address inconsistent `text-transform` inheritance for `button` and `select`. 218 | * All other form control elements do not inherit `text-transform` values. 219 | * Correct `button` style inheritance in Chrome, Safari 5+, and IE 6+. 220 | * Correct `select` style inheritance in Firefox 4+ and Opera. 221 | */ 222 | 223 | button, 224 | select { 225 | text-transform: none; 226 | } 227 | 228 | /** 229 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 230 | * and `video` controls. 231 | * 2. Correct inability to style clickable `input` types in iOS. 232 | * 3. Improve usability and consistency of cursor style between image-type 233 | * `input` and others. 234 | * 4. Remove inner spacing in IE 7 without affecting normal text inputs. 235 | * Known issue: inner spacing remains in IE 6. 236 | */ 237 | 238 | button, 239 | html input[type="button"], /* 1 */ 240 | input[type="reset"], 241 | input[type="submit"] { 242 | -webkit-appearance: button; /* 2 */ 243 | cursor: pointer; /* 3 */ 244 | *overflow: visible; /* 4 */ 245 | } 246 | 247 | /** 248 | * Re-set default cursor for disabled elements. 249 | */ 250 | 251 | button[disabled], 252 | html input[disabled] { 253 | cursor: default; 254 | } 255 | 256 | /** 257 | * 1. Address box sizing set to content-box in IE 8/9. 258 | * 2. Remove excess padding in IE 8/9. 259 | * 3. Remove excess padding in IE 7. 260 | * Known issue: excess padding remains in IE 6. 261 | */ 262 | 263 | input[type="checkbox"], 264 | input[type="radio"] { 265 | box-sizing: border-box; /* 1 */ 266 | padding: 0; /* 2 */ 267 | *height: 13px; /* 3 */ 268 | *width: 13px; /* 3 */ 269 | } 270 | 271 | /** 272 | * 1. Address `appearance` set to `searchfield` in Safari 5 and Chrome. 273 | * 2. Address `box-sizing` set to `border-box` in Safari 5 and Chrome 274 | * (include `-moz` to future-proof). 275 | */ 276 | 277 | input[type="search"] { 278 | -webkit-appearance: textfield; /* 1 */ 279 | -moz-box-sizing: content-box; 280 | -webkit-box-sizing: content-box; /* 2 */ 281 | box-sizing: content-box; 282 | } 283 | 284 | /** 285 | * Remove inner padding and search cancel button in Safari 5 and Chrome 286 | * on OS X. 287 | */ 288 | 289 | input[type="search"]::-webkit-search-cancel-button, 290 | input[type="search"]::-webkit-search-decoration { 291 | -webkit-appearance: none; 292 | } 293 | 294 | /** 295 | * Remove inner padding and border in Firefox 3+. 296 | */ 297 | 298 | button::-moz-focus-inner, 299 | input::-moz-focus-inner { 300 | border: 0; 301 | padding: 0; 302 | } 303 | 304 | /** 305 | * 1. Remove default vertical scrollbar in IE 6/7/8/9. 306 | * 2. Improve readability and alignment in all browsers. 307 | */ 308 | 309 | textarea { 310 | overflow: auto; /* 1 */ 311 | vertical-align: top; /* 2 */ 312 | } 313 | 314 | /** 315 | * Remove most spacing between table cells. 316 | */ 317 | 318 | table { 319 | border-collapse: collapse; 320 | border-spacing: 0; 321 | } 322 | 323 | html, 324 | button, 325 | input, 326 | select, 327 | textarea { 328 | color: #222; 329 | } 330 | 331 | 332 | ::-moz-selection { 333 | background: #b3d4fc; 334 | text-shadow: none; 335 | } 336 | 337 | ::selection { 338 | background: #b3d4fc; 339 | text-shadow: none; 340 | } 341 | 342 | img { 343 | vertical-align: middle; 344 | } 345 | 346 | fieldset { 347 | border: 0; 348 | margin: 0; 349 | padding: 0; 350 | } 351 | 352 | textarea { 353 | resize: vertical; 354 | } 355 | 356 | .chromeframe { 357 | margin: 0.2em 0; 358 | background: #ccc; 359 | color: #000; 360 | padding: 0.2em 0; 361 | } 362 | -------------------------------------------------------------------------------- /src/assets/img/circle_blue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 26 | 35 | 40 | 45 | 47 | 56 | 61 | 66 | 68 | 78 | 83 | 88 | 90 | 98 | 103 | 105 | 113 | 118 | 120 | 122 | 146 | 151 | 161 | 171 | 182 | 188 | 190 | 192 | 194 | 196 | image/svg+xml 199 | 202 | 205 | 207 | 210 | Openclipart 213 | 215 | 217 | blue circle 220 | 2007-02-13T16:45:28 223 | A blue circle. Inspired by Molumen's splashscreen submission, so I wanted to experiment with some shapes and shading. 226 | https://openclipart.org/detail/3201/blue-circle-by-nlyl 229 | 231 | 233 | nlyl 236 | 238 | 240 | 242 | 244 | ball 247 | blue 250 | circle 253 | round 256 | shape 259 | 261 | 263 | 265 | 268 | 271 | 274 | 277 | 279 | 281 | 283 | 285 | -------------------------------------------------------------------------------- /src/components/Main/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 199 | 200 | 262 | 263 | 429 | -------------------------------------------------------------------------------- /src/views/Login.vue: -------------------------------------------------------------------------------- 1 | 113 | 114 | 314 | 315 | 487 | -------------------------------------------------------------------------------- /src/components/Main/Header.vue: -------------------------------------------------------------------------------- 1 | 163 | 164 | 326 | 327 | 589 | -------------------------------------------------------------------------------- /src/views/History.vue: -------------------------------------------------------------------------------- 1 | 271 | 272 | 409 | 410 | 601 | --------------------------------------------------------------------------------