Fixed couple of endpoints, implemented more of the frontend

This commit is contained in:
2023-10-25 01:24:28 +03:00
parent e4de55f255
commit 0baac602e4
18 changed files with 300 additions and 204 deletions

View File

@@ -22,11 +22,7 @@
<artifactId>spring-boot-starter-web</artifactId> <artifactId>spring-boot-starter-web</artifactId>
<version>3.1.4</version> <version>3.1.4</version>
</dependency> </dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId> <artifactId>spring-boot-starter-data-jpa</artifactId>

View File

@@ -8,6 +8,9 @@ import com.faf223.expensetrackerfaf.service.UserService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.validation.BindingResult; import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@@ -33,11 +36,16 @@ public class UserController {
} }
} }
@GetMapping("/{userUuid}") @GetMapping("/getUserData")
public ResponseEntity<UserDTO> getUser(@PathVariable String userUuid) { public ResponseEntity<UserDTO> getUser() {
User user = userService.getUserById(userUuid); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (user != null) return ResponseEntity.ok(userMapper.toDto(user));
else return ResponseEntity.notFound().build(); if (authentication != null && authentication.getPrincipal() instanceof UserDetails userDetails) {
User user = userService.getUserByEmail(userDetails.getUsername());
if (user != null) return ResponseEntity.ok(userMapper.toDto(user));
else return ResponseEntity.notFound().build();
}
return ResponseEntity.notFound().build();
} }
@GetMapping() @GetMapping()

View File

@@ -1,7 +1,6 @@
package com.faf223.expensetrackerfaf.dto; package com.faf223.expensetrackerfaf.dto;
import com.faf223.expensetrackerfaf.model.ExpenseCategory; import com.faf223.expensetrackerfaf.model.ExpenseCategory;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -10,7 +9,6 @@ import java.time.LocalDate;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@JsonIgnoreProperties({"expenseCategory"})
public class ExpenseDTO { public class ExpenseDTO {
private long expenseId; private long expenseId;
private UserDTO userDTO; private UserDTO userDTO;

View File

@@ -1,7 +1,6 @@
package com.faf223.expensetrackerfaf.dto; package com.faf223.expensetrackerfaf.dto;
import com.faf223.expensetrackerfaf.model.IncomeCategory; import com.faf223.expensetrackerfaf.model.IncomeCategory;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
@@ -10,7 +9,6 @@ import java.time.LocalDate;
@Data @Data
@AllArgsConstructor @AllArgsConstructor
@JsonIgnoreProperties({"incomeCategory"})
public class IncomeDTO { public class IncomeDTO {
private long incomeId; private long incomeId;
private UserDTO userDTO; private UserDTO userDTO;

View File

@@ -34,6 +34,7 @@
setCookie('access_token', access_token); setCookie('access_token', access_token);
setCookie('refresh_token', refresh_token); setCookie('refresh_token', refresh_token);
console.log(access_token, refresh_token); console.log(access_token, refresh_token);
window.location.href = '/dashboard'
} catch (error) { } catch (error) {
console.error('Login failed:', error); console.error('Login failed:', error);
} }

View File

@@ -16,5 +16,6 @@
display: flex; display: flex;
align-items: stretch; align-items: stretch;
min-height: 100vh; min-height: 100vh;
max-height: 100%;
} }
</style> </style>

View File

@@ -2,7 +2,14 @@
import DashHeader from "./DashHeader.svelte"; import DashHeader from "./DashHeader.svelte";
import DataMenu from "./DataMenu.svelte"; import DataMenu from "./DataMenu.svelte";
import QuickInfobar from "./QuickInfobar.svelte"; import QuickInfobar from "./QuickInfobar.svelte";
import NotificationBoard from "./NotificationBoard.svelte"; import { getCookie } from "svelte-cookie";
import {onMount} from "svelte";
onMount(() => {
if (getCookie('access_token') === null ) {
window.location.href = '/auth/login';
}
})
</script> </script>
<div id="dashboard"> <div id="dashboard">
@@ -17,7 +24,7 @@
background-color: rgb(245,242,243); background-color: rgb(245,242,243);
border-radius: 20px; border-radius: 20px;
margin: 20px; margin: 20px;
min-width: 100px; min-width: 100px;
display: flex; display: flex;
flex:1 1 auto; flex:1 1 auto;
flex-direction: column; flex-direction: column;

View File

@@ -2,8 +2,8 @@
import Graph1 from './graphs/Graph1.svelte'; import Graph1 from './graphs/Graph1.svelte';
import Graph2 from './graphs/Graph2.svelte'; import Graph2 from './graphs/Graph2.svelte';
import Graph3 from './graphs/Graph3.svelte'; import Graph3 from './graphs/Graph3.svelte';
import Graph4 from './graphs/Graph4.svelte'; import Expenses from "./Expenses.svelte";
import Graph5 from './graphs/Graph5.svelte'; import Incomes from "./Incomes.svelte";
</script> </script>
<div id="dataMenu"> <div id="dataMenu">
@@ -14,9 +14,9 @@
<div id="oneVertical"> <div id="oneVertical">
<Graph3 /> <Graph3 />
</div> </div>
<div id="twoHorizontal"> <div id="dataPanel">
<Graph4 /> <Incomes />
<Graph5 /> <Expenses />
</div> </div>
</div> </div>
@@ -34,7 +34,6 @@
} }
#twoVertical { #twoVertical {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -53,9 +52,9 @@
min-height:0; min-height:0;
} }
#twoHorizontal { #dataPanel {
display: flex; display: flex;
flex-direction: column; flex-direction: row;
align-self: stretch; align-self: stretch;
flex-grow: 1; flex-grow: 1;
min-width: 0; min-width: 0;

View File

@@ -0,0 +1,71 @@
<script>
import { onMount, afterUpdate } from 'svelte';
import axios from 'axios';
import { getCookie } from "svelte-cookie";
let data = [];
let parentHeight;
onMount(async () => {
const token = getCookie('access_token');
const config = {
headers: {
'Authorization': `Bearer ${token}`
}
};
try {
const response = await axios.get('http://localhost:8081/expenses/personal-expenses', config);
data = response.data;
parentHeight = document.querySelector('#expenseInfo').offsetHeight;
} catch (error) {
console.error('Error:', error);
}
});
afterUpdate(() => {
parentHeight = document.querySelector('#expenseInfo').offsetHeight;
});
</script>
<div id="expenseInfo" style="max-height: {parentHeight}px;">
<h2>Expenses</h2>
<ul>
{#each data as item}
<li>
{item.incomeCategory ? `${item.incomeCategory.name}: ` : `${item.expenseCategory.name}: `}
{item.incomeCategory ? `+${item.amount}$` : `-${item.amount}$`}
{`${item.date}`}
</li>
{/each}
</ul>
</div>
<style>
#expenseInfo {
flex: 1;
border-radius: 10px;
margin: 10px;
overflow-y: auto;
max-height: 100%;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 20px;
background-color: #f2f2f2;
padding: 10px;
border-radius: 5px;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}
li:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}
</style>

View File

@@ -0,0 +1,71 @@
<script>
import { onMount, afterUpdate } from 'svelte';
import axios from 'axios';
import { getCookie } from "svelte-cookie";
let data = [];
let parentHeight;
onMount(async () => {
const token = getCookie('access_token');
const config = {
headers: {
'Authorization': `Bearer ${token}`
}
};
try {
const response = await axios.get('http://localhost:8081/incomes/personal-incomes', config);
data = response.data;
parentHeight = document.querySelector('#incomeInfo').offsetHeight;
} catch (error) {
console.error('Error:', error);
}
});
afterUpdate(() => {
parentHeight = document.querySelector('#incomeInfo').offsetHeight;
});
</script>
<div id="incomeInfo" style="max-height: {parentHeight}px;">
<h2>Incomes</h2>
<ul>
{#each data as item}
<li>
{item.incomeCategory ? `${item.incomeCategory.name}: ` : `${item.expenseCategory.name}: `}
{item.incomeCategory ? `+${item.amount}$` : `-${item.amount}$`}
{`${item.date}`}
</li>
{/each}
</ul>
</div>
<style>
#incomeInfo {
flex: 1;
border-radius: 10px;
margin: 10px;
overflow-y: auto;
max-height: 100%;
}
ul {
list-style: none;
padding: 0;
}
li {
margin-bottom: 20px;
background-color: #f2f2f2;
padding: 10px;
border-radius: 5px;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
}
li:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}
</style>

View File

@@ -1,15 +0,0 @@
<script>
</script>
<div id="notificationBoard">
</div>
<style>
#notificationBoard {
display: none;
width:0;
height:0;
}
</style>

View File

@@ -23,15 +23,12 @@
height: 100px; height: 100px;
padding: 10px; padding: 10px;
border-radius: 10px; border-radius: 10px;
background-color: #ffdde2; background-color: #d3d3d3;
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1); transition: all 0.3s cubic-bezier(.25,.8,.25,1);
/* border: 1px solid black; */
} }
.infobarElement:hover { .infobarElement:hover {
/* color:white; */
/* background-color: black; */
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
} }
</style> </style>

View File

@@ -10,6 +10,7 @@
onMount(async () => { onMount(async () => {
const token = getCookie('access_token'); const token = getCookie('access_token');
const config = { const config = {
headers: { headers: {
'Authorization': `Bearer ${token}` 'Authorization': `Bearer ${token}`
@@ -17,21 +18,22 @@
}; };
try { try {
const response = await axios.get('http://localhost:8081/incomes/00112233-4455-6677-8899-aabbccddeeaa', config); const response = await axios.get('http://localhost:8081/incomes/personal-incomes', config);
console.log(response.data);
const incomeData = response.data; const incomeData = response.data;
const chartLabels = incomeData.map(item => item.category.categoryName); const chartLabels = incomeData.map(item => item.incomeCategory.name);
const chartValues = incomeData.map(item => item.amount); const chartValues = incomeData.map(item => item.amount);
ctx = chartCanvas.getContext('2d'); ctx = chartCanvas.getContext('2d');
new Chart(ctx, { new Chart(ctx, {
type: 'bar', // Set chart type to 'bar' for a bar graph type: 'bar',
data: { data: {
labels: chartLabels, labels: chartLabels,
datasets: [{ datasets: [{
label: 'Revenue', label: 'Revenue',
backgroundColor: 'rgb(255, 99, 132)', backgroundColor: 'rgb(255, 99, 132)',
data: chartValues // Changed from 'data' to 'chartValues' data: chartValues
}] }]
}, },
options: { options: {
@@ -56,7 +58,7 @@
flex: 1; flex: 1;
border-radius: 10px; border-radius: 10px;
margin: 10px; margin: 10px;
background-color: #ffdde2; background-color: #d3d3d3;
} }
#chart:hover { #chart:hover {

View File

@@ -1,48 +1,70 @@
<script> <script>
import chartjs from 'chart.js/auto'; import Chart from 'chart.js/auto';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import axios from 'axios';
import {getCookie} from "svelte-cookie";
var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
var config = {
type: 'line',
data: {
labels: ["January", "February", "March", "April", "May", "June", "July"],
datasets: [{
fill: false,
data: [
5,
6,
3,
4
]
}, {
label: "My Second dataset ",
fill: false,
data: [
5,
6,
3,
4
]
}]
},
options: {
title:{
display:true,
text: "Chart.js Line Chart - Animation Progress Bar"
},
maintainAspectRatio: false
}
};
let chartValues = [20, 10, 5, 2, 20, 30, 45];
let chartLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
let ctx; let ctx;
let chartCanvas; let chartCanvas;
onMount(async (promise) => { onMount(async () => {
ctx = chartCanvas.getContext('2d');
var chart = new chartjs(ctx, config); const token = getCookie('access_token');
const config = {
headers: {
'Authorization': `Bearer ${token}`
}
};
try {
const response = await axios.get('http://localhost:8081/expenses/personal-expenses', config);
const aggregatedData = {};
response.data.forEach(item => {
const category = item.expenseCategory.name;
const amount = item.amount;
if (aggregatedData[category]) {
aggregatedData[category] += amount;
} else {
aggregatedData[category] = amount;
}
});
const chartLabels = Object.keys(aggregatedData);
const chartValues = Object.values(aggregatedData);
ctx = chartCanvas.getContext('2d');
new Chart(ctx, {
type: 'bar',
data: {
labels: chartLabels,
datasets: [{
label: 'Revenue',
backgroundColor: 'rgb(255, 99, 132)',
data: chartValues
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
legend: {
display: false
},
tooltips: {
callbacks: {
label: (tooltipItem) => {
return tooltipItem.yLabel;
}
}
}
}
});
} catch (error) {
console.error('Error:', error);
}
}); });
</script> </script>
@@ -52,15 +74,15 @@
<style> <style>
#chart { #chart {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24); box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1); transition: all 0.3s cubic-bezier(.25,.8,.25,1);
flex: 1; flex: 1;
border-radius: 10px; border-radius: 10px;
margin:10px; margin: 10px;
background-color: #ffdde2; background-color: #d3d3d3;
} }
#chart:hover { #chart:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
} }
</style> </style>

View File

@@ -2,33 +2,44 @@
import chartjs from 'chart.js/auto'; import chartjs from 'chart.js/auto';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import axios from 'axios'; import axios from 'axios';
import { getCookie } from "svelte-cookie";
let ctx; let ctx;
let chartCanvas; let chartCanvas;
onMount(async () => { onMount(async () => {
const token = getCookie('access_token');
const config = { const config = {
headers: { headers: {
'Authorization': `Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJkYW4uYmFsYW5AZ21haWwuY29tIiwiaWF0IjoxNjk3NzQ0MjY3LCJleHAiOjE2OTc4MzA2Njd9.hzbEDDuOVCY_EQAA8xGlJskQ2FQjw8o0CtFKB1dKYOU` 'Authorization': `Bearer ${token}`
} }
}; };
try { try {
const response = await axios.get('http://localhost:8081/incomes/00112233-4455-6677-8899-aabbccddeeaa', config); const [incomesResponse, expensesResponse] = await Promise.all([
const incomeData = response.data; // Assuming the response is an array of income data axios.get('http://localhost:8081/incomes/personal-incomes', config),
axios.get('http://localhost:8081/expenses/personal-expenses', config)
]);
// Extract income categories and their values const incomesData = incomesResponse.data;
const chartLabels = incomeData.map(item => item.category.categoryName); const expensesData = expensesResponse.data;
const chartValues = incomeData.map(item => item.amount);
const totalIncomes = incomesData.reduce((total, item) => total + item.amount, 0);
const totalExpenses = expensesData.reduce((total, item) => total + item.amount, 0);
const chartLabels = ['Incomes', 'Expenses'];
const chartValues = [totalIncomes, totalExpenses];
ctx = chartCanvas.getContext('2d'); ctx = chartCanvas.getContext('2d');
new chartjs(ctx, { new chartjs(ctx, {
type: 'pie', // Set chart type to 'pie' for a pie chart type: 'pie',
data: { data: {
labels: chartLabels, labels: chartLabels,
datasets: [{ datasets: [{
data: chartValues, data: chartValues,
backgroundColor: ['red', 'orange', 'yellow', 'green', 'blue'], // Customize colors as needed backgroundColor: ['green', 'red'],
}] }]
}, },
options: { options: {
@@ -53,7 +64,7 @@
flex: 1; flex: 1;
border-radius: 10px; border-radius: 10px;
margin: 10px; margin: 10px;
background-color: #ffdde2; background-color: #d3d3d3;
} }
#chart:hover { #chart:hover {

View File

@@ -1,48 +0,0 @@
<script>
import chartjs from 'chart.js/auto';
import { onMount } from 'svelte';
let chartValues = [20, 10, 5, 2, 20, 30, 45];
let chartLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
let ctx;
let chartCanvas;
onMount(async (promise) => {
ctx = chartCanvas.getContext('2d');
var chart = new chartjs(ctx, {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: 'Revenue',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: chartValues
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
});
</script>
<div id="chart">
<canvas bind:this={chartCanvas}></canvas>
</div>
<style>
#chart {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
flex: 1;
border-radius: 10px;
margin:10px;
background-color: #ffdde2;
}
#chart:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}
</style>

View File

@@ -1,48 +0,0 @@
<script>
import chartjs from 'chart.js/auto';
import { onMount } from 'svelte';
let chartValues = [20, 10, 5, 2, 20, 30, 45];
let chartLabels = ['January', 'February', 'March', 'April', 'May', 'June', 'July'];
let ctx;
let chartCanvas;
onMount(async (promise) => {
ctx = chartCanvas.getContext('2d');
var chart = new chartjs(ctx, {
type: 'line',
data: {
labels: chartLabels,
datasets: [{
label: 'Revenue',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: chartValues
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
});
</script>
<div id="chart">
<canvas bind:this={chartCanvas}></canvas>
</div>
<style>
#chart {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
transition: all 0.3s cubic-bezier(.25,.8,.25,1);
flex: 1;
border-radius: 10px;
margin:10px;
background-color: #ffdde2;
}
#chart:hover {
box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);
}
</style>

View File

@@ -1,4 +1,29 @@
<script> <script>
import { onMount } from 'svelte';
import axios from 'axios';
import { getCookie } from "svelte-cookie";
let username;
onMount(async () => {
const token = getCookie('access_token');
const config = {
headers: {
'Authorization': `Bearer ${token}`
}
};
try {
const response = await axios.get('http://localhost:8081/users/getUserData', config);
const data = response.data;
username = data.username;
console.log(username)
} catch (error) {
console.error('Error:', error);
}
});
</script> </script>
@@ -37,7 +62,7 @@
</div> </div>
<div id="profileSpace"> <div id="profileSpace">
<div id="profileInfo">Profile Info</div> <div id="profileInfo">Hello, {username}</div>
</div> </div>
</div> </div>