Compare commits
No commits in common. "8ed19746831edb318dd9283713b7de4a9d471331" and "112d509274c1ceebd12282204901083d1d0c3e2a" have entirely different histories.
8ed1974683
...
112d509274
21
package-lock.json
generated
21
package-lock.json
generated
|
@ -7,9 +7,6 @@
|
||||||
"": {
|
"": {
|
||||||
"name": "fin-check-front",
|
"name": "fin-check-front",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"dependencies": {
|
|
||||||
"chart.js": "^4.4.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@sveltejs/adapter-auto": "^3.0.0",
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@sveltejs/kit": "^2.0.0",
|
||||||
|
@ -514,12 +511,6 @@
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@kurkle/color": {
|
|
||||||
"version": "0.3.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
|
||||||
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
|
@ -1222,18 +1213,6 @@
|
||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"license": "CC-BY-4.0"
|
||||||
},
|
},
|
||||||
"node_modules/chart.js": {
|
|
||||||
"version": "4.4.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.6.tgz",
|
|
||||||
"integrity": "sha512-8Y406zevUPbbIBA/HRk33khEmQPk5+cxeflWE/2rx1NJsjVWMPw/9mSP9rxHP5eqi6LNoPBVMfZHxbwLSgldYA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@kurkle/color": "^0.3.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"pnpm": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.1.tgz",
|
||||||
|
|
|
@ -22,8 +22,5 @@
|
||||||
"tailwindcss": "^3.4.9",
|
"tailwindcss": "^3.4.9",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.0.0",
|
||||||
"vite": "^5.0.3"
|
"vite": "^5.0.3"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"chart.js": "^4.4.6"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -137,17 +137,6 @@ export interface Currency {
|
||||||
symbol: string;
|
symbol: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StatsType {
|
|
||||||
value: number;
|
|
||||||
name: string;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StatsTypeCurrencyChart {
|
|
||||||
label: string;
|
|
||||||
elements: StatsType[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export const EntityTypes = {
|
export const EntityTypes = {
|
||||||
card: "Card",
|
card: "Card",
|
||||||
type: "Type",
|
type: "Type",
|
||||||
|
@ -159,9 +148,6 @@ export const EntityTypes = {
|
||||||
metric: "Metric",
|
metric: "Metric",
|
||||||
currency: "Currency",
|
currency: "Currency",
|
||||||
expense_bulk: "ExpenseBulk",
|
expense_bulk: "ExpenseBulk",
|
||||||
// I don't know if I should put Statistics interfaces to Entities
|
|
||||||
stats_type: "StatsType",
|
|
||||||
stats_type_currency_chart: "StatsTypeCurrencyChart",
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type EntityName = keyof typeof EntityTypes;
|
export type EntityName = keyof typeof EntityTypes;
|
||||||
|
@ -176,8 +162,6 @@ export type EntityType<T extends EntityName> =
|
||||||
T extends "metric" ? Metric :
|
T extends "metric" ? Metric :
|
||||||
T extends "currency" ? Currency :
|
T extends "currency" ? Currency :
|
||||||
T extends "expense_bulk" ? ExpenseBulk :
|
T extends "expense_bulk" ? ExpenseBulk :
|
||||||
T extends "stats_type" ? StatsType :
|
|
||||||
T extends "stats_type_currency_chart" ? StatsTypeCurrencyChart :
|
|
||||||
never;
|
never;
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -373,31 +357,3 @@ export async function filter<F, R>(groupName: string, data: F, session?: string)
|
||||||
return { message: error.message };
|
return { message: error.message };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function get_stats_for<R>(groupName: string, session?: string): Promise<R | ErrorMessage> {
|
|
||||||
const url = `${BASE_API_URL}/statistics/${groupName}`
|
|
||||||
const defaultHeaders = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
};
|
|
||||||
|
|
||||||
const headers = session
|
|
||||||
? { ...defaultHeaders, Cookie: `session=${session}` }
|
|
||||||
: defaultHeaders
|
|
||||||
|
|
||||||
const config: RequestInit = {
|
|
||||||
method: 'GET',
|
|
||||||
headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, config);
|
|
||||||
if (!response.ok) {
|
|
||||||
const body = await response.json()
|
|
||||||
throw new Error(`Failed to update ${groupName}: ${body.message}`);
|
|
||||||
}
|
|
||||||
return await response.json() as R;
|
|
||||||
} catch (err) {
|
|
||||||
const error = err as Error
|
|
||||||
return { message: error.message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,85 +1 @@
|
||||||
<script lang="ts">
|
<p>Hello, World!</p>
|
||||||
import { onMount, onDestroy } from "svelte";
|
|
||||||
import {
|
|
||||||
Chart,
|
|
||||||
DoughnutController,
|
|
||||||
ArcElement,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
type ChartConfiguration,
|
|
||||||
} from "chart.js";
|
|
||||||
import type { StatsTypeCurrencyChart } from "$lib/entities";
|
|
||||||
|
|
||||||
Chart.register(DoughnutController, ArcElement, Tooltip, Legend);
|
|
||||||
|
|
||||||
let error: string | null = null;
|
|
||||||
let data: StatsTypeCurrencyChart[] = [];
|
|
||||||
let configs: ChartConfiguration[] = [];
|
|
||||||
let charts: Chart[] = [];
|
|
||||||
let canvases: HTMLCanvasElement[] = [];
|
|
||||||
|
|
||||||
async function fetchChartStats() {
|
|
||||||
try {
|
|
||||||
const result = await fetch("/api/statistics/type");
|
|
||||||
if (!result.ok) {
|
|
||||||
const obj = await result.json();
|
|
||||||
error = obj.message;
|
|
||||||
} else {
|
|
||||||
data = await result.json();
|
|
||||||
|
|
||||||
configs = data.map((chart) => ({
|
|
||||||
type: "doughnut",
|
|
||||||
data: {
|
|
||||||
labels: chart.elements.map((type) => type.name),
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Chart Dataset",
|
|
||||||
data: chart.elements.map((type) => type.value / 100),
|
|
||||||
backgroundColor: chart.elements.map((type) => type.color),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error("Error fetching data:", e);
|
|
||||||
error = "Failed to fetch chart data.";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createTypeCharts() {
|
|
||||||
charts.forEach((chart) => chart.destroy());
|
|
||||||
charts = configs.map((config, index) => new Chart(canvases[index], config));
|
|
||||||
}
|
|
||||||
onMount(async () => {
|
|
||||||
await fetchChartStats();
|
|
||||||
createTypeCharts();
|
|
||||||
});
|
|
||||||
|
|
||||||
onDestroy(() => {
|
|
||||||
charts.forEach((chart) => chart.destroy());
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#if error}
|
|
||||||
<p class="bg-red-100 text-red-700 p-4 rounded">{error}</p>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<!-- Render canvas elements for charts -->
|
|
||||||
<div class="w-1/2 grid grid-cols-2 gap-4">
|
|
||||||
{#each configs as _, index}
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div class="type-chart">
|
|
||||||
<canvas bind:this={canvases[index]}></canvas>
|
|
||||||
<span class="flex justify-center">{data[index].label}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style lang="postcss">
|
|
||||||
.type-chart {
|
|
||||||
width: 20vw;
|
|
||||||
height: 20vw;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
import type { ErrorMessage } from "$lib/api";
|
|
||||||
import { get_stats_for, type StatsTypeCurrencyChart } from "$lib/entities";
|
|
||||||
import type { RequestHandler } from "./$types";
|
|
||||||
|
|
||||||
function isErrorMessage(value: any): value is ErrorMessage {
|
|
||||||
return value && typeof value.message === 'string';
|
|
||||||
}
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ cookies }): Promise<Response> => {
|
|
||||||
const session = cookies.get('session');
|
|
||||||
|
|
||||||
// const queryParams = url.searchParams.toString();
|
|
||||||
// Check if the entity is valid
|
|
||||||
if (!session) {
|
|
||||||
return new Response(JSON.stringify("no cookies"), { status: 401 });
|
|
||||||
}
|
|
||||||
|
|
||||||
// TypeScript type inference for entity
|
|
||||||
const result = await get_stats_for<StatsTypeCurrencyChart[]>("type", session);
|
|
||||||
|
|
||||||
if (isErrorMessage(result)) {
|
|
||||||
console.log("ERROR");
|
|
||||||
return new Response(JSON.stringify(result), { status: 500 });
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(JSON.stringify(result), { status: 200 });
|
|
||||||
}
|
|
|
@ -117,6 +117,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to get the name of the parent category
|
// 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 getTypeColor(typeId: number) {
|
function getTypeColor(typeId: number) {
|
||||||
if (typeId === 0) return "None";
|
if (typeId === 0) return "None";
|
||||||
const type = types.find((card) => card.id === typeId);
|
const type = types.find((card) => card.id === typeId);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user