Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions web-app/admin/src/app/admin/admin-event/admin-events.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatTableModule } from '@angular/material/table';
import { MatPaginatorModule } from '@angular/material/paginator';
import { MatSortModule } from '@angular/material/sort';
import { MatDialogModule } from '@angular/material/dialog';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { MatButtonModule } from '@angular/material/button';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatIconModule } from '@angular/material/icon';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';

import { CoreModule } from '../../core/core.module';
import { AdminBreadcrumbModule } from '../admin-breadcrumb/admin-breadcrumb.module';
import { EventDashboardComponent } from './dashboard/event-dashboard.component';
import { MatSelectModule } from '@angular/material/select';
import { MatOptionModule } from '@angular/material/core';
import { EventService } from 'src/app/event/event.service';
import { CreateEventDialogComponent } from './create-event/create-event.component';
import { MatTooltipModule } from '@angular/material/tooltip';

@NgModule({
declarations: [
EventDashboardComponent,
CreateEventDialogComponent
],
imports: [
CommonModule,
FormsModule,
CoreModule,
ReactiveFormsModule,
MatTableModule,
MatPaginatorModule,
MatSortModule,
MatDialogModule,
MatFormFieldModule,
MatInputModule,
MatButtonModule,
MatCheckboxModule,
MatIconModule,
MatProgressSpinnerModule,
AdminBreadcrumbModule,
MatSelectModule,
MatOptionModule,
MatTooltipModule,
],
exports: [
EventDashboardComponent
],
providers: [
EventService
]
})
export class AdminEventsModule { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<div class="dialog-modal">
<div class="dialog-header">
<h2 class="dialog-title">Create a New Event</h2>
</div>

<div class="dialog-content">
<div *ngIf="errorMessage" class="error-popover">
<i class="fa fa-exclamation-triangle"></i>
<span>{{ errorMessage }}</span>
</div>

<form [formGroup]="eventForm" class="event-form">
<div class="form-field">
<label class="field-label">Event Name</label>
<input
type="text"
class="form-input"
formControlName="name"
placeholder="Enter event name"
/>
<div class="field-error">
<span
class="error-text"
[class.visible]="
eventForm.get('name').invalid &&
(eventForm.get('name').touched || eventForm.get('name').dirty)
"
>
Name is required
</span>
</div>
</div>

<div class="form-field">
<label class="field-label">Description</label>
<textarea
class="form-input"
formControlName="description"
rows="3"
placeholder="Enter event description (optional)"
></textarea>
</div>
</form>
</div>

<div class="dialog-actions">
<button class="action-button" (click)="cancel()">Cancel</button>
<button
class="action-button btn-primary"
(click)="save()"
[disabled]="eventForm.invalid"
>
<i class="fa fa-save"></i>
Create Event
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
.error-alert {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 1rem;
background-color: #fef2f2;
border: 1px solid #fecaca;
border-radius: 8px;
margin-bottom: 1.5rem;
color: #dc2626;
font-size: 0.875rem;

i {
flex-shrink: 0;
font-size: 1rem;
}
}

.event-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}

.form-field {
display: flex;
flex-direction: column;
gap: 0.5rem;

.field-label {
font-size: 0.875rem;
font-weight: 600;
color: #374151;
text-transform: uppercase;
letter-spacing: 0.05em;
}

.form-input {
width: 100%;
padding: 0.75rem 1rem;
border: 1px solid #d1d5db;
border-radius: 8px;
font-size: 0.875rem;
background-color: #f9fafb;
transition: all 0.2s ease;
resize: vertical;

&:focus {
outline: none;
border-color: #3b82f6;
background-color: white;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
}

&::placeholder {
color: #9ca3af;
}

&:disabled {
background-color: #f3f4f6;
color: #9ca3af;
cursor: not-allowed;
}
}

.field-error {
min-height: 1.2em;
display: flex;
align-items: center;
gap: 4px;
}

.field-error .error-text {
opacity: 0;
transition: opacity 0.2s ease;
color: #d9534f;
}

.field-error .error-text.visible {
opacity: 1;
}
}

::ng-deep .mat-dialog-container {
padding: 0;
border-radius: 12px;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1),
0 10px 10px -5px rgba(0, 0, 0, 0.04) !important;
}

::ng-deep mat-dialog-container {
height: 460px;
width: 700px;
}

::ng-deep .mat-dialog-actions {
padding: 0;
margin: 0;
}

::ng-deep .mat-dialog-content {
padding: 0;
margin: 0;
}

::ng-deep .mat-dialog-title {
padding: 0;
margin: 0;
}

.error-popover {
position: absolute;
top: 10px;
left: 50%;
transform: translateX(-50%);
background-color: #f8d7da;
color: #721c24;
padding: 10px 20px;
border-radius: 4px;
border: 1px solid #f5c6cb;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
gap: 8px;
z-index: 1000;
animation: slideDown 0.3s ease;
}

@keyframes slideDown {
from {
opacity: 0;
transform: translate(-50%, -10px);
}
to {
opacity: 1;
transform: translate(-50%, 0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Component, Inject } from '@angular/core';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { EventsService } from '../events.service';
import { Event } from 'src/app/filter/filter.types';

/**
* Dialog component for creating new events.
* Provides a form interface with validation for event name (required) and description (optional).
*/
@Component({
selector: 'mage-admin-event-create',
templateUrl: './create-event.component.html',
styleUrls: ['./create-event.component.scss']
})
export class CreateEventDialogComponent {
eventForm: FormGroup;
errorMessage: string = '';

constructor(
public dialogRef: MatDialogRef<CreateEventDialogComponent>,
@Inject(MAT_DIALOG_DATA) public data: { event: Partial<Event> },
private fb: FormBuilder,
private eventsService: EventsService
) {
this.eventForm = this.fb.group({
name: [data.event?.name ?? '', [Validators.required]],
description: [data.event?.description ?? '']
});
}

/**
* Handles form submission for creating a new event.
* Validates the form, creates the event via the events service, and closes the dialog on success.
*/
save(): void {
if (this.eventForm.invalid) {
this.errorMessage = 'Please fill in all required fields.';
return;
}

this.errorMessage = '';
const eventData = this.eventForm.value;
this.eventsService.createEvent(eventData).subscribe({
next: (newEvent) => {
this.dialogRef.close(newEvent);
},
error: (err) => {
if (err.status === 400 && err.error?.errors) {
const fieldErrors = err.error.errors;
if (fieldErrors.name?.type === 'unique') {
this.errorMessage = fieldErrors.name.message;
} else {
this.errorMessage = err.error.message ?? 'Validation failed';
}
} else {
this.errorMessage = 'Failed to create event. Please try again.';
}
}
});
}

/**
* Closes the dialog without saving any data or making any changes.
*/
cancel(): void {
this.dialogRef.close();
}
}
Loading