Feature: Create Event - First Step Frontend #17
|
@ -1,23 +1,18 @@
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<div class="w-full bg-base-100 max-w-full sticky top-0 z-10 pt-4">
|
<div
|
||||||
<ul class="steps steps-horizontal w-full py-2">
|
class="w-full bg-base-100 max-w-full sticky top-0 z-10 pt-4 px-4 sm:px-8">
|
||||||
@for (step of steps; track $index) {
|
<div class="w-full max-w-4xl mx-auto">
|
||||||
<li
|
<app-stepper-indicator
|
||||||
[class.step-primary]="$index === currentStep()"
|
[steps]="steps"
|
||||||
[class.step-success]="$index < currentStep() && isStepValid($index)"
|
[currentStep]="currentStep()"
|
||||||
[class.step-warning]="$index < currentStep() && !isStepValid($index)"
|
[isStepValid]="isStepValid.bind(this)"
|
||||||
[attr.data-content]="getStepContent($index)"
|
[canAdvanceToStep]="canAdvanceToStep.bind(this)"
|
||||||
class="step text-sm md:text-base"
|
(stepChange)="goToStep($event)"></app-stepper-indicator>
|
||||||
(click)="goToStep($index)"
|
</div>
|
||||||
[class.cursor-pointer]="
|
|
||||||
$index <= currentStep() || canAdvanceToStep($index)
|
|
||||||
">
|
|
||||||
<span>{{ step }}</span>
|
|
||||||
</li>
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Rest of the component remains the same -->
|
||||||
|
|
||||||
<div class="flex-grow overflow-y-auto px-4 sm:px-8 py-8">
|
<div class="flex-grow overflow-y-auto px-4 sm:px-8 py-8">
|
||||||
<div class="w-full max-w-4xl mx-auto">
|
<div class="w-full max-w-4xl mx-auto">
|
||||||
@if (currentStep() === 0) {
|
@if (currentStep() === 0) {
|
||||||
|
@ -33,29 +28,24 @@
|
||||||
class="w-full bg-base-100 max-w-full sticky bottom-0 z-10 px-4 sm:px-8 py-4">
|
class="w-full bg-base-100 max-w-full sticky bottom-0 z-10 px-4 sm:px-8 py-4">
|
||||||
<div class="flex justify-between max-w-4xl mx-auto">
|
<div class="flex justify-between max-w-4xl mx-auto">
|
||||||
<div>
|
<div>
|
||||||
@if (currentStep() > 0) {
|
<button
|
||||||
<button
|
type="button"
|
||||||
type="button"
|
class="btn btn-primary btn-outline"
|
||||||
class="btn btn-primary btn-outline"
|
(click)="prevStep()"
|
||||||
(click)="prevStep()"
|
[disabled]="currentStep() === 0"
|
||||||
[disabled]="currentStep() === 0"
|
[attr.aria-label]="getBackButtonAriaLabel()"
|
||||||
[class.opacity-50]="currentStep() === 0">
|
[class.opacity-50]="currentStep() === 0">
|
||||||
<span>
|
<span [innerHTML]="getBackButtonText()"></span>
|
||||||
Back to
|
</button>
|
||||||
<span class="font-bold">{{ steps[currentStep() - 1] }}</span>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@if (currentStep() < steps.length - 1) {
|
@if (currentStep() < steps.length - 1) {
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
[class]="getNextButtonClass()"
|
[class]="getNextButtonClass()"
|
||||||
(click)="nextStep()">
|
(click)="nextStep()"
|
||||||
<span>
|
[attr.aria-label]="getNextButtonAriaLabel()">
|
||||||
{{ getNextButtonText() }}
|
<span [innerHTML]="getNextButtonText()"></span>
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,8 @@ import {
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
|
||||||
|
import { StepperIndicatorComponent } from '../../../shared/components/stepper-indicator/stepper-indicator.component';
|
||||||
|
|
||||||
import { BasicStepComponent } from './steps/basic-step.component';
|
import { BasicStepComponent } from './steps/basic-step.component';
|
||||||
import { TicketsStepComponent } from './steps/tickets-step.component';
|
import { TicketsStepComponent } from './steps/tickets-step.component';
|
||||||
|
|
||||||
|
@ -16,7 +18,12 @@ import { TicketsStepComponent } from './steps/tickets-step.component';
|
||||||
selector: 'app-create-event',
|
selector: 'app-create-event',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
providers: [],
|
providers: [],
|
||||||
imports: [CommonModule, BasicStepComponent, TicketsStepComponent],
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
BasicStepComponent,
|
||||||
|
TicketsStepComponent,
|
||||||
|
StepperIndicatorComponent,
|
||||||
|
],
|
||||||
templateUrl: './create-event.component.html',
|
templateUrl: './create-event.component.html',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
|
@ -114,9 +121,35 @@ export class CreateEventComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
public getNextButtonText(): string {
|
public getNextButtonText(): string {
|
||||||
return this.hasAttemptedNextStep() && !this.isCurrentStepValid()
|
if (this.hasAttemptedNextStep() && !this.isCurrentStepValid()) {
|
||||||
? 'Required Fields Need Attention'
|
return 'Required Fields Need Attention';
|
||||||
: `Next to ${this.steps[this.currentStep() + 1]}`;
|
} else {
|
||||||
|
return `Next to <span class="font-bold">${this.steps[this.currentStep() + 1]}</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getNextButtonAriaLabel(): string {
|
||||||
|
if (this.hasAttemptedNextStep() && !this.isCurrentStepValid()) {
|
||||||
|
return 'Required Fields Need Attention';
|
||||||
|
} else {
|
||||||
|
return `Next to ${this.steps[this.currentStep() + 1]}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBackButtonText(): string {
|
||||||
|
if (this.currentStep() === 0) {
|
||||||
|
return 'Back';
|
||||||
|
} else {
|
||||||
|
return `Back to <span class="font-bold">${this.steps[this.currentStep() - 1]}</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getBackButtonAriaLabel(): string {
|
||||||
|
if (this.currentStep() === 0) {
|
||||||
|
return 'Back (disabled)';
|
||||||
|
} else {
|
||||||
|
return `Back to ${this.steps[this.currentStep() - 1]}`;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private markCurrentStepAsTouched(): void {
|
private markCurrentStepAsTouched(): void {
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<div class="w-full max-w-4xl mx-auto">
|
||||||
|
<div class="relative flex justify-between w-full mb-4">
|
||||||
|
<!-- Connecting lines -->
|
||||||
|
<div class="absolute top-5 left-0 right-0 h-1 flex">
|
||||||
|
@for (step of steps; track $index) {
|
||||||
|
@if ($index < steps.length - 1) {
|
||||||
|
<div
|
||||||
|
class="flex-1 mx-2 transition-all duration-300"
|
||||||
|
[class.bg-primary]="$index < currentStep"
|
||||||
|
[class.bg-success]="
|
||||||
|
$index < currentStep - 1 &&
|
||||||
|
isStepValid($index) &&
|
||||||
|
isStepValid($index + 1)
|
||||||
|
"
|
||||||
|
[class.bg-warning]="
|
||||||
|
$index < currentStep - 1 &&
|
||||||
|
(!isStepValid($index) || !isStepValid($index + 1))
|
||||||
|
"
|
||||||
|
[class.bg-base-300]="$index >= currentStep"></div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Steps -->
|
||||||
|
@for (step of steps; track $index) {
|
||||||
|
<div class="flex flex-col items-center z-10">
|
||||||
|
<button
|
||||||
|
[class.bg-primary]="$index <= currentStep"
|
||||||
|
[class.bg-success]="$index < currentStep && isStepValid($index)"
|
||||||
|
[class.bg-warning]="$index < currentStep && !isStepValid($index)"
|
||||||
|
[class.bg-base-300]="$index > currentStep"
|
||||||
|
class="w-10 h-10 rounded-full flex items-center justify-center text-sm font-bold text-base-100 transition-all duration-300 mb-2 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary"
|
||||||
|
[class.cursor-pointer]="
|
||||||
|
$index <= currentStep || canAdvanceToStep($index)
|
||||||
|
"
|
||||||
|
[attr.aria-label]="'Go to step ' + ($index + 1)"
|
||||||
|
[attr.tabindex]="
|
||||||
|
$index <= currentStep || canAdvanceToStep($index) ? 0 : -1
|
||||||
|
"
|
||||||
|
(click)="goToStep($index)"
|
||||||
|
(keydown.enter)="goToStep($index)"
|
||||||
|
(keydown.space)="goToStep($index); $event.preventDefault()">
|
||||||
|
@if ($index < currentStep && isStepValid($index)) {
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-6 w-6"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
aria-hidden="true">
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
} @else {
|
||||||
|
{{ $index + 1 }}
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
<span
|
||||||
|
class="text-center text-sm"
|
||||||
|
[class.font-bold]="$index === currentStep">
|
||||||
|
{{ step }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import { Component, Input, Output, EventEmitter } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-stepper-indicator',
|
||||||
|
standalone: true,
|
||||||
|
imports: [CommonModule],
|
||||||
|
templateUrl: './stepper-indicator.component.html',
|
||||||
|
styles: [],
|
||||||
|
})
|
||||||
|
export class StepperIndicatorComponent {
|
||||||
|
@Output() public stepChange: EventEmitter<number> =
|
||||||
|
new EventEmitter<number>();
|
||||||
|
@Input() public steps: string[] = [];
|
||||||
|
@Input() public currentStep: number = 0;
|
||||||
|
@Input() public isStepValid: (index: number) => boolean = () => true;
|
||||||
|
@Input() public canAdvanceToStep: (index: number) => boolean = () => true;
|
||||||
|
|
||||||
|
public goToStep(index: number): void {
|
||||||
|
if (index <= this.currentStep || this.canAdvanceToStep(index)) {
|
||||||
|
this.stepChange.emit(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue