Created initial menu structure

This commit is contained in:
Jim Martens 2023-11-12 15:40:46 +01:00
parent fbd602e18a
commit 30cc326168
14 changed files with 261 additions and 67 deletions

6
.run/Debug.run.xml Normal file
View File

@ -0,0 +1,6 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug" type="JavascriptDebugType" uri="http://localhost:4200"
useFirstLineBreakpoints="true">
<method v="2"/>
</configuration>
</component>

View File

@ -1,8 +1,9 @@
{ {
"name": "tsw-timetable-frontend", "name": "tsw-timetable-frontend",
"version": "0.0.1", "version": "0.0.1",
"author": "Ionic Framework", "author": "Jim Martens",
"homepage": "https://ionicframework.com/", "homepage": "https://2martens.de/",
"description": "Frontend for timetable application",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start": "ng serve",
@ -76,6 +77,5 @@
"ts-node": "^8.3.0", "ts-node": "^8.3.0",
"typescript": "~5.0.2", "typescript": "~5.0.2",
"webpack-bundle-analyzer": "^4.9.0" "webpack-bundle-analyzer": "^4.9.0"
}, }
"description": "An Ionic project"
} }

View File

@ -1,26 +1,36 @@
<ion-app> <ion-app>
<ion-split-pane contentId="main-content"> <ion-split-pane when="md" contentId="main-content">
<ion-menu contentId="main-content" type="overlay"> <ion-menu contentId="main-content" type="overlay">
<ion-content> <ion-content>
<ion-list id="inbox-list"> <ion-list id="menu-list">
<ion-list-header>Inbox</ion-list-header> <ion-list-header i18n>Timetable</ion-list-header>
<ion-note>hi@ionicframework.com</ion-note> <ion-note *ngIf="isLoggedIn$ | async">{{ username$ | async }}</ion-note>
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index"> <ng-container *ngIf="isLoggedIn$ | async; else loggedOut">
<ion-item routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" <ion-item *ngFor="let p of accountPages; let i = index"
routerLinkActive="selected"> [routerDirection]="'root'" [routerLink]="[p.url]" [routerLinkActiveOptions]="{exact: true}"
lines="none" [detail]="false"
[routerLinkActive]="'selected'" [ariaCurrentWhenActive]="'page'" [button]="true">
<ion-icon aria-hidden="true" [slot]="'start'" [ios]="p.icon + '-outline'"
[md]="p.icon + '-sharp'"></ion-icon>
<ion-label>{{ p.title }}</ion-label>
</ion-item>
</ng-container>
<ion-item-divider></ion-item-divider>
<ng-template #loggedOut>
<ion-item *ngFor="let p of loggedOutPages; let i = index"
routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false"
routerLinkActive="selected" [routerLinkActiveOptions]="{exact: true}" [button]="true">
<ion-icon aria-hidden="true" slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon> <ion-icon aria-hidden="true" slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
<ion-label>{{ p.title }}</ion-label> <ion-label>{{ p.title }}</ion-label>
</ion-item> </ion-item>
</ion-menu-toggle> </ng-template>
</ion-list>
<ion-list id="labels-list" *ngIf="labels.length > 0"> <ion-item *ngFor="let p of appPages; let i = index"
<ion-list-header>Labels</ion-list-header> routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false"
routerLinkActive="selected" [routerLinkActiveOptions]="{exact: true}" [button]="true">
<ion-item *ngFor="let label of labels" lines="none"> <ion-icon aria-hidden="true" slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
<ion-icon aria-hidden="true" slot="start" ios="bookmark-outline" md="bookmark-sharp"></ion-icon> <ion-label>{{ p.title }}</ion-label>
<ion-label>{{ label }}</ion-label>
</ion-item> </ion-item>
</ion-list> </ion-list>
</ion-content> </ion-content>

View File

@ -1,11 +1,13 @@
import {CommonModule} from '@angular/common'; import {AsyncPipe, NgFor, NgIf} from '@angular/common';
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {RouterLink, RouterLinkActive} from '@angular/router'; import {RouterLink, RouterLinkActive} from '@angular/router';
import { import {
IonApp, IonApp,
IonContent, IonContent,
IonHeader,
IonIcon, IonIcon,
IonItem, IonItem,
IonItemDivider,
IonLabel, IonLabel,
IonList, IonList,
IonListHeader, IonListHeader,
@ -13,61 +15,74 @@ import {
IonMenuToggle, IonMenuToggle,
IonNote, IonNote,
IonRouterOutlet, IonRouterOutlet,
IonSplitPane IonSplitPane,
IonTitle,
IonToolbar
} from '@ionic/angular/standalone'; } from '@ionic/angular/standalone';
import {addIcons} from 'ionicons'; import {addIcons} from 'ionicons';
import { import {
archiveOutline, homeOutline,
archiveSharp, homeSharp,
bookmarkOutline, logInOutline,
bookmarkSharp, logInSharp,
heartOutline, logOutOutline,
heartSharp, logOutSharp,
mailOutline, readerOutline,
mailSharp, readerSharp,
paperPlaneOutline, shieldOutline,
paperPlaneSharp, shieldSharp
trashOutline,
trashSharp,
warningOutline,
warningSharp
} from 'ionicons/icons'; } from 'ionicons/icons';
import {KeycloakService} from "keycloak-angular";
import {from, Observable, of, switchMap} from "rxjs";
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
templateUrl: 'app.component.html', templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'], styleUrls: ['app.component.scss'],
standalone: true, standalone: true,
imports: [RouterLink, RouterLinkActive, CommonModule, IonApp, IonSplitPane, IonMenu, IonContent, IonList, imports: [RouterLink, RouterLinkActive, IonApp, IonSplitPane, IonMenu, IonContent, IonList,
IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet], IonListHeader, IonNote, IonMenuToggle, IonItem, IonIcon, IonLabel, IonRouterOutlet, NgIf, NgFor, AsyncPipe, IonItemDivider, IonHeader, IonToolbar, IonTitle],
}) })
export class AppComponent { export class AppComponent {
public appPages = [ public appPages = [
{title: 'Inbox', url: '/folder/inbox', icon: 'mail'}, {title: $localize`Dashboard`, url: '', icon: 'home'},
{title: 'Outbox', url: '/folder/outbox', icon: 'paper-plane'}, {title: $localize`Privacy Policy`, url: '/privacy-policy', icon: 'shield'},
{title: 'Favorites', url: '/folder/favorites', icon: 'heart'}, {title: $localize`Legal Notice`, url: '/legal-notice', icon: 'reader'},
{title: 'Archived', url: '/folder/archived', icon: 'archive'},
{title: 'Trash', url: '/folder/trash', icon: 'trash'},
{title: 'Spam', url: '/folder/spam', icon: 'warning'},
]; ];
public labels = []; public accountPages = [
{title: $localize`Logout`, url: '/logout', icon: 'log-out'},
];
public loggedOutPages = [
{title: $localize`Logout`, url: '/login', icon: 'log-in'},
]
public isLoggedIn$: Observable<boolean>;
public username$: Observable<string>;
constructor() { constructor(
private keycloakService: KeycloakService
) {
this.isLoggedIn$ = from(this.keycloakService.isLoggedIn());
this.username$ = this.isLoggedIn$.pipe(
switchMap(loggedIn => {
if (loggedIn) {
return of(this.keycloakService.getUsername());
} else {
return of('');
}
})
)
addIcons({ addIcons({
mailOutline, homeOutline,
mailSharp, homeSharp,
paperPlaneOutline, logInOutline,
paperPlaneSharp, logInSharp,
heartOutline, logOutOutline,
heartSharp, logOutSharp,
archiveOutline, shieldOutline,
archiveSharp, shieldSharp,
trashOutline, readerOutline,
trashSharp, readerSharp,
warningOutline,
warningSharp,
bookmarkOutline,
bookmarkSharp
}); });
} }
} }

View File

@ -1,15 +1,11 @@
import {Routes} from '@angular/router'; import {Routes} from '@angular/router';
import {AppAuthGuard} from "./auth/auth.guard";
export const ROOT_ROUTES: Routes = [ export const ROOT_ROUTES: Routes = [
{
path: '',
loadComponent: () => import("./dashboard/dashboard.component").then(mod => mod.DashboardComponent),
pathMatch: 'full',
},
{ {
path: 'permission-denied', path: 'permission-denied',
loadComponent: () => import("./permission-denied/permission-denied.component") loadComponent: () => import("./permission-denied/permission-denied.component")
.then(mod => mod.PermissionDeniedComponent) .then(mod => mod.PermissionDeniedComponent),
}, },
{ {
path: 'legal-notice', path: 'legal-notice',
@ -19,4 +15,18 @@ export const ROOT_ROUTES: Routes = [
path: 'privacy-policy', path: 'privacy-policy',
loadComponent: () => import("./privacy-policy/privacy-policy.component").then(mod => mod.PrivacyPolicyComponent) loadComponent: () => import("./privacy-policy/privacy-policy.component").then(mod => mod.PrivacyPolicyComponent)
}, },
{
path: 'login',
loadComponent: () => import("./login/login.component").then(mod => mod.LoginComponent)
},
{
path: 'logout',
loadComponent: () => import("./logout/logout.component").then(mod => mod.LogoutComponent),
canActivate: [AppAuthGuard]
},
{
path: '',
loadComponent: () => import("./dashboard/dashboard.component").then(mod => mod.DashboardComponent),
pathMatch: 'full',
},
]; ];

View File

@ -1,5 +1,13 @@
import {Component} from '@angular/core'; import {Component} from '@angular/core';
import {IonButtons, IonContent, IonHeader, IonMenuButton, IonTitle, IonToolbar} from "@ionic/angular/standalone"; import {
IonButtons,
IonContent,
IonHeader,
IonMenuButton,
IonMenuToggle,
IonTitle,
IonToolbar
} from "@ionic/angular/standalone";
@Component({ @Component({
selector: 'app-dashboard', selector: 'app-dashboard',
@ -12,7 +20,8 @@ import {IonButtons, IonContent, IonHeader, IonMenuButton, IonTitle, IonToolbar}
IonMenuButton, IonMenuButton,
IonTitle, IonTitle,
IonToolbar, IonToolbar,
IonContent IonContent,
IonMenuToggle
] ]
}) })
export class DashboardComponent { export class DashboardComponent {

View File

@ -0,0 +1,22 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title i18n="page title">Timetable</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large" i18n="page title">Timetable</ion-title>
</ion-toolbar>
</ion-header>
<div id="container">
<p i18n="welcome text|A welcome to users">
TODO
</p>
</div>
</ion-content>

View File

View File

@ -0,0 +1,24 @@
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {IonicModule} from '@ionic/angular';
import {LoginComponent} from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [IonicModule.forRoot()]
}).compileComponents();
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,26 @@
import {Component, OnInit} from '@angular/core';
import {IonButtons, IonContent, IonHeader, IonMenuButton, IonTitle, IonToolbar} from "@ionic/angular/standalone";
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss'],
imports: [
IonButtons,
IonContent,
IonHeader,
IonMenuButton,
IonTitle,
IonToolbar
],
standalone: true
})
export class LoginComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}

View File

@ -0,0 +1,22 @@
<ion-header [translucent]="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button></ion-menu-button>
</ion-buttons>
<ion-title i18n="page title">Timetable</ion-title>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
<ion-header collapse="condense">
<ion-toolbar>
<ion-title size="large" i18n="page title">Timetable</ion-title>
</ion-toolbar>
</ion-header>
<div id="container">
<p i18n="welcome text|A welcome to users">
TODO
</p>
</div>
</ion-content>

View File

View File

@ -0,0 +1,24 @@
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {IonicModule} from '@ionic/angular';
import {LogoutComponent} from './logout.component';
describe('LogoutComponent', () => {
let component: LogoutComponent;
let fixture: ComponentFixture<LogoutComponent>;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LogoutComponent],
imports: [IonicModule.forRoot()]
}).compileComponents();
fixture = TestBed.createComponent(LogoutComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,26 @@
import {Component, OnInit} from '@angular/core';
import {IonButtons, IonContent, IonHeader, IonMenuButton, IonTitle, IonToolbar} from "@ionic/angular/standalone";
@Component({
selector: 'app-logout',
templateUrl: './logout.component.html',
styleUrls: ['./logout.component.scss'],
imports: [
IonButtons,
IonContent,
IonHeader,
IonMenuButton,
IonTitle,
IonToolbar
],
standalone: true
})
export class LogoutComponent implements OnInit {
constructor() {
}
ngOnInit() {
}
}