Skip to content

Commit

Permalink
feat(core): add change password dialog and logout function
Browse files Browse the repository at this point in the history
  • Loading branch information
simonwep committed Jul 29, 2023
1 parent 07f69f9 commit b1ffbea
Show file tree
Hide file tree
Showing 19 changed files with 251 additions and 77 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"lint:i18n:fix": "pnpm run lint:i18n --fix",
"lint": "pnpm run lint:i18n && pnpm run lint:src",
"lint:fix": "pnpm run lint:i18n:fix && pnpm run lint:src:fix",
"test:ci": "pnpm run build && pnpm run lint:fix",
"test:ci": "pnpm run lint:fix && pnpm run build",
"gen:icons": "node scripts/icons.js"
},
"dependencies": {
Expand Down
36 changes: 36 additions & 0 deletions src/app/components/base/alert/Alert.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<template>
<p :class="[$style.alert, classes]">
{{ text }}
</p>
</template>

<script lang="ts" setup>
import { computed } from 'vue';
import { Color, useThemeStyles } from '@composables';
import { ClassNames } from '@utils';
const props = withDefaults(
defineProps<{
class?: ClassNames;
text: string;
color?: Color;
}>(),
{
color: 'primary'
}
);
const classes = computed(() => props.class);
const theme = useThemeStyles(() => props.color);
</script>

<style lang="scss" module>
.alert {
background: v-bind('theme.color.base');
color: v-bind('theme.text.base');
padding: 4px 6px;
font-weight: var(--font-weight-l);
font-size: var(--font-size-xs);
border-radius: var(--border-radius-m);
}
</style>
1 change: 1 addition & 0 deletions src/app/components/base/icon/Icon.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export type AppIcon =
| 'google-fill'
| 'grid-line'
| 'hand-coin'
| 'key-2-line'
| 'magic-line'
| 'menu-line'
| 'moon-fill'
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/base/input-field/InputField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const passwordBarColor = computed(() => {
.label {
font-weight: var(--font-weight-l);
font-size: var(--font-size-s);
font-size: var(--font-size-xs);
}
.passwordStrength {
Expand Down
41 changes: 41 additions & 0 deletions src/app/components/base/input-field/InputFields.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<div :class="$style.fields">
<h3 :class="$style.title">{{ title }}</h3>
<slot />
<Button :class="$style.btn" :text="submitLabel ?? title" color="success" @click="emit('submit')" />
</div>
</template>
<script lang="ts" setup>
import Button from '@components/base/button/Button.vue';
const emit = defineEmits<{
(e: 'submit'): void;
}>();
defineProps<{
title: string;
submitLabel?: string;
}>();
</script>
<style lang="scss" module>
.fields {
display: flex;
flex-direction: column;
width: 200px;
gap: 8px;
padding-top: 8px;
.title {
text-align: center;
font-weight: var(--font-weight-l);
font-style: var(--font-size-m);
padding-bottom: 8px;
}
.btn {
text-align: center;
}
}
</style>
1 change: 0 additions & 1 deletion src/app/pages/dashboard/summary/Summary.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const expenses = computed(() => totals(state.expenses));
flex-direction: column;
grid-gap: 20px;
flex-grow: 1;
height: 100%;
padding-bottom: 10px;
}
Expand Down
41 changes: 14 additions & 27 deletions src/app/pages/navigation/auth/LoginDialog.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<template>
<Dialog :open="open" @close="emit('close')">
<div :class="$style.fields">
<h3 :class="$style.title">Sign in</h3>
<InputField v-model="username" placeholder="Enter your username" label="User" type="text" />
<InputField v-model="password" placeholder="Your password" label="Password" type="password" />
<Button :class="$style.btn" text="Sign in" color="success" @click="signIn" />
</div>
<InputFields :title="t('auth.signIn')" @submit="signIn">
<InputField v-model="username" :label="t('auth.username')" type="text" />
<InputField v-model="password" :label="t('auth.password')" type="password" />
<Alert v-if="state === 'errored'" :text="t('auth.errors.login')" color="danger" />
</InputFields>
</Dialog>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import Button from '@components/base/button/Button.vue';
import { useI18n } from 'vue-i18n';
import Alert from '@components/base/alert/Alert.vue';
import Dialog from '@components/base/dialog/Dialog.vue';
import InputField from '@components/base/input-field/InputField.vue';
import InputFields from '@components/base/input-field/InputFields.vue';
import { useStorage } from '@storage/index';
const emit = defineEmits<{
Expand All @@ -24,14 +25,20 @@ defineProps<{
open: boolean;
}>();
const { t } = useI18n();
const { login } = useStorage();
const username = ref(import.meta.env.APP_USERNAME ?? '');
const password = ref(import.meta.env.APP_PASSWORD ?? '');
const state = ref<'idle' | 'loading' | 'errored'>('idle');
const signIn = async () => {
if (state.value !== 'loading') {
state.value = 'loading';
if (await login(username.value, password.value)) {
username.value = '';
password.value = '';
state.value = 'idle';
emit('close');
} else {
Expand All @@ -40,23 +47,3 @@ const signIn = async () => {
}
};
</script>

<style lang="scss" module>
.fields {
display: flex;
flex-direction: column;
width: 200px;
gap: 8px;
padding-top: 8px;
.title {
text-align: center;
font-weight: var(--font-weight-xl);
font-style: var(--font-size-m);
}
.btn {
text-align: center;
}
}
</style>
2 changes: 2 additions & 0 deletions src/app/pages/navigation/tools/ToolsButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<template #options>
<LoadDemoDataButton v-if="status === 'idle'" />
<ChangePasswordButton />
<PrivacyModeButton />
<ExportButton />
<ImportButton />
Expand All @@ -26,6 +27,7 @@ import ContextMenu from '@components/base/context-menu/ContextMenu.vue';
import { useMediaQuery } from '@composables';
import { useStorage } from '@storage/index';
import { ClassNames } from '@utils';
import ChangePasswordButton from './change-password/ChangePasswordButton.vue';
import CopyButton from './copy-paste/CopyButton.vue';
import PasteButton from './copy-paste/PasteButton.vue';
import LoadDemoDataButton from './demo/LoadDemoDataButton.vue';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<ContextMenuButton icon="key-2-line" :text="t('auth.changePassword')" @click="showChangePasswordDialog = true" />
<ChangePasswordDialog :open="showChangePasswordDialog" @close="showChangePasswordDialog = false" />
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import ContextMenuButton from '@components/base/context-menu/ContextMenuButton.vue';
import ChangePasswordDialog from './ChangePasswordDialog.vue';
const { t } = useI18n();
const showChangePasswordDialog = ref(false);
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<template>
<Dialog :open="open" @close="emit('close')">
<InputFields :title="t('auth.changePassword')" @submit="submit">
<InputField v-model="currentPassword" :label="t('auth.currentPassword')" type="password" />
<InputField v-model="newPassword" :label="t('auth.newPassword')" type="password" show-password-strength />
<Alert v-if="state === 'errored'" :text="t('auth.errors.changePassword')" color="danger" />
</InputFields>
</Dialog>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { useI18n } from 'vue-i18n';
import Alert from '@components/base/alert/Alert.vue';
import Dialog from '@components/base/dialog/Dialog.vue';
import InputField from '@components/base/input-field/InputField.vue';
import InputFields from '@components/base/input-field/InputFields.vue';
import { useStorage } from '@storage/index';
const emit = defineEmits<{
(e: 'close'): void;
}>();
defineProps<{
open: boolean;
}>();
const { t } = useI18n();
const { updatePassword } = useStorage();
const currentPassword = ref(import.meta.env.APP_PASSWORD ?? '');
const newPassword = ref('');
const state = ref<'idle' | 'loading' | 'errored'>('idle');
const submit = async () => {
if (state.value !== 'loading') {
if (await updatePassword(currentPassword.value, newPassword.value)) {
currentPassword.value = '';
newPassword.value = '';
state.value = 'idle';
emit('close');
} else {
state.value = 'errored';
}
}
};
</script>

<style lang="scss" module>
.fields {
display: flex;
flex-direction: column;
width: 200px;
gap: 8px;
padding-top: 8px;
.title {
text-align: center;
font-weight: var(--font-weight-xl);
font-style: var(--font-size-m);
}
.btn {
text-align: center;
}
}
</style>
12 changes: 12 additions & 0 deletions src/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
"app": {
"name": "Ocular"
},
"auth": {
"changePassword": "Passwort ändern",
"currentPassword": "Aktuelles Passwort",
"errors": {
"changePassword": "Aktuelles Passwort ungültig oder neues Passwort zu unsicher. Stellen Sie sicher, dass es mindestens 6 Zeichen hat.",
"login": "Anmeldung fehlgeschlagen, Benutzername und/oder Passwort ungültig."
},
"newPassword": "Neues Passwort",
"password": "Passwort",
"signIn": "Anmelden",
"username": "Benutzername"
},
"budget": {
"addGroup": "Gruppe hinzufügen",
"append": "„{from}“ nach „{to}“ verschieben",
Expand Down
12 changes: 12 additions & 0 deletions src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@
"app": {
"name": "Ocular"
},
"auth": {
"changePassword": "Change password",
"currentPassword": "Current password",
"errors": {
"changePassword": "Current password invalid or new one too unsafe. Make sure it has at least 6 characters.",
"login": "Login failed, username and/or password invalid."
},
"newPassword": "New password",
"password": "Password",
"signIn": "Sign in",
"username": "Username"
},
"budget": {
"addGroup": "Add Group",
"append": "Move “{from}” after “{to}”",
Expand Down
1 change: 1 addition & 0 deletions src/icons/key-2-line.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ export const createGenesisStore = (baseUrl: string) => {
}
};

const logout = async () => {
await fetch(`${baseUrl}/logout`, { method: 'POST' });
removeToken();
};

const refreshToken = async (): Promise<void> => {
const response = await fetch(`${baseUrl}/login/refresh`, { method: 'POST' });

Expand Down Expand Up @@ -140,8 +145,6 @@ export const createGenesisStore = (baseUrl: string) => {
refreshToken().catch(removeToken);
}

const logout = () => removeToken();

const isLoggedIn = () => !!tokenDataObj;

return {
Expand Down
Loading

0 comments on commit b1ffbea

Please sign in to comment.