git init v2

This commit is contained in:
2025-03-11 23:13:19 +01:00
parent 4d4c8a362a
commit abae328d8f
62 changed files with 3461 additions and 421 deletions

View File

@@ -0,0 +1,84 @@
<div class="container">
<div class="heading">
<button
appearance="primary"
iconStart="@tui.chevron-left"
size="m"
tuiIconButton
(click)="previousDay()"
type="button">
</button>
<h1 class="date" (click)="toggleCalendar()">{{ getDate() }}</h1>
<button
appearance="primary"
iconStart="@tui.chevron-right"
size="m"
tuiIconButton
(click)="nextDay()"
type="button">
</button>
</div>
<div class="calendar" *ngIf="showCalendar">
<tui-calendar
[value]="value"
(dayClick)="onDayClick($event)"
/>
</div>
<div class="toolbar">
<button
*ngIf="today.getDate() != selectedDate.getDate()"
appearance="secondary"
size="m"
tuiButton
(click)="setToday()"
type="button">
Vandaag
</button>
<button
appearance="secondary"
size="m"
tuiButton
(click)="isModalOpen = true"
type="button">
<tui-icon
icon="@tui.plus"
[style.height.rem]="1"
/>
Afspraak maken
</button>
</div>
<div class="content">
<div class="agenda-container">
<div *ngFor="let hour of timeSlots" class="time-slot">
<span class="time">{{ hour.toString().padStart(2, '0') }}:00</span>
<div *ngFor="let appointment of getAppointmentsForHour(hour)"
class="appointment"
(click)="selectAppointment(appointment)"
[ngClass]="{ 'large': (getAppointmentHeight(appointment) > 50) }"
[ngStyle]="getInlineStyles(appointment)">
<strong>{{ appointment.title }}</strong>
<span class="appointment-time" *ngIf="appointment.customer">
<tui-icon icon="@tui.user" [style.font-size.rem]="1"></tui-icon>
{{ appointment.customer.firstName }} {{ appointment.customer.lastName }}</span>
<span class="appointment-time">
<tui-icon icon="@tui.clock" [style.font-size.rem]="1"></tui-icon>
{{ getFormattedTime(appointment.startHour, appointment.startMinute) }}
- {{ getFormattedTime(appointment.endHour, appointment.endMinute) }}
</span>
</div>
</div>
</div>
</div>
</div>
<app-modal title="Nieuwe afspraak" (close)="closeNewItemModal()" *ngIf="isModalOpen">
<app-new-item [testForm]="appointmentForm" (appointmentAddedEvent)="registerAppointment($event)"></app-new-item>
</app-modal>
<app-modal title="{{selectedAppointment.title}}" (close)="selectedAppointment = undefined"
*ngIf="selectedAppointment != undefined">
<app-details [appointment]="selectedAppointment" (appointmentDeleted)="appointmentIsDeleted($event)"
(appointmentEdited)="appointmentIsEdited($event)"></app-details>
</app-modal>

View File

@@ -0,0 +1,141 @@
.container {
max-width: 800px;
margin: 0 auto;
padding: 20px;
}
ul {
list-style-type: none;
padding: 0;
}
li {
background-color: #f9f9f9;
margin: 10px 0;
padding: 10px;
border-radius: 5px;
}
h2 {
margin: 0;
}
p {
margin: 5px 0 0;
}
.heading {
margin-bottom: 12px;
display: flex;
flex-direction: row;
justify-content: space-between;
}
.calendar {
margin-bottom: 12px;
display: flex;
flex-direction: row;
justify-content: center;
}
.time-slot {
height: 25px;
display: flex;
align-items: center;
font-size: 14px;
background: #f9f9f9;
transition: background 0.3s ease;
}
.time-slot.claimed {
background: #d1e7dd; /* Lichtgroene achtergrond voor gereserveerde blokken */
color: #155724;
font-weight: bold;
}
.time-slot:nth-child(odd) {
background: #ececec;
}
.time {
width: 50px;
font-weight: bold;
margin-top: 20px;
margin-left: 8px;
}
.content {
overflow-y: scroll;
max-height: 70vh;
}
.agenda-container {
display: flex;
flex-direction: column;
border: 1px solid #ddd;
position: relative;
}
.time-slot {
position: relative;
border-bottom: 1px solid #ccc;
height: 59px;
display: flex;
flex-direction: column;
align-items: flex-start;
font-size: 16px;
background: #f9f9f9;
}
.appointment {
position: absolute;
left: 80px;
width: calc(100% - 100px);
background: #1e88e5;
color: white;
border-radius: 4px;
border-left: 5px solid #1565c0;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.2);
z-index: 10;
cursor: pointer;
display: grid;
grid-auto-flow: column;
align-items: center;
//gap: 5px;
min-height: 20px;
}
.appointment.large {
grid-auto-flow: row;
align-items: flex-start;
}
strong, p {
margin-left: 8px;
}
.appointment-time {
margin-left: 8px;
}
.toolbar {
button {
margin-left: 8px;
}
margin-bottom: 12px;
display: flex;
flex-direction: row;
justify-content: center;
}
.date {
cursor: pointer;
}
.date:hover {
text-decoration: underline;
}

View File

@@ -0,0 +1,206 @@
import {Component, inject, OnInit} from '@angular/core';
import {CommonModule, NgFor, NgIf} from '@angular/common';
import {TuiAlertService, TuiButton, TuiCalendar, tuiDateFormatProvider, TuiIcon} from '@taiga-ui/core';
import {Appointment} from '../../models/appointment';
import {ModalComponent} from '../../components/modal/modal.component';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {
TuiInputDateModule,
TuiInputModule,
TuiInputTimeModule,
TuiTextareaModule,
TuiTextfieldControllerModule
} from '@taiga-ui/legacy';
import {TuiDay, TuiTime} from '@taiga-ui/cdk';
import {AppointmentService} from '../../services/appointment.service';
import {DetailsComponent} from '../../components/details/details.component';
import {DateFormatter} from '../../utils/date-formatter';
import {WeekDay} from '../../models/week-day';
import {NewItemComponent} from '../../components/new-item/new-item.component';
@Component({
selector: 'app-agenda',
imports: [NgFor,
TuiButton, CommonModule, NgIf, ModalComponent, ReactiveFormsModule,
TuiInputTimeModule, TuiTextfieldControllerModule,
TuiInputModule, TuiTextareaModule, TuiInputDateModule, TuiIcon, DetailsComponent, TuiCalendar, NewItemComponent],
templateUrl: './agenda.component.html',
providers: [tuiDateFormatProvider({separator: '-'}), AppointmentService],
styleUrl: './agenda.component.scss'
})
export class AgendaComponent implements OnInit {
constructor(private appointmentService: AppointmentService) {
}
ngOnInit(): void {
this.getAppointmentsByDate(this.selectedDate);
}
timeSlots: number[] = Array.from({length: 24}, (_, i) => i); // 24 uren
appointments: Appointment[] = [];
isModalOpen = false
today = new Date()
selectedDate = new Date()
waiting: boolean = false;
selectedAppointment: Appointment;
protected value: TuiDay | null = null;
showCalendar: boolean = false;
private readonly alerts = inject(TuiAlertService);
protected appointmentForm = new FormGroup({
title: new FormControl('', Validators.required),
notes: new FormControl(''),
startTime: new FormControl(new TuiTime(this.today.getHours(), this.today.getMinutes()), Validators.required),
endTime: new FormControl(new TuiTime(this.getHours(), this.getMinutes()), Validators.required),
date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required),
});
registerAppointment(title: string): void {
this.getAppointmentsByDate(this.selectedDate);
this.waiting = false
this.isModalOpen = false
this.showNotification(title)
this.resetForms()
}
protected showNotification(message: string): void {
this.alerts
.open(`Afspraak <strong>${message}</strong> is aangemaakt.`)
.subscribe();
}
getAppointmentsForHour(hour: number) {
return this.appointments.filter(appointment => appointment.startHour === hour);
}
getDate(): string {
const date = this.selectedDate
const weekDay = WeekDay[date.getDay()];
const day = date.getDate().toString().padStart(2, '0'); // Dag met leading zero (01, 02, ..., 31)
const monthName = date.toLocaleString('nl-NL', {month: 'long'});
const year = date.getFullYear().toString(); // Volledig jaar
return `${weekDay} ${day} ${monthName} ${year}`;
}
resetForms() {
this.appointmentForm = new FormGroup({
title: new FormControl('', Validators.required),
notes: new FormControl(''),
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),
date: new FormControl(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()), Validators.required),
});
}
nextDay() {
this.selectedDate.setDate(this.selectedDate.getDate() + 1);
this.getAppointmentsByDate(this.selectedDate);
this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()))
}
previousDay() {
this.selectedDate.setDate(this.selectedDate.getDate() - 1);
this.getAppointmentsByDate(this.selectedDate);
this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()))
}
getHours() {
let hours = this.today.getHours()
console.log(hours)
if (hours > 23) {
return 23
}
return hours
}
getMinutes() {
let minutes = this.today.getMinutes() + 30
if (minutes > 59) {
return 59
}
return minutes
}
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;
}
setToday() {
this.selectedDate = new Date()
this.getAppointmentsByDate(this.selectedDate);
this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()))
}
getAppointmentsByDate(date: Date) {
this.appointmentService.getAppointmentsByDate(date).subscribe(appointments => {
this.appointments = appointments;
})
}
getInlineStyles(appointment: Appointment): { [key: string]: string } {
return {
'--duration': `${appointment.durationInMinutes}`,
'--start-minute': `${appointment.startMinute}`,
'top': `${appointment.startMinute}px`, // Startpositie binnen het uur
'height': `${appointment.durationInMinutes}px`, // Hoogte over meerdere uren
};
}
getFormattedTime(hour: number, minute: number): string {
return `${hour.toString().padStart(2, '0')}:${minute.toString().padStart(2, '0')}`;
}
getAppointmentHeight(appointment: Appointment): number {
const startInMinutes = (appointment.startHour * 60) + appointment.startMinute;
const endInMinutes = (appointment.endHour * 60) + appointment.endMinute;
return (endInMinutes - startInMinutes); // 50px per uur
}
selectAppointment(appointment: Appointment) {
this.selectedAppointment = appointment;
}
protected readonly DateFormatter = DateFormatter;
onDayClick(day: TuiDay) {
this.value = day;
this.selectedDate = new Date(day.year, day.month, day.day);
this.getAppointmentsByDate(this.selectedDate);
this.appointmentForm.get('date').setValue(new TuiDay(this.selectedDate.getFullYear(), this.selectedDate.getMonth(), this.selectedDate.getDate()))
this.toggleCalendar()
}
toggleCalendar() {
this.showCalendar = !this.showCalendar
}
closeNewItemModal() {
this.isModalOpen = false
this.resetForms()
}
appointmentIsDeleted(appointment: Appointment) {
this.selectedAppointment = undefined;
this.alerts
.open(`Afspraak <strong>${appointment.title}</strong> is verwijderd.`)
.subscribe();
this.getAppointmentsByDate(this.selectedDate);
}
appointmentIsEdited($event: Appointment) {
this.getAppointmentsByDate(this.selectedDate);
}
}

View File

@@ -0,0 +1,265 @@
<div [style.display]="'flex'">
<aside
[style.height.rem]="27"
[tuiNavigationAside]="expanded()"
>
<header>
<button
iconStart="@tui.home"
tuiAsideItem
type="button"
>
<span tuiFade>A very very long product name</span>
</button>
</header>
<button
iconStart="@tui.search"
tuiAsideItem
type="button"
>
Search
<ng-container *ngIf="expanded()">
<tui-badge appearance="accent">12</tui-badge>
</ng-container>
</button>
<a
iconStart="@tui.users"
tuiAsideItem
[routerLink]="routes.Navigation"
>
Groups
</a>
<tui-aside-group>
<button
automation-id="setting"
iconStart="@tui.settings"
tuiAsideItem
tuiChevron
type="button"
>
Settings
<ng-template>
<button
tuiAsideItem
type="button"
>
Account
</button>
<button
tuiAsideItem
type="button"
>
Notifications
</button>
<button
tuiAsideItem
type="button"
>
Privacy
</button>
</ng-template>
</button>
</tui-aside-group>
<button
automation-id="hint"
iconStart="@tui.heart"
tuiAsideItem
type="button"
>
<span tuiFade>By default ellipsis is used but you can use fade too</span>
</button>
<button
iconEnd="@tui.chevron-right"
iconStart="@tui.ellipsis"
tuiAsideItem
tuiDropdownHover
tuiDropdownOpen
type="button"
[tuiDropdown]="more"
>
More
<ng-template
#more
let-close
>
<tui-data-list tuiDataListDropdownManager>
<button
iconStart="@tui.pencil"
tuiAsideItem
type="button"
>
Write
</button>
<button
iconStart="@tui.pie-chart"
tuiAsideItem
type="button"
[tuiDropdown]="submenu"
>
Categories
<ng-template #submenu>
<tui-data-list>
<button
tuiAsideItem
type="button"
(click)="close()"
>
Fiction (will close menu)
</button>
<button
tuiAsideItem
type="button"
>
Non-Fiction
</button>
<button
tuiAsideItem
type="button"
>
Children
</button>
</tui-data-list>
</ng-template>
</button>
</tui-data-list>
</ng-template>
</button>
<hr/>
<button
iconStart="@tui.plus"
tuiAsideItem
type="button"
>
Add
</button>
<footer>
<button
iconStart="@tui.star"
tuiAsideItem
type="button"
>
Favorites
</button>
<button
tuiAsideItem
type="button"
[iconStart]="expanded() ? '@tui.chevron-left' : '@tui.chevron-right'"
(click)="handleToggle()"
>
{{ expanded() ? 'Collapse' : 'Expand' }}
</button>
</footer>
</aside>
<main tuiNavigationMain>
<nav
compact
tuiSubheader
[style.position]="'sticky'"
>
<tui-breadcrumbs [itemsLimit]="10">
<ng-container *ngFor="let item of breadcrumbs; let last = last">
<ng-container *ngIf="last">
<strong
*tuiItem
tuiFade
>
{{ item }}
</strong>
</ng-container>
<ng-container *ngIf="!last">
<button
*tuiItem
tuiLink
type="button"
>
{{ item }}
</button>
</ng-container>
</ng-container>
</tui-breadcrumbs>
<tui-tabs tuiFade>
<button
tuiTab
type="button"
>
Default view
</button>
<button
tuiTab
type="button"
>
Details
</button>
<button
tuiTab
type="button"
>
Followers
</button>
</tui-tabs>
<button
appearance="secondary"
tuiButton
type="button"
>
Secondary
</button>
<button
tuiButton
type="button"
>
Primary
</button>
</nav>
<ng-container *tuiRepeatTimes="let index of 10">
<form
tuiAppearance="floating"
tuiCardLarge
tuiForm="m"
[style.grid-column]="'2 / span 7'"
[style.margin-top.rem]="1"
>
<header tuiHeader>
<h2 tuiTitle>
Registration form
<span tuiSubtitle>Tell us about yourself</span>
</h2>
</header>
<tui-textfield>
<label tuiLabel>Name</label>
<input
placeholder="John Wick"
tuiTextfield
/>
</tui-textfield>
<footer>
<button
appearance="secondary"
tuiButton
type="button"
>
Cancel
</button>
<button
tuiButton
type="submit"
>
Ok
</button>
</footer>
</form>
<div
tuiAppearance="outline-grayscale"
tuiCardLarge
[style.grid-column]="'span 3'"
[style.margin-top.rem]="1"
>
<h2 tuiTitle>
Sidebar content
<span tuiSubtitle>Use CSS grid to position</span>
</h2>
</div>
</ng-container>
</main>
</div>

View File

@@ -0,0 +1,67 @@
import {NgForOf, NgIf} from '@angular/common';
import {Component, signal} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {RouterLink} from '@angular/router';
import {TuiPortals, TuiRepeatTimes} from '@taiga-ui/cdk';
import {TuiAppearance, TuiButton, TuiDataList, TuiDropdown, TuiLink, TuiTextfield, TuiTitle,} from '@taiga-ui/core';
import {TuiBadge, TuiBreadcrumbs, TuiChevron, TuiDataListDropdownManager, TuiFade, TuiTabs,} from '@taiga-ui/kit';
import {TuiCardLarge, TuiForm, TuiHeader, TuiNavigation} from '@taiga-ui/layout';
const ICON =
"data:image/svg+xml,%0A%3Csvg width='32' height='32' viewBox='0 0 32 32' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Crect width='32' height='32' rx='8' fill='url(%23paint0_linear_2036_35276)'/%3E%3Cmask id='mask0_2036_35276' style='mask-type:alpha' maskUnits='userSpaceOnUse' x='6' y='5' width='20' height='21'%3E%3Cpath d='M18.2399 9.36607C21.1347 10.1198 24.1992 9.8808 26 7.4922C26 7.4922 21.5645 5 16.4267 5C11.2888 5 5.36726 8.69838 6.05472 16.6053C6.38707 20.4279 6.65839 23.7948 6.65839 23.7948C8.53323 22.1406 9.03427 19.4433 8.97983 16.9435C8.93228 14.7598 9.55448 12.1668 12.1847 10.4112C14.376 8.94865 16.4651 8.90397 18.2399 9.36607Z' fill='url(%23paint1_linear_2036_35276)'/%3E%3Cpath d='M11.3171 20.2647C9.8683 17.1579 10.7756 11.0789 16.4267 11.0789C20.4829 11.0789 23.1891 12.8651 22.9447 18.9072C22.9177 19.575 22.9904 20.2455 23.2203 20.873C23.7584 22.3414 24.7159 24.8946 24.7159 24.8946C23.6673 24.5452 22.8325 23.7408 22.4445 22.7058L21.4002 19.921L21.2662 19.3848C21.0202 18.4008 20.136 17.7104 19.1217 17.7104H17.5319L17.6659 18.2466C17.9119 19.2306 18.7961 19.921 19.8104 19.921L22.0258 26H10.4754C10.7774 24.7006 12.0788 23.2368 11.3171 20.2647Z' fill='url(%23paint2_linear_2036_35276)'/%3E%3C/mask%3E%3Cg mask='url(%23mask0_2036_35276)'%3E%3Crect x='4' y='4' width='24' height='24' fill='white'/%3E%3C/g%3E%3Cdefs%3E%3ClinearGradient id='paint0_linear_2036_35276' x1='0' y1='0' x2='32' y2='32' gradientUnits='userSpaceOnUse'%3E%3Cstop stop-color='%23A681D4'/%3E%3Cstop offset='1' stop-color='%237D31D4'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint1_linear_2036_35276' x1='6.0545' y1='24.3421' x2='28.8119' y2='3.82775' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0.0001' stop-opacity='0.996458'/%3E%3Cstop offset='0.317708'/%3E%3Cstop offset='1' stop-opacity='0.32'/%3E%3C/linearGradient%3E%3ClinearGradient id='paint2_linear_2036_35276' x1='6.0545' y1='24.3421' x2='28.8119' y2='3.82775' gradientUnits='userSpaceOnUse'%3E%3Cstop offset='0.0001' stop-opacity='0.996458'/%3E%3Cstop offset='0.317708'/%3E%3Cstop offset='1' stop-opacity='0.32'/%3E%3C/linearGradient%3E%3C/defs%3E%3C/svg%3E%0A";
@Component({
selector: 'app-dashboard',
imports: [
FormsModule,
NgForOf,
NgIf,
RouterLink,
TuiAppearance,
TuiBadge,
TuiBreadcrumbs,
TuiButton,
TuiCardLarge,
TuiChevron,
TuiDataList,
TuiDataListDropdownManager,
TuiDropdown,
TuiFade,
TuiForm,
TuiHeader,
TuiLink,
TuiNavigation,
TuiRepeatTimes,
TuiTabs,
TuiTextfield,
TuiTitle,
],
templateUrl: './dashboard.component.html',
styleUrl: './dashboard.component.scss'
})
export class DashboardComponent extends TuiPortals {
protected expanded = signal(false);
protected open = false;
protected switch = false;
protected readonly routes: any = {};
protected readonly breadcrumbs = ['Home', 'Angular', 'Repositories', 'Taiga UI'];
protected readonly drawer = {
Components: [
{name: 'Button', icon: ICON},
{name: 'Input', icon: ICON},
{name: 'Tooltip', icon: ICON},
],
Essentials: [
{name: 'Getting started', icon: ICON},
{name: 'Showcase', icon: ICON},
{name: 'Typography', icon: ICON},
],
};
protected handleToggle(): void {
this.expanded.update((e) => !e);
}
}

View File

@@ -0,0 +1,41 @@
<!-- Ignore this part, it is only here to position drawer inside the example block -->
<div class="custom-portal">
<ng-container #viewContainer/>
</div>
<header>
<div class="navbar">
<img src="assets/logo-minimal.png" alt="App Logo" class="img-fluid">
<ul>
<li><a routerLink="/home/agenda" routerLinkActive="active">Agenda</a></li>
<li><a routerLink="/home/klanten" routerLinkActive="active">Klanten</a></li>
</ul>
<hr/>
<button
tuiChevron
type="button"
[tuiDropdown]="dropdownContent"
[tuiDropdownManual]="open"
[tuiObscuredEnabled]="open"
(click)="onClick()"
(tuiActiveZoneChange)="onActiveZone($event)"
(tuiObscured)="onObscured($event)"
>
<tui-avatar src="{{getInitials()}}"/>
<ng-template #dropdownContent>
<div class="dropdown">
<h3>{{fullName}}</h3>
<button
size="m"
tuiButton
type="button"
(click)="logout()"
>
Uitloggen
</button>
</div>
</ng-template>
</button>
</div>
</header>
<router-outlet></router-outlet>

View File

@@ -0,0 +1,62 @@
.navbar {
background-color: #f8f9fa;
display: flex
}
ul {
display: flex;
flex-direction: row;
justify-content: center;
list-style-type: none;
align-items: center;
}
li {
margin: 0 10px;
}
a {
text-decoration: none;
color: black;
font-size: 16px;
}
img {
display: block;
max-width: 230px;
max-height: 95px;
width: auto;
height: auto;
}
hr {
clear: both;
visibility: hidden;
}
tui-avatar {
margin-right: 32px;
cursor: pointer;
}
.active {
font-weight: bold;
}
.dropdown {
font-size: 0.8125rem;
line-height: 1.25rem;
padding: 0.25rem 0.75rem;
margin-left: 4px;
margin-right: 4px;
margin-bottom: 8px;
}
.t-content{
margin-right: 24px;
}
button{
background: transparent;
border: none;
}

View File

@@ -0,0 +1,55 @@
import {Component, OnInit} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {Router, RouterLink, RouterLinkActive, RouterModule} from '@angular/router';
import {TuiAvatar, TuiChevron,} from '@taiga-ui/kit';
import {AuthService} from '../../services/auth.service';
import {User} from '../../models/user';
import {TuiButton, TuiDropdown} from '@taiga-ui/core';
import {TuiActiveZone, TuiObscured} from '@taiga-ui/cdk';
@Component({
selector: 'app-home',
imports: [TuiAvatar, RouterModule, FormsModule, RouterLink, RouterLinkActive, TuiDropdown, TuiObscured, TuiActiveZone, TuiButton, TuiChevron],
templateUrl: './home.component.html',
styleUrl: './home.component.scss'
})
export class HomeComponent implements OnInit {
user: User
fullName: string
getInitials(): string {
this.user = this.authService.getUserInfo();
this.fullName = this.user.firstName + ' ' + this.user.lastName;
return this.fullName.split(' ').map((n) => n[0]).join('').substring(0, 2);
}
protected open = false;
protected onClick(): void {
this.open = !this.open;
}
protected onObscured(obscured: boolean): void {
if (obscured) {
this.open = false;
}
}
protected onActiveZone(active: boolean): void {
this.open = active && this.open;
}
constructor(private authService: AuthService, private router: Router) {
}
ngOnInit(): void {
if (!this.authService.isAuthenticated()) {
this.router.navigate(['login']);
}
}
logout() {
this.authService.logout();
this.router.navigate(['login']);
}
}

View File

@@ -0,0 +1,101 @@
<div class="container">
<div class="heading">
<h1>Klanten</h1>
</div>
<div class="content">
<div class="toolbar">
<button
size="m"
tuiButton
appearance="secondary"
type="button"
(click)="toggleCustomerModal()"
>
<tui-icon
icon="@tui.plus"
[style.height.rem]="1"
/>
Klant toevoegen
</button>
</div>
<table class="styled-table">
<thead>
<tr>
<th>Naam</th>
<th>Email</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let customer of customers" (click)="selectCustomer(customer)">
<td>{{ customer.firstName }} {{ customer.lastName }}</td>
<td>{{ customer.email }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<app-modal title="Nieuwe klant toevoegen" *ngIf="showNewCustomer" (close)="toggleCustomerModal()">
<form [formGroup]="customerForm">
<tui-input
formControlName="firstName"
tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true"
>
Voornaam
<input
tuiTextfieldLegacy
type="text"
formControlName="firstName"
/>
</tui-input>
<br>
<tui-input
formControlName="lastName"
tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true"
>
Achternaam
<input
tuiTextfieldLegacy
type="text"
formControlName="lastName"
/>
</tui-input>
<br>
<tui-input
formControlName="email"
tuiTextfieldSize="m"
[tuiTextfieldCleaner]="true"
>
Email
<input
tuiTextfieldLegacy
autocomplete="email"
type="email"
formControlName="email"
/>
</tui-input>
<br>
<button
appearance="secondary"
size="m"
tuiButton
(click)="saveCustomer()"
[disabled]="customerForm.invalid"
type="button">
<tui-icon
icon="@tui.save"
[style.height.rem]="1"
/>
Opslaan
</button>
</form>
</app-modal>
<app-modal title="Klant bekijken" *ngIf="selectedCustomer != undefined" (close)="selectedCustomer = undefined">
<app-customer-details [customer]="selectedCustomer"></app-customer-details>
</app-modal>

View File

@@ -0,0 +1,72 @@
h1 {
text-align: center;
}
h2 {
margin-left: 8px;
}
.heading {
justify-content: center;
}
.content {
display: flex;
flex-direction: column;
justify-content: center;
}
.styled-table {
width: 100%; /* Tabel breedte */
border-collapse: collapse; /* Verwijdert dubbele randen */
margin: 20px 0; /* Ruimte rondom de tabel */
font-size: 16px;
text-align: left; /* Tekst uitlijning */
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); /* Zachte schaduw */
border-radius: 10px; /* Afgeronde hoeken */
overflow: hidden; /* Hoeken correct afronden */
}
.styled-table thead {
background-color: #868383; /* Blauwe kopregel */
color: #ffffff; /* Witte tekst */
//text-transform: uppercase; /* Hoofdletters */
font-weight: bold;
}
.styled-table th, .styled-table td {
padding: 12px 15px; /* Ruimte binnen de cellen */
}
.styled-table tbody tr {
border-bottom: 1px solid #dddddd; /* Lichte scheidingslijn */
}
.styled-table tbody tr:nth-child(even) {
background-color: #f3f3f3; /* Afwisselende rijkleuren */
}
.styled-table tbody tr:hover {
background-color: #d3d2d2; /* Hover effect */
cursor: pointer;
transition: background-color 0.3s ease;
}
.styled-table tbody tr:last-of-type {
border-bottom: none; /* Verwijdert de onderste scheidingslijn */
}
.toolbar {
button {
margin-left: 8px;
}
margin-bottom: 12px;
display: flex;
flex-direction: row;
justify-content: center;
}

View File

@@ -0,0 +1,71 @@
import {Component, OnInit} from '@angular/core';
import {NgForOf, NgIf} from '@angular/common';
import {Customer} from '../../models/customer';
import {CustomerService} from '../../services/customer.service';
import {TuiButton, TuiIcon} from '@taiga-ui/core';
import {ModalComponent} from '../../components/modal/modal.component';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {TuiInputCopyModule, TuiInputModule, TuiTextfieldControllerModule} from '@taiga-ui/legacy';
import {CustomerDetailsComponent} from '../../components/customer-details/customer-details.component';
@Component({
selector: 'app-klanten',
imports: [
NgForOf,
TuiButton,
TuiIcon,
ModalComponent,
NgIf,
ReactiveFormsModule,
TuiInputCopyModule,
TuiInputModule,
TuiTextfieldControllerModule,
CustomerDetailsComponent
],
templateUrl: './klanten.component.html',
styleUrl: './klanten.component.scss'
})
export class KlantenComponent implements OnInit {
customers: Customer[];
showNewCustomer: boolean = false;
customerForm: FormGroup;
selectedCustomer: Customer;
constructor(private customerService: CustomerService) {
}
ngOnInit(): void {
this.getCustomers();
this.customerForm = new FormGroup({
firstName: new FormControl('', Validators.required),
lastName: new FormControl('', Validators.required),
email: new FormControl('', [Validators.required, Validators.email]),
})
}
toggleCustomerModal() {
this.showNewCustomer = !this.showNewCustomer;
}
saveCustomer() {
const firstName = this.customerForm.get('firstName').value
const lastName = this.customerForm.get('lastName').value
const email = this.customerForm.get('email').value
const customer = new Customer(firstName, lastName, email);
this.customerService.addCustomer(customer).subscribe(() => {
this.showNewCustomer = false;
this.getCustomers()
})
}
getCustomers() {
this.customerService.getCustomers().subscribe(customers => {
this.customers = customers
});
}
selectCustomer(customer: Customer) {
this.selectedCustomer = customer
}
}

View File

@@ -0,0 +1,23 @@
<div class="container">
<div class="center-container">
<form tuiAppearance="floating" tuiCardLarge tuiForm="m" [formGroup]="form" [style.max-width.rem]="32">
<header tuiHeader>
<img class="header" src="assets/logo-minimal.png" alt="Logo" tuiHeaderIcon/>
</header>
<tui-textfield>
<label tuiLabel>Gebruikersnaam</label>
<input formControlName="username" placeholder="Voer je gebruikersnaam in" tuiTextfield/>
</tui-textfield>
<tui-textfield>
<label tuiLabel>Wachtwoord</label>
<input formControlName="password" type="password" placeholder="Voer je wachtwoord in" tuiTextfield/>
</tui-textfield>
<tui-error [error]="computedError" />
<footer>
<button tuiButton class="custom-button" type="submit" [disabled]="form.invalid" (click)="login()">
Inloggen
</button>
</footer>
</form>
</div>
</div>

View File

@@ -0,0 +1,49 @@
.center-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
/* Adjust as needed */
}
tui-error{
text-align: center;
}
.header {
text-align: center;
margin-bottom: 28px;
max-width: 300px;
max-height: 300px;
}
::ng-deep button.custom-button {
background-color: #222222 !important;
/* Pas kleur aan */
color: white !important;
/* Tekstkleur aanpassen */
width: 300px;
}
.container {
max-width: 100%;
width: 100%;
height: 100%;
--s: 200px; /* control the size */
--c1: #1d1d1d;
--c2: #4e4f51;
--c3: #3c3c3c;
background: repeating-conic-gradient(
from 30deg,
#0000 0 120deg,
var(--c3) 0 180deg
) calc(0.5 * var(--s)) calc(0.5 * var(--s) * 0.577),
repeating-conic-gradient(
from 30deg,
var(--c1) 0 60deg,
var(--c2) 0 120deg,
var(--c3) 0 180deg
);
background-size: var(--s) calc(var(--s) * 0.577);
}

View File

@@ -0,0 +1,57 @@
import {Component} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {Router} from '@angular/router';
import {TuiAppearance, TuiButton, TuiError, TuiTextfield,} from '@taiga-ui/core';
import {TuiCardLarge, TuiForm, TuiHeader} from '@taiga-ui/layout';
import {AuthService} from '../../services/auth.service';
import {UserDto} from '../../models/user-dto';
import {HttpErrorResponse} from '@angular/common/http';
import {TuiValidationError} from '@taiga-ui/cdk';
@Component({
selector: 'app-login',
imports: [
ReactiveFormsModule,
TuiAppearance,
TuiButton,
TuiCardLarge,
TuiForm,
TuiHeader,
TuiTextfield,
TuiError,
],
templateUrl: './login.component.html',
styleUrl: './login.component.scss'
})
export class LoginComponent {
form: FormGroup;
protected enabled = false;
protected error = new TuiValidationError('Ongeldige gebruikersnaam of wachtwoord.');
protected get computedError(): TuiValidationError | null {
return this.enabled ? this.error : null;
}
constructor(private router: Router, private authService: AuthService) {
this.form = new FormGroup({
username: new FormControl('', Validators.required),
password: new FormControl('', Validators.required)
});
}
login() {
this.authService.login(this.form.get('username').value, this.form.get('password').value).subscribe({
next: (user: UserDto) => {
localStorage.setItem('token', user.token);
this.router.navigate(['/home/agenda']);
},
error: (err: HttpErrorResponse) => {
if (err.status === 401) {
this.enabled = true;
}
},
});
}
}