Compare commits

..

9 Commits

4 changed files with 90 additions and 38 deletions

View File

@ -20,6 +20,8 @@ export interface Card {
name: string;
last_digits: string;
currency_id: number;
currency: Currency;
display_name: string;
}
export interface Category {
@ -50,7 +52,13 @@ export interface Transfer {
from_card_id: number;
to_card_id: number;
value: number;
from_value: number;
to_value: number;
date: string;
show_value: string;
have_diff_currs: boolean;
from_card: Card;
to_card: Card;
}
export interface Payment {
@ -142,8 +150,8 @@ export type EntityType<T extends EntityName> =
//
export async function getAll<T>(groupName: string, session?: string): Promise<T[] | ErrorMessage> {
const url = `${BASE_API_URL}/${groupName}/all`
export async function getAll<T>(groupName: string, session?: string, queryParams?: string): Promise<T[] | ErrorMessage> {
const url = queryParams ? `${BASE_API_URL}/${groupName}/all?${queryParams}` : `${BASE_API_URL}/${groupName}/all`
const defaultHeaders = {
'Content-Type': 'application/json',
};

View File

@ -6,10 +6,11 @@ function isErrorMessage(value: any): value is ErrorMessage {
return value && typeof value.message === 'string';
}
export const GET: RequestHandler = async ({ cookies, params }): Promise<Response> => {
export const GET: RequestHandler = async ({ cookies, params, url }): Promise<Response> => {
const session = cookies.get('session');
const { entity } = params;
const queryParams = url.searchParams.toString();
// Check if the entity is valid
if (!session) {
return new Response(JSON.stringify("no cookies"), { status: 401 });
@ -21,7 +22,7 @@ export const GET: RequestHandler = async ({ cookies, params }): Promise<Response
// TypeScript type inference for entity
const entityName = entity as EntityName
const result = await getAll<EntityType<typeof entityName>>(entityName, session);
const result = await getAll<EntityType<typeof entityName>>(entityName, session, queryParams);
if (isErrorMessage(result)) {
console.log("ERROR");

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { onMount } from "svelte";
import { onMount, tick } from "svelte";
import { type Card, type Currency } from "$lib/entities";
import { NumberToFPA } from "$lib/util/fpa";
@ -64,14 +64,18 @@
name: "",
};
}
if (balanceRef) {
balanceRef.value = "";
}
}
}
function editCard(card: Card) {
async function editCard(card: Card) {
editingCard = { ...card };
if (balanceRef) {
balanceRef.value = NumberToFPA(card.balance);
}
await tick();
if (creditLineRef) {
creditLineRef.value = NumberToFPA(card.credit_line);
}

View File

@ -11,18 +11,25 @@
let error: string | null = $state(null);
let editingTransfer: Transfer | null = $state(null);
let newTransfer: Partial<Transfer> = $state({
from_card_id: 0,
to_card_id: 0,
card_id: 0,
value: 0,
to_value: 0,
from_value: 0,
date: "2006-01-02T15:04:05Z",
});
onMount(async () => {
await fetchCategories();
await fetchCards();
await fetchCards(true);
});
async function fetchCards() {
const result = await fetch("/api/card/all");
async function fetchCards(preloadCurrencies = false) {
const queryParams = new URLSearchParams({
preload_currencies: preloadCurrencies.toString(),
});
const result = await fetch(`/api/card/all?${queryParams}`);
if (!result.ok) {
const obj = await result.json();
error = obj.message;
@ -72,6 +79,8 @@
from_card_id: 0,
to_card_id: 0,
value: 0,
to_value: 0,
from_value: 0,
date: "",
};
}
@ -83,6 +92,9 @@
const parts = transfer.date.split("T");
mutateDate = parts[0];
selectedTime = parts[1].split("Z")[0];
if (valueRef) {
valueRef.value = NumberToFPA(transfer.value);
}
}
async function deleteTransfer(id: number) {
@ -100,19 +112,28 @@
}
// Helper function to get the name of the parent category
function getCardName(cardId: number) {
if (cardId === 0) return "None";
const card = cards.find((card) => card.id === cardId);
return card ? card.name : "Unknown";
function doesCurrentTransferHasSameCurrencies(): boolean {
if (currentTransfer.from_card_id == 0 || currentTransfer.to_card_id == 0) {
return true;
}
return (
cards.find((card) => card.id == currentTransfer.from_card_id)
?.currency_id ==
cards.find((card) => card.id == currentTransfer.to_card_id)?.currency_id
);
}
function handleValueInput(
event: Event & { currentTarget: EventTarget & HTMLInputElement },
): void {
const target = event.target as HTMLInputElement;
const rawValue = target.value.replace(/[^0-9]/g, "");
currentTransfer.value = parseInt(rawValue || "0");
target.value = NumberToFPA(currentTransfer.value);
let valueRef: HTMLInputElement | null = $state(null);
function createHandleValueInput(field: "value" | "from_value" | "to_value") {
return function (
event: Event & { currentTarget: EventTarget & HTMLInputElement },
): void {
const target = event.target as HTMLInputElement;
const rawValue = target.value.replace(/[^0-9]/g, "");
const parsedValue = parseInt(rawValue || "0");
currentTransfer[field] = parsedValue;
target.value = NumberToFPA(parsedValue);
};
}
const constructedTime = $derived(`${mutateDate}T${selectedTime}Z`);
@ -161,15 +182,39 @@
</select>
</label>
<label class="block">
<span class="text-gray-700">Value:</span>
<input
type="text"
oninput={handleValueInput}
required
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md focus:ring focus:ring-indigo-200 focus:border-indigo-500"
/>
</label>
{#if doesCurrentTransferHasSameCurrencies()}
<label class="block">
<span class="text-gray-700">Value:</span>
<input
type="text"
oninput={createHandleValueInput("value")}
bind:this={valueRef}
required
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md focus:ring focus:ring-indigo-200 focus:border-indigo-500"
/>
</label>
{:else}
<label class="block">
<span class="text-gray-700">From Value:</span>
<input
type="text"
oninput={createHandleValueInput("from_value")}
bind:this={valueRef}
required
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md focus:ring focus:ring-indigo-200 focus:border-indigo-500"
/>
</label>
<label class="block">
<span class="text-gray-700">To Value:</span>
<input
type="text"
oninput={createHandleValueInput("to_value")}
bind:this={valueRef}
required
class="mt-1 block w-full px-4 py-2 border border-gray-300 rounded-md focus:ring focus:ring-indigo-200 focus:border-indigo-500"
/>
</label>
{/if}
<div class="flex items-center space-x-4">
<label>
@ -218,18 +263,12 @@
class="bg-white p-4 rounded-lg shadow-md flex justify-between items-center"
>
<div>
<strong class="block text-lg">{NumberToFPA(transfer.value)}</strong>
<strong class="block text-lg">{transfer.show_value}</strong>
<div class="text-sm text-gray-600">
<span class="font-bold">From:</span>
{getCardName(transfer.from_card_id)}
<span class="text-sm"
>{`•${cards.find((card) => card.id == transfer.from_card_id)?.last_digits}`}</span
>
{transfer.from_card.display_name}
<span class="font-bold">To:</span>
{getCardName(transfer.to_card_id)}
<span class="text-sm"
>{`•${cards.find((card) => card.id == transfer.to_card_id)?.last_digits}`}</span
>
{transfer.to_card.display_name}
<span class="font-bold">Date:</span>
{transfer.date}
</div>