Add scouthub-attivita-fe

This commit is contained in:
Lorenzo Sanesi
2026-07-23 19:35:45 +02:00
parent 2ee7d402b6
commit cfd2de4ca3
61 changed files with 11437 additions and 0 deletions
@@ -0,0 +1,99 @@
.header {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 16px;
padding: 16px 24px;
background: var(--color-header);
border-bottom: 1px solid var(--color-border);
}
.back-link {
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--color-text);
}
.back-arrow {
font-size: 20px;
}
.brand {
display: flex;
align-items: center;
gap: 10px;
color: var(--color-text);
}
.brand--right {
margin-left: auto;
}
.logo {
width: 34px;
height: 34px;
border-radius: 8px;
background: var(--color-primary);
color: #fff;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
font-size: 18px;
}
.header--back .logo {
width: 32px;
height: 32px;
}
.brand-name {
font-weight: 700;
font-size: 19px;
letter-spacing: 0.2px;
}
.nav-items {
display: flex;
gap: 4px;
margin-left: auto;
flex-wrap: wrap;
}
.nav-item {
cursor: pointer;
padding: 8px 14px;
border-radius: 8px;
font-weight: 600;
font-size: 15px;
color: var(--color-text);
background: transparent;
}
.nav-item--active {
color: var(--color-primary-text-strong);
background: var(--color-primary-soft);
}
.auth-section {
display: flex;
align-items: center;
gap: 10px;
}
.auth-name {
font-weight: 600;
font-size: 14px;
color: var(--color-text);
}
.btn-secondary--sm {
padding: 6px 12px;
font-size: 13px;
}
@@ -0,0 +1,38 @@
@if (isDetailOrForm()) {
<div class="header header--back">
<div class="back-link" (click)="back()">
<span class="back-arrow"></span>
<span>Indietro</span>
</div>
<div class="brand brand--right">
<div class="logo">S</div>
<span class="brand-name">Scouthub</span>
</div>
</div>
} @else {
<div class="header header--nav">
<a class="brand" routerLink="/">
<div class="logo">S</div>
<span class="brand-name">Scouthub</span>
</a>
<nav class="nav-items">
@for (item of navItems; track item.path) {
<a
class="nav-item"
[class.nav-item--active]="isActive(item.path)"
[routerLink]="item.path"
>
{{ item.label }}
</a>
}
</nav>
<div class="auth-section">
@if (isAuthenticated()) {
<span class="auth-name">{{ displayName() }}</span>
<div class="btn-secondary btn-secondary--sm" (click)="logout()">Esci</div>
} @else {
<div class="btn-secondary btn-secondary--sm" (click)="login()">Accedi</div>
}
</div>
</div>
}
@@ -0,0 +1,71 @@
import { Location } from '@angular/common';
import { Component, computed, inject, signal } from '@angular/core';
import { NavigationEnd, Router, RouterLink } from '@angular/router';
import { filter } from 'rxjs';
import Keycloak from 'keycloak-js';
import { KEYCLOAK_EVENT_SIGNAL } from 'keycloak-angular';
interface NavItem {
path: string;
label: string;
}
@Component({
selector: 'app-header',
imports: [RouterLink],
templateUrl: './header.html',
styleUrl: './header.css',
})
export class Header {
private readonly router = inject(Router);
private readonly location = inject(Location);
private readonly keycloak = inject(Keycloak);
private readonly keycloakEvent = inject(KEYCLOAK_EVENT_SIGNAL);
readonly currentUrl = signal(this.router.url);
readonly isAuthenticated = computed(() => {
this.keycloakEvent();
return !!this.keycloak.authenticated;
});
readonly displayName = computed(() => {
this.keycloakEvent();
const token = this.keycloak.tokenParsed;
return (token?.['name'] as string) ?? (token?.['preferred_username'] as string) ?? '';
});
readonly isDetailOrForm = computed(() => {
const url = this.currentUrl();
return url.startsWith('/attivita/dettaglio') || url.startsWith('/nuova-attivita') || url.startsWith('/modifica-attivita');
});
readonly navItems: NavItem[] = [
{ path: '/', label: 'Home' },
{ path: '/ricerca', label: 'Cerca' },
{ path: '/mie-attivita', label: 'Le mie attività' },
{ path: '/profilo', label: 'Profilo' },
];
constructor() {
this.router.events.pipe(filter((event) => event instanceof NavigationEnd)).subscribe(() => {
this.currentUrl.set(this.router.url);
});
}
isActive(path: string): boolean {
return path === '/' ? this.currentUrl() === '/' : this.currentUrl().startsWith(path);
}
back(): void {
this.location.back();
}
login(): void {
this.keycloak.login({ redirectUri: window.location.href });
}
logout(): void {
this.keycloak.logout({ redirectUri: window.location.origin + '/' });
}
}