Skip to content

Commit 28c8dff

Browse files
author
Kirill Serebrennikov
committed
feat: add planning
1 parent 74daf35 commit 28c8dff

28 files changed

+1450
-192
lines changed

.env

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
VUE_APP_FIXER = 938cc1128efd7a6c62e816f4d9376df9

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ node_modules
33
/dist
44

55
# local env files
6-
.env.local
7-
.env.*.local
6+
# .env.local
7+
# .env.*.local
88

99
# Log files
1010
npm-debug.log*

package-lock.json

+461-9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
},
1010
"dependencies": {
1111
"core-js": "^3.6.5",
12+
"firebase": "^7.17.1",
1213
"materialize-css": "^1.0.0-rc.2",
1314
"register-service-worker": "^1.7.1",
1415
"vue": "^2.6.11",

src/components/CategoryCreate.vue

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
<div class="col s12 m6">
3+
<div>
4+
<div class="page-subtitle">
5+
<h4>Создать</h4>
6+
</div>
7+
8+
<form @submit.prevent="submitHandler">
9+
<div class="input-field">
10+
<input id="name" type="text" v-model="title" :class="{invalid: $v.title.$dirty && !$v.title.required}"/>
11+
<label for="name">Название</label>
12+
<span class="helper-text invalid" v-if="$v.title.$dirty && !$v.title.required">Введите название категории</span>
13+
</div>
14+
15+
<div class="input-field">
16+
<input id="limit" type="number" v-model.number="limit" :class="{invalid: $v.limit.$dirty && !$v.limit.minValue}" />
17+
<label for="limit">Лимит</label>
18+
<span class="helper-text invalid" v-if="$v.limit.$dirty && !$v.limit.minValue">Минимальная величина {{$v.limit.$params.minValue.min}}</span>
19+
</div>
20+
21+
<button class="btn waves-effect waves-light" type="submit">
22+
Создать
23+
<i class="material-icons right">send</i>
24+
</button>
25+
</form>
26+
</div>
27+
</div>
28+
</template>
29+
30+
<script>
31+
import {required, minValue} from 'vuelidate/lib/validators'
32+
import M from 'materialize-css'
33+
34+
export default {
35+
data: () => ({
36+
title: '',
37+
limit: 100
38+
}),
39+
validations: {
40+
title: {required},
41+
limit: {minValue: minValue(100)}
42+
},
43+
mounted() {
44+
M.updateTextFields()
45+
},
46+
methods: {
47+
async submitHandler() {
48+
if(this.$v.$invalid) {
49+
this.$v.$touch()
50+
return
51+
}
52+
try {
53+
const category = await this.$store.dispatch('createCategory', {
54+
title: this.title,
55+
limit: this.limit
56+
})
57+
this.title = ''
58+
this.limit = 100
59+
this.$v.$reset()
60+
this.$message('Категория была создана')
61+
this.$emit('created', category)
62+
} catch(e) {
63+
64+
}
65+
66+
67+
}
68+
}
69+
}
70+
</script>

src/components/CategoryEdit.vue

+116
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<template>
2+
<div class="col s12 m6">
3+
<div>
4+
<div class="page-subtitle">
5+
<h4>Редактировать</h4>
6+
</div>
7+
8+
<form @submit.prevent="submitHandler">
9+
<div class="input-field">
10+
<select ref="select" v-model="current">
11+
<option v-for="c of categories" :key="c.id" :value="c.id">{{c.title}}</option>
12+
</select>
13+
<label>Выберите категорию</label>
14+
</div>
15+
16+
<div class="input-field">
17+
<input
18+
id="name"
19+
type="text"
20+
v-model="title"
21+
:class="{invalid: $v.title.$dirty && !$v.title.required}"
22+
/>
23+
<label for="name">Название</label>
24+
<span
25+
v-if="$v.title.$dirty && !$v.title.required"
26+
class="helper-text invalid"
27+
>Введите название категории</span>
28+
</div>
29+
30+
<div class="input-field">
31+
<input
32+
id="limit"
33+
type="number"
34+
v-model.number="limit"
35+
:class="{invalid: $v.limit.$dirty && !$v.limit.minValue}"
36+
/>
37+
<label for="limit">Лимит</label>
38+
<span
39+
v-if="$v.limit.$dirty && !$v.limit.minValue"
40+
class="helper-text invalid"
41+
>Минимальная значение {{$v.limit.$params.minValue.min}}</span>
42+
</div>
43+
44+
<button class="btn waves-effect waves-light" type="submit">
45+
Обновить
46+
<i class="material-icons right">send</i>
47+
</button>
48+
</form>
49+
</div>
50+
</div>
51+
</template>
52+
53+
<script>
54+
import { required, minValue } from "vuelidate/lib/validators";
55+
import M from "materialize-css"
56+
57+
export default {
58+
props: {
59+
categories: {
60+
type: Array,
61+
required: true
62+
}
63+
},
64+
data: () => ({
65+
select: null,
66+
title: "",
67+
limit: 100,
68+
current: null
69+
}),
70+
validations: {
71+
title: { required },
72+
limit: { minValue: minValue(100) }
73+
},
74+
watch: {
75+
current(catId) {
76+
const { title, limit } = this.categories.find(c => c.id === catId);
77+
this.title = title;
78+
this.limit = limit;
79+
}
80+
},
81+
created() {
82+
const { id, title, limit } = this.categories[0];
83+
this.current = id;
84+
this.title = title;
85+
this.limit = limit;
86+
},
87+
methods: {
88+
async submitHandler() {
89+
if (this.$v.$invalid) {
90+
this.$v.$touch();
91+
return;
92+
}
93+
94+
try {
95+
const categoryData = {
96+
id: this.current,
97+
title: this.title,
98+
limit: this.limit
99+
};
100+
await this.$store.dispatch("updateCategory", categoryData);
101+
this.$message("Категория упешно обновлена");
102+
this.$emit("updated", categoryData);
103+
} catch (e) {}
104+
}
105+
},
106+
mounted() {
107+
this.select = M.FormSelect.init(this.$refs.select);
108+
M.updateTextFields();
109+
},
110+
destroyed() {
111+
if (this.select && this.select.destroy) {
112+
this.select.destroy();
113+
}
114+
}
115+
};
116+
</script>

src/components/HomeBill.vue

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<template>
2+
<div class="col s12 m6 l4">
3+
<div class="card light-blue bill-card">
4+
<div class="card-content white-text">
5+
<span class="card-title">Счет в валюте</span>
6+
7+
<p v-for="cur of currencies" :key="cur" class="currency-line">
8+
<span>
9+
{{getCurrency(cur) | currency(cur)}}
10+
</span>
11+
</p>
12+
</div>
13+
</div>
14+
</div>
15+
</template>
16+
17+
<script>
18+
export default {
19+
props: ['rates'],
20+
data: () => ({
21+
currencies: ['KZT', 'USD', 'EUR']
22+
}),
23+
computed: {
24+
base() {
25+
return this.$store.getters.info.bill / (this.rates['KZT'] / this.rates['EUR'])
26+
}
27+
},
28+
methods: {
29+
getCurrency(currency) {
30+
return Math.floor(this.base * this.rates[currency])
31+
}
32+
}
33+
}
34+
</script>

src/components/HomeCurrency.vue

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<template>
2+
<div class="col s12 m6 l8">
3+
<div class="card orange darken-3 bill-card">
4+
<div class="card-content white-text">
5+
<div class="card-header">
6+
<span class="card-title">Курс валют</span>
7+
</div>
8+
<table>
9+
<thead>
10+
<tr>
11+
<th>Валюта</th>
12+
<th>Курс</th>
13+
<th>Дата</th>
14+
</tr>
15+
</thead>
16+
17+
<tbody>
18+
<tr v-for="cur of currencies" :key="cur">
19+
<td>{{cur}}</td>
20+
<td>{{rates[cur].toFixed(2)}}</td>
21+
<td>{{date | date('date')}}</td>
22+
</tr>
23+
</tbody>
24+
</table>
25+
</div>
26+
</div>
27+
</div>
28+
</template>
29+
30+
<script>
31+
export default {
32+
props: ['rates', 'date'],
33+
data: () => ({
34+
currencies: ['KZT', 'USD', 'EUR']
35+
}),
36+
37+
38+
}
39+
</script>

src/components/app/Loader.vue

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<template>
2+
<div class="app-loader">
3+
<div class="preloader-wrapper active">
4+
<div class="spinner-layer" :class="color">
5+
<div class="circle-clipper left">
6+
<div class="circle"></div>
7+
</div><div class="gap-patch">
8+
<div class="circle"></div>
9+
</div><div class="circle-clipper right">
10+
<div class="circle"></div>
11+
</div>
12+
</div>
13+
</div>
14+
</div>
15+
</template>
16+
17+
<script>
18+
export default {
19+
computed: {
20+
color() {
21+
const colors = ['spinner-red-only', 'spinner-blue-only', 'spinner-green-only']
22+
return colors[Math.floor(Math.random() * 3)]
23+
}
24+
}
25+
}
26+
</script>

src/components/app/Navbar.vue

+8-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<ul class="right hide-on-small-and-down">
1212
<li>
1313
<a class="dropdown-trigger black-text" href="#" data-target="dropdown" ref="dropdown">
14-
USER NAME
14+
{{name}}
1515
<i class="material-icons right">arrow_drop_down</i>
1616
</a>
1717

@@ -43,11 +43,16 @@ export default {
4343
dropdown: null
4444
}),
4545
methods: {
46-
logout() {
47-
console.log('logout')
46+
async logout() {
47+
await this.$store.dispatch('logout')
4848
this.$router.push('/login?message=logout')
4949
}
5050
},
51+
computed: {
52+
name() {
53+
return this.$store.getters.info.name
54+
}
55+
},
5156
mounted() {
5257
this.interval = setInterval(() => {
5358
this.date = new Date()

src/directives/tooltip.directive.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import M from 'materialize-css'
2+
export default {
3+
4+
bind(el, {value}) {
5+
M.Tooltip.init(el, {html: value})
6+
},
7+
unbind(el) {
8+
const tooltip = M.Tooltip.getInstance(el)
9+
10+
if(tooltip && tooltip.destroy) {
11+
tooltip.destroy()
12+
}
13+
}
14+
}

src/filters/currency.filter.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export default function currencyFilter(value, currency = 'KZT') {
2+
return new Intl.NumberFormat('ru-RU', {
3+
style: 'currency',
4+
currency
5+
}).format(value)
6+
}

src/layout/EmptyLayout.vue

+17
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,20 @@
33
<router-view />
44
</div>
55
</template>
6+
7+
<script>
8+
import messages from '@/utils/messages'
9+
export default {
10+
computed: {
11+
error() {
12+
return this.$store.getters.error
13+
}
14+
},
15+
watch: {
16+
error(fbError) {
17+
console.log(fbError);
18+
this.$error(messages[fbError.code] || 'Что-то пошло не так')
19+
}
20+
}
21+
}
22+
</script>

0 commit comments

Comments
 (0)