Skip to content
Snippets Groups Projects

Transaction dialog view

Merged Birthe Emilie Christiansen requested to merge TransactionDialogView into development
1 file
+ 147
124
Compare changes
  • Side-by-side
  • Inline
+ 147
124
@@ -18,7 +18,7 @@ import {
@@ -18,7 +18,7 @@ import {
Select,
Select,
SelectContent,
SelectContent,
SelectGroup,
SelectGroup,
SelectItem,
SelectItem, SelectLabel,
SelectTrigger,
SelectTrigger,
SelectValue,
SelectValue,
} from "@/components/ui/select";
} from "@/components/ui/select";
@@ -88,9 +88,9 @@ const selectedAccount = ref(null);
@@ -88,9 +88,9 @@ const selectedAccount = ref(null);
const parseDateString = (dateStr) => {
const parseDateString = (dateStr) => {
const parts = dateStr.split("/");
const parts = dateStr.split("/");
return new Date(
return new Date(
parseInt(parts[2], 10) + 2000,
parseInt(parts[2], 10) + 2000,
parseInt(parts[1], 10) - 1,
parseInt(parts[1], 10) - 1,
parseInt(parts[0], 10)
parseInt(parts[0], 10)
);
);
};
};
@@ -146,9 +146,9 @@ const saveTransaction = async (transaction, field, value) => {
@@ -146,9 +146,9 @@ const saveTransaction = async (transaction, field, value) => {
try {
try {
await transactionsStore.updateTransaction(
await transactionsStore.updateTransaction(
selectedAccount.value.accountNumber,
selectedAccount.value.accountNumber,
transaction.transactionId,
transaction.transactionId,
updateData
updateData
);
);
console.log("Update successful:", updateData);
console.log("Update successful:", updateData);
} catch (error) {
} catch (error) {
@@ -172,12 +172,14 @@ const df = new DateFormatter("nb-NO", {
@@ -172,12 +172,14 @@ const df = new DateFormatter("nb-NO", {
* @const {Object} formSchema - Zod schema object for form validation.
* @const {Object} formSchema - Zod schema object for form validation.
*/
*/
const formSchema = toTypedSchema(
const formSchema = toTypedSchema(
z.object({
z.object({
date: z.string({ required_error: "Dato er påkrevd" }),
date: z.string()
name: z.string().min(1, "Navn er påkrevd"),
.refine(v => v, { message: 'Dato må være valgt' }),
sum: z.number({ invalid_type_error: "Sum må være et tall" }),
name: z.string().min(1, "Navn er påkrevd"),
category: z.number({ required_error: "Kategori er påkrevd" }),
sum: z.number().min(0, "Beløp må være større enn 0"),
})
category: z.number({ required_error: "Kategori er påkrevd" }),
 
transactionType: z.enum(['income', 'expense']),
 
})
);
);
/**
/**
@@ -196,10 +198,7 @@ const form = useForm({
@@ -196,10 +198,7 @@ const form = useForm({
initialValues: {
initialValues: {
name: "",
name: "",
sum: "",
sum: "",
},
transactionType: "expense",
defaultValues: {
name: "",
sum: "",
},
},
});
});
@@ -210,11 +209,14 @@ const form = useForm({
@@ -210,11 +209,14 @@ const form = useForm({
*/
*/
const onSubmit = form.handleSubmit(async (values) => {
const onSubmit = form.handleSubmit(async (values) => {
console.log(values);
console.log(values);
 
const isExpense = values.transactionType === 'expense';
 
const sum = isExpense ? -Math.abs(parseFloat(values.sum)) : parseFloat(values.sum);
 
const transactionData = {
const transactionData = {
categoryId: values.category ? parseInt(values.category, 10) : null,
categoryId: values.category ? parseInt(values.category, 10) : null,
date: new Date(values.date).toISOString(),
date: new Date(values.date).toISOString(),
description: values.name,
description: values.name,
sum: parseFloat(values.sum),
sum: sum,
accountNumber: parseInt(selectedAccount.value.accountNumber, 10),
accountNumber: parseInt(selectedAccount.value.accountNumber, 10),
};
};
@@ -222,8 +224,8 @@ const onSubmit = form.handleSubmit(async (values) => {
@@ -222,8 +224,8 @@ const onSubmit = form.handleSubmit(async (values) => {
try {
try {
await transactionsStore.addTransaction(
await transactionsStore.addTransaction(
transactionData,
transactionData,
selectedAccount.value.accountNumber
selectedAccount.value.accountNumber
);
);
form.resetForm();
form.resetForm();
open.value = false;
open.value = false;
@@ -301,13 +303,13 @@ watchEffect(() => {
@@ -301,13 +303,13 @@ watchEffect(() => {
* @watch selectedAccount
* @watch selectedAccount
*/
*/
watch(
watch(
selectedAccount,
selectedAccount,
(newAccount, oldAccount) => {
(newAccount, oldAccount) => {
if (newAccount && newAccount.accountNumber !== oldAccount?.accountNumber) {
if (newAccount && newAccount.accountNumber !== oldAccount?.accountNumber) {
transactionsStore.fetchTransactions(newAccount.accountNumber);
transactionsStore.fetchTransactions(newAccount.accountNumber);
}
}
},
},
{ immediate: true }
{ immediate: true }
);
);
/**
/**
@@ -333,8 +335,8 @@ function formatDate(isoString) {
@@ -333,8 +335,8 @@ function formatDate(isoString) {
const displayName = computed(() => {
const displayName = computed(() => {
const userStore = useUserStore();
const userStore = useUserStore();
return userStore.user.nickName
return userStore.user.nickName
? userStore.user.nickName
? userStore.user.nickName
: userStore.user.firstName;
: userStore.user.firstName;
});
});
/**
/**
@@ -349,8 +351,8 @@ const open = ref(false);
@@ -349,8 +351,8 @@ const open = ref(false);
<template>
<template>
<MainLayout>
<MainLayout>
<main
<main
class="p-4 flex flex-col max-w-screen-lg mx-auto"
class="p-4 flex flex-col max-w-screen-lg mx-auto"
data-testid="main-container"
data-testid="main-container"
>
>
<Card class="md:p-5 px-1 py-5 border-primary-dark bg-transparent border-4">
<Card class="md:p-5 px-1 py-5 border-primary-dark bg-transparent border-4">
<div class="flex flex-row justify-center items-center">
<div class="flex flex-row justify-center items-center">
@@ -363,7 +365,7 @@ const open = ref(false);
@@ -363,7 +365,7 @@ const open = ref(false);
<Popover>
<Popover>
<PopoverTrigger>
<PopoverTrigger>
<Button
<Button
class="bg-transparent active:bg-transparent hover:bg-transparent text-black translate-x-32 xl:translate-x-44 xl:translate-y-2"
class="bg-transparent active:bg-transparent hover:bg-transparent text-black translate-x-32 xl:translate-x-44 xl:translate-y-2"
>
>
<CircleHelp size="25" />
<CircleHelp size="25" />
</Button>
</Button>
@@ -372,14 +374,14 @@ const open = ref(false);
@@ -372,14 +374,14 @@ const open = ref(false);
<Sparti :theme="theme" class="-translate-x-8">
<Sparti :theme="theme" class="-translate-x-8">
<span v-if="!savingsAccount && !checkingAccount">
<span v-if="!savingsAccount && !checkingAccount">
Hmm <strong>{{ displayName }}</strong
Hmm <strong>{{ displayName }}</strong
>,<br />
>,<br />
Det ser ut som at du ikke har <br />integrert noen
Det ser ut som at du ikke har <br />integrert noen
bankkontoer. Gå til <br />profilsiden for å integrere<br />
bankkontoer. Gå til <br />profilsiden for å integrere<br />
bankkontoer.
bankkontoer.
</span>
</span>
<span v-else>
<span v-else>
Psst <strong>{{ displayName }}</strong
Psst <strong>{{ displayName }}</strong
>!<br />
>!<br />
Her kan du se, legge til, og <br />redigere alle dine
Her kan du se, legge til, og <br />redigere alle dine
transaksjoner.
transaksjoner.
</span>
</span>
@@ -395,33 +397,33 @@ const open = ref(false);
@@ -395,33 +397,33 @@ const open = ref(false);
<div class="flex-1 flex justify-center pb-2">
<div class="flex-1 flex justify-center pb-2">
<DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger
<DropdownMenuTrigger
v-if="savingsAccount || checkingAccount"
v-if="savingsAccount || checkingAccount"
as="Button"
as="Button"
class="dropdown-button flex flex-row justify-center align-middle text-center border-none
class="dropdown-button flex flex-row justify-center align-middle text-center border-none
rounded-md bg-white px-1"
rounded-md bg-white px-1"
>
>
{{ selectedAccount?.name || "Select Account" }}
{{ selectedAccount?.name || "Select Account" }}
<ChevronDown
<ChevronDown
v-if="savingsAccount && checkingAccount"
v-if="savingsAccount && checkingAccount"
></ChevronDown>
></ChevronDown>
</DropdownMenuTrigger>
</DropdownMenuTrigger>
<DropdownMenuContent
<DropdownMenuContent
class="dropdown-menu-content bg-white"
class="dropdown-menu-content bg-white"
v-if="savingsAccount && checkingAccount"
v-if="savingsAccount && checkingAccount"
>
>
<DropdownMenuItem
<DropdownMenuItem
class="dropdown-item"
class="dropdown-item"
:key="checkingAccount"
:key="checkingAccount"
@click="selectedAccount = checkingAccount"
@click="selectedAccount = checkingAccount"
data-testid="brukskonto-option"
data-testid="brukskonto-option"
>{{ checkingAccount.name }}</DropdownMenuItem
>{{ checkingAccount.name }}</DropdownMenuItem
>
>
<DropdownMenuItem
<DropdownMenuItem
class="dropdown-item"
class="dropdown-item"
:key="savingsAccount"
:key="savingsAccount"
@click="selectedAccount = savingsAccount"
@click="selectedAccount = savingsAccount"
data-testid="brukskonto-option"
data-testid="brukskonto-option"
>{{ savingsAccount.name }}</DropdownMenuItem
>{{ savingsAccount.name }}</DropdownMenuItem
>
>
</DropdownMenuContent>
</DropdownMenuContent>
</DropdownMenu>
</DropdownMenu>
@@ -429,9 +431,9 @@ const open = ref(false);
@@ -429,9 +431,9 @@ const open = ref(false);
</div>
</div>
<div class="w-1/3 flex justify-end">
<div class="w-1/3 flex justify-end">
<Dialog
<Dialog
v-if="savingsAccount || checkingAccount"
v-if="savingsAccount || checkingAccount"
:open="open"
:open="open"
@update:open="(value) => (open = value)"
@update:open="(value) => (open = value)"
>
>
<DialogTrigger as-child>
<DialogTrigger as-child>
<Button variant="icons">
<Button variant="icons">
@@ -439,19 +441,38 @@ const open = ref(false);
@@ -439,19 +441,38 @@ const open = ref(false);
</Button>
</Button>
</DialogTrigger>
</DialogTrigger>
<DialogContent
<DialogContent
style="width: 100vw"
style="width: 100vw"
class="flex items-center justify-center p-4 bg-standard border-none rounded-xl"
class="flex items-center justify-center p-4 bg-standard border-none rounded-xl"
>
>
<form
<form
@submit.prevent="onSubmit"
@submit.prevent="onSubmit"
ref="popoverForm"
ref="popoverForm"
class="w-full max-w-md flex justify-center align-middle"
class="w-full max-w-md flex justify-center align-middle"
>
>
<div
<div
class="flex flex-col space-y-3 w-full justify-center align-middle items-center"
class="flex flex-col space-y-3 w-full justify-center align-middle items-center"
>
>
 
<h1 class="text-xl"><strong>Legg til transaksjon</strong></h1>
 
<FormField v-slot="{ componentField }" name="transactionType">
 
<FormItem class="flex flex-col">
 
<FormLabel>Type transaksjon:</FormLabel>
 
<Select v-bind="componentField">
 
<SelectTrigger class="border-primary-dark border-2 w-64">
 
<SelectValue />
 
</SelectTrigger>
 
<SelectContent>
 
<SelectGroup>
 
<SelectItem value="income"> Inntekt </SelectItem>
 
<SelectItem value="expense"> Utgift </SelectItem>
 
</SelectGroup>
 
</SelectContent>
 
</Select>
 
<FormMessage />
 
</FormItem>
 
</FormField>
 
<div
<div
class="flex flex-col space-y-0.5 justify-center align-middle"
class="flex flex-col space-y-0.5 justify-center align-middle"
>
>
<FormField name="date">
<FormField name="date">
<FormItem class="flex flex-col">
<FormItem class="flex flex-col">
@@ -460,10 +481,10 @@ const open = ref(false);
@@ -460,10 +481,10 @@ const open = ref(false);
<PopoverTrigger as-child>
<PopoverTrigger as-child>
<FormControl>
<FormControl>
<Button
<Button
type="button"
type="button"
variant="outline"
variant="outline"
class="md:w-96 w-64 h-8 rounded-md border-2 border-primary-dark"
class="md:w-96 w-64 h-8 rounded-md border-2 border-primary-dark"
:class="
:class="
cn(
cn(
'ps-3 text-start font-normal',
'ps-3 text-start font-normal',
!value && 'text-muted-foreground'
!value && 'text-muted-foreground'
@@ -471,12 +492,12 @@ const open = ref(false);
@@ -471,12 +492,12 @@ const open = ref(false);
"
"
>
>
<span>{{
<span>{{
value
value
? df.format(toDate(value))
? df.format(toDate(value))
: "Velg dato"
: "Velg dato"
}}</span>
}}</span>
<CalendarIcon
<CalendarIcon
class="ms-auto h-4 w-4 opacity-50"
class="ms-auto h-4 w-4 opacity-50"
/>
/>
</Button>
</Button>
<input hidden class="date-field" />
<input hidden class="date-field" />
@@ -485,11 +506,11 @@ const open = ref(false);
@@ -485,11 +506,11 @@ const open = ref(false);
</PopoverTrigger>
</PopoverTrigger>
<PopoverContent class="w-auto p-0">
<PopoverContent class="w-auto p-0">
<Calendar
<Calendar
v-model:placeholder="placeholder"
v-model:placeholder="placeholder"
v-model="value"
v-model="value"
calendar-label="Velg dato"
calendar-label="Velg dato"
initial-focus
initial-focus
@update:model-value="
@update:model-value="
(newValue) => {
(newValue) => {
if (newValue) {
if (newValue) {
form.setValues({
form.setValues({
@@ -513,11 +534,11 @@ const open = ref(false);
@@ -513,11 +534,11 @@ const open = ref(false);
<FormItem class="flex flex-col">
<FormItem class="flex flex-col">
<FormLabel>Navn:</FormLabel>
<FormLabel>Navn:</FormLabel>
<FormControl>
<FormControl>
<input
<Input
:value="form.values.name"
:value="form.values.name"
placeholder=" Fyll inn navn på transaksjonen"
v-bind="componentField"
v-bind="componentField"
placeholder="Fyll inn transaksjonsnavn"
class="name-field rounded-md border-2 border-primary-dark md:w-96
class="placeholder:text-darkest name-field rounded-md border-2 border-primary-dark md:w-96
w-64 focus:outline-none focus:ring-0"
w-64 focus:outline-none focus:ring-0"
/>
/>
</FormControl>
</FormControl>
@@ -531,17 +552,17 @@ const open = ref(false);
@@ -531,17 +552,17 @@ const open = ref(false);
<FormLabel for="category">Kategori:</FormLabel>
<FormLabel for="category">Kategori:</FormLabel>
<Select v-bind="componentField">
<Select v-bind="componentField">
<SelectTrigger
<SelectTrigger
v-bind="componentField"
v-bind="componentField"
class="category-field border-2 border-primary-dark h-8 md:w-96 w-64 focus:outline-none focus:ring-0"
class="category-field border-2 border-primary-dark h-8 md:w-96 w-64 focus:outline-none focus:ring-0"
>
>
<SelectValue placeholder="Velg en kategori" />
<SelectValue placeholder="Velg en kategori" />
</SelectTrigger>
</SelectTrigger>
<SelectContent class="border-none">
<SelectContent class="border-none">
<SelectGroup>
<SelectGroup>
<SelectItem
<SelectItem
v-for="category in categoryStore.categories"
v-for="category in categoryStore.categories"
:key="category.categoryId"
:key="category.categoryId"
:value="
:value="
category.categoryId || 'Ukategorisert'
category.categoryId || 'Ukategorisert'
"
"
>
>
@@ -559,12 +580,14 @@ const open = ref(false);
@@ -559,12 +580,14 @@ const open = ref(false);
<FormItem class="flex flex-col">
<FormItem class="flex flex-col">
<FormLabel>Sum:</FormLabel>
<FormLabel>Sum:</FormLabel>
<FormControl>
<FormControl>
<input
<Input
type="number"
type="number"
:value="form.values.sum"
:value="form.values.sum"
v-bind="componentField"
v-bind="componentField"
class="sum-field md:w-96 w-64 rounded-md border-2 border-primary-dark
placeholder="Fyll inn beløp"
 
class="placeholder:text-darkest sum-field md:w-96 w-64 rounded-md border-2 border-primary-dark
focus:outline-none focus:ring-0"
focus:outline-none focus:ring-0"
 
min="0"
/>
/>
</FormControl>
</FormControl>
<FormMessage />
<FormMessage />
@@ -572,10 +595,10 @@ const open = ref(false);
@@ -572,10 +595,10 @@ const open = ref(false);
</FormField>
</FormField>
</div>
</div>
<Button
<Button
type="submit"
type="submit"
variant="primary"
variant="primary"
class="submit-button md:w-96 w-64 h-10"
class="submit-button md:w-96 w-64 h-10"
>Legg til</Button
>Legg til</Button
>
>
</div>
</div>
</form>
</form>
@@ -594,21 +617,21 @@ const open = ref(false);
@@ -594,21 +617,21 @@ const open = ref(false);
</TableHeader>
</TableHeader>
<TableBody>
<TableBody>
<TableRow
<TableRow
class="invoice-row h-12 border-primary-dark"
class="invoice-row h-12 border-primary-dark"
v-for="transaction in activeTransactions"
v-for="transaction in activeTransactions"
:key="transaction.transactionId"
:key="transaction.transactionId"
data-testid="transaction-row"
data-testid="transaction-row"
>
>
<TableCell class="w-1/4">{{
<TableCell class="w-1/4">{{
formatDate(transaction.date)
formatDate(transaction.date)
}}</TableCell>
}}</TableCell>
<TableCell class="w-1/4">
<TableCell class="w-1/4">
<template v-if="transaction.editing">
<template v-if="transaction.editing">
<input
<input
type="text"
type="text"
class="w-full border-none focus:outline-none"
class="w-full border-none focus:outline-none"
v-model="transaction.description"
v-model="transaction.description"
@blur="
@blur="
transaction.description.trim()
transaction.description.trim()
? saveTransaction(
? saveTransaction(
transaction,
transaction,
@@ -617,7 +640,7 @@ const open = ref(false);
@@ -617,7 +640,7 @@ const open = ref(false);
)
)
: null
: null
"
"
@keyup.enter="
@keyup.enter="
transaction.description.trim()
transaction.description.trim()
? saveTransaction(
? saveTransaction(
transaction,
transaction,
@@ -630,8 +653,8 @@ const open = ref(false);
@@ -630,8 +653,8 @@ const open = ref(false);
</template>
</template>
<template v-else>
<template v-else>
<div
<div
@click="editTransaction(transaction)"
@click="editTransaction(transaction)"
class="cursor-pointer flex justify-between items-center"
class="cursor-pointer flex justify-between items-center"
>
>
<span class="break-words max-w-[calc(100%-20px)]">
<span class="break-words max-w-[calc(100%-20px)]">
{{ transaction.description }}
{{ transaction.description }}
@@ -644,23 +667,23 @@ const open = ref(false);
@@ -644,23 +667,23 @@ const open = ref(false);
<template v-if="transaction.editing">
<template v-if="transaction.editing">
<DropdownMenu>
<DropdownMenu>
<DropdownMenuTrigger
<DropdownMenuTrigger
as="Button"
as="Button"
class="justify-between w-full text-left border-none bg-white h-5"
class="justify-between w-full text-left border-none bg-white h-5"
>
>
{{
{{
transaction.category
transaction.category
? transaction.category.name
? transaction.category.name
: "Ukategorisert"
: "Ukategorisert"
}}
}}
</DropdownMenuTrigger>
</DropdownMenuTrigger>
<DropdownMenuContent
<DropdownMenuContent
class="w-full sm:-translate-x-5 md:-translate-x-10 lg:-translate-x-20"
class="w-full sm:-translate-x-5 md:-translate-x-10 lg:-translate-x-20"
>
>
<DropdownMenuItem
<DropdownMenuItem
v-for="category in categoryStore.categories"
v-for="category in categoryStore.categories"
:key="category.categoryId"
:key="category.categoryId"
:value="category.categoryId"
:value="category.categoryId"
@click="
@click="
() =>
() =>
saveTransaction(
saveTransaction(
transaction,
transaction,
@@ -668,7 +691,7 @@ const open = ref(false);
@@ -668,7 +691,7 @@ const open = ref(false);
category.categoryId
category.categoryId
)
)
"
"
class="text-gray-700 block px-4 py-2 text-sm"
class="text-gray-700 block px-4 py-2 text-sm"
>
>
{{ category.name }}
{{ category.name }}
</DropdownMenuItem>
</DropdownMenuItem>
@@ -677,14 +700,14 @@ const open = ref(false);
@@ -677,14 +700,14 @@ const open = ref(false);
</template>
</template>
<template v-else>
<template v-else>
<div
<div
@click="editTransaction(transaction)"
@click="editTransaction(transaction)"
class="cursor-pointer flex justify-between items-center"
class="cursor-pointer flex justify-between items-center"
>
>
<span class="break-words max-w-[calc(100%-20px)]">
<span class="break-words max-w-[calc(100%-20px)]">
{{
{{
transaction.category
transaction.category
? transaction.category.name
? transaction.category.name
: "Ukategorisert"
: "Ukategorisert"
}}
}}
</span>
</span>
<Pencil class="h-2.5"></Pencil>
<Pencil class="h-2.5"></Pencil>
@@ -700,12 +723,12 @@ const open = ref(false);
@@ -700,12 +723,12 @@ const open = ref(false);
</TableBody>
</TableBody>
</Table>
</Table>
<span v-if="!savingsAccount && !checkingAccount" class="p-4"
<span v-if="!savingsAccount && !checkingAccount" class="p-4"
>Integrer en bankkonto i profil for å starte...</span
>Integrer en bankkonto i profil for å starte...</span
>
>
<span
<span
v-else-if="transactionsStore.transactions.length === 0"
v-else-if="transactionsStore.transactions.length === 0"
class="p-4"
class="p-4"
>Legg til en transaksjon for å starte...</span
>Legg til en transaksjon for å starte...</span
>
>
</Card>
</Card>
</main>
</main>
Loading