forked from Kispi/Core
feat(webapp): add banker login
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
0b087a38bf
commit
36638fbf74
10 changed files with 291 additions and 118 deletions
|
@ -1,111 +1,4 @@
|
||||||
[
|
[
|
||||||
{
|
|
||||||
"id": "w85pgrtrmovf916",
|
|
||||||
"name": "transactions",
|
|
||||||
"type": "base",
|
|
||||||
"system": false,
|
|
||||||
"schema": [
|
|
||||||
{
|
|
||||||
"id": "5aeh5giq",
|
|
||||||
"name": "account",
|
|
||||||
"type": "relation",
|
|
||||||
"system": false,
|
|
||||||
"required": true,
|
|
||||||
"options": {
|
|
||||||
"collectionId": "74ftooxenpeq14b",
|
|
||||||
"cascadeDelete": false,
|
|
||||||
"minSelect": null,
|
|
||||||
"maxSelect": 1,
|
|
||||||
"displayFields": [
|
|
||||||
"firstName",
|
|
||||||
"lastName"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "2bzqyeuk",
|
|
||||||
"name": "label",
|
|
||||||
"type": "text",
|
|
||||||
"system": false,
|
|
||||||
"required": true,
|
|
||||||
"options": {
|
|
||||||
"min": null,
|
|
||||||
"max": null,
|
|
||||||
"pattern": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "zsx9i8w7",
|
|
||||||
"name": "amount",
|
|
||||||
"type": "number",
|
|
||||||
"system": false,
|
|
||||||
"required": false,
|
|
||||||
"options": {
|
|
||||||
"min": null,
|
|
||||||
"max": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indexes": [],
|
|
||||||
"listRule": "",
|
|
||||||
"viewRule": "",
|
|
||||||
"createRule": "",
|
|
||||||
"updateRule": null,
|
|
||||||
"deleteRule": null,
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "5msnfxat1sc2c1r",
|
|
||||||
"name": "companyTransactions",
|
|
||||||
"type": "base",
|
|
||||||
"system": false,
|
|
||||||
"schema": [
|
|
||||||
{
|
|
||||||
"id": "7rnyupog",
|
|
||||||
"name": "account",
|
|
||||||
"type": "relation",
|
|
||||||
"system": false,
|
|
||||||
"required": true,
|
|
||||||
"options": {
|
|
||||||
"collectionId": "s854d2w72fvyl54",
|
|
||||||
"cascadeDelete": false,
|
|
||||||
"minSelect": null,
|
|
||||||
"maxSelect": 1,
|
|
||||||
"displayFields": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "ruby9fp9",
|
|
||||||
"name": "label",
|
|
||||||
"type": "text",
|
|
||||||
"system": false,
|
|
||||||
"required": true,
|
|
||||||
"options": {
|
|
||||||
"min": null,
|
|
||||||
"max": null,
|
|
||||||
"pattern": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "4hiu46ib",
|
|
||||||
"name": "amount",
|
|
||||||
"type": "number",
|
|
||||||
"system": false,
|
|
||||||
"required": false,
|
|
||||||
"options": {
|
|
||||||
"min": null,
|
|
||||||
"max": null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"indexes": [],
|
|
||||||
"listRule": "",
|
|
||||||
"viewRule": "",
|
|
||||||
"createRule": "",
|
|
||||||
"updateRule": null,
|
|
||||||
"deleteRule": null,
|
|
||||||
"options": {}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"id": "s854d2w72fvyl54",
|
"id": "s854d2w72fvyl54",
|
||||||
"name": "companies",
|
"name": "companies",
|
||||||
|
@ -431,5 +324,135 @@
|
||||||
"updateRule": "(@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.grade = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.data.shift = null && @request.data.wage = null) || (@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.auth.id = company.id)",
|
"updateRule": "(@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.grade = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.data.shift = null && @request.data.wage = null) || (@request.data.id = null && @request.data.accountNumber = null && @request.data.firstName = null && @request.data.lastName = null && @request.data.created = null && @request.data.updated = null && @request.data.company = null && @request.auth.id = company.id)",
|
||||||
"deleteRule": null,
|
"deleteRule": null,
|
||||||
"options": {}
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "w1au07idupp27qv",
|
||||||
|
"name": "bankers",
|
||||||
|
"type": "auth",
|
||||||
|
"system": false,
|
||||||
|
"schema": [],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": null,
|
||||||
|
"viewRule": null,
|
||||||
|
"createRule": null,
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {
|
||||||
|
"allowEmailAuth": true,
|
||||||
|
"allowOAuth2Auth": true,
|
||||||
|
"allowUsernameAuth": true,
|
||||||
|
"exceptEmailDomains": null,
|
||||||
|
"manageRule": null,
|
||||||
|
"minPasswordLength": 8,
|
||||||
|
"onlyEmailDomains": null,
|
||||||
|
"requireEmail": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "w85pgrtrmovf916",
|
||||||
|
"name": "transactions",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"id": "5aeh5giq",
|
||||||
|
"name": "account",
|
||||||
|
"type": "relation",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "74ftooxenpeq14b",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": [
|
||||||
|
"firstName",
|
||||||
|
"lastName"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "2bzqyeuk",
|
||||||
|
"name": "label",
|
||||||
|
"type": "text",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zsx9i8w7",
|
||||||
|
"name": "amount",
|
||||||
|
"type": "number",
|
||||||
|
"system": false,
|
||||||
|
"required": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"viewRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"createRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "5msnfxat1sc2c1r",
|
||||||
|
"name": "companyTransactions",
|
||||||
|
"type": "base",
|
||||||
|
"system": false,
|
||||||
|
"schema": [
|
||||||
|
{
|
||||||
|
"id": "7rnyupog",
|
||||||
|
"name": "account",
|
||||||
|
"type": "relation",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"options": {
|
||||||
|
"collectionId": "s854d2w72fvyl54",
|
||||||
|
"cascadeDelete": false,
|
||||||
|
"minSelect": null,
|
||||||
|
"maxSelect": 1,
|
||||||
|
"displayFields": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ruby9fp9",
|
||||||
|
"name": "label",
|
||||||
|
"type": "text",
|
||||||
|
"system": false,
|
||||||
|
"required": true,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null,
|
||||||
|
"pattern": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "4hiu46ib",
|
||||||
|
"name": "amount",
|
||||||
|
"type": "number",
|
||||||
|
"system": false,
|
||||||
|
"required": false,
|
||||||
|
"options": {
|
||||||
|
"min": null,
|
||||||
|
"max": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"indexes": [],
|
||||||
|
"listRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"viewRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"createRule": "@request.auth.id != \"\" && @collection.bankers.id ?= @request.auth.id",
|
||||||
|
"updateRule": null,
|
||||||
|
"deleteRule": null,
|
||||||
|
"options": {}
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<input
|
<input
|
||||||
|
ref="input"
|
||||||
:value="value"
|
:value="value"
|
||||||
type="number"
|
type="number"
|
||||||
class="input w-full border-primary text-right"
|
class="input w-full border-primary text-right"
|
||||||
|
@ -26,6 +27,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
const input = ref<InstanceType<typeof HTMLInputElement>>();
|
||||||
|
|
||||||
const props = withDefaults(defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
suffixMargin?: boolean,
|
suffixMargin?: boolean,
|
||||||
perHour?: boolean,
|
perHour?: boolean,
|
||||||
|
@ -53,9 +58,19 @@ function onKeyDown(event: KeyboardEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reset() {
|
||||||
|
if(input.value) {
|
||||||
|
input.value.value = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
change: [value: number],
|
change: [value: number],
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
reset,
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
|
@ -5,6 +5,9 @@
|
||||||
:required="required"
|
:required="required"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
class="input w-full border-primary"
|
class="input w-full border-primary"
|
||||||
|
:class="{
|
||||||
|
'!border-error': error,
|
||||||
|
}"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -22,6 +25,10 @@ defineProps({
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
|
error: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/* global defineModel */
|
/* global defineModel */
|
||||||
|
|
63
webapp/src/components/molecules/MoleculeBankerAuthDialog.vue
Normal file
63
webapp/src/components/molecules/MoleculeBankerAuthDialog.vue
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
<template>
|
||||||
|
<div class="hero min-h-full">
|
||||||
|
<form
|
||||||
|
class="hero-content mx-auto flex-col text-center"
|
||||||
|
@submit.prevent="handleAuth"
|
||||||
|
>
|
||||||
|
<AtomInput
|
||||||
|
v-model="username"
|
||||||
|
placeholder="Nutzername"
|
||||||
|
type="text"
|
||||||
|
:error="error"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<AtomInput
|
||||||
|
v-model="password"
|
||||||
|
placeholder="Passwort"
|
||||||
|
type="password"
|
||||||
|
:error="error"
|
||||||
|
required
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
v-if="error"
|
||||||
|
class="alert alert-error flex w-[211px] items-center gap-2"
|
||||||
|
>
|
||||||
|
<XCircleIcon class="h-12 w-12" />
|
||||||
|
<span class="text-base font-normal">Nutzername oder Passwort falsch.</span>
|
||||||
|
</div>
|
||||||
|
<button class="btn-primary btn gap-2 self-end">
|
||||||
|
<ArrowRightOnRectangleIcon class="h-6 w-6" />
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
import { AuthService } from '../../services/auth.service';
|
||||||
|
import { ArrowRightOnRectangleIcon, XCircleIcon } from '@heroicons/vue/24/outline';
|
||||||
|
import AtomInput from '../atoms/AtomInput.vue';
|
||||||
|
|
||||||
|
const username = ref('');
|
||||||
|
const password = ref('');
|
||||||
|
const error = ref(false);
|
||||||
|
|
||||||
|
async function handleAuth() {
|
||||||
|
try {
|
||||||
|
await AuthService.loginAsBanker(username.value, password.value);
|
||||||
|
} catch (e) {
|
||||||
|
error.value = true;
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch([username, password], () => {
|
||||||
|
error.value = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -8,8 +8,9 @@
|
||||||
<div class="flex place-items-center justify-between">
|
<div class="flex place-items-center justify-between">
|
||||||
<span>{{ inputLabel }}</span>
|
<span>{{ inputLabel }}</span>
|
||||||
<AtomCurrencyInput
|
<AtomCurrencyInput
|
||||||
v-model="amount"
|
ref="input"
|
||||||
class="w-4/12"
|
class="w-4/12"
|
||||||
|
@change="amount = $event"
|
||||||
@keyup.esc="modal?.close()"
|
@keyup.esc="modal?.close()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -40,6 +41,7 @@ import AtomCurrencyInput from '../atoms/AtomCurrencyInput.vue';
|
||||||
import AtomModal from '../atoms/AtomModal.vue';
|
import AtomModal from '../atoms/AtomModal.vue';
|
||||||
|
|
||||||
const modal = ref<InstanceType<typeof AtomModal>>();
|
const modal = ref<InstanceType<typeof AtomModal>>();
|
||||||
|
const input= ref<InstanceType<typeof AtomCurrencyInput>>();
|
||||||
const amount = ref();
|
const amount = ref();
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
|
@ -64,7 +66,9 @@ defineProps({
|
||||||
const emit = defineEmits(['submit']);
|
const emit = defineEmits(['submit']);
|
||||||
|
|
||||||
function handleClose() {
|
function handleClose() {
|
||||||
amount.value = '';
|
if(input.value) {
|
||||||
|
input.value.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
|
|
|
@ -61,6 +61,21 @@
|
||||||
</li>
|
</li>
|
||||||
<li class="menu-title !p-0" />
|
<li class="menu-title !p-0" />
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="isBanker">
|
||||||
|
<li class="pointer-events-none rounded-md bg-info text-warning-content">
|
||||||
|
<span>
|
||||||
|
Angemeldet als Banker
|
||||||
|
<BuildingLibraryIcon class="h-6 w-6" />
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<span @click="AuthService.logout()">
|
||||||
|
<ArrowRightOnRectangleIcon class="h-6 w-6" />
|
||||||
|
Abmelden
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
<li class="menu-title !p-0" />
|
||||||
|
</template>
|
||||||
<li
|
<li
|
||||||
v-for="navigationEntry of navigationEntries"
|
v-for="navigationEntry of navigationEntries"
|
||||||
:class="getNavigationEntryClass(navigationEntry)"
|
:class="getNavigationEntryClass(navigationEntry)"
|
||||||
|
@ -96,11 +111,12 @@ import { useRouter } from 'vue-router';
|
||||||
import { INavigationEntry } from '../../interfaces/navigation-entry.interface';
|
import { INavigationEntry } from '../../interfaces/navigation-entry.interface';
|
||||||
import { AuthService } from '../../services/auth.service';
|
import { AuthService } from '../../services/auth.service';
|
||||||
import { isDark, toggleDark } from '../../utils/darkMode';
|
import { isDark, toggleDark } from '../../utils/darkMode';
|
||||||
import { ArrowRightOnRectangleIcon, Bars3Icon, ExclamationTriangleIcon, MoonIcon, SunIcon } from '@heroicons/vue/24/outline';
|
import { ArrowRightOnRectangleIcon, Bars3Icon, BuildingLibraryIcon, ExclamationTriangleIcon, MoonIcon, SunIcon } from '@heroicons/vue/24/outline';
|
||||||
import AtomLogo from '../atoms/AtomLogo.vue';
|
import AtomLogo from '../atoms/AtomLogo.vue';
|
||||||
import AtomSwap from '../atoms/AtomSwap.vue';
|
import AtomSwap from '../atoms/AtomSwap.vue';
|
||||||
|
|
||||||
const isAdmin = ref(false);
|
const isAdmin = ref(false);
|
||||||
|
const isBanker = ref(false);
|
||||||
const hideNavigation = ref(false);
|
const hideNavigation = ref(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const drawerCheckbox = ref();
|
const drawerCheckbox = ref();
|
||||||
|
@ -125,10 +141,12 @@ function getNavigationEntryClass(navigationEntry: INavigationEntry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
useEventBus<boolean>('isAdmin').on(state => (isAdmin.value = state));
|
useEventBus<boolean>('isAdmin').on(state => (isAdmin.value = state));
|
||||||
|
useEventBus<boolean>('isBanker').on(state => (isBanker.value = state));
|
||||||
useEventBus<boolean>('hideNavigation').on(state => (hideNavigation.value = state));
|
useEventBus<boolean>('hideNavigation').on(state => (hideNavigation.value = state));
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isAdmin.value = AuthService.isAdmin();
|
isAdmin.value = AuthService.isAdmin();
|
||||||
|
isBanker.value = AuthService.isBanker();
|
||||||
});
|
});
|
||||||
|
|
||||||
router.afterEach(() => {
|
router.afterEach(() => {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<MoleculeBankerAuthDialog
|
||||||
|
v-if="!isBanker && !AuthService.isAdmin()"
|
||||||
|
/>
|
||||||
|
<div v-else>
|
||||||
<div
|
<div
|
||||||
v-if="account || company"
|
v-if="account || company"
|
||||||
class="hero-content flex-col pt-0 text-center lg:p-6"
|
class="hero-content flex-col pt-0 text-center lg:p-6"
|
||||||
|
@ -94,26 +97,29 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted, onUnmounted, ref } from 'vue';
|
import { onMounted, onUnmounted, ref, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { onKeyStroke, promiseTimeout } from '@vueuse/core';
|
import { onKeyStroke, promiseTimeout, useEventBus } from '@vueuse/core';
|
||||||
import { RecordSubscription, UnsubscribeFunc } from 'pocketbase';
|
import { RecordSubscription, UnsubscribeFunc } from 'pocketbase';
|
||||||
import { AccountsResponse, CompaniesResponse, SettingsResponse, TransactionsResponse } from '../../types/pocketbase.types';
|
import { AccountsResponse, CompaniesResponse, SettingsResponse, TransactionsResponse } from '../../types/pocketbase.types';
|
||||||
import { AccountService } from '../../services/account.service';
|
import { AccountService } from '../../services/account.service';
|
||||||
import { AccountType, BankService, TransactionType } from '../../services/bank.service';
|
import { AccountType, BankService, TransactionType } from '../../services/bank.service';
|
||||||
|
import { AuthService } from '../../services/auth.service';
|
||||||
import { CompanyService } from '../../services/company.service';
|
import { CompanyService } from '../../services/company.service';
|
||||||
import { CurrencyService } from '../../services/currency.service';
|
import { CurrencyService } from '../../services/currency.service';
|
||||||
|
import { SettingsService } from '../../services/settings.service';
|
||||||
import { ChevronDownIcon, ChevronUpIcon, XCircleIcon } from '@heroicons/vue/24/outline';
|
import { ChevronDownIcon, ChevronUpIcon, XCircleIcon } from '@heroicons/vue/24/outline';
|
||||||
import AtomHeroText from '../atoms/AtomHeroText.vue';
|
import AtomHeroText from '../atoms/AtomHeroText.vue';
|
||||||
import MoleculeInputModal from '../molecules/MoleculeInputModal.vue';
|
import MoleculeInputModal from '../molecules/MoleculeInputModal.vue';
|
||||||
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
|
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
|
||||||
import { SettingsService } from '../../services/settings.service';
|
|
||||||
import AtomLogo from '../atoms/AtomLogo.vue';
|
import AtomLogo from '../atoms/AtomLogo.vue';
|
||||||
|
import MoleculeBankerAuthDialog from '../molecules/MoleculeBankerAuthDialog.vue';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const subscription = ref<UnsubscribeFunc>();
|
const subscription = ref<UnsubscribeFunc>();
|
||||||
const companySubscription = ref<UnsubscribeFunc>();
|
const companySubscription = ref<UnsubscribeFunc>();
|
||||||
|
|
||||||
|
const isBanker = ref(false);
|
||||||
const account = ref<AccountsResponse|null>();
|
const account = ref<AccountsResponse|null>();
|
||||||
const company = ref<CompaniesResponse|null>();
|
const company = ref<CompaniesResponse|null>();
|
||||||
const transactions = ref<TransactionsResponse[]>();
|
const transactions = ref<TransactionsResponse[]>();
|
||||||
|
@ -132,6 +138,7 @@ if(router.currentRoute.value.query.accountNumber) {
|
||||||
}
|
}
|
||||||
|
|
||||||
onKeyStroke(['e', 'a'], (e) => {
|
onKeyStroke(['e', 'a'], (e) => {
|
||||||
|
if(!isBanker.value) { return; }
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if(depositModal.value?.isOpen() || withdrawModal.value?.isOpen()) {
|
if(depositModal.value?.isOpen() || withdrawModal.value?.isOpen()) {
|
||||||
return;
|
return;
|
||||||
|
@ -147,6 +154,7 @@ onKeyStroke(['e', 'a'], (e) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
onKeyStroke(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Enter'], (e) => {
|
onKeyStroke(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'Enter'], (e) => {
|
||||||
|
if(!isBanker.value) { return; }
|
||||||
if(depositModal.value?.isOpen() || withdrawModal.value?.isOpen()) {
|
if(depositModal.value?.isOpen() || withdrawModal.value?.isOpen()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -243,15 +251,26 @@ function handleRealtimeUpdates(transactionSubscription: RecordSubscription<Trans
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
subscription.value = await BankService.subscribeToTransactionChanges(handleRealtimeUpdates);
|
isBanker.value = AuthService.isBanker();
|
||||||
companySubscription.value = await BankService.subscribeToCompanyTransactionChanges(handleRealtimeUpdates);
|
|
||||||
settings.value = await SettingsService.getSettings();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEventBus<boolean>('isBanker').on(state => (isBanker.value = state));
|
||||||
|
|
||||||
onUnmounted(() => {
|
onUnmounted(() => {
|
||||||
subscription.value?.();
|
subscription.value?.();
|
||||||
companySubscription.value?.();
|
companySubscription.value?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(isBanker, async (newValue) => {
|
||||||
|
if(newValue) {
|
||||||
|
subscription.value = await BankService.subscribeToTransactionChanges(handleRealtimeUpdates);
|
||||||
|
companySubscription.value = await BankService.subscribeToCompanyTransactionChanges(handleRealtimeUpdates);
|
||||||
|
settings.value = await SettingsService.getSettings();
|
||||||
|
} else {
|
||||||
|
subscription.value?.();
|
||||||
|
companySubscription.value?.();
|
||||||
|
}
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Admin } from 'pocketbase';
|
import { Admin } from 'pocketbase';
|
||||||
import { PocketbaseService } from './pocketbase.service';
|
import { PocketbaseService } from './pocketbase.service';
|
||||||
import { useEventBus } from '@vueuse/core';
|
import { useEventBus } from '@vueuse/core';
|
||||||
|
import { BankersResponse, Collections } from '../types/pocketbase.types';
|
||||||
|
|
||||||
const API = PocketbaseService.getApi();
|
const API = PocketbaseService.getApi();
|
||||||
|
|
||||||
|
@ -29,5 +30,22 @@ export class AuthService {
|
||||||
public static logout(): void {
|
public static logout(): void {
|
||||||
API.authStore.clear();
|
API.authStore.clear();
|
||||||
useEventBus<boolean>('isAdmin').emit(false);
|
useEventBus<boolean>('isAdmin').emit(false);
|
||||||
|
useEventBus<boolean>('isBanker').emit(false);
|
||||||
}
|
}
|
||||||
|
public static async loginAsBanker(username: string, password: string): Promise<BankersResponse> {
|
||||||
|
const response = await API.collection(Collections.Bankers).authWithPassword<BankersResponse>(username, password);
|
||||||
|
if(response) {
|
||||||
|
useEventBus<boolean>('isBanker').emit(true);
|
||||||
|
return response.record;
|
||||||
|
}
|
||||||
|
throw new Error('Invalid credentials');
|
||||||
|
}
|
||||||
|
public static isBanker(): boolean {
|
||||||
|
if(API.authStore.model && API.authStore.model?.collectionName == Collections.Bankers) {
|
||||||
|
useEventBus<boolean>('isBanker').emit(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -30,7 +30,7 @@ export class CompanyService {
|
||||||
}
|
}
|
||||||
public static isAuthenticated(): boolean {
|
public static isAuthenticated(): boolean {
|
||||||
if(API.authStore.isValid && API.authStore.model) {
|
if(API.authStore.isValid && API.authStore.model) {
|
||||||
return Boolean(API.authStore.model.collectionId);
|
return API.authStore.model.collectionName == Collections.Companies;
|
||||||
}
|
}
|
||||||
this.logout();
|
this.logout();
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
export enum Collections {
|
export enum Collections {
|
||||||
Accounts = "accounts",
|
Accounts = "accounts",
|
||||||
AccountsList = "accountsList",
|
AccountsList = "accountsList",
|
||||||
|
Bankers = "bankers",
|
||||||
Companies = "companies",
|
Companies = "companies",
|
||||||
CompanyTransactions = "companyTransactions",
|
CompanyTransactions = "companyTransactions",
|
||||||
Settings = "settings",
|
Settings = "settings",
|
||||||
|
@ -54,6 +55,8 @@ export type AccountsListRecord<Tbalance = unknown, Tname = unknown> = {
|
||||||
balance?: null | Tbalance
|
balance?: null | Tbalance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type BankersRecord = never
|
||||||
|
|
||||||
export type CompaniesRecord = {
|
export type CompaniesRecord = {
|
||||||
accountNumber?: string
|
accountNumber?: string
|
||||||
name: string
|
name: string
|
||||||
|
@ -84,6 +87,7 @@ export type TransactionsRecord = {
|
||||||
// Response types include system fields and match responses from the PocketBase API
|
// Response types include system fields and match responses from the PocketBase API
|
||||||
export type AccountsResponse<Texpand = unknown> = Required<AccountsRecord> & BaseSystemFields<Texpand>
|
export type AccountsResponse<Texpand = unknown> = Required<AccountsRecord> & BaseSystemFields<Texpand>
|
||||||
export type AccountsListResponse<Tbalance = unknown, Tname = unknown, Texpand = unknown> = Required<AccountsListRecord<Tbalance, Tname>> & BaseSystemFields<Texpand>
|
export type AccountsListResponse<Tbalance = unknown, Tname = unknown, Texpand = unknown> = Required<AccountsListRecord<Tbalance, Tname>> & BaseSystemFields<Texpand>
|
||||||
|
export type BankersResponse<Texpand = unknown> = Required<BankersRecord> & AuthSystemFields<Texpand>
|
||||||
export type CompaniesResponse<Texpand = unknown> = Required<CompaniesRecord> & AuthSystemFields<Texpand>
|
export type CompaniesResponse<Texpand = unknown> = Required<CompaniesRecord> & AuthSystemFields<Texpand>
|
||||||
export type CompanyTransactionsResponse<Texpand = unknown> = Required<CompanyTransactionsRecord> & BaseSystemFields<Texpand>
|
export type CompanyTransactionsResponse<Texpand = unknown> = Required<CompanyTransactionsRecord> & BaseSystemFields<Texpand>
|
||||||
export type SettingsResponse<Texpand = unknown> = Required<SettingsRecord> & BaseSystemFields<Texpand>
|
export type SettingsResponse<Texpand = unknown> = Required<SettingsRecord> & BaseSystemFields<Texpand>
|
||||||
|
@ -94,6 +98,7 @@ export type TransactionsResponse<Texpand = unknown> = Required<TransactionsRecor
|
||||||
export type CollectionRecords = {
|
export type CollectionRecords = {
|
||||||
accounts: AccountsRecord
|
accounts: AccountsRecord
|
||||||
accountsList: AccountsListRecord
|
accountsList: AccountsListRecord
|
||||||
|
bankers: BankersRecord
|
||||||
companies: CompaniesRecord
|
companies: CompaniesRecord
|
||||||
companyTransactions: CompanyTransactionsRecord
|
companyTransactions: CompanyTransactionsRecord
|
||||||
settings: SettingsRecord
|
settings: SettingsRecord
|
||||||
|
@ -103,6 +108,7 @@ export type CollectionRecords = {
|
||||||
export type CollectionResponses = {
|
export type CollectionResponses = {
|
||||||
accounts: AccountsResponse
|
accounts: AccountsResponse
|
||||||
accountsList: AccountsListResponse
|
accountsList: AccountsListResponse
|
||||||
|
bankers: BankersResponse
|
||||||
companies: CompaniesResponse
|
companies: CompaniesResponse
|
||||||
companyTransactions: CompanyTransactionsResponse
|
companyTransactions: CompanyTransactionsResponse
|
||||||
settings: SettingsResponse
|
settings: SettingsResponse
|
||||||
|
|
Loading…
Reference in a new issue