forked from Kispi/Core
		
	feat(webapp): add modal for bank operations
This commit is contained in:
		
							parent
							
								
									8151cad10b
								
							
						
					
					
						commit
						4555ca1140
					
				
					 5 changed files with 234 additions and 7 deletions
				
			
		
							
								
								
									
										20
									
								
								src/components/atoms/AtomCurrencyInput.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/components/atoms/AtomCurrencyInput.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <div>
 | 
			
		||||
    <AtomInput
 | 
			
		||||
      ref="input"
 | 
			
		||||
      type="number"
 | 
			
		||||
      class="text-right pr-16"
 | 
			
		||||
      step="1"
 | 
			
		||||
    />
 | 
			
		||||
    <span class="text-sm opacity-50 absolute -ml-16 top-2/4 -mt-8 pointer-events-none">,00 Öro</span>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import AtomInput from './AtomInput.vue';
 | 
			
		||||
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										32
									
								
								src/components/atoms/AtomInput.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/components/atoms/AtomInput.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <input
 | 
			
		||||
    ref="input"
 | 
			
		||||
    :type="type"
 | 
			
		||||
    :placeholder="placeholder"
 | 
			
		||||
    class="input input-bordered w-full max-w-xs"
 | 
			
		||||
  />
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
defineProps({
 | 
			
		||||
  type: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: 'text',
 | 
			
		||||
  },
 | 
			
		||||
  placeholder: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    default: '',
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
input::-webkit-outer-spin-button,
 | 
			
		||||
input::-webkit-inner-spin-button {
 | 
			
		||||
  -webkit-appearance: none;
 | 
			
		||||
  margin: 0;
 | 
			
		||||
}
 | 
			
		||||
input[type=number] {
 | 
			
		||||
  -moz-appearance: textfield;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										38
									
								
								src/components/atoms/AtomModal.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/components/atoms/AtomModal.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,38 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <Teleport to="body">
 | 
			
		||||
    <input
 | 
			
		||||
      :id="id"
 | 
			
		||||
      type="checkbox"
 | 
			
		||||
      class="modal-toggle"
 | 
			
		||||
      @change="$emit('open', {id, open: ($event.target as HTMLInputElement).checked})"
 | 
			
		||||
    />
 | 
			
		||||
    <div class="modal">
 | 
			
		||||
      <div class="modal-box">
 | 
			
		||||
        <h3 class="font-bold text-lg">{{ title }}</h3>
 | 
			
		||||
        <p class="py-4"><slot /></p>
 | 
			
		||||
        <div class="modal-action">
 | 
			
		||||
          <slot name="action" />
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </Teleport>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
defineProps({
 | 
			
		||||
  id: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
  title: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
defineEmits(['open']);
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										82
									
								
								src/components/molecules/MoleculeInputModal.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/components/molecules/MoleculeInputModal.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,82 @@
 | 
			
		|||
<template>
 | 
			
		||||
  <AtomModal
 | 
			
		||||
    :id="id"
 | 
			
		||||
    :title="title"
 | 
			
		||||
    @open="handleOpen"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="flex justify-between place-items-center">
 | 
			
		||||
      <span>{{ inputLabel }}</span>
 | 
			
		||||
      <AtomCurrencyInput
 | 
			
		||||
        ref="input"
 | 
			
		||||
        class="w-4/12"
 | 
			
		||||
        @keyup.enter="handleSubmit()"
 | 
			
		||||
        @keyup.esc="abortButton.click()"
 | 
			
		||||
      />
 | 
			
		||||
    </div>
 | 
			
		||||
    <template #action>
 | 
			
		||||
      <label
 | 
			
		||||
        ref="abortButton"
 | 
			
		||||
        class="btn gap-2"
 | 
			
		||||
        :for="id"
 | 
			
		||||
      >
 | 
			
		||||
        <XCircleIcon class="w-6 h-6" />
 | 
			
		||||
        Abbrechen
 | 
			
		||||
      </label>
 | 
			
		||||
      <button
 | 
			
		||||
        class="btn gap-2"
 | 
			
		||||
        @click="handleSubmit()"
 | 
			
		||||
      >
 | 
			
		||||
        <CheckCircleIcon class="w-6 h-6" />
 | 
			
		||||
        {{ actionLabel }}
 | 
			
		||||
      </button>
 | 
			
		||||
    </template>
 | 
			
		||||
  </AtomModal>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { CheckCircleIcon, XCircleIcon } from '@heroicons/vue/outline';
 | 
			
		||||
import AtomModal from '../atoms/AtomModal.vue';
 | 
			
		||||
import AtomCurrencyInput from '../atoms/AtomCurrencyInput.vue';
 | 
			
		||||
import { ref } from 'vue';
 | 
			
		||||
 | 
			
		||||
const input = ref();
 | 
			
		||||
const abortButton = ref();
 | 
			
		||||
 | 
			
		||||
defineProps({
 | 
			
		||||
  id: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
  title: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
  actionLabel: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
  inputLabel: {
 | 
			
		||||
    type: String,
 | 
			
		||||
    required: true,
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const emit = defineEmits(['submit']);
 | 
			
		||||
 | 
			
		||||
function handleOpen(event: {id: string, open: boolean}) {
 | 
			
		||||
  if(event.open) {
 | 
			
		||||
    // This is (currently) the only method to focus a input field inside a custom component.
 | 
			
		||||
    input.value.$refs.input.$refs.input.value = '';
 | 
			
		||||
    input.value.$refs.input.$refs.input.focus();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSubmit() {
 | 
			
		||||
  emit('submit', input.value.$refs.input.$refs.input.value);
 | 
			
		||||
  abortButton.value.click();
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -10,27 +10,62 @@
 | 
			
		|||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="sticky flex place-content-center lg:gap-32 gap-16 bg-base-100 bottom-0 w-100 p-5">
 | 
			
		||||
    <button class="btn gap-2">
 | 
			
		||||
    <MoleculeInputModal
 | 
			
		||||
      :id="depositModalId"
 | 
			
		||||
      title="Einzahlung"
 | 
			
		||||
      action-label="Einzahlen"
 | 
			
		||||
      input-label="Einzuzahlender Betrag:"
 | 
			
		||||
      @submit="handleDeposit"
 | 
			
		||||
    />
 | 
			
		||||
    <label
 | 
			
		||||
      class="btn gap-2"
 | 
			
		||||
      :for="depositModalId"
 | 
			
		||||
    >
 | 
			
		||||
      <ChevronUpIcon class="w-6 h-6 text-success" />
 | 
			
		||||
      Einzahlen
 | 
			
		||||
    </button>
 | 
			
		||||
    <button class="btn gap-2">
 | 
			
		||||
    </label>
 | 
			
		||||
    <MoleculeInputModal
 | 
			
		||||
      :id="salaryModalId"
 | 
			
		||||
      title="Gehalt"
 | 
			
		||||
      action-label="Gehalt hinzufügen"
 | 
			
		||||
      input-label="Gehalt Betrag:"
 | 
			
		||||
      @submit="handleSalary"
 | 
			
		||||
    />
 | 
			
		||||
    <label
 | 
			
		||||
      class="btn gap-2"
 | 
			
		||||
      :for="salaryModalId"
 | 
			
		||||
    >
 | 
			
		||||
      <CurrencyDollarIcon class="w-6 h-6 text-warning" />
 | 
			
		||||
      Gehalt
 | 
			
		||||
    </button>
 | 
			
		||||
    <button class="btn gap-2">
 | 
			
		||||
    </label>
 | 
			
		||||
    <MoleculeInputModal
 | 
			
		||||
      :id="withdrawModalId"
 | 
			
		||||
      title="Auszahlung"
 | 
			
		||||
      action-label="Auszahlen"
 | 
			
		||||
      input-label="Auszuzahlender Betrag:"
 | 
			
		||||
      @submit="handleWithdraw"
 | 
			
		||||
    />
 | 
			
		||||
    <label
 | 
			
		||||
      class="btn gap-2"
 | 
			
		||||
      :for="withdrawModalId"
 | 
			
		||||
    >
 | 
			
		||||
      <ChevronDownIcon class="w-6 h-6 text-error" />
 | 
			
		||||
      Auszahlen
 | 
			
		||||
    </button>
 | 
			
		||||
    </label>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts" setup>
 | 
			
		||||
import { ITransaction } from '../../interfaces/transaction.interface';
 | 
			
		||||
import { CurrencyService } from '../services/currency.service';
 | 
			
		||||
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
 | 
			
		||||
import { CurrencyDollarIcon, ChevronDownIcon, ChevronUpIcon } from '@heroicons/vue/outline';
 | 
			
		||||
import MoleculeTransactionTable from '../molecules/MoleculeTransactionTable.vue';
 | 
			
		||||
import MoleculeInputModal from '../molecules/MoleculeInputModal.vue';
 | 
			
		||||
import { onKeyStroke } from '@vueuse/core';
 | 
			
		||||
 | 
			
		||||
const depositModalId = 'deposit-modal';
 | 
			
		||||
const salaryModalId = 'salary-modal';
 | 
			
		||||
const withdrawModalId = 'withdraw-modal';
 | 
			
		||||
const balance = 13500;
 | 
			
		||||
const transactions: ITransaction[] = [
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			@ -99,6 +134,26 @@ const transactions: ITransaction[] = [
 | 
			
		|||
    amount: -2000,
 | 
			
		||||
  },
 | 
			
		||||
].sort((a, b) => b.date.getTime() - a.date.getTime());
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
onKeyStroke('e', (e) => {
 | 
			
		||||
  e.preventDefault();
 | 
			
		||||
  console.log('e pressed');
 | 
			
		||||
  // TODO: open deposit modal
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function handleDeposit(amount: number) {
 | 
			
		||||
  console.log('Deposit:', amount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleSalary(amount: number) {
 | 
			
		||||
  console.log('Salary:', amount);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function handleWithdraw(amount: number) {
 | 
			
		||||
  console.log('Withdraw:', amount);
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue