Modifiche
This commit is contained in:
@@ -8,7 +8,7 @@ WORKDIR /app
|
||||
COPY package*.json ./
|
||||
|
||||
# Install dependencies
|
||||
RUN npm ci
|
||||
RUN npm install
|
||||
|
||||
# Copy application code
|
||||
COPY . .
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"serve": {
|
||||
"builder": "@angular/build:dev-server",
|
||||
"options": {
|
||||
"port": 3000
|
||||
"port": 4200
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
|
||||
@@ -1,15 +1,36 @@
|
||||
import '@angular/compiler';
|
||||
import { bootstrapApplication } from '@angular/platform-browser';
|
||||
import { provideZonelessChangeDetection } from '@angular/core';
|
||||
import { provideHttpClient, withFetch } from '@angular/common/http';
|
||||
import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
|
||||
import Keycloak from 'keycloak-js';
|
||||
|
||||
import { AppComponent } from './src/app.component';
|
||||
import { KEYCLOAK_INSTANCE } from './src/core/tokens/keycloak.token';
|
||||
import { authInterceptor } from './src/core/interceptors/auth.interceptor';
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideZonelessChangeDetection(),
|
||||
provideHttpClient(withFetch()),
|
||||
],
|
||||
}).catch(err => console.error(err));
|
||||
const keycloak = new Keycloak({
|
||||
url: 'http://localhost:8080',
|
||||
realm: 'scommettiapp',
|
||||
clientId: 'scommetti-frontend',
|
||||
});
|
||||
|
||||
// AI Studio always uses an `index.tsx` file for all project types.
|
||||
keycloak.init({
|
||||
onLoad: 'login-required',
|
||||
checkLoginIframe: false,
|
||||
pkceMethod: 'S256',
|
||||
}).then(authenticated => {
|
||||
if (!authenticated) {
|
||||
keycloak.login();
|
||||
return;
|
||||
}
|
||||
|
||||
bootstrapApplication(AppComponent, {
|
||||
providers: [
|
||||
provideZonelessChangeDetection(),
|
||||
provideHttpClient(withFetch(), withInterceptors([authInterceptor])),
|
||||
{ provide: KEYCLOAK_INSTANCE, useValue: keycloak },
|
||||
],
|
||||
}).catch(err => console.error(err));
|
||||
}).catch(err => {
|
||||
console.error('Keycloak initialization failed:', err);
|
||||
});
|
||||
|
||||
Generated
+26
@@ -15,6 +15,7 @@
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"@angular/core": "^20.3.0",
|
||||
"@angular/platform-browser": "^20.3.0",
|
||||
"keycloak-js": "^24.0.0",
|
||||
"rxjs": "^7.8.2",
|
||||
"tailwindcss": "latest"
|
||||
},
|
||||
@@ -4931,6 +4932,12 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/js-sha256": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/js-sha256/-/js-sha256-0.11.1.tgz",
|
||||
"integrity": "sha512-o6WSo/LUvY2uC4j7mO50a2ms7E/EAdbP0swigLV+nzHKTTaYnaLIWJ02VdXrsJX0vGedDESQnLsOekr94ryfjg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
@@ -4991,6 +4998,25 @@
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/jwt-decode": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
|
||||
"integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/keycloak-js": {
|
||||
"version": "24.0.5",
|
||||
"resolved": "https://registry.npmjs.org/keycloak-js/-/keycloak-js-24.0.5.tgz",
|
||||
"integrity": "sha512-VQOSn3j13DPB6OuavKAq+sRjDERhIKrXgBzekoHRstifPuyULILguugX6yxRUYFSpn3OMYUXmSX++tkdCupOjA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"js-sha256": "^0.11.0",
|
||||
"jwt-decode": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/listr2": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"@angular/build": "^20.3.0",
|
||||
"@angular/cli": "^20.3.0",
|
||||
"@angular/compiler-cli": "^20.3.0",
|
||||
"keycloak-js": "^24.0.0",
|
||||
"tailwindcss": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, signal, inject } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { HomeComponent } from './features/home/home.component';
|
||||
import { DiscoverComponent } from './features/discover/discover.component';
|
||||
import { GroupsComponent } from './features/groups/groups.component';
|
||||
import { ProfileComponent } from './features/profile/profile.component';
|
||||
import { CreateBetComponent } from './features/bets/create-bet.component';
|
||||
import { DataService } from './core/services/data.service';
|
||||
|
||||
type View = 'home' | 'discover' | 'groups' | 'profile' | 'create-bet';
|
||||
|
||||
@@ -23,9 +24,15 @@ type View = 'home' | 'discover' | 'groups' | 'profile' | 'create-bet';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppComponent {
|
||||
private dataService = inject(DataService);
|
||||
|
||||
currentView = signal<View>('home');
|
||||
private previousView = signal<View>('home');
|
||||
|
||||
constructor() {
|
||||
this.dataService.loadCurrentUser();
|
||||
}
|
||||
|
||||
setView(view: View) {
|
||||
if (view === 'create-bet') {
|
||||
this.previousView.set(this.currentView());
|
||||
@@ -38,4 +45,4 @@ export class AppComponent {
|
||||
onBetActionClosed() {
|
||||
this.currentView.set(this.previousView());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { HttpInterceptorFn } from '@angular/common/http';
|
||||
import { inject } from '@angular/core';
|
||||
import { from, switchMap } from 'rxjs';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
|
||||
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
||||
const authService = inject(AuthService);
|
||||
|
||||
return from(authService.getValidToken()).pipe(
|
||||
switchMap(token => {
|
||||
if (!token) return next(req);
|
||||
const authReq = req.clone({
|
||||
headers: req.headers.set('Authorization', `Bearer ${token}`)
|
||||
});
|
||||
return next(authReq);
|
||||
})
|
||||
);
|
||||
};
|
||||
@@ -1,8 +1,3 @@
|
||||
import { User } from './user.model';
|
||||
import { Group } from './group.model';
|
||||
import { Bet } from './bet.model';
|
||||
import { FriendRequest } from './friend-request.model';
|
||||
|
||||
export interface SuccessResponse<T = any> {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
@@ -21,16 +16,3 @@ export interface ErrorResponse {
|
||||
errors?: ErrorDetail[];
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
export interface InitialData {
|
||||
currentUser: User;
|
||||
users: User[];
|
||||
friends: User[];
|
||||
friendRequests: FriendRequest[];
|
||||
receivedFriendRequests?: FriendRequest[]; // Legacy or convenience
|
||||
sentFriendRequests?: FriendRequest[]; // Legacy or convenience
|
||||
groups: Group[];
|
||||
userBets: Bet[];
|
||||
bets?: Bet[]; // Compatibility with data.service
|
||||
publicBets: Bet[];
|
||||
}
|
||||
|
||||
@@ -21,6 +21,18 @@ export class BetApiService {
|
||||
private http = inject(HttpClient);
|
||||
private readonly apiUrl = 'http://localhost:3000/api/v1/bets';
|
||||
|
||||
getUserBets(): Observable<Bet[]> {
|
||||
return this.http.get<SuccessResponse<Bet[]>>(`${this.apiUrl}/user`).pipe(
|
||||
map(res => res.data.map(mapBet))
|
||||
);
|
||||
}
|
||||
|
||||
getPublicBets(): Observable<Bet[]> {
|
||||
return this.http.get<SuccessResponse<Bet[]>>(`${this.apiUrl}/public`).pipe(
|
||||
map(res => res.data.map(mapBet))
|
||||
);
|
||||
}
|
||||
|
||||
getBets(): Observable<Bet[]> {
|
||||
return this.http.get<SuccessResponse<Bet[]>>(this.apiUrl).pipe(
|
||||
map(res => res.data.map(mapBet))
|
||||
|
||||
@@ -13,6 +13,18 @@ export class FriendRequestApiService {
|
||||
private http = inject(HttpClient);
|
||||
private readonly apiUrl = 'http://localhost:3000/api/v1/friend-requests';
|
||||
|
||||
getFriends(): Observable<User[]> {
|
||||
return this.http.get<SuccessResponse<User[]>>(this.apiUrl).pipe(
|
||||
map(res => res.data)
|
||||
);
|
||||
}
|
||||
|
||||
getPendingRequests(): Observable<FriendRequest[]> {
|
||||
return this.http.get<SuccessResponse<FriendRequest[]>>(`${this.apiUrl}/pending`).pipe(
|
||||
map(res => res.data)
|
||||
);
|
||||
}
|
||||
|
||||
sendRequest(request: SendFriendRequestRequest): Observable<FriendRequest> {
|
||||
return this.http.post<SuccessResponse<FriendRequest>>(this.apiUrl, request).pipe(
|
||||
map(res => res.data)
|
||||
|
||||
@@ -2,24 +2,8 @@ import { inject, Injectable } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { User, UpdateUserRequest } from '../../models/user.model';
|
||||
import { SuccessResponse, InitialData } from '../../models/api-responses.model';
|
||||
import { SuccessResponse } from '../../models/api-responses.model';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { toDate } from '../../utils/type-guards';
|
||||
|
||||
function mapInitialData(data: InitialData): InitialData {
|
||||
const mapBet = (bet: any) => ({
|
||||
...bet,
|
||||
startDate: toDate(bet.startDate)!,
|
||||
endDate: toDate(bet.endDate)
|
||||
});
|
||||
|
||||
return {
|
||||
...data,
|
||||
userBets: (data.userBets || []).map(mapBet),
|
||||
publicBets: (data.publicBets || []).map(mapBet),
|
||||
bets: (data.bets || []).map(mapBet)
|
||||
};
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -28,9 +12,9 @@ export class UserApiService {
|
||||
private http = inject(HttpClient);
|
||||
private readonly apiUrl = 'http://localhost:3000/api/v1';
|
||||
|
||||
getInitialData(): Observable<InitialData> {
|
||||
return this.http.get<SuccessResponse<InitialData>>(`${this.apiUrl}/initial-data`).pipe(
|
||||
map(res => mapInitialData(res.data))
|
||||
getCurrentUser(): Observable<User> {
|
||||
return this.http.get<SuccessResponse<User>>(`${this.apiUrl}/users/current`).pipe(
|
||||
map(res => res.data)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -39,6 +23,4 @@ export class UserApiService {
|
||||
map(res => res.data)
|
||||
);
|
||||
}
|
||||
|
||||
// Potential future: getProfile(id), etc.
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { Injectable, inject } from '@angular/core';
|
||||
import { KEYCLOAK_INSTANCE } from '../tokens/keycloak.token';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthService {
|
||||
private keycloak = inject(KEYCLOAK_INSTANCE);
|
||||
|
||||
async getValidToken(): Promise<string> {
|
||||
try {
|
||||
await this.keycloak.updateToken(30);
|
||||
} catch {
|
||||
this.keycloak.login();
|
||||
return '';
|
||||
}
|
||||
return this.keycloak.token!;
|
||||
}
|
||||
|
||||
get tokenParsed() {
|
||||
return this.keycloak.tokenParsed;
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.keycloak.logout({ redirectUri: window.location.origin });
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@ export class DataService {
|
||||
private userApiService = inject(UserApiService);
|
||||
private friendRequestApiService = inject(FriendRequestApiService);
|
||||
|
||||
private readonly _users = signal<User[]>([]);
|
||||
private readonly _bets = signal<Bet[]>([]);
|
||||
private readonly _groups = signal<Group[]>([]);
|
||||
private readonly _friends = signal<User[]>([]);
|
||||
@@ -47,26 +46,64 @@ export class DataService {
|
||||
|
||||
readonly getPublicBets = computed(() => this._publicBets());
|
||||
|
||||
constructor() {
|
||||
this._initializeData();
|
||||
// --- Load methods: chiamati da ogni componente al momento della visualizzazione ---
|
||||
|
||||
async loadCurrentUser(): Promise<void> {
|
||||
try {
|
||||
const user = await firstValueFrom(this.userApiService.getCurrentUser());
|
||||
this.currentUser.set(user);
|
||||
} catch (error) {
|
||||
console.error('Failed to load current user', error);
|
||||
}
|
||||
}
|
||||
|
||||
private _initializeData() {
|
||||
this.userApiService.getInitialData().subscribe({
|
||||
next: data => {
|
||||
this.currentUser.set(data.currentUser);
|
||||
this._users.set(data.users);
|
||||
this._friends.set(data.friends);
|
||||
// Combine received and sent requests if they come separated, or use the array if unified
|
||||
this._friendRequests.set(data.friendRequests || []);
|
||||
this._bets.set(data.userBets || data.bets || []);
|
||||
this._publicBets.set(data.publicBets);
|
||||
this._groups.set(data.groups);
|
||||
},
|
||||
error: err => console.error("Failed to load initial data", err)
|
||||
});
|
||||
async loadUserBets(): Promise<void> {
|
||||
try {
|
||||
const bets = await firstValueFrom(this.betApiService.getUserBets());
|
||||
this._bets.set(bets);
|
||||
} catch (error) {
|
||||
console.error('Failed to load user bets', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadPublicBets(): Promise<void> {
|
||||
try {
|
||||
const bets = await firstValueFrom(this.betApiService.getPublicBets());
|
||||
this._publicBets.set(bets);
|
||||
} catch (error) {
|
||||
console.error('Failed to load public bets', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadFriends(): Promise<void> {
|
||||
try {
|
||||
const friends = await firstValueFrom(this.friendRequestApiService.getFriends());
|
||||
this._friends.set(friends);
|
||||
} catch (error) {
|
||||
console.error('Failed to load friends', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadFriendRequests(): Promise<void> {
|
||||
try {
|
||||
const requests = await firstValueFrom(this.friendRequestApiService.getPendingRequests());
|
||||
this._friendRequests.set(requests);
|
||||
} catch (error) {
|
||||
console.error('Failed to load friend requests', error);
|
||||
}
|
||||
}
|
||||
|
||||
async loadGroups(): Promise<void> {
|
||||
try {
|
||||
const groups = await firstValueFrom(this.groupApiService.getGroups());
|
||||
this._groups.set(groups);
|
||||
} catch (error) {
|
||||
console.error('Failed to load groups', error);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Utility ---
|
||||
|
||||
getGroupById(id: string): Group | undefined {
|
||||
return this._groups().find(g => g.id === id);
|
||||
}
|
||||
@@ -75,6 +112,8 @@ export class DataService {
|
||||
return this._bets().filter(b => b.groupId === groupId);
|
||||
}
|
||||
|
||||
// --- Mutations ---
|
||||
|
||||
async sendFriendRequest(identifier: string): Promise<{ success: boolean, message: string }> {
|
||||
try {
|
||||
const newRequest = await firstValueFrom(this.friendRequestApiService.sendRequest({ identifier }));
|
||||
@@ -90,7 +129,7 @@ export class DataService {
|
||||
await firstValueFrom(this.friendRequestApiService.cancelRequest(requestId));
|
||||
this._friendRequests.update(reqs => reqs.filter(r => r.id !== requestId));
|
||||
} catch (error) {
|
||||
console.error("Failed to cancel friend request", error);
|
||||
console.error('Failed to cancel friend request', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +139,7 @@ export class DataService {
|
||||
this._friends.update(f => [...f, newFriend]);
|
||||
this._friendRequests.update(reqs => reqs.filter(r => r.id !== request.id));
|
||||
} catch (error) {
|
||||
console.error("Failed to accept friend request", error);
|
||||
console.error('Failed to accept friend request', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +148,7 @@ export class DataService {
|
||||
await firstValueFrom(this.friendRequestApiService.declineRequest(requestId));
|
||||
this._friendRequests.update(reqs => reqs.filter(r => r.id !== requestId));
|
||||
} catch (error) {
|
||||
console.error("Failed to decline friend request", error);
|
||||
console.error('Failed to decline friend request', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,7 +158,7 @@ export class DataService {
|
||||
const newGroup = await firstValueFrom(this.groupApiService.createGroup({ name, members: memberIds }));
|
||||
this._groups.update(groups => [...groups, newGroup]);
|
||||
} catch (error) {
|
||||
console.error("Failed to add group", error);
|
||||
console.error('Failed to add group', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +167,7 @@ export class DataService {
|
||||
const updatedGroup = await firstValueFrom(this.groupApiService.removeMember(groupId, userId));
|
||||
this._groups.update(groups => groups.map(g => g.id === groupId ? updatedGroup : g));
|
||||
} catch (error) {
|
||||
console.error("Failed to remove user from group", error);
|
||||
console.error('Failed to remove user from group', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +177,7 @@ export class DataService {
|
||||
const updatedGroup = await firstValueFrom(this.groupApiService.addMembers(groupId, { userIds }));
|
||||
this._groups.update(groups => groups.map(g => g.id === groupId ? updatedGroup : g));
|
||||
} catch (error) {
|
||||
console.error("Failed to add users to group", error);
|
||||
console.error('Failed to add users to group', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,9 +185,8 @@ export class DataService {
|
||||
try {
|
||||
const updatedUser = await firstValueFrom(this.userApiService.updateCurrentUser(data));
|
||||
this.currentUser.set(updatedUser);
|
||||
this._users.update(users => users.map(u => u.id === updatedUser.id ? updatedUser : u));
|
||||
} catch (error) {
|
||||
console.error("Failed to update user", error);
|
||||
console.error('Failed to update user', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +218,7 @@ export class DataService {
|
||||
}));
|
||||
this._bets.update(bets => [newBet, ...bets]);
|
||||
} catch (error) {
|
||||
console.error("Failed to add bet", error);
|
||||
console.error('Failed to add bet', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import Keycloak from 'keycloak-js';
|
||||
|
||||
export const KEYCLOAK_INSTANCE = new InjectionToken<Keycloak>('KeycloakInstance');
|
||||
@@ -2,7 +2,6 @@ import { Component, ChangeDetectionStrategy, output, inject, signal, computed, O
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../core/services/data.service';
|
||||
import { BetParticipant, BetRole } from '../../core/models/bet.model';
|
||||
import { Group } from '../../core/models/group.model';
|
||||
import { User } from '../../core/models/user.model';
|
||||
import { getUserId } from '../../core/utils/type-guards';
|
||||
|
||||
@@ -51,6 +50,9 @@ export class CreateBetComponent implements OnInit {
|
||||
});
|
||||
|
||||
ngOnInit() {
|
||||
this.dataService.loadFriends();
|
||||
this.dataService.loadGroups();
|
||||
|
||||
const user = this.currentUser();
|
||||
if (user) {
|
||||
this.participants.set([{ user, role: BetRole.Participant }]);
|
||||
@@ -59,13 +61,13 @@ export class CreateBetComponent implements OnInit {
|
||||
|
||||
addParticipant(friend: User) {
|
||||
this.participants.update(p => [...p, { user: friend, role: BetRole.Participant }]);
|
||||
this.groupId.set(undefined); // Reset group if participants are manually changed
|
||||
this.groupId.set(undefined);
|
||||
this.showFriendSelector.set(false);
|
||||
}
|
||||
|
||||
removeParticipant(userId: string) {
|
||||
this.participants.update(p => p.filter(participant => getUserId(participant.user) !== userId));
|
||||
this.groupId.set(undefined); // Reset group if participants are manually changed
|
||||
this.groupId.set(undefined);
|
||||
}
|
||||
|
||||
updateParticipantRole(userId: string, newRole: BetRole) {
|
||||
@@ -94,7 +96,6 @@ export class CreateBetComponent implements OnInit {
|
||||
this.participants.set(groupParticipants);
|
||||
}
|
||||
} else {
|
||||
// Reset to just the current user if "Nessun gruppo" is selected
|
||||
const user = this.currentUser();
|
||||
if (user) {
|
||||
this.participants.set([{ user, role: BetRole.Participant }]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Component, ChangeDetectionStrategy, inject } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, inject, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../core/services/data.service';
|
||||
import { BetCardComponent } from '../../shared/components/bet-card/bet-card.component';
|
||||
@@ -10,7 +10,11 @@ import { BetCardComponent } from '../../shared/components/bet-card/bet-card.comp
|
||||
templateUrl: './discover.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DiscoverComponent {
|
||||
export class DiscoverComponent implements OnInit {
|
||||
private dataService = inject(DataService);
|
||||
publicBets = this.dataService.getPublicBets;
|
||||
}
|
||||
readonly publicBets = this.dataService.getPublicBets;
|
||||
|
||||
ngOnInit() {
|
||||
this.dataService.loadPublicBets();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import { Component, ChangeDetectionStrategy, input, output, inject, signal, computed, effect } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, input, output, inject, signal, computed } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../../core/services/data.service';
|
||||
import { Bet, BetStatus } from '../../../core/models/bet.model';
|
||||
import { Group } from '../../../core/models/group.model';
|
||||
import { User } from '../../../core/models/user.model';
|
||||
import { BetCardComponent } from '../../../shared/components/bet-card/bet-card.component';
|
||||
import { DataService } from '@/src/core/services/data.service';
|
||||
import { Bet, BetStatus } from '@/src/core/models/bet.model';
|
||||
import { BetCardComponent } from '@/src/shared/components/bet-card/bet-card.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-group-detail',
|
||||
@@ -22,30 +20,21 @@ export class GroupDetailComponent {
|
||||
private allGroups = this.dataService.userGroups;
|
||||
group = computed(() => this.allGroups().find(g => g.id === this.groupId()) ?? null);
|
||||
|
||||
groupBets = signal<Bet[]>([]);
|
||||
readonly groupBets = computed(() => this.dataService.getBetsByGroupId(this.groupId()));
|
||||
|
||||
isAddingMembers = signal(false);
|
||||
selectedFriendIds = signal<{ [key: string]: boolean }>({});
|
||||
|
||||
constructor() {
|
||||
effect(() => {
|
||||
const id = this.groupId();
|
||||
if (id) {
|
||||
this.groupBets.set(this.dataService.getBetsByGroupId(id));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sortedMembers = computed(() => {
|
||||
return this.group()?.members.slice().sort((a, b) => b.score - a.score) ?? [];
|
||||
});
|
||||
|
||||
activeBets = computed(() => {
|
||||
return this.groupBets().filter(b => b.status === BetStatus.Active || b.status === BetStatus.AwaitingValidation);
|
||||
return this.groupBets().filter((b: Bet) => b.status === BetStatus.Active || b.status === BetStatus.AwaitingValidation);
|
||||
});
|
||||
|
||||
pastBets = computed(() => {
|
||||
return this.groupBets().filter(b => b.status === BetStatus.Completed || b.status === BetStatus.Expired);
|
||||
return this.groupBets().filter((b: Bet) => b.status === BetStatus.Completed || b.status === BetStatus.Expired);
|
||||
});
|
||||
|
||||
availableFriends = computed(() => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, computed } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, computed, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../core/services/data.service';
|
||||
import { User } from '../../core/models/user.model';
|
||||
import { GroupDetailComponent } from './group-detail/group-detail.component';
|
||||
|
||||
@Component({
|
||||
@@ -11,7 +10,7 @@ import { GroupDetailComponent } from './group-detail/group-detail.component';
|
||||
templateUrl: './groups.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class GroupsComponent {
|
||||
export class GroupsComponent implements OnInit {
|
||||
private dataService = inject(DataService);
|
||||
|
||||
groups = this.dataService.userGroups;
|
||||
@@ -27,6 +26,12 @@ export class GroupsComponent {
|
||||
return this.newGroupName().trim().length > 0 && Object.keys(this.selectedFriendIds()).length > 0;
|
||||
});
|
||||
|
||||
ngOnInit() {
|
||||
this.dataService.loadGroups();
|
||||
this.dataService.loadFriends();
|
||||
this.dataService.loadUserBets();
|
||||
}
|
||||
|
||||
selectGroup(groupId: string) {
|
||||
this.selectedGroupId.set(groupId);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../core/services/data.service';
|
||||
import { Bet } from '../../core/models/bet.model';
|
||||
import { BetsByTimeComponent } from './bets-by-time/bets-by-time.component';
|
||||
import { BetsByGroupComponent } from './bets-by-group/bets-by-group.component';
|
||||
|
||||
@@ -16,12 +15,10 @@ export class HomeComponent implements OnInit {
|
||||
private dataService = inject(DataService);
|
||||
|
||||
viewMode = signal<'time' | 'group'>('time');
|
||||
allBets = signal<Bet[]>([]);
|
||||
readonly allBets = this.dataService.getActiveBets;
|
||||
|
||||
ngOnInit() {
|
||||
// FIX: getActiveBets is a computed signal which returns a value when called once.
|
||||
// The extra parentheses caused an error because it tried to execute an array.
|
||||
this.allBets.set(this.dataService.getActiveBets());
|
||||
this.dataService.loadUserBets();
|
||||
}
|
||||
|
||||
setViewMode(mode: 'time' | 'group') {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- Header with Logout -->
|
||||
<header class="flex justify-between items-center mb-4">
|
||||
<h1 class="text-3xl font-bold">Profilo</h1>
|
||||
<button title="Logout" class="p-2 text-slate-400 hover:text-white rounded-full hover:bg-slate-700 transition-colors">
|
||||
<button (click)="logout()" title="Logout" class="p-2 text-slate-400 hover:text-white rounded-full hover:bg-slate-700 transition-colors">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
|
||||
</svg>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, ChangeDetectionStrategy, inject, signal } from '@angular/core';
|
||||
import { Component, ChangeDetectionStrategy, inject, signal, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DataService } from '../../core/services/data.service';
|
||||
import { AuthService } from '../../core/services/auth.service';
|
||||
import { EditProfileComponent } from './edit-profile/edit-profile.component';
|
||||
import { FriendRequest } from '../../core/models/friend-request.model';
|
||||
|
||||
@@ -11,8 +12,9 @@ import { FriendRequest } from '../../core/models/friend-request.model';
|
||||
templateUrl: './profile.component.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ProfileComponent {
|
||||
export class ProfileComponent implements OnInit {
|
||||
private dataService = inject(DataService);
|
||||
private authService = inject(AuthService);
|
||||
|
||||
currentUser = this.dataService.currentUser;
|
||||
friends = this.dataService.friends;
|
||||
@@ -26,6 +28,12 @@ export class ProfileComponent {
|
||||
addFriendError = signal<string | null>(null);
|
||||
addFriendSuccess = signal<string | null>(null);
|
||||
|
||||
ngOnInit() {
|
||||
this.dataService.loadCurrentUser();
|
||||
this.dataService.loadFriends();
|
||||
this.dataService.loadFriendRequests();
|
||||
}
|
||||
|
||||
toggleEditMode() {
|
||||
this.isEditing.update(val => !val);
|
||||
}
|
||||
@@ -74,4 +82,8 @@ export class ProfileComponent {
|
||||
declineRequest(requestId: string) {
|
||||
this.dataService.declineFriendRequest(requestId);
|
||||
}
|
||||
|
||||
logout() {
|
||||
this.authService.logout();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user