Compare commits

..

2 Commits

Author SHA1 Message Date
eed9e30fd3 Merge remote-tracking branch 'origin/main'
Some checks are pending
Increase Version / build-and-run (push) Waiting to run
Increase Version / bump-version (push) Successful in 12s
# Conflicts:
#	src/app/services/appointment.service.ts
#	src/app/services/customer.service.ts
2025-03-12 23:45:09 +01:00
c3663e4c6e new shizzle for nizzle 2025-03-12 23:44:29 +01:00
17 changed files with 177 additions and 71 deletions

View File

@@ -58,7 +58,6 @@ export class EditItemComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
console.log(this.appointment);
let date = new Date(this.appointment.startDate); let date = new Date(this.appointment.startDate);
this.testForm = new FormGroup({ this.testForm = new FormGroup({
title: new FormControl(this.appointment.title, Validators.required), title: new FormControl(this.appointment.title, Validators.required),

View File

@@ -1,4 +1,4 @@
<form [formGroup]="testForm"> <form [formGroup]="appointmentForm">
<tui-input formControlName="title"> <tui-input formControlName="title">
Titel Titel
<input <input
@@ -8,7 +8,7 @@
/> />
</tui-input> </tui-input>
<div <div
*ngIf="testForm.get('title').value == ''" *ngIf="appointmentForm.get('title').value == ''"
tuiGroup tuiGroup
class="group" class="group"
[collapsed]="true"> [collapsed]="true">
@@ -25,7 +25,7 @@
<br> <br>
<div class="toolbar"> <div class="toolbar">
<tui-combo-box <tui-combo-box
[formControl]="control" [formControl]="customerControl"
[stringify]="stringify" [stringify]="stringify"
tuiTextfieldSize="m"> tuiTextfieldSize="m">
Klant Klant
@@ -43,18 +43,17 @@
type="button"> type="button">
</button> </button>
</div> </div>
<br> <br>
<div <div
tuiGroup tuiGroup
class="group" class="group">
>
<div> <div>
<tui-input-date <tui-input-date
formControlName="date" formControlName="date"
tuiTextfieldSize="m" tuiTextfieldSize="m"
class="tui-space_vertical-4" class="tui-space_vertical-4"
[tuiTextfieldLabelOutside]="false" [tuiTextfieldLabelOutside]="false">
>
Datum Datum
</tui-input-date> </tui-input-date>
</div> </div>
@@ -72,12 +71,38 @@
formControlName="endTime" formControlName="endTime"
tuiTextfieldSize="m" tuiTextfieldSize="m"
class="tui-space_top-2" class="tui-space_top-2"
[tuiTextfieldLabelOutside]="false" [tuiTextfieldLabelOutside]="false">
>
Tot Tot
</tui-input-time> </tui-input-time>
</div> </div>
</div> </div>
<span *ngIf="appointmentForm.get('endTime')?.errors?.['endTimeInvalid']">
<tui-error [error]="timeError"/>
</span>
<br>
<ng-container *ngIf="appointments.length > 0">
<h3>Geplande afspraken die dag:</h3>
<div class="appointments-container">
<div *ngFor="let appointment of appointments" class="appointment-object">
<span class="title">{{ appointment.title }}</span>
<span class="name">
<tui-icon
icon="@tui.user"
[style.font-size.rem]="1"
[style.color]="'var(--tui-background-accent-1)'"/>
{{ appointment.customer.firstName }} {{ appointment.customer.lastName }}</span>
<span class="time">
<tui-icon
icon="@tui.clock"
[style.font-size.rem]="1"
[style.color]="'var(--tui-background-accent-1)'"/>
{{ getFormattedTime(appointment.startHour, appointment.startMinute) }} -
{{ getFormattedTime(appointment.endHour, appointment.endMinute) }}
</span>
</div>
</div>
</ng-container>
<br> <br>
<tui-textarea formControlName="notes">Notities</tui-textarea> <tui-textarea formControlName="notes">Notities</tui-textarea>
</form> </form>
@@ -95,42 +120,35 @@
<tui-input <tui-input
formControlName="firstName" formControlName="firstName"
tuiTextfieldSize="m" tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true" [tuiTextfieldCleaner]="true">
>
Voornaam Voornaam
<input <input
tuiTextfieldLegacy tuiTextfieldLegacy
type="text" type="text"
formControlName="firstName" formControlName="firstName"/>
/>
</tui-input> </tui-input>
<br> <br>
<tui-input <tui-input
formControlName="lastName" formControlName="lastName"
tuiTextfieldSize="m" tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true" [tuiTextfieldCleaner]="true">
>
Achternaam Achternaam
<input <input
tuiTextfieldLegacy tuiTextfieldLegacy
type="text" type="text"
formControlName="lastName" formControlName="lastName"/>
/>
</tui-input> </tui-input>
<br> <br>
<tui-input <tui-input
formControlName="email" formControlName="email"
tuiTextfieldSize="m" tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true" [tuiTextfieldCleaner]="true">
>
Email Email
<input <input
tuiTextfieldLegacy tuiTextfieldLegacy
autocomplete="email" autocomplete="email"
type="email" type="email"
formControlName="email" formControlName="email"/>
/>
</tui-input> </tui-input>
<br> <br>
<button <button
@@ -142,8 +160,7 @@
type="button"> type="button">
<tui-icon <tui-icon
icon="@tui.plus" icon="@tui.plus"
[style.height.rem]="1" [style.height.rem]="1"/>
/>
Klant toevoegen Klant toevoegen
</button> </button>
</form> </form>

View File

@@ -13,3 +13,48 @@
justify-content: center; justify-content: center;
} }
[tuiAppearance][data-appearance='custom'] {
background: #526ed3;
color: white;
margin-right: 8px;
}
.appointments-container {
display: flex;
gap: 5px; /* Ruimte tussen de objecten */
overflow-x: auto; /* Alleen horizontaal scrollen als nodig */
white-space: nowrap; /* Voorkomt dat items naar een nieuwe regel springen */
padding-bottom: 5px; /* Ruimte voor scrollbar */
}
.appointment-object {
background-color: #f5f5f5;
padding: 5px;
border-radius: 3px;
box-shadow: 1px 1px 3px rgba(0, 0, 0, 0.1);
min-width: 120px;
text-align: left;
font-size: 12px;
display: flex;
flex-direction: column;
gap: 2px;
flex-shrink: 0; /* Voorkomt dat items kleiner worden bij weinig ruimte */
}
/* Optioneel: Specifieke styling voor de tekstregels */
.name {
color: #555;
}
.title {
font-weight: bold;
}
.time {
font-size: 11px;
color: #777;
}

View File

@@ -1,7 +1,7 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
import {NgForOf, NgIf} from "@angular/common"; import {NgForOf, NgIf} from "@angular/common";
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms"; import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
import {TuiButton, TuiGroup, TuiIcon} from "@taiga-ui/core"; import {TuiButton, TuiError, TuiGroup, TuiIcon} from "@taiga-ui/core";
import { import {
TuiComboBoxModule, TuiComboBoxModule,
TuiInputDateModule, TuiInputDateModule,
@@ -11,7 +11,7 @@ import {
TuiTextfieldControllerModule TuiTextfieldControllerModule
} from "@taiga-ui/legacy"; } from "@taiga-ui/legacy";
import {Appointment} from '../../models/appointment'; import {Appointment} from '../../models/appointment';
import {TuiDay} from '@taiga-ui/cdk'; import {TuiDay, TuiTime, TuiValidationError} from '@taiga-ui/cdk';
import {AppointmentService} from '../../services/appointment.service'; import {AppointmentService} from '../../services/appointment.service';
import { import {
TuiButtonLoading, TuiButtonLoading,
@@ -22,6 +22,7 @@ import {
import {Customer} from '../../models/customer'; import {Customer} from '../../models/customer';
import {CustomerService} from '../../services/customer.service'; import {CustomerService} from '../../services/customer.service';
import {ModalComponent} from '../modal/modal.component'; import {ModalComponent} from '../modal/modal.component';
import {timeAfterStartValidator} from '../../models/validators/time-after-start-validator';
@Component({ @Component({
selector: 'app-new-item', selector: 'app-new-item',
@@ -42,42 +43,65 @@ import {ModalComponent} from '../modal/modal.component';
TuiFilterByInputPipe, TuiFilterByInputPipe,
ModalComponent, ModalComponent,
TuiIcon, TuiIcon,
TuiButtonLoading TuiButtonLoading,
TuiError
], ],
templateUrl: './new-item.component.html', templateUrl: './new-item.component.html',
styleUrl: './new-item.component.scss' styleUrl: './new-item.component.scss'
}) })
export class NewItemComponent implements OnInit { export class NewItemComponent implements OnInit {
@Input() testForm: FormGroup; @Input() appointmentForm: FormGroup;
@Input() appointments: Appointment[];
quickActions = ['Knippen', 'Kleuren', 'Knippen + Kleuren'] quickActions = ['Knippen', 'Kleuren', 'Knippen + Kleuren']
waiting: boolean = false; waiting: boolean = false;
protected value: TuiDay | null = null; protected value: TuiDay | null = null;
@Output() appointmentAddedEvent = new EventEmitter(); @Output() appointmentAddedEvent = new EventEmitter();
showNewCustomer = false; showNewCustomer = false;
customerForm: FormGroup; customerForm: FormGroup;
today: Date;
timeError = new TuiValidationError('Begintijd moet voor de eindtijd liggen.');
constructor(private appointmentService: AppointmentService, private customerService: CustomerService) { constructor(private appointmentService: AppointmentService, private customerService: CustomerService) {
} }
ngOnInit(): void { ngOnInit(): void {
this.getCustomers() this.getCustomers()
this.today = new Date();
this.customerForm = new FormGroup({ this.customerForm = new FormGroup({
firstName: new FormControl('', Validators.required), firstName: new FormControl('', Validators.required),
lastName: new FormControl('', Validators.required), lastName: new FormControl('', Validators.required),
email: new FormControl('', Validators.required), email: new FormControl('', Validators.required),
}) })
this.appointmentForm.get('startTime').setValue(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required)
this.appointmentForm.get('endTime').setValue(new TuiTime(this.getEndTime().getHours(), this.getEndTime().getMinutes()), [Validators.required, timeAfterStartValidator('startTime')])
this.appointments.sort((a, b) => {
return a.startHour - b.startHour || a.startMinute - b.startMinute;
});
}
getEndTime(): Date {
const endTime = new Date(this.today); // Kopieer startTime
endTime.setMinutes(endTime.getMinutes() + 30); // 30 minuten toevoegen
// Controleer of de dag nog steeds hetzelfde is
if (endTime.getDate() !== this.today.getDate()) {
endTime.setHours(23, 59, 59, 999); // Zet naar 23:59:59 als het overloopt
}
return endTime;
} }
registerAppointment() { registerAppointment() {
const title = this.testForm.get('title').value const title = this.appointmentForm.get('title').value
const description = this.testForm.get('notes').value const description = this.appointmentForm.get('notes').value
const startTime = this.testForm.get('startTime').value const startTime = this.appointmentForm.get('startTime').value
const endTime = this.testForm.get('endTime').value const endTime = this.appointmentForm.get('endTime').value
let date = this.testForm.get('date').value let date = this.appointmentForm.get('date').value
let correctDate = new Date(date.year, date.month, date.day, startTime.hours, startTime.minutes); let correctDate = new Date(date.year, date.month, date.day, startTime.hours, startTime.minutes);
const customer = this.control.value; const customer = this.customerControl.value;
const appointment = new Appointment(title, description, startTime.hours, startTime.minutes, endTime.hours, endTime.minutes, correctDate, customer) const appointment = new Appointment(title, description, startTime.hours, startTime.minutes, endTime.hours, endTime.minutes, correctDate, customer)
this.waiting = true this.waiting = true
@@ -88,15 +112,15 @@ export class NewItemComponent implements OnInit {
} }
formIsValid() { formIsValid() {
return this.testForm.valid ? 'active' : 'disabled' return this.appointmentForm.valid && this.customerControl.valid ? 'active' : 'disabled'
} }
addAction(action: string) { addAction(action: string) {
this.testForm.get('title').setValue(`${action} `) this.appointmentForm.get('title').setValue(`${action} `)
} }
protected readonly control = new FormControl<Customer | null>( protected readonly customerControl = new FormControl<Customer | null>(
null, null, Validators.required
); );
toggleCustomerModal() { toggleCustomerModal() {
@@ -125,4 +149,8 @@ export class NewItemComponent implements OnInit {
this.items = response; this.items = response;
}) })
} }
getFormattedTime(hour: number, minute: number): string {
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
}
} }

View File

@@ -1,5 +1,5 @@
import { HttpInterceptorFn } from '@angular/common/http'; import {HttpInterceptorFn} from '@angular/common/http';
import { inject } from '@angular/core'; import {inject} from '@angular/core';
import {AuthService} from '../services/auth.service'; import {AuthService} from '../services/auth.service';
const excludedUrls = ['/auth/login']; const excludedUrls = ['/auth/login'];

View File

@@ -22,8 +22,6 @@ export class Appointment {
this.startDate = date; this.startDate = date;
this.customer = customer; this.customer = customer;
console.log(this)
// Bereken de totale duur in minuten // Bereken de totale duur in minuten
this.durationInMinutes = (endHour * 60 + endMinute) - (startHour * 60 + startMinute); this.durationInMinutes = (endHour * 60 + endMinute) - (startHour * 60 + startMinute);
} }

View File

@@ -1,8 +1,8 @@
export class UserDto { export class UserDto {
username:string username: string
fullName:string fullName: string
email:string email: string
token:string token: string
constructor() { constructor() {
} }

View File

@@ -0,0 +1,18 @@
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';
export function timeAfterStartValidator(startTimeField: string): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const startTime = control.parent?.get(startTimeField)?.value;
const endTime = control.value;
if (!startTime || !endTime) {
return null; // Niet valideren als een van beide leeg is
}
// Omzetten naar Date-objecten voor vergelijking
const start = new Date(`1970-01-01T${startTime}:00`);
const end = new Date(`1970-01-01T${endTime}:00`);
return end > start ? null : {endTimeInvalid: true};
};
}

View File

@@ -74,7 +74,8 @@
</div> </div>
<app-modal title="Nieuwe afspraak" (close)="closeNewItemModal()" *ngIf="isModalOpen"> <app-modal title="Nieuwe afspraak" (close)="closeNewItemModal()" *ngIf="isModalOpen">
<app-new-item [testForm]="appointmentForm" (appointmentAddedEvent)="registerAppointment($event)"></app-new-item> <app-new-item [appointments]="appointments" [appointmentForm]="appointmentForm"
(appointmentAddedEvent)="registerAppointment($event)"></app-new-item>
</app-modal> </app-modal>
<app-modal title="{{selectedAppointment.title}}" (close)="selectedAppointment = undefined" <app-modal title="{{selectedAppointment.title}}" (close)="selectedAppointment = undefined"

View File

@@ -17,6 +17,7 @@ import {DetailsComponent} from '../../components/details/details.component';
import {DateFormatter} from '../../utils/date-formatter'; import {DateFormatter} from '../../utils/date-formatter';
import {WeekDay} from '../../models/week-day'; import {WeekDay} from '../../models/week-day';
import {NewItemComponent} from '../../components/new-item/new-item.component'; import {NewItemComponent} from '../../components/new-item/new-item.component';
import {timeAfterStartValidator} from '../../models/validators/time-after-start-validator';
@Component({ @Component({
selector: 'app-agenda', selector: 'app-agenda',
@@ -52,7 +53,7 @@ export class AgendaComponent implements OnInit {
title: new FormControl('', Validators.required), title: new FormControl('', Validators.required),
notes: new FormControl(''), notes: new FormControl(''),
startTime: new FormControl(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required), startTime: new FormControl(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required),
endTime: new FormControl(new TuiTime(this.getHours(), this.getMinutes()), Validators.required), endTime: new FormControl(new TuiTime(this.getHours(), this.getMinutes()), [Validators.required, timeAfterStartValidator('startTime')]),
date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required), date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required),
}); });
@@ -90,7 +91,7 @@ export class AgendaComponent implements OnInit {
title: new FormControl('', Validators.required), title: new FormControl('', Validators.required),
notes: new FormControl(''), notes: new FormControl(''),
startTime: new FormControl(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required), startTime: new FormControl(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required),
endTime: new FormControl(new TuiTime(this.getEndTime().getHours(), this.getEndTime().getMinutes()), Validators.required), endTime: new FormControl(new TuiTime(this.getEndTime().getHours(), this.getEndTime().getMinutes()), [Validators.required, timeAfterStartValidator('startTime')]),
date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required), date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required),
}); });
} }
@@ -109,7 +110,6 @@ export class AgendaComponent implements OnInit {
getHours() { getHours() {
let hours = this.today.getHours() let hours = this.today.getHours()
console.log(hours)
if (hours > 23) { if (hours > 23) {
return 23 return 23
} }

View File

@@ -23,7 +23,7 @@
<tui-avatar src="{{getInitials()}}"/> <tui-avatar src="{{getInitials()}}"/>
<ng-template #dropdownContent> <ng-template #dropdownContent>
<div class="dropdown"> <div class="dropdown">
<h3>{{fullName}}</h3> <h3>{{ fullName }}</h3>
<button <button
size="m" size="m"
tuiButton tuiButton

View File

@@ -52,11 +52,11 @@ tui-avatar {
margin-bottom: 8px; margin-bottom: 8px;
} }
.t-content{ .t-content {
margin-right: 24px; margin-right: 24px;
} }
button{ button {
background: transparent; background: transparent;
border: none; border: none;
} }

View File

@@ -18,25 +18,25 @@ h2 {
} }
.styled-table { .styled-table {
width: 100%; /* Tabel breedte */ width: 100%; /* Tabel breedte */
border-collapse: collapse; /* Verwijdert dubbele randen */ border-collapse: collapse; /* Verwijdert dubbele randen */
margin: 20px 0; /* Ruimte rondom de tabel */ margin: 20px 0; /* Ruimte rondom de tabel */
font-size: 16px; font-size: 16px;
text-align: left; /* Tekst uitlijning */ text-align: left; /* Tekst uitlijning */
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* Zachte schaduw */ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* Zachte schaduw */
border-radius: 10px; /* Afgeronde hoeken */ border-radius: 10px; /* Afgeronde hoeken */
overflow: hidden; /* Hoeken correct afronden */ overflow: hidden; /* Hoeken correct afronden */
} }
.styled-table thead { .styled-table thead {
background-color: #868383; /* Blauwe kopregel */ background-color: #868383; /* Blauwe kopregel */
color: #ffffff; /* Witte tekst */ color: #ffffff; /* Witte tekst */
//text-transform: uppercase; /* Hoofdletters */ //text-transform: uppercase; /* Hoofdletters */
font-weight: bold; font-weight: bold;
} }
.styled-table th, .styled-table td { .styled-table th, .styled-table td {
padding: 12px 15px; /* Ruimte binnen de cellen */ padding: 12px 15px; /* Ruimte binnen de cellen */
} }
.styled-table tbody tr { .styled-table tbody tr {

View File

@@ -12,7 +12,7 @@
<label tuiLabel>Wachtwoord</label> <label tuiLabel>Wachtwoord</label>
<input formControlName="password" type="password" placeholder="Voer je wachtwoord in" tuiTextfield/> <input formControlName="password" type="password" placeholder="Voer je wachtwoord in" tuiTextfield/>
</tui-textfield> </tui-textfield>
<tui-error [error]="computedError" /> <tui-error [error]="computedError"/>
<footer> <footer>
<button tuiButton class="custom-button" type="submit" [disabled]="form.invalid" (click)="login()"> <button tuiButton class="custom-button" type="submit" [disabled]="form.invalid" (click)="login()">
Inloggen Inloggen

View File

@@ -6,7 +6,7 @@
/* Adjust as needed */ /* Adjust as needed */
} }
tui-error{ tui-error {
text-align: center; text-align: center;
} }

View File

@@ -1,7 +1,7 @@
import { Injectable } from '@angular/core'; import {Injectable} from '@angular/core';
import { HttpClient } from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import { JwtHelperService } from '@auth0/angular-jwt'; import {JwtHelperService} from '@auth0/angular-jwt';
import { Observable } from 'rxjs'; import {Observable} from 'rxjs';
import {jwtDecode} from 'jwt-decode'; import {jwtDecode} from 'jwt-decode';
@Injectable({ @Injectable({
@@ -12,10 +12,11 @@ export class AuthService {
baseApi = "https://api.melvanveen.nl/api/auth/login"; baseApi = "https://api.melvanveen.nl/api/auth/login";
jwtHelper = new JwtHelperService(); jwtHelper = new JwtHelperService();
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {
}
login(username: string, password: string): Observable<any> { login(username: string, password: string): Observable<any> {
return this.http.post(this.baseApi, { username, password }); return this.http.post(this.baseApi, {username, password});
} }
isAuthenticated(): boolean { isAuthenticated(): boolean {

View File

@@ -7,7 +7,6 @@ export class DateFormatter {
static getHours(date: Date) { static getHours(date: Date) {
let hours = date.getHours() let hours = date.getHours()
console.log(hours)
if (hours > 23) { if (hours > 23) {
return 23 return 23
} }