Skip to content

Commit

Permalink
feat/let user remove custom exchange rate (#195)
Browse files Browse the repository at this point in the history
* feat/let user remove custom exchange rate

* feat/Delete 'watchEffect' from component

* feat/let user remove custom exchange rate

* fix/fix the comments

* fix: linter npm script

* fix/fix linter problems

* fix/change input field and checkbox UI

* chore: Improve namings and fix eslint warnings

---------

Co-authored-by: Dmitry Sviridenko <[email protected]>
  • Loading branch information
cripacrip and letehaha authored Jul 14, 2023
1 parent 6d8799b commit 650e399
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = {
},
],

ignorePatterns: ['cypress/**/*', 'cypress.config.ts'],
ignorePatterns: ['cypress/**/*', 'cypress.config.ts', 'backend'],

extends: [
'plugin:vue/vue3-essential',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"scripts": {
"build": "vue-tsc --noEmit && vite build",
"lint": "eslint -c ./.eslintrc.js --ignore-path ./.eslintignore",
"lint": "eslint -c ./.eslintrc.js --ignore-path ./.eslintignore .",
"dev": "vite",
"prod": "vite --mode production",
"docker-build": "docker build . -t letehaha/budget-tracker-fe",
Expand Down
7 changes: 7 additions & 0 deletions src/api/currencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ export const loadUserCurrencies = async () => {
return result.map(item => new UserCurrencyRecord(item));
};

export const deleteCustomRate = (
pairs: {
baseCode: string;
quoteCode: string;
}[],
) => api.delete('/user/currency/rates', { pairs });

export const loadUserCurrenciesExchangeRates = async () => {
const result = await api.get('/user/currencies/rates');

Expand Down
10 changes: 9 additions & 1 deletion src/components/fields/input-field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div
:class="{
'input-field--error': errorMessage,
'input-field--disabled': $attrs.disabled
'input-field--disabled': disabled,
}"
class="input-field"
>
Expand All @@ -18,6 +18,7 @@
:type="type"
:value="modelValue"
:style="inputFieldStyles"
:disabled="disabled"
:tabindex="tabindex"
:min="minValue"
class="input-field__input"
Expand Down Expand Up @@ -62,6 +63,7 @@ export default defineComponent({
label: { type: String, default: undefined },
modelValue: { type: [String, Number], default: undefined },
type: { type: String, default: undefined },
disabled: { type: Boolean, default: false },
tabindex: { type: String, default: undefined },
errorMessage: { type: String, default: undefined },
inputFieldStyles: { type: Object, default: undefined },
Expand All @@ -78,12 +80,15 @@ export default defineComponent({
onInput: (event: InputChangeEvent) => {
let value: string | number = event.target.value;
if (props.disabled) return;
if (props.modelValue === value) return;
if (props.type === 'number') value = Number(value);
emit(MODEL_EVENTS.input, value);
},
onkeypress: (event: KeyboardEvent) => {
if (props.disabled) return;
if (props.type === 'number') {
if (event.keyCode === KEYBOARD_CODES.keyE) {
event.preventDefault();
Expand Down Expand Up @@ -131,6 +136,9 @@ export default defineComponent({
width: 100%;
flex: 1;
}
.input-field--disabled {
opacity: 0.6;
}
.input-field__input {
font-size: 16px;
line-height: 1;
Expand Down
4 changes: 4 additions & 0 deletions src/js/records/models/exchange-rate.record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ interface Record {
baseId: number;
quoteId: number;
rate: number;
custom: boolean;
baseCode: string;
quoteCode: string;
date: string;
Expand All @@ -21,6 +22,8 @@ export class ExchangeRateRecord {

quoteRate: number;

custom: boolean;

baseCode: string;

quoteCode: string;
Expand All @@ -33,6 +36,7 @@ export class ExchangeRateRecord {
this.id = record.id;
this.baseId = record.baseId;
this.quoteId = record.quoteId;
this.custom = record.custom;
this.rate = record.rate;
this.quoteRate = Number(Number(1 / record.rate).toFixed(5));
this.baseCode = record.baseCode;
Expand Down
77 changes: 72 additions & 5 deletions src/pages/settings/tabs/currencies/edit-currency.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@
<input-field
v-model="form.baseRate"
:label="`1 ${currency.code} =`"
:custom-disabled="!isChecked"
@focus="onBaseFocus"
/>
<input-field
v-model="form.quoteRate"
:label="`1 ${currency.quoteCode} =`"
:custom-disabled="!isChecked"
@focus="onQuoteFocus"
/>
<div class="edit-currency__checkbox">
<label class="edit-currency__label">
<span class="edit-currency__live-span">Live update</span>
<input
:checked="!currency.custom"
class="tick-field__input"
type="checkbox"
@change="toggleChange"
>
</label>
</div>
</div>
<div class="edit-currency__actions">
<ui-tooltip :content="!isFormDirty ? 'Nothing to save' : ''">
Expand All @@ -28,7 +41,7 @@
:disabled="deletionDisabled"
@click="onDeleteHandler"
>
Delete
Delete currency
</ui-button>
</ui-tooltip>
</div>
Expand All @@ -40,14 +53,15 @@ import {
defineComponent,
reactive,
computed,
onMounted,
ref,
watch,
PropType,
} from 'vue';
import { API_ERROR_CODES } from 'shared-types';
import { storeToRefs } from 'pinia';
import { useCurrenciesStore } from '@/stores';
import { editUserCurrenciesExchangeRates } from '@/api/currencies';
import { editUserCurrenciesExchangeRates, deleteCustomRate } from '@/api/currencies';
import UiButton, { BUTTON_THEMES } from '@/components/common/ui-button.vue';
import InputField from '@/components/fields/input-field.vue';
import UiTooltip from '@/components/common/tooltip.vue';
Expand Down Expand Up @@ -89,13 +103,17 @@ export default defineComponent({
});
const isBaseEditing = ref(false);
const isQuoteEditing = ref(false);
const isChecked = ref<boolean>(false);
const isRateChanged = computed(() => (
+props.currency.rate !== +form.baseRate
|| +props.currency.quoteRate !== +form.quoteRate
));
const isFormDirty = computed(() => isRateChanged.value);
const isFormDirty = computed(() => (
isRateChanged.value
|| (props.currency.custom && !isChecked.value)
));
const onBaseFocus = () => {
isBaseEditing.value = true;
Expand All @@ -105,6 +123,9 @@ export default defineComponent({
isQuoteEditing.value = true;
isBaseEditing.value = false;
};
const toggleChange = (event) => {
isChecked.value = !event.target.checked;
};
watch(
() => form.baseRate,
Expand All @@ -123,10 +144,39 @@ export default defineComponent({
},
);
onMounted(() => {
isChecked.value = props.currency.custom;
});
const onDeleteHandler = () => {
emit('delete');
};
const deleteExchangeRates = async () => {
try {
await deleteCustomRate([
{
baseCode: props.currency.code,
quoteCode: props.currency.quoteCode,
},
{
baseCode: props.currency.quoteCode,
quoteCode: props.currency.code,
},
]);
emit('submit');
addSuccessNotification('Successfully updated.');
} catch (e) {
if (e.data.code === API_ERROR_CODES.validationError) {
addErrorNotification(e.data.message);
return;
}
addErrorNotification('Unexpected error');
}
};
const updateExchangeRates = async () => {
try {
await editUserCurrenciesExchangeRates([
Expand Down Expand Up @@ -156,14 +206,18 @@ export default defineComponent({
};
const onSaveHandler = async () => {
if (isRateChanged.value) {
if (isRateChanged.value && !props.currency.custom) {
await updateExchangeRates();
} else if (props.currency.custom) {
await deleteExchangeRates();
}
};
return {
DISABLED_DELETE_TEXT,
BUTTON_THEMES,
isChecked,
toggleChange,
onSaveHandler,
onDeleteHandler,
form,
Expand All @@ -190,8 +244,21 @@ export default defineComponent({
gap: 32px;
margin-top: 32px;
}
.edit-currency__checkbox {
display: flex;
justify-content: center;
align-items: center;
}
.edit-currency__label {
display: flex;
justify-content: center;
align-items: center;
}
.edit-currency__live-span {
margin-right: 10px;
}
.edit-currency__ratio {
max-width: 360px;
max-width: 485px;
display: flex;
gap: 16px;
}
Expand Down
4 changes: 3 additions & 1 deletion src/pages/settings/tabs/currencies/list.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export default defineComponent({
return {
...item,
rate: Number(rate?.rate?.toFixed(4)),
custom: rate?.custom ?? false,
quoteCode: rate?.quoteCode,
quoteRate: Number(rate?.quoteRate?.toFixed(4)),
};
Expand Down Expand Up @@ -147,6 +148,7 @@ export default defineComponent({
const onSubmitHandler = () => {
loadRates();
toggleActiveItem(null);
};
const isDeletionDisabled = (currency: UserCurrencyRecord) => (
Expand Down Expand Up @@ -184,7 +186,7 @@ export default defineComponent({
padding: var(--settings-currency-list-item-padding);
display: grid;
grid-template-columns: 50px 300px 1fr;
grid-template-columns: 50px 300px 1fr 50px;
grid-gap: 16px;
transition: background-color .2s ease-out;
Expand Down
1 change: 1 addition & 0 deletions src/pages/settings/tabs/currencies/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ import { UserCurrencyRecord } from '@/js/records';
export type CurrencyWithExchangeRate = UserCurrencyRecord & {
rate: number;
quoteCode: string;
custom?: boolean;
quoteRate: number;
}

0 comments on commit 650e399

Please sign in to comment.