v0.0.1 (download not working)
This commit is contained in:
@@ -2,6 +2,8 @@ import { Injectable } from '@angular/core';
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {Userdata, AuthKeys} from "../entities/userdata";
|
||||
import {firstValueFrom} from "rxjs";
|
||||
import {environment} from "../../environments/environment";
|
||||
import {Course} from "../entities/course";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@@ -10,7 +12,7 @@ export class IServService {
|
||||
|
||||
public userdata?: Userdata;
|
||||
public keys?: AuthKeys;
|
||||
public backend: string = "http://localhost:5273";
|
||||
public backend: string = environment.backend;
|
||||
|
||||
public courseNames: {[id: string]: string} = {
|
||||
["Bi"]: "Biologie",
|
||||
@@ -32,6 +34,14 @@ export class IServService {
|
||||
["Sf"]: "Seminarfach",
|
||||
["DS"]: "Darstellendes Spiel",
|
||||
};
|
||||
public colors: {name: string; val: string}[] = [
|
||||
{name: "Blau", val: "primary"},
|
||||
{name: "Hellblau", val: "secondary"},
|
||||
{name: "Lila", val: "tertiary"},
|
||||
{name: "Grün", val: "success"},
|
||||
{name: "Gelb", val: "warning"},
|
||||
{name: "Rot", val: "danger"}
|
||||
];
|
||||
|
||||
constructor(private client: HttpClient) {
|
||||
const data = localStorage.getItem("userdata");
|
||||
@@ -83,7 +93,13 @@ export class IServService {
|
||||
}
|
||||
}
|
||||
|
||||
public async getCoursesAndClass(groups?: string[]): Promise<{class: string, courses: string[]}> {
|
||||
public async getCoursesAndClass(groups?: string[]): Promise<{class: string, courses: Course[]}> {
|
||||
if (localStorage.getItem("courses") && localStorage.getItem("class")) {
|
||||
const courses = JSON.parse(localStorage.getItem("courses")) as Course[];
|
||||
const className = localStorage.getItem("class");
|
||||
return {courses, class: className};
|
||||
}
|
||||
|
||||
if (groups == undefined) {
|
||||
groups = await this.getGroups();
|
||||
}
|
||||
@@ -99,13 +115,37 @@ export class IServService {
|
||||
result.class = grades[0].replace("Jahrgang ", "").toUpperCase();
|
||||
}
|
||||
}
|
||||
localStorage.setItem("class", result.class);
|
||||
|
||||
for (let group of groups) {
|
||||
if (!group.includes(".") || !group.toLowerCase().startsWith("q")) continue;
|
||||
result.courses.push(group.split(".")[1]);
|
||||
}
|
||||
|
||||
return result;
|
||||
if (result.class.startsWith("Q")) {
|
||||
const courses: Course[] = [];
|
||||
for (let course of result.courses) {
|
||||
const short = course.substring(1, 3);
|
||||
const name = this.courseNames[short];
|
||||
if (name == undefined) continue;
|
||||
courses.push({
|
||||
id: course,
|
||||
short: short.toUpperCase(),
|
||||
name: name,
|
||||
color: this.colors[Math.floor(Math.random() * this.colors.length)].val
|
||||
});
|
||||
}
|
||||
courses.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
localStorage.setItem("courses", JSON.stringify(courses));
|
||||
return {class: result.class, courses};
|
||||
}
|
||||
|
||||
return {class: result.class, courses: []};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ export class AppComponent {
|
||||
{ title: 'Übersicht', url: '/home', icon: 'home' },
|
||||
{ title: 'E-Mail', url: '/mails', icon: 'mail' },
|
||||
{ title: 'Dateien', url: '/files', icon: 'folder' },
|
||||
{ title: 'Aufgaben', url: '/tasks', icon: 'clipboard' },
|
||||
{ title: 'Stundenplan', url: '/schedule', icon: 'grid' },
|
||||
{ title: 'Vertretungsplan', url: '/substitution', icon: 'list' },
|
||||
];
|
||||
|
||||
@@ -23,12 +23,12 @@ export const routes: Routes = [
|
||||
loadComponent: () => import('./pages/mails/mails.page').then( m => m.MailsPage)
|
||||
},
|
||||
{
|
||||
path: 'substitution',
|
||||
loadComponent: () => import('./pages/substitution/substitution.page').then( m => m.SubstitutionPage)
|
||||
path: 'mails/:id',
|
||||
loadComponent: () => import('./pages/mails/mails.page').then( m => m.MailsPage)
|
||||
},
|
||||
{
|
||||
path: 'tasks',
|
||||
loadComponent: () => import('./pages/tasks/tasks.page').then( m => m.TasksPage)
|
||||
path: 'substitution',
|
||||
loadComponent: () => import('./pages/substitution/substitution.page').then( m => m.SubstitutionPage)
|
||||
},
|
||||
{
|
||||
path: 'schedule',
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<span
|
||||
class="lesson-icon ion-text-center"
|
||||
[style]="'--background: var(--ion-color-' + findCourse(lesson?.course)?.color + '); --foreground: var(--ion-color-' + findCourse(lesson?.course)?.color + '-contrast)'"
|
||||
>
|
||||
{{findCourse(lesson?.course)?.short || "PH"}}
|
||||
</span>
|
||||
</ion-card-header>
|
||||
<ion-card-content class="ion-text-center">
|
||||
<ion-label>{{lesson?.room || "⠀ ⠀"}}</ion-label>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
@@ -0,0 +1,8 @@
|
||||
ion-card {
|
||||
margin: 10px 0 0;
|
||||
width: 100%;
|
||||
|
||||
ion-card-header, ion-card-content {
|
||||
padding: 7px;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import {Component, Input} from '@angular/core';
|
||||
import {IonicModule} from "@ionic/angular";
|
||||
import {Course, Lesson} from "../../entities/course";
|
||||
|
||||
@Component({
|
||||
selector: 'lesson',
|
||||
templateUrl: './lesson.component.html',
|
||||
styleUrls: ['./lesson.component.scss'],
|
||||
imports: [
|
||||
IonicModule
|
||||
],
|
||||
standalone: true
|
||||
})
|
||||
export class LessonComponent {
|
||||
|
||||
@Input('courses') courses: Course[];
|
||||
@Input('lesson') lesson: Lesson;
|
||||
|
||||
public findCourse(id: string): Course {
|
||||
for (let course of this.courses)
|
||||
if (course.id == id) return course;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<ion-item class="mail pointer">
|
||||
<div>
|
||||
<div>
|
||||
<ion-icon ios="mail-unread-outline" md="mail-unread-sharp" *ngIf="!message.read" />
|
||||
<ion-label>{{message.sender.displayName}}</ion-label>
|
||||
<ion-label class="date">{{message.time.toLocaleDateString()}}</ion-label>
|
||||
</div>
|
||||
<ion-label class="subject">{{message.subject}}</ion-label>
|
||||
</div>
|
||||
</ion-item>
|
||||
@@ -0,0 +1,25 @@
|
||||
.mail > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
margin-block: 15px;
|
||||
width: 100%;
|
||||
|
||||
.subject, .date {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
|
||||
ion-icon {
|
||||
margin-right: 5px;
|
||||
align-self: center;
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
.date {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
BetterIServ.Mobile/src/app/components/mail/mail.component.ts
Normal file
20
BetterIServ.Mobile/src/app/components/mail/mail.component.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {IonicModule} from "@ionic/angular";
|
||||
import {MailContent} from "../../entities/mail";
|
||||
import {NgIf} from "@angular/common";
|
||||
|
||||
@Component({
|
||||
selector: 'mail',
|
||||
templateUrl: './mail.component.html',
|
||||
styleUrls: ['./mail.component.scss'],
|
||||
imports: [
|
||||
IonicModule,
|
||||
NgIf
|
||||
],
|
||||
standalone: true
|
||||
})
|
||||
export class MailComponent {
|
||||
|
||||
@Input('mail') message: MailContent;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<ion-card
|
||||
class="subs {{subs?.type.replace(' ', '').replace('.', '')}}"
|
||||
>
|
||||
<ion-card-content>
|
||||
<ion-label class="times">{{subs?.times.join(" - ")}}</ion-label>
|
||||
<div>
|
||||
<ion-label class="type">{{subs?.type}}</ion-label>
|
||||
<ion-label class="desc" [innerHtml]="getDetails(subs)"></ion-label>
|
||||
</div>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
@@ -0,0 +1,44 @@
|
||||
.subs {
|
||||
ion-card-content {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
color: #FFF;
|
||||
|
||||
.times {
|
||||
font-size: 25px;
|
||||
line-height: 25px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
div {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
.type {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.Raumtausch {
|
||||
--background: var(--ion-color-success-shade);
|
||||
}
|
||||
|
||||
&.bittebeachten, &.stregulärem {
|
||||
--background: var(--ion-color-tertiary-shade);
|
||||
}
|
||||
|
||||
&.Vertretung {
|
||||
--background: var(--ion-color-primary-shade);
|
||||
}
|
||||
|
||||
&.Entfall, &.Stillarbeit {
|
||||
--background: var(--ion-color-danger-shade);
|
||||
}
|
||||
|
||||
&.Verlegung {
|
||||
--background: var(--ion-color-warning-shade);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import {Component, Input, OnInit} from '@angular/core';
|
||||
import {Substitution} from "../../entities/substitution";
|
||||
import {IonicModule} from "@ionic/angular";
|
||||
|
||||
@Component({
|
||||
selector: 'substitution',
|
||||
templateUrl: './substitution.component.html',
|
||||
styleUrls: ['./substitution.component.scss'],
|
||||
imports: [
|
||||
IonicModule
|
||||
],
|
||||
standalone: true
|
||||
})
|
||||
export class SubstitutionComponent {
|
||||
|
||||
@Input('subs') subs: Substitution;
|
||||
|
||||
public getDetails(subs: Substitution): string {
|
||||
if (subs.type == "bitte beachten") {
|
||||
const desc = subs.description != " " ? ' - ' + subs.description : "";
|
||||
let info = `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
if (subs.lesson != subs.newLesson) {
|
||||
info = `${subs.newLesson} (${subs.representative}) statt ${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
}
|
||||
|
||||
return info + desc;
|
||||
}
|
||||
|
||||
switch (subs.type) {
|
||||
case "Vertretung":
|
||||
case "st. regulärem Unt.":
|
||||
return `${subs.lesson} (${subs.representative} statt ${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Raumtausch":
|
||||
return `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Entfall":
|
||||
return `${subs.lesson} (${subs.teacher})`;
|
||||
|
||||
case "Stillarbeit":
|
||||
return `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Verlegung":
|
||||
return `${subs.newLesson} (${subs.representative}) statt ${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
default:
|
||||
return subs.lesson + ' (' + subs.teacher + ') ' + subs.room;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,4 +13,64 @@
|
||||
<ion-title size="large">Übersicht</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Willkommen {{iserv.userdata?.username}}</ion-card-title>
|
||||
<ion-card-subtitle *ngIf="classData != undefined">
|
||||
Du hast {{unreadMails?.length || 0}} ungelesene E-Mails<br>
|
||||
<span>{{classData?.class.startsWith('Q') ? 'Jahrgang' : 'Klasse'}} {{classData?.class}}</span><br>
|
||||
{{dayName}} der {{today?.toLocaleDateString()}}
|
||||
</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Stundenplan</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content class="lesson-content">
|
||||
<ion-card *ngIf="lessons == undefined && classData != undefined">
|
||||
<ion-card-header>
|
||||
<ion-card-subtitle>Kein Unterricht</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
|
||||
<div class="lesson-container">
|
||||
<lesson *ngFor="let lesson of lessons" [lesson]="lesson" [courses]="classData.courses" />
|
||||
</div>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Vertretungsplan</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-card *ngIf="subs?.length == 0 && subs != undefined">
|
||||
<ion-card-header>
|
||||
<ion-card-subtitle>Keine Vertretungen</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
|
||||
<substitution *ngFor="let sub of subs" [subs]="sub" />
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
|
||||
<ion-card>
|
||||
<ion-card-header>
|
||||
<ion-card-title>Ungelesene E-Mails</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-list>
|
||||
<ion-card *ngIf="unreadMails?.length == 0 && unreadMails != undefined">
|
||||
<ion-card-header>
|
||||
<ion-card-subtitle>Keine ungelesenen E-Mails</ion-card-subtitle>
|
||||
</ion-card-header>
|
||||
</ion-card>
|
||||
|
||||
<mail *ngFor="let mail of unreadMails" [mail]="mail" (click)="router.navigate(['mails/' + mail.id])" />
|
||||
</ion-list>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</ion-content>
|
||||
|
||||
@@ -1 +1,9 @@
|
||||
.lesson-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.lesson-content {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
@@ -2,18 +2,56 @@ import {Component, OnInit} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
import {IServService} from "../../api/iserv.service";
|
||||
import {MailService} from "../../api/mail.service";
|
||||
import {MailContent} from "../../entities/mail";
|
||||
import {UnitsService} from "../../api/units.service";
|
||||
import {Substitution} from "../../entities/substitution";
|
||||
import {SubstitutionComponent} from "../../components/substitution/substitution.component";
|
||||
import {MailComponent} from "../../components/mail/mail.component";
|
||||
import {Router} from "@angular/router";
|
||||
import {Course, Lesson, Timetable} from "../../entities/course";
|
||||
import {LessonComponent} from "../../components/lesson/lesson.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-home',
|
||||
templateUrl: './home.page.html',
|
||||
styleUrls: ['./home.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule]
|
||||
imports: [IonicModule, CommonModule, FormsModule, SubstitutionComponent, MailComponent, LessonComponent]
|
||||
})
|
||||
export class HomePage implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
public unreadMails: MailContent[];
|
||||
public today: Date;
|
||||
public dayName: string;
|
||||
public subs: Substitution[];
|
||||
public classData: {class: string, courses: Course[]};
|
||||
public lessons: Lesson[];
|
||||
|
||||
public constructor(public iserv: IServService, public mails: MailService, public units: UnitsService, public router: Router) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.today = new Date();
|
||||
this.dayName = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"][this.today.getDay()];
|
||||
const scheduleDay = [undefined, "mon", "tue", "wed", "thu", "fri", undefined][this.today.getDay()];
|
||||
|
||||
const mailPromise = this.mails.getMails("INBOX", 0);
|
||||
const classPromise = this.iserv.getCoursesAndClass();
|
||||
const subsPromise = this.units.getSubstitutionPlan("today");
|
||||
await Promise.all([mailPromise, classPromise, subsPromise]);
|
||||
|
||||
this.unreadMails = (await mailPromise).filter(mail => !mail.read);
|
||||
this.classData = await classPromise;
|
||||
this.subs = (await subsPromise).substitutions.filter(subs => subs.classes.includes(this.classData.class));
|
||||
|
||||
if (this.classData.class.startsWith("Q")) {
|
||||
this.subs = this.subs.filter(subs => this.classData.courses.filter(course => course.id == subs.lesson).length > 0);
|
||||
}
|
||||
|
||||
if (scheduleDay != undefined && localStorage.getItem("timetable")) {
|
||||
this.lessons = (JSON.parse(localStorage.getItem("timetable")) as Timetable)[scheduleDay].filter(lesson => lesson != undefined);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
<ion-card-title>IServ Anmeldedaten</ion-card-title>
|
||||
</ion-card-header>
|
||||
<ion-card-content>
|
||||
<ion-input label="E-Mail" type="email" #email/>
|
||||
<ion-input label="Passwort" type="password" #password/>
|
||||
<ion-button (click)="onLogin(email.value?.toString(), password.value?.toString())">Einloggen</ion-button>
|
||||
<form (submit)="$event.preventDefault(); onLogin(email.value?.toString(), password.value?.toString())">
|
||||
<ion-input label="E-Mail" type="email" #email/>
|
||||
<ion-input label="Passwort" type="password" #password/>
|
||||
<ion-button type="submit">Einloggen</ion-button>
|
||||
</form>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
</div>
|
||||
|
||||
@@ -23,7 +23,9 @@ export class LoginPage implements OnInit {
|
||||
if (email == undefined || password == undefined) return;
|
||||
|
||||
if (await this.iservApi.login(email, password)) {
|
||||
await this.router.navigate(['home']);
|
||||
setTimeout(async () => {
|
||||
await this.router.navigate(['home']);
|
||||
}, 500);
|
||||
}else {
|
||||
const alert = await this.alerts.create({
|
||||
header: "Fehler",
|
||||
|
||||
@@ -53,16 +53,7 @@
|
||||
</ion-item>
|
||||
|
||||
<ion-list>
|
||||
<ion-item *ngFor="let message of mails" class="mail pointer" (click)="selectMail(message, mailModal)">
|
||||
<div>
|
||||
<div>
|
||||
<ion-icon ios="mail-unread-outline" md="mail-unread-sharp" *ngIf="!message.read" />
|
||||
<ion-label>{{message.sender.displayName}}</ion-label>
|
||||
<ion-label class="date">{{message.time.toLocaleDateString()}}</ion-label>
|
||||
</div>
|
||||
<ion-label class="subject">{{message.subject}}</ion-label>
|
||||
</div>
|
||||
</ion-item>
|
||||
<mail *ngFor="let message of mails" [mail]="message" (click)="selectMail(message, mailModal)" />
|
||||
</ion-list>
|
||||
<ion-infinite-scroll (ionInfinite)="loadMore($event)">
|
||||
<ion-infinite-scroll-content></ion-infinite-scroll-content>
|
||||
@@ -81,9 +72,9 @@
|
||||
</ion-header>
|
||||
<ion-content class="ion-padding current-mail">
|
||||
<div class="header">
|
||||
<ion-label class="subject">{{currentMail?.subject}}</ion-label>
|
||||
<ion-label class="subject">{{currentMail?.subject}}</ion-label><br>
|
||||
<ion-label class="time">{{currentMail?.time.toLocaleDateString()}}</ion-label>
|
||||
<ion-list *ngIf="currentMail?.attachments?.length > 0">
|
||||
<ion-list *ngIf="currentMail?.attachments?.length > 0" style="margin-top: 10px">
|
||||
<ion-item *ngFor="let attachment of currentMail?.attachments" class="pointer" (click)="downloadAttachment(attachment, currentMail?.id)">
|
||||
<ion-label>{{attachment}}</ion-label>
|
||||
</ion-item>
|
||||
|
||||
@@ -1,44 +1,3 @@
|
||||
.mail > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 7px;
|
||||
margin-block: 15px;
|
||||
width: 100%;
|
||||
|
||||
.subject, .date {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
|
||||
ion-icon {
|
||||
margin-right: 5px;
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
.date {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.current-mail {
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
.subject {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.time {
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.new-email {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {InfiniteScrollCustomEvent, IonicModule, IonModal, Platform, ToastController} from '@ionic/angular';
|
||||
import {IonicModule, IonModal, Platform, ToastController} from '@ionic/angular';
|
||||
import {MailService} from "../../api/mail.service";
|
||||
import {MailContent, MailFolder} from "../../entities/mail";
|
||||
import {marked} from "marked";
|
||||
import {HttpDownloadProgressEvent, HttpEventType} from "@angular/common/http";
|
||||
import {HttpEventType} from "@angular/common/http";
|
||||
import {File} from "@awesome-cordova-plugins/file/ngx";
|
||||
import {saveAs} from "file-saver";
|
||||
import {MailComponent} from "../../components/mail/mail.component";
|
||||
import {ActivatedRoute} from "@angular/router";
|
||||
|
||||
@Component({
|
||||
selector: 'app-mails',
|
||||
templateUrl: './mails.page.html',
|
||||
styleUrls: ['./mails.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule]
|
||||
imports: [IonicModule, CommonModule, FormsModule, MailComponent]
|
||||
})
|
||||
export class MailsPage implements OnInit {
|
||||
|
||||
@@ -25,7 +27,9 @@ export class MailsPage implements OnInit {
|
||||
private currentPage = 0;
|
||||
private currentFolder: MailFolder;
|
||||
|
||||
constructor(private mail: MailService, private platform: Platform, private toasts: ToastController) { }
|
||||
@ViewChild('mailModal') mailModal: IonModal;
|
||||
|
||||
constructor(private mail: MailService, private platform: Platform, private toasts: ToastController, private route: ActivatedRoute) { }
|
||||
|
||||
async ngOnInit() {
|
||||
this.showLoading = true;
|
||||
@@ -38,6 +42,14 @@ export class MailsPage implements OnInit {
|
||||
|
||||
this.currentFolder = this.folders.filter(folder => folder.name == "INBOX")[0];
|
||||
this.showLoading = false;
|
||||
|
||||
this.route.params.subscribe((params: {id: string}) => {
|
||||
if (params.id != undefined) {
|
||||
const id = Number(params.id);
|
||||
const email = this.mails.filter(mail => mail.id == id)[0];
|
||||
this.selectMail(email, this.mailModal);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public async changeFolder(folder: MailFolder) {
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
<ion-content class="ion-padding course-content">
|
||||
<ion-item>
|
||||
<ion-label position="stacked">Farbe</ion-label>
|
||||
<ion-select aria-label="Farbe" interface="action-sheet" [value]="colors[0].val" #color>
|
||||
<ion-select-option *ngFor="let color of colors" [value]="color.val">
|
||||
<ion-select aria-label="Farbe" interface="action-sheet" [value]="iserv.colors[0].val" #color>
|
||||
<ion-select-option *ngFor="let color of iserv.colors" [value]="color.val">
|
||||
{{color.name}}
|
||||
</ion-select-option>
|
||||
</ion-select>
|
||||
@@ -124,7 +124,7 @@
|
||||
<ion-card *ngFor="let course of courses" (click)="onEditOrAdd(course)">
|
||||
<ion-card-header>
|
||||
<span
|
||||
class="icon ion-text-center"
|
||||
class="lesson-icon ion-text-center"
|
||||
[style]="'--background: var(--ion-color-' + course.color + '); --foreground: var(--ion-color-' + course.color + '-contrast)'"
|
||||
>
|
||||
{{course.short}}
|
||||
@@ -146,19 +146,7 @@
|
||||
<ion-label *ngIf="day == 'thu'">Do</ion-label>
|
||||
<ion-label *ngIf="day == 'fri'">Fr</ion-label>
|
||||
|
||||
<ion-card *ngFor="let lesson of timetable[day] | week; let i = index" [ngClass]="{'hide': lesson == undefined}" (click)="onEditOrAdd(undefined, {lesson, day, time: i})">
|
||||
<ion-card-header>
|
||||
<span
|
||||
class="icon ion-text-center"
|
||||
[style]="'--background: var(--ion-color-' + findCourse(lesson?.course)?.color + '); --foreground: var(--ion-color-' + findCourse(lesson?.course)?.color + '-contrast)'"
|
||||
>
|
||||
{{findCourse(lesson?.course)?.short || "⠀ ⠀"}}
|
||||
</span>
|
||||
</ion-card-header>
|
||||
<ion-card-content class="ion-text-center">
|
||||
<ion-label>{{lesson?.room || "⠀ ⠀"}}</ion-label>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
<lesson *ngFor="let lesson of timetable[day] | week; let i = index" [ngClass]="{'hide': lesson == undefined}" (click)="onEditOrAdd(undefined, {lesson, day, time: i})" [lesson]="lesson" [courses]="courses" />
|
||||
</div>
|
||||
</section>
|
||||
</ion-content>
|
||||
|
||||
@@ -2,19 +2,6 @@
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
|
||||
.icon {
|
||||
margin-inline: auto;
|
||||
aspect-ratio: 1;
|
||||
width: max-content;
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
line-height: 20px;
|
||||
|
||||
background-color: var(--background);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
.courses {
|
||||
ion-item {
|
||||
--background: transparent;
|
||||
@@ -48,15 +35,6 @@
|
||||
flex-basis: 0;
|
||||
padding-block: 16px;
|
||||
}
|
||||
|
||||
ion-card {
|
||||
margin: 10px 0 0;
|
||||
width: 100%;
|
||||
|
||||
ion-card-header, ion-card-content {
|
||||
padding: 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hide {
|
||||
|
||||
@@ -5,13 +5,14 @@ import {IonicModule, IonModal} from '@ionic/angular';
|
||||
import {IServService} from "../../api/iserv.service";
|
||||
import {Course, Lesson, Timetable} from "../../entities/course";
|
||||
import {WeekPipe} from "../../pipes/week.pipe";
|
||||
import {LessonComponent} from "../../components/lesson/lesson.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-schedule',
|
||||
templateUrl: './schedule.page.html',
|
||||
styleUrls: ['./schedule.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule, WeekPipe]
|
||||
imports: [IonicModule, CommonModule, FormsModule, WeekPipe, LessonComponent]
|
||||
})
|
||||
export class SchedulePage implements OnInit {
|
||||
|
||||
@@ -20,48 +21,14 @@ export class SchedulePage implements OnInit {
|
||||
public currentCourse: Course;
|
||||
public timetable: Timetable = {mon: [], tue: [], wed: [], thu: [], fri: []};
|
||||
public currentLesson: {lesson: Lesson, day: string, time: number};
|
||||
public rerender: boolean = false;
|
||||
public colors: {name: string; val: string}[] = [
|
||||
{name: "Blau", val: "primary"},
|
||||
{name: "Hellblau", val: "secondary"},
|
||||
{name: "Lila", val: "tertiary"},
|
||||
{name: "Grün", val: "success"},
|
||||
{name: "Gelb", val: "warning"},
|
||||
{name: "Rot", val: "danger"}
|
||||
];
|
||||
|
||||
@ViewChild('courseModal') courseModal: IonModal;
|
||||
@ViewChild('tableModal') tableModal: IonModal;
|
||||
|
||||
constructor(private iserv: IServService) { }
|
||||
constructor(public iserv: IServService) { }
|
||||
|
||||
async ngOnInit() {
|
||||
if (localStorage.getItem("courses") == undefined) {
|
||||
const data = await this.iserv.getCoursesAndClass();
|
||||
|
||||
if (data.class.startsWith("Q")) {
|
||||
for (let course of data.courses) {
|
||||
const short = course.substring(1, 3);
|
||||
const name = this.iserv.courseNames[short];
|
||||
if (name == undefined) continue;
|
||||
this.courses.push({
|
||||
id: course,
|
||||
short: short.toUpperCase(),
|
||||
name: name,
|
||||
color: this.colors[Math.floor(Math.random() * this.colors.length)].val
|
||||
});
|
||||
}
|
||||
this.courses.sort((a, b) => {
|
||||
if (a.name < b.name) return -1;
|
||||
if (a.name > b.name) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
localStorage.setItem("courses", JSON.stringify(this.courses));
|
||||
}
|
||||
}else {
|
||||
this.courses = JSON.parse(localStorage.getItem("courses"));
|
||||
}
|
||||
this.courses = (await this.iserv.getCoursesAndClass()).courses;
|
||||
|
||||
if (localStorage.getItem("timetable") == undefined) {
|
||||
for (let day of ['mon', 'tue', 'wed', 'thu', 'fri']) {
|
||||
@@ -120,10 +87,4 @@ export class SchedulePage implements OnInit {
|
||||
location.reload();
|
||||
}
|
||||
|
||||
public findCourse(id: string): Course {
|
||||
for (let course of this.courses)
|
||||
if (course.id == id) return course;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -42,19 +42,7 @@
|
||||
<ion-checkbox justify="space-between" [(ngModel)]="filterByClasses" (ionChange)="showOnlyCourses(classes.checked)" #classes>Nur eigene Kurse anzeigen</ion-checkbox>
|
||||
</ion-item>
|
||||
|
||||
<ion-card
|
||||
*ngFor="let subs of data?.substitutions"
|
||||
class="subs {{subs.type.replace(' ', '').replace('.', '')}}"
|
||||
[ngClass]="{'hide': (subs.classes.indexOf(currentClass) == -1 && currentClass != 'all') || !hasClass(subs.lesson)}"
|
||||
>
|
||||
<ion-card-content>
|
||||
<ion-label class="times">{{subs.times.join(" - ")}}</ion-label>
|
||||
<div>
|
||||
<ion-label class="type">{{subs.type}}</ion-label>
|
||||
<ion-label class="desc" [innerHtml]="getDetails(subs)"></ion-label>
|
||||
</div>
|
||||
</ion-card-content>
|
||||
</ion-card>
|
||||
<substitution *ngFor="let subs of data?.substitutions" [subs]="subs" [ngClass]="{'hide': (subs.classes.indexOf(currentClass) == -1 && currentClass != 'all') || !hasClass(subs.lesson)}" />
|
||||
</section>
|
||||
</ion-content>
|
||||
|
||||
|
||||
@@ -1,48 +1,3 @@
|
||||
.subs {
|
||||
ion-card-content {
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
color: #FFF;
|
||||
|
||||
.times {
|
||||
font-size: 25px;
|
||||
line-height: 25px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
div {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
|
||||
.type {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.Raumtausch {
|
||||
--background: var(--ion-color-success-shade);
|
||||
}
|
||||
|
||||
&.bittebeachten, &.stregulärem {
|
||||
--background: var(--ion-color-tertiary-shade);
|
||||
}
|
||||
|
||||
&.Vertretung {
|
||||
--background: var(--ion-color-primary-shade);
|
||||
}
|
||||
|
||||
&.Entfall, &.Stillarbeit {
|
||||
--background: var(--ion-color-danger-shade);
|
||||
}
|
||||
|
||||
&.Verlegung {
|
||||
--background: var(--ion-color-warning-shade);
|
||||
}
|
||||
|
||||
&.hide {
|
||||
display: none;
|
||||
}
|
||||
.hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import {Component, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {AlertController, IonicModule} from '@ionic/angular';
|
||||
import {UnitsService} from "../../api/units.service";
|
||||
import {Substitution, UnitsData} from "../../entities/substitution";
|
||||
import {UnitsData} from "../../entities/substitution";
|
||||
import {IServService} from "../../api/iserv.service";
|
||||
import {Course} from "../../entities/course";
|
||||
import {SubstitutionComponent} from "../../components/substitution/substitution.component";
|
||||
|
||||
@Component({
|
||||
selector: 'app-substitution',
|
||||
templateUrl: './substitution.page.html',
|
||||
styleUrls: ['./substitution.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule]
|
||||
imports: [IonicModule, CommonModule, FormsModule, SubstitutionComponent]
|
||||
})
|
||||
export class SubstitutionPage implements OnInit {
|
||||
|
||||
@@ -42,29 +42,23 @@ export class SubstitutionPage implements OnInit {
|
||||
this.data = await this.units.getSubstitutionPlan("today");
|
||||
|
||||
const data = await this.iserv.getCoursesAndClass();
|
||||
if (localStorage.getItem("class") == null) {
|
||||
if (!data.class.startsWith("Q")) {
|
||||
this.changeClass(data.class);
|
||||
}else {
|
||||
this.changeClass(data.class);
|
||||
if (data.class.startsWith("Q")) {
|
||||
for (let course of data.courses) {
|
||||
this.courses.push(course.id);
|
||||
}
|
||||
|
||||
if (localStorage.getItem("filterByClasses") == null) {
|
||||
this.showOnlyCourses(true);
|
||||
this.filterByClasses = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.class.startsWith("Q")) {
|
||||
if (localStorage.getItem("courses") != undefined) {
|
||||
const courses = JSON.parse(localStorage.getItem("courses")) as Course[];
|
||||
for (let course of courses) {
|
||||
this.courses.push(course.id);
|
||||
if (data.courses.length == 0) {
|
||||
const alert = await this.alerts.create({
|
||||
header: "Achtung",
|
||||
message: "Füge deine Kurse im Stundenplan hinzu um sie hier zu filtern!",
|
||||
buttons: ["Ok"]
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
}else {
|
||||
const alert = await this.alerts.create({
|
||||
header: "Achtung",
|
||||
message: "Füge deine Kurse im Stundenplan hinzu um sie hier zu filtern!",
|
||||
buttons: ["Ok"]
|
||||
});
|
||||
await alert.present();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,40 +75,6 @@ export class SubstitutionPage implements OnInit {
|
||||
this.showOnlyCourses(false);
|
||||
}
|
||||
|
||||
public getDetails(subs: Substitution): string {
|
||||
if (subs.type == "bitte beachten") {
|
||||
const desc = subs.description != " " ? ' - ' + subs.description : "";
|
||||
let info = `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
if (subs.lesson != subs.newLesson) {
|
||||
info = `${subs.newLesson} (${subs.representative}) statt ${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
}
|
||||
|
||||
return info + desc;
|
||||
}
|
||||
|
||||
switch (subs.type) {
|
||||
case "Vertretung":
|
||||
case "st. regulärem Unt.":
|
||||
return `${subs.lesson} (${subs.representative} statt ${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Raumtausch":
|
||||
return `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Entfall":
|
||||
return `${subs.lesson} (${subs.teacher})`;
|
||||
|
||||
case "Stillarbeit":
|
||||
return `${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
case "Verlegung":
|
||||
return `${subs.newLesson} (${subs.representative}) statt ${subs.lesson} (${subs.teacher}) in ${subs.room}`;
|
||||
|
||||
default:
|
||||
return subs.lesson + ' (' + subs.teacher + ') ' + subs.room;
|
||||
}
|
||||
}
|
||||
|
||||
public showOnlyCourses(toggle: boolean) {
|
||||
localStorage.setItem("filterByClasses", toggle.toString());
|
||||
}
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
<ion-header [translucent]="true">
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-menu-button></ion-menu-button>
|
||||
</ion-buttons>
|
||||
<ion-title>Aufgaben</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content [fullscreen]="true">
|
||||
<ion-header collapse="condense">
|
||||
<ion-toolbar>
|
||||
<ion-title size="large">Aufgaben</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-label>Coming soon!</ion-label>
|
||||
</ion-content>
|
||||
|
||||
<ion-footer>
|
||||
<ion-toolbar>
|
||||
<ion-grid>
|
||||
<ion-row>
|
||||
<ion-col>
|
||||
<ion-tab-button>
|
||||
<ion-icon ios="list-outline" md="list-sharp" />
|
||||
Aktuelle Aufgaben
|
||||
</ion-tab-button>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-tab-button>
|
||||
<ion-icon ios="folder-outline" md="folder-sharp" />
|
||||
Vergangene Aufgaben
|
||||
</ion-tab-button>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
</ion-toolbar>
|
||||
</ion-footer>
|
||||
@@ -1,20 +0,0 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { IonicModule } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tasks',
|
||||
templateUrl: './tasks.page.html',
|
||||
styleUrls: ['./tasks.page.scss'],
|
||||
standalone: true,
|
||||
imports: [IonicModule, CommonModule, FormsModule]
|
||||
})
|
||||
export class TasksPage implements OnInit {
|
||||
|
||||
constructor() { }
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
||||
BIN
BetterIServ.Mobile/src/assets/icon/favicon.ico
Normal file
BIN
BetterIServ.Mobile/src/assets/icon/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 84 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 930 B |
@@ -1,3 +1,4 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
production: true,
|
||||
backend: "https://iserv.leon-hoppe.de"
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
// The list of file replacements can be found in `angular.json`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
production: false,
|
||||
backend: "http://localhost:5273"
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -36,3 +36,15 @@ ion-menu-button {
|
||||
.active {
|
||||
color: var(--ion-color-primary);
|
||||
}
|
||||
|
||||
.lesson-icon {
|
||||
margin-inline: auto;
|
||||
aspect-ratio: 1;
|
||||
width: 45px;
|
||||
padding: 10px;
|
||||
border-radius: 50%;
|
||||
line-height: 25px;
|
||||
|
||||
background-color: var(--background);
|
||||
color: var(--foreground);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<meta name="format-detection" content="telephone=no" />
|
||||
<meta name="msapplication-tap-highlight" content="no" />
|
||||
|
||||
<link rel="icon" type="image/png" href="assets/icon/favicon.png" />
|
||||
<link rel="icon" type="image/png" href="assets/icon/favicon.ico" />
|
||||
|
||||
<!-- add to homescreen for ios -->
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
|
||||
Reference in New Issue
Block a user