feat: frontend send

This commit is contained in:
eneller
2026-03-18 15:30:19 +01:00
parent 7f3326c4a5
commit 4f8da032b7
8 changed files with 45 additions and 12 deletions

View File

@@ -41,6 +41,7 @@ export class ScreenLogin {
this.router.navigateByUrl(returnUrl); this.router.navigateByUrl(returnUrl);
}, },
error: (err) => { error: (err) => {
//FIXME error message displaying delayed, display message from server response
this.error = err.error?.message || 'Login failed. Please try again.'; this.error = err.error?.message || 'Login failed. Please try again.';
this.loading = false; this.loading = false;
} }

View File

@@ -37,7 +37,7 @@
<!-- Note Field --> <!-- Note Field -->
<div class="mb-4"> <div class="mb-4">
<label class="form-label">Note (Optional)</label> <label class="form-label">Note (Optional)</label>
<textarea class="form-control" rows="2" [(ngModel)]="note"></textarea> <textarea class="form-control" rows="2" [(ngModel)]="reference"></textarea>
</div> </div>
<!-- Action Buttons --> <!-- Action Buttons -->

View File

@@ -1,5 +1,6 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { APIService } from '../../services/api';
@Component({ @Component({
selector: 'app-screen-send', selector: 'app-screen-send',
@@ -10,16 +11,27 @@ import { FormsModule } from '@angular/forms';
export class ScreenSend { export class ScreenSend {
amount: number = 0; amount: number = 0;
recipient: string = ''; recipient: string = '';
note: string = ''; reference: string = '';
constructor(
private api: APIService,
){}
sendMoney() { sendMoney() {
console.log('Sending:', this.amount, 'to', this.recipient); this.api.send(this.amount, this.recipient, this.reference).subscribe({
// Add your logic here (e.g., API call) next:()=> {
this.cancel()
//TODO show success message
},
error:()=> {
//TODO show error message
}
});
} }
cancel() { cancel() {
this.amount = 0; this.amount = 0;
this.recipient = ''; this.recipient = '';
this.note = ''; this.reference = '';
} }
} }

View File

@@ -2,6 +2,7 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { BehaviorSubject, catchError, map, Observable, of, tap } from 'rxjs'; import { BehaviorSubject, catchError, map, Observable, of, tap } from 'rxjs';
import Transaction from '@model/transaction' import Transaction from '@model/transaction'
import { SendRequest, SendResponse } from '@message/Send';
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@@ -13,9 +14,6 @@ export class APIService {
constructor(private http: HttpClient){} constructor(private http: HttpClient){}
getTransactions(): Observable<Transaction[]>{
return this.http.get<Transaction[]>(`${this.apiUrl}/transactions`);
}
login(username: string, password: string): Observable<any>{ login(username: string, password: string): Observable<any>{
return this.http.post(`${this.apiUrl}/auth/login`,{ 'username': username, 'password': password}); return this.http.post(`${this.apiUrl}/auth/login`,{ 'username': username, 'password': password});
} }
@@ -23,7 +21,7 @@ export class APIService {
return this.http.post(`${this.apiUrl}/auth/logout`, {}); return this.http.post(`${this.apiUrl}/auth/logout`, {});
} }
checkAuthStatus(): Observable<boolean> { checkAuthStatus(): Observable<boolean> {
return this.http.get(`${this.apiUrl}/auth/status`, { withCredentials: true}).pipe( return this.http.get(`${this.apiUrl}/auth/status`).pipe(
map(() => true), map(() => true),
catchError(() => of(false)), catchError(() => of(false)),
tap({ tap({
@@ -32,4 +30,11 @@ export class APIService {
}) })
); );
} }
getTransactions(): Observable<Transaction[]>{
return this.http.get<Transaction[]>(`${this.apiUrl}/transactions`);
}
send(amount: number, recipientID: string, reference: string = ""): Observable<SendResponse>{
let request: SendRequest = {amount, recipientID, reference};
return this.http.post<SendResponse>(`${this.apiUrl}/send`, request);
}
} }

View File

@@ -7,7 +7,7 @@ export const authGuard: CanActivateFn = (route, state) => {
const api = inject(APIService); const api = inject(APIService);
const router = inject(Router); const router = inject(Router);
//TODO check for cookie //FIXME always redirected to login after page load
return api.isAuthenticated$.pipe( return api.isAuthenticated$.pipe(
map((isAuthenticated) => { map((isAuthenticated) => {
if (isAuthenticated) { if (isAuthenticated) {

View File

@@ -0,0 +1,9 @@
export class LoginRequest{
constructor(
username: string,
password: string
){}
}
export class LoginResponse{
constructor(){}
}

View File

@@ -2,6 +2,9 @@ import express from 'express';
import { logger } from '../util/logging'; import { logger } from '../util/logging';
import User from '../model/user'; import User from '../model/user';
import { getJWT, requireAuth } from '../util/auth'; import { getJWT, requireAuth } from '../util/auth';
import { LoginRequest } from '@message/Login';
import { SendRequest, SendResponse } from '@message/Send';
const router = express.Router(); const router = express.Router();
@@ -26,7 +29,7 @@ router.post('/login', async (req, res) => {
res.json({ message: 'Logged in successfully' }); res.json({ message: 'Logged in successfully' });
}catch (err) { }catch (err) {
logger.error('Failed to authenticate:', err); logger.error('Failed to authenticate:', err);
res.status(500).json({ error: 'Failed to authenticate' }); res.status(500).json({ message: 'Failed to authenticate' });
} }
}); });

View File

@@ -7,7 +7,10 @@ const router = express.Router();
router.get('/', requireAuth, async (req, res) => { router.get('/', requireAuth, async (req, res) => {
try { try {
const transactions = await Transaction.findAll({ limit: 10 }); const transactions = await Transaction.findAll({
limit: 100,
order: [['date', 'DESC']]
});
res.json(transactions); res.json(transactions);
} catch (err) { } catch (err) {
logger.error('Failed to fetch transactions:', err); logger.error('Failed to fetch transactions:', err);