added partial first page of basic event setup step
This commit is contained in:
parent
f8e7b816bc
commit
0314855a0d
|
@ -1,6 +1,25 @@
|
|||
<div class="flex flex-col h-full">
|
||||
<div class="flex-grow overflow-y-auto px-8 py-8">
|
||||
<div class="w-full flex flex-col">
|
||||
<div class="w-full bg-neutral max-w-full sticky top-0 z-20 pt-2">
|
||||
<ul class="steps steps-horizontal w-full py-2">
|
||||
@for (step of steps; track $index) {
|
||||
<li
|
||||
[class.step-primary]="$index === currentStep()"
|
||||
[class.step-success]="$index < currentStep() && isStepValid($index)"
|
||||
[class.step-warning]="$index < currentStep() && !isStepValid($index)"
|
||||
[attr.data-content]="getStepContent($index)"
|
||||
class="step text-sm md:text-base"
|
||||
(click)="goToStep($index)"
|
||||
[class.cursor-pointer]="
|
||||
$index <= currentStep() || canAdvanceToStep($index)
|
||||
">
|
||||
<span>{{ step }}</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<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="">
|
||||
@if (currentStep() === 0) {
|
||||
<app-basic-step></app-basic-step>
|
||||
|
@ -10,23 +29,34 @@
|
|||
</div>
|
||||
|
||||
<div
|
||||
[ngStyle]="actionbar"
|
||||
class="w-full max-w-full bg-primary sticky bottom-0 z-50">
|
||||
<!-- <button type="button" class="btn btn-primary" (click)="prevStep()">
|
||||
Prev
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary" (click)="nextStep()">
|
||||
Next
|
||||
</button> -->
|
||||
|
||||
<ul class="steps steps-horizontal w-full">
|
||||
@for (step of steps; track $index) {
|
||||
<li
|
||||
[class.step-accent]="$index <= currentStep()"
|
||||
class="step text-sm md:text-base">
|
||||
<span>{{ step }}</span>
|
||||
</li>
|
||||
}
|
||||
</ul>
|
||||
class="w-full bg-neutral max-w-full sticky bottom-0 z-20 px-4 sm:px-8 py-4">
|
||||
<div class="flex justify-between max-w-4xl mx-auto">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
(click)="prevStep()"
|
||||
[disabled]="currentStep() === 0"
|
||||
[class.opacity-50]="currentStep() === 0">
|
||||
@if (currentStep() > 0) {
|
||||
<span>
|
||||
Back to
|
||||
<span class="font-bold">{{ steps[currentStep() - 1] }}</span>
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
(click)="nextStep()"
|
||||
[disabled]="currentStep() === steps.length - 1"
|
||||
[class.opacity-50]="currentStep() === steps.length - 1">
|
||||
@if (currentStep() < steps.length - 1) {
|
||||
<span>
|
||||
Next to
|
||||
<span class="font-bold">{{ steps[currentStep() + 1] }}</span>
|
||||
</span>
|
||||
}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,15 +4,9 @@ import {
|
|||
ChangeDetectionStrategy,
|
||||
signal,
|
||||
WritableSignal,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
BackgroundPatternService,
|
||||
ThemeService,
|
||||
} from '../../../shared/service';
|
||||
|
||||
import { BasicStepComponent } from './steps/basic-step.component';
|
||||
|
||||
@Component({
|
||||
|
@ -24,42 +18,51 @@ import { BasicStepComponent } from './steps/basic-step.component';
|
|||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateEventComponent implements OnInit {
|
||||
public actionbar: { 'background-image': string } | null = null;
|
||||
public currentStep: WritableSignal<number> = signal(0);
|
||||
public readonly steps: string[] = ['Basic', 'Tickets', 'Review'];
|
||||
|
||||
public constructor(
|
||||
private readonly backgroundPatternService: BackgroundPatternService,
|
||||
private readonly themeService: ThemeService,
|
||||
private readonly el: ElementRef
|
||||
) {}
|
||||
public constructor() {}
|
||||
|
||||
public ngOnInit(): void {
|
||||
this.setBackground();
|
||||
public ngOnInit(): void {}
|
||||
|
||||
public getStepContent(index: number): string {
|
||||
if (index < this.currentStep()) {
|
||||
return this.isStepValid(index) ? '✓' : '?';
|
||||
} else {
|
||||
return (index + 1).toString();
|
||||
}
|
||||
}
|
||||
|
||||
public setBackground(): void {
|
||||
const theme = this.themeService.getTheme();
|
||||
let opacity: number;
|
||||
|
||||
if (theme === 'dark') {
|
||||
opacity = 0.05;
|
||||
} else {
|
||||
opacity = 0.1;
|
||||
public goToStep(stepIndex: number): void {
|
||||
if (stepIndex < this.currentStep()) {
|
||||
this.currentStep.set(stepIndex);
|
||||
} else if (this.canAdvanceToStep(stepIndex)) {
|
||||
this.currentStep.set(stepIndex);
|
||||
}
|
||||
}
|
||||
|
||||
const colorPrimaryC = getComputedStyle(
|
||||
this.el.nativeElement
|
||||
).getPropertyValue('--pc');
|
||||
public canAdvanceToStep(stepIndex: number): boolean {
|
||||
for (let i = this.currentStep(); i < stepIndex; i++) {
|
||||
if (!this.isStepValid(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
const svgUrlActionbar = this.backgroundPatternService.getBankNotePattern(
|
||||
colorPrimaryC,
|
||||
opacity
|
||||
);
|
||||
public isStepValid(stepIndex: number): boolean {
|
||||
// TODO: Implementiere die tatsächliche Validierungslogik hier
|
||||
// Für Demonstrationszwecke nehmen wir an, dass der erste Schritt immer gültig ist,
|
||||
// und die anderen zufällig gültig oder ungültig sind
|
||||
return true;
|
||||
|
||||
this.actionbar = {
|
||||
'background-image': `url("${svgUrlActionbar}")`,
|
||||
};
|
||||
// In Zukunft könnte es so aussehen:
|
||||
// switch(stepIndex) {
|
||||
// case 0: return this.isBasicStepValid();
|
||||
// case 1: return this.isTicketsStepValid();
|
||||
// case 2: return this.isReviewStepValid();
|
||||
// default: return false;
|
||||
// }
|
||||
}
|
||||
|
||||
public nextStep(): void {
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
<form class="flex flex-col">
|
||||
<h2 class="text-2xl font-bold mb-6">Event Basic Information</h2>
|
||||
<h1 class="text-2xl font-bold mb-6">Event Basic Information</h1>
|
||||
|
||||
<div class="form-control mb-4">
|
||||
<label class="label" for="event-title">
|
||||
<label class="form-control w-full mb-4">
|
||||
<div class="label">
|
||||
<span class="label-text">Event Title</span>
|
||||
</label>
|
||||
<span class="label-text-alt">Make it catchy and descriptive</span>
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
id="event-title"
|
||||
placeholder="Enter your event title"
|
||||
class="input input-bordered w-full" />
|
||||
<div class="label">
|
||||
<span class="label-text-alt"></span>
|
||||
<span class="label-text-alt"></span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
<div class="form-control w-full mb-4">
|
||||
<app-dropdown
|
||||
label="Event Location"
|
||||
placeholder="Search for a Location"
|
||||
hintTopRight="Select or create a new Location by using the dropdown"
|
||||
dividerText="Or add a new Location"
|
||||
newItemText="Create new Location"
|
||||
emptyStateText="No matching Locations found"
|
||||
[items]="items"></app-dropdown>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<!-- <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div class="form-control">
|
||||
<label class="label" for="event-date">
|
||||
<span class="label-text">Date</span>
|
||||
|
@ -25,8 +40,8 @@
|
|||
</label>
|
||||
<input type="time" id="event-time" class="input input-bordered" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
<!--
|
||||
<div class="form-control mb-4">
|
||||
<label class="label" for="location-title">
|
||||
<span class="label-text">Location</span>
|
||||
|
@ -36,9 +51,9 @@
|
|||
id="location-title"
|
||||
placeholder="Enter your event title"
|
||||
class="input input-bordered w-full" />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="form-control mb-4">
|
||||
<!-- <div class="form-control mb-4">
|
||||
<label class="label" for="event-street">
|
||||
<span class="label-text">Street</span>
|
||||
</label>
|
||||
|
@ -47,9 +62,9 @@
|
|||
id="event-street"
|
||||
placeholder="Street name"
|
||||
class="input input-bordered" />
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<!-- <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||
<div class="form-control">
|
||||
<label class="label" for="event-number">
|
||||
<span class="label-text">Street Number</span>
|
||||
|
@ -70,9 +85,9 @@
|
|||
placeholder="Postal code"
|
||||
class="input input-bordered" />
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="form-control mb-6">
|
||||
<!-- <div class="form-control mb-6">
|
||||
<label class="label" for="event-city">
|
||||
<span class="label-text">City</span>
|
||||
</label>
|
||||
|
@ -81,5 +96,5 @@
|
|||
id="event-city"
|
||||
placeholder="City"
|
||||
class="input input-bordered" />
|
||||
</div>
|
||||
</div> -->
|
||||
</form>
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
import { DropdownComponent } from '../../../../shared/components/dropdown/dropdown.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-basic-step',
|
||||
standalone: true,
|
||||
templateUrl: './basic-step.component.html',
|
||||
providers: [],
|
||||
imports: [],
|
||||
imports: [CommonModule, FormsModule, DropdownComponent],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class BasicStepComponent {
|
||||
public items: string[] = ['Option 1', 'Option 2', 'Option 3'];
|
||||
|
||||
public constructor() {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<div class="form-control w-full mb-4">
|
||||
<div class="label">
|
||||
<span class="label-text">{{ label() }}</span>
|
||||
<span class="label-text-alt">
|
||||
{{ hintTopRight() }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<label class="input input-bordered flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
[placeholder]="placeholder()"
|
||||
class="grow"
|
||||
(focus)="onFocus()"
|
||||
(blur)="onBlur()"
|
||||
(input)="onInput($event)"
|
||||
[(ngModel)]="searchTerm" />
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6"
|
||||
[class.rotate-180]="showDropdown()">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m19.5 8.25-7.5 7.5-7.5-7.5" />
|
||||
</svg>
|
||||
</label>
|
||||
@if (showDropdown()) {
|
||||
<ul
|
||||
class="menu bg-base-100 w-full mt-1 shadow-lg rounded-box absolute top-full left-0 z-10">
|
||||
@if (filteredItems().length > 0) {
|
||||
@for (item of filteredItems(); track item) {
|
||||
<li>
|
||||
<a
|
||||
(mousedown)="selectItem(item)"
|
||||
class="flex justify-between items-center"
|
||||
[class.font-bold]="item === selectedItem()"
|
||||
[class.text-primary]="item === selectedItem()">
|
||||
<span>{{ item }}</span>
|
||||
@if (item === selectedItem()) {
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m4.5 12.75 6 6 9-13.5" />
|
||||
</svg>
|
||||
}
|
||||
</a>
|
||||
</li>
|
||||
}
|
||||
<div class="divider">{{ dividerText() }}</div>
|
||||
} @else if (searchTerm()) {
|
||||
<li
|
||||
(mousedown)="submitNewItem()"
|
||||
class="text-center py-2 text-gray-500">
|
||||
{{ emptyStateText() }}
|
||||
</li>
|
||||
}
|
||||
<li>
|
||||
<a
|
||||
(mousedown)="submitNewItem()"
|
||||
class="flex items-center text-primary">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="size-6 mr-2">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M12 4.5v15m7.5-7.5h-15" />
|
||||
</svg>
|
||||
{{ newItemText() }}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
}
|
||||
</div>
|
||||
<div class="label">
|
||||
<span class="label-text-alt">{{ hintBottomLeft() }}</span>
|
||||
<span class="label-text-alt">{{ hintBottomRight() }}</span>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,69 @@
|
|||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
Component,
|
||||
input,
|
||||
InputSignal,
|
||||
output,
|
||||
OutputEmitterRef,
|
||||
signal,
|
||||
WritableSignal,
|
||||
} from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@Component({
|
||||
selector: 'app-dropdown',
|
||||
templateUrl: './dropdown.component.html',
|
||||
standalone: true,
|
||||
imports: [CommonModule, FormsModule],
|
||||
})
|
||||
export class DropdownComponent {
|
||||
public label: InputSignal<string> = input.required<string>();
|
||||
public placeholder: InputSignal<string> = input.required<string>();
|
||||
public items: InputSignal<string[]> = input.required<string[]>();
|
||||
public emptyStateText: InputSignal<string> = input<string>('');
|
||||
public dividerText: InputSignal<string> = input<string>('');
|
||||
public newItemText: InputSignal<string> = input<string>('');
|
||||
public required: InputSignal<boolean> = input<boolean>(false);
|
||||
public hintTopRight: InputSignal<string> = input<string>('');
|
||||
public hintBottomLeft: InputSignal<string> = input<string>('');
|
||||
public hintBottomRight: InputSignal<string> = input<string>('');
|
||||
public itemSelected: OutputEmitterRef<string> = output<string>();
|
||||
public searchTerm: WritableSignal<string> = signal('');
|
||||
public showDropdown: WritableSignal<boolean> = signal<boolean>(false);
|
||||
public filteredItems: WritableSignal<string[]> = signal<string[]>([]);
|
||||
public selectedItem: WritableSignal<string | null> = signal<string | null>(
|
||||
null
|
||||
);
|
||||
|
||||
public onInput(event: Event): void {
|
||||
const value = (event.target as HTMLInputElement).value;
|
||||
|
||||
this.searchTerm.set(value);
|
||||
this.filteredItems.set(
|
||||
this.items().filter((item) =>
|
||||
item.toLowerCase().includes(value.toLowerCase())
|
||||
)
|
||||
);
|
||||
this.showDropdown.set(true);
|
||||
}
|
||||
|
||||
public selectItem(item: string): void {
|
||||
this.searchTerm.set(item);
|
||||
this.selectedItem.set(item);
|
||||
this.showDropdown.set(false);
|
||||
this.itemSelected.emit(item);
|
||||
}
|
||||
|
||||
public submitNewItem(): void {}
|
||||
|
||||
public onFocus(): void {
|
||||
this.showDropdown.set(true);
|
||||
this.filteredItems.set(this.items());
|
||||
}
|
||||
|
||||
public onBlur(): void {
|
||||
setTimeout(() => {
|
||||
this.showDropdown.set(false);
|
||||
}, 200);
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from './dropdown/dropdown.component';
|
Loading…
Reference in New Issue