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 flex-col h-full">
|
||||||
<div class="flex-grow overflow-y-auto px-8 py-8">
|
<div class="w-full bg-neutral max-w-full sticky top-0 z-20 pt-2">
|
||||||
<div class="w-full flex flex-col">
|
<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="">
|
<div class="">
|
||||||
@if (currentStep() === 0) {
|
@if (currentStep() === 0) {
|
||||||
<app-basic-step></app-basic-step>
|
<app-basic-step></app-basic-step>
|
||||||
|
@ -10,23 +29,34 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
[ngStyle]="actionbar"
|
class="w-full bg-neutral max-w-full sticky bottom-0 z-20 px-4 sm:px-8 py-4">
|
||||||
class="w-full max-w-full bg-primary sticky bottom-0 z-50">
|
<div class="flex justify-between max-w-4xl mx-auto">
|
||||||
<!-- <button type="button" class="btn btn-primary" (click)="prevStep()">
|
<button
|
||||||
Prev
|
type="button"
|
||||||
</button>
|
class="btn btn-primary"
|
||||||
<button type="button" class="btn btn-primary" (click)="nextStep()">
|
(click)="prevStep()"
|
||||||
Next
|
[disabled]="currentStep() === 0"
|
||||||
</button> -->
|
[class.opacity-50]="currentStep() === 0">
|
||||||
|
@if (currentStep() > 0) {
|
||||||
<ul class="steps steps-horizontal w-full">
|
<span>
|
||||||
@for (step of steps; track $index) {
|
Back to
|
||||||
<li
|
<span class="font-bold">{{ steps[currentStep() - 1] }}</span>
|
||||||
[class.step-accent]="$index <= currentStep()"
|
</span>
|
||||||
class="step text-sm md:text-base">
|
}
|
||||||
<span>{{ step }}</span>
|
</button>
|
||||||
</li>
|
<button
|
||||||
}
|
type="button"
|
||||||
</ul>
|
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>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,15 +4,9 @@ import {
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
signal,
|
signal,
|
||||||
WritableSignal,
|
WritableSignal,
|
||||||
ElementRef,
|
|
||||||
OnInit,
|
OnInit,
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
|
|
||||||
import {
|
|
||||||
BackgroundPatternService,
|
|
||||||
ThemeService,
|
|
||||||
} from '../../../shared/service';
|
|
||||||
|
|
||||||
import { BasicStepComponent } from './steps/basic-step.component';
|
import { BasicStepComponent } from './steps/basic-step.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -24,42 +18,51 @@ import { BasicStepComponent } from './steps/basic-step.component';
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class CreateEventComponent implements OnInit {
|
export class CreateEventComponent implements OnInit {
|
||||||
public actionbar: { 'background-image': string } | null = null;
|
|
||||||
public currentStep: WritableSignal<number> = signal(0);
|
public currentStep: WritableSignal<number> = signal(0);
|
||||||
public readonly steps: string[] = ['Basic', 'Tickets', 'Review'];
|
public readonly steps: string[] = ['Basic', 'Tickets', 'Review'];
|
||||||
|
|
||||||
public constructor(
|
public constructor() {}
|
||||||
private readonly backgroundPatternService: BackgroundPatternService,
|
|
||||||
private readonly themeService: ThemeService,
|
|
||||||
private readonly el: ElementRef
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {}
|
||||||
this.setBackground();
|
|
||||||
|
public getStepContent(index: number): string {
|
||||||
|
if (index < this.currentStep()) {
|
||||||
|
return this.isStepValid(index) ? '✓' : '?';
|
||||||
|
} else {
|
||||||
|
return (index + 1).toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setBackground(): void {
|
public goToStep(stepIndex: number): void {
|
||||||
const theme = this.themeService.getTheme();
|
if (stepIndex < this.currentStep()) {
|
||||||
let opacity: number;
|
this.currentStep.set(stepIndex);
|
||||||
|
} else if (this.canAdvanceToStep(stepIndex)) {
|
||||||
if (theme === 'dark') {
|
this.currentStep.set(stepIndex);
|
||||||
opacity = 0.05;
|
|
||||||
} else {
|
|
||||||
opacity = 0.1;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const colorPrimaryC = getComputedStyle(
|
public canAdvanceToStep(stepIndex: number): boolean {
|
||||||
this.el.nativeElement
|
for (let i = this.currentStep(); i < stepIndex; i++) {
|
||||||
).getPropertyValue('--pc');
|
if (!this.isStepValid(i)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const svgUrlActionbar = this.backgroundPatternService.getBankNotePattern(
|
public isStepValid(stepIndex: number): boolean {
|
||||||
colorPrimaryC,
|
// TODO: Implementiere die tatsächliche Validierungslogik hier
|
||||||
opacity
|
// 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 = {
|
// In Zukunft könnte es so aussehen:
|
||||||
'background-image': `url("${svgUrlActionbar}")`,
|
// switch(stepIndex) {
|
||||||
};
|
// case 0: return this.isBasicStepValid();
|
||||||
|
// case 1: return this.isTicketsStepValid();
|
||||||
|
// case 2: return this.isReviewStepValid();
|
||||||
|
// default: return false;
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
public nextStep(): void {
|
public nextStep(): void {
|
||||||
|
|
|
@ -1,18 +1,33 @@
|
||||||
<form class="flex flex-col">
|
<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="form-control w-full mb-4">
|
||||||
<label class="label" for="event-title">
|
<div class="label">
|
||||||
<span class="label-text">Event Title</span>
|
<span class="label-text">Event Title</span>
|
||||||
</label>
|
<span class="label-text-alt">Make it catchy and descriptive</span>
|
||||||
|
</div>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="event-title"
|
|
||||||
placeholder="Enter your event title"
|
placeholder="Enter your event title"
|
||||||
class="input input-bordered w-full" />
|
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>
|
||||||
|
|
||||||
<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">
|
<div class="form-control">
|
||||||
<label class="label" for="event-date">
|
<label class="label" for="event-date">
|
||||||
<span class="label-text">Date</span>
|
<span class="label-text">Date</span>
|
||||||
|
@ -25,8 +40,8 @@
|
||||||
</label>
|
</label>
|
||||||
<input type="time" id="event-time" class="input input-bordered" />
|
<input type="time" id="event-time" class="input input-bordered" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
<!--
|
||||||
<div class="form-control mb-4">
|
<div class="form-control mb-4">
|
||||||
<label class="label" for="location-title">
|
<label class="label" for="location-title">
|
||||||
<span class="label-text">Location</span>
|
<span class="label-text">Location</span>
|
||||||
|
@ -36,9 +51,9 @@
|
||||||
id="location-title"
|
id="location-title"
|
||||||
placeholder="Enter your event title"
|
placeholder="Enter your event title"
|
||||||
class="input input-bordered w-full" />
|
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">
|
<label class="label" for="event-street">
|
||||||
<span class="label-text">Street</span>
|
<span class="label-text">Street</span>
|
||||||
</label>
|
</label>
|
||||||
|
@ -47,9 +62,9 @@
|
||||||
id="event-street"
|
id="event-street"
|
||||||
placeholder="Street name"
|
placeholder="Street name"
|
||||||
class="input input-bordered" />
|
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">
|
<div class="form-control">
|
||||||
<label class="label" for="event-number">
|
<label class="label" for="event-number">
|
||||||
<span class="label-text">Street Number</span>
|
<span class="label-text">Street Number</span>
|
||||||
|
@ -70,9 +85,9 @@
|
||||||
placeholder="Postal code"
|
placeholder="Postal code"
|
||||||
class="input input-bordered" />
|
class="input input-bordered" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
|
|
||||||
<div class="form-control mb-6">
|
<!-- <div class="form-control mb-6">
|
||||||
<label class="label" for="event-city">
|
<label class="label" for="event-city">
|
||||||
<span class="label-text">City</span>
|
<span class="label-text">City</span>
|
||||||
</label>
|
</label>
|
||||||
|
@ -81,5 +96,5 @@
|
||||||
id="event-city"
|
id="event-city"
|
||||||
placeholder="City"
|
placeholder="City"
|
||||||
class="input input-bordered" />
|
class="input input-bordered" />
|
||||||
</div>
|
</div> -->
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
|
import { DropdownComponent } from '../../../../shared/components/dropdown/dropdown.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-basic-step',
|
selector: 'app-basic-step',
|
||||||
standalone: true,
|
standalone: true,
|
||||||
templateUrl: './basic-step.component.html',
|
templateUrl: './basic-step.component.html',
|
||||||
providers: [],
|
providers: [],
|
||||||
imports: [],
|
imports: [CommonModule, FormsModule, DropdownComponent],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class BasicStepComponent {
|
export class BasicStepComponent {
|
||||||
|
public items: string[] = ['Option 1', 'Option 2', 'Option 3'];
|
||||||
|
|
||||||
public constructor() {}
|
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