produzione 1 #9

Merged
JoeKung merged 135 commits from dev into main 2026-03-03 09:58:04 +01:00
12 changed files with 112 additions and 92 deletions
Showing only changes of commit 54d12f4da0 - Show all commits

View File

@@ -46,14 +46,11 @@
</div> </div>
<!-- User Type Selector --> <!-- User Type Selector -->
<div class="user-type-selector"> <app-toggle-selector
<div class="type-option" [class.selected]="!isCompany" (click)="setCustomerType(false)"> [options]="userTypeOptions"
{{ 'CONTACT.TYPE_PRIVATE' | translate }} [selectedValue]="checkoutForm.get('customerType')?.value"
</div> (selectionChange)="setCustomerType($event)">
<div class="type-option" [class.selected]="isCompany" (click)="setCustomerType(true)"> </app-toggle-selector>
{{ 'CONTACT.TYPE_COMPANY' | translate }}
</div>
</div>
<app-input formControlName="addressLine1" [label]="'CHECKOUT.ADDRESS_1' | translate" [required]="true"></app-input> <app-input formControlName="addressLine1" [label]="'CHECKOUT.ADDRESS_1' | translate" [required]="true"></app-input>
<app-input formControlName="addressLine2" [label]="'CHECKOUT.ADDRESS_2' | translate"></app-input> <app-input formControlName="addressLine2" [label]="'CHECKOUT.ADDRESS_2' | translate"></app-input>

View File

@@ -64,39 +64,7 @@
} }
} }
/* User Type Selector - Matching Contact Form Style */ /* User Type Selector CSS has been extracted to app-toggle-selector component */
.user-type-selector {
display: flex;
background-color: var(--color-neutral-100);
border-radius: var(--radius-md);
padding: 4px;
margin: var(--space-6) 0;
gap: 4px;
width: 100%;
max-width: 400px;
}
.type-option {
flex: 1;
text-align: center;
padding: 8px 16px;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-muted);
transition: all 0.2s ease;
user-select: none;
&:hover { color: var(--color-text); }
&.selected {
background-color: var(--color-brand);
color: #000;
font-weight: 600;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
}
.company-fields { .company-fields {
display: flex; display: flex;

View File

@@ -7,6 +7,7 @@ import { QuoteEstimatorService } from '../calculator/services/quote-estimator.se
import { AppInputComponent } from '../../shared/components/app-input/app-input.component'; import { AppInputComponent } from '../../shared/components/app-input/app-input.component';
import { AppButtonComponent } from '../../shared/components/app-button/app-button.component'; import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
import { AppCardComponent } from '../../shared/components/app-card/app-card.component'; import { AppCardComponent } from '../../shared/components/app-card/app-card.component';
import { AppToggleSelectorComponent, ToggleOption } from '../../shared/components/app-toggle-selector/app-toggle-selector.component';
@Component({ @Component({
selector: 'app-checkout', selector: 'app-checkout',
@@ -17,7 +18,8 @@ import { AppCardComponent } from '../../shared/components/app-card/app-card.comp
TranslateModule, TranslateModule,
AppInputComponent, AppInputComponent,
AppButtonComponent, AppButtonComponent,
AppCardComponent AppCardComponent,
AppToggleSelectorComponent
], ],
templateUrl: './checkout.component.html', templateUrl: './checkout.component.html',
styleUrls: ['./checkout.component.scss'] styleUrls: ['./checkout.component.scss']
@@ -35,6 +37,11 @@ export class CheckoutComponent implements OnInit {
isSubmitting = signal(false); // Add signal for submit state isSubmitting = signal(false); // Add signal for submit state
quoteSession = signal<any>(null); // Add signal for session details quoteSession = signal<any>(null); // Add signal for session details
userTypeOptions: ToggleOption[] = [
{ label: 'CONTACT.TYPE_PRIVATE', value: 'PRIVATE' },
{ label: 'CONTACT.TYPE_COMPANY', value: 'BUSINESS' }
];
constructor() { constructor() {
this.checkoutForm = this.fb.group({ this.checkoutForm = this.fb.group({
email: ['', [Validators.required, Validators.email]], email: ['', [Validators.required, Validators.email]],
@@ -73,9 +80,9 @@ export class CheckoutComponent implements OnInit {
return this.checkoutForm.get('customerType')?.value === 'BUSINESS'; return this.checkoutForm.get('customerType')?.value === 'BUSINESS';
} }
setCustomerType(isCompany: boolean) { setCustomerType(type: string) {
const type = isCompany ? 'BUSINESS' : 'PRIVATE';
this.checkoutForm.patchValue({ customerType: type }); this.checkoutForm.patchValue({ customerType: type });
const isCompany = type === 'BUSINESS';
const billingGroup = this.checkoutForm.get('billingAddress') as FormGroup; const billingGroup = this.checkoutForm.get('billingAddress') as FormGroup;
const companyControl = billingGroup.get('companyName'); const companyControl = billingGroup.get('companyName');

View File

@@ -107,6 +107,12 @@ export class OrderComponent implements OnInit {
if (lang === 'de') { if (lang === 'de') {
return 'https://go.twint.ch/static/img/button_dark_de.svg'; return 'https://go.twint.ch/static/img/button_dark_de.svg';
} }
if (lang === 'it'){
return 'https://go.twint.ch/static/img/button_dark_it.svg';
}
if (lang === 'fr'){
return 'https://go.twint.ch/static/img/button_dark_fr.svg';
}
// Default to EN for everything else (it, fr, en) as instructed or if not DE // Default to EN for everything else (it, fr, en) as instructed or if not DE
return 'https://go.twint.ch/static/img/button_dark_en.svg'; return 'https://go.twint.ch/static/img/button_dark_en.svg';
} }

View File

@@ -8,18 +8,11 @@
<div class="locations-grid"> <div class="locations-grid">
<div class="locations-controls"> <div class="locations-controls">
<div class="location-tabs"> <div class="location-tabs">
<button <app-toggle-selector
class="tab-btn" [options]="locationOptions"
[class.active]="selectedLocation === 'ticino'" [selectedValue]="selectedLocation"
(click)="selectLocation('ticino')"> (selectionChange)="selectLocation($event)">
{{ 'LOCATIONS.TICINO' | translate }} </app-toggle-selector>
</button>
<button
class="tab-btn"
[class.active]="selectedLocation === 'bienne'"
(click)="selectLocation('bienne')">
{{ 'LOCATIONS.BIENNE' | translate }}
</button>
</div> </div>
<div class="location-details"> <div class="location-details">
@@ -43,12 +36,12 @@
<div class="map-container"> <div class="map-container">
<iframe <iframe
*ngIf="selectedLocation === 'ticino'" *ngIf="selectedLocation === 'ticino'"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d177305.41680509657!2d8.826330925208468!3d46.00511195679905!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x4784407817452d5b%3A0x6a0c0e86b976660!2sTicino%2C%20Svizzera!5e0!3m2!1sit!2sit!4v1700000000000!5m2!1sit!2sit" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2753.843657475132!2d8.96627047671755!3d46.35265507912163!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x4785ca88d01d4a03%3A0xc312484ab7592fb!2sVia%20Giov.Bta.%20Pioda%2029A%2C%206710%20Biasca%2C%20Switzerland!5e0!3m2!1sen!2sch!4v1700000000000!5m2!1sen!2sch"
width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"> width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade">
</iframe> </iframe>
<iframe <iframe
*ngIf="selectedLocation === 'bienne'" *ngIf="selectedLocation === 'bienne'"
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d43521.94247549427!2d7.211756262451172!3d47.13677764958641!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x478e192760f384f9%3A0xcb281f622956f4e6!2sBiel%2C%20Svizzera!5e0!3m2!1sit!2sit!4v1700000000000!5m2!1sit!2sit" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2729.0438104193587!2d7.240752176735282!3d47.126435979155985!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x478e1eb84efba295%3A0x95924d5ba8b6f3b0!2sLyss-Strasse%2071%2C%202560%20Nidau%2C%20Switzerland!5e0!3m2!1sen!2sch!4v1700000000000!5m2!1sen!2sch"
width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade"> width="100%" height="450" style="border:0;" allowfullscreen="" loading="lazy" referrerpolicy="no-referrer-when-downgrade">
</iframe> </iframe>
</div> </div>

View File

@@ -34,36 +34,10 @@
} }
.location-tabs { .location-tabs {
display: flex;
gap: 1rem;
margin-bottom: 2rem; margin-bottom: 2rem;
background: var(--color-bg); width: 100%;
padding: 0.5rem;
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
} }
.tab-btn {
flex: 1;
padding: 0.75rem;
border: none;
background: transparent;
border-radius: var(--radius-sm);
color: var(--color-text-muted);
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease;
&:hover {
color: var(--color-text-main);
}
&.active {
background: var(--color-primary-500);
color: var(--color-neutral-900);
box-shadow: var(--shadow-sm);
}
}
.location-details { .location-details {
padding: 2rem; padding: 2rem;

View File

@@ -2,18 +2,24 @@ import { Component } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
import { AppToggleSelectorComponent, ToggleOption } from '../app-toggle-selector/app-toggle-selector.component';
@Component({ @Component({
selector: 'app-locations', selector: 'app-locations',
standalone: true, standalone: true,
imports: [CommonModule, TranslateModule, RouterLink], imports: [CommonModule, TranslateModule, RouterLink, AppToggleSelectorComponent],
templateUrl: './app-locations.component.html', templateUrl: './app-locations.component.html',
styleUrl: './app-locations.component.scss' styleUrl: './app-locations.component.scss'
}) })
export class AppLocationsComponent { export class AppLocationsComponent {
selectedLocation: 'ticino' | 'bienne' = 'ticino'; selectedLocation: 'ticino' | 'bienne' = 'ticino';
selectLocation(location: 'ticino' | 'bienne') { locationOptions: ToggleOption[] = [
{ label: 'LOCATIONS.TICINO', value: 'ticino' },
{ label: 'LOCATIONS.BIENNE', value: 'bienne' },
];
selectLocation(location: any) {
this.selectedLocation = location; this.selectedLocation = location;
} }
} }

View File

@@ -0,0 +1,9 @@
<div class="user-type-selector">
@for (option of options(); track option.value) {
<div class="type-option"
[class.selected]="selectedValue() === option.value"
(click)="selectOption(option.value)">
{{ option.label | translate }}
</div>
}
</div>

View File

@@ -0,0 +1,34 @@
:host {
display: block;
}
.user-type-selector {
display: flex;
background-color: var(--color-neutral-100);
border-radius: var(--radius-md);
padding: 4px;
gap: 4px;
width: 100%;
}
.type-option {
flex: 1;
text-align: center;
padding: 8px 16px;
border-radius: var(--radius-sm);
cursor: pointer;
font-size: 0.875rem;
font-weight: 500;
color: var(--color-text-muted);
transition: all 0.2s ease;
user-select: none;
&:hover { color: var(--color-text); }
&.selected {
background-color: var(--color-brand);
color: #000;
font-weight: 600;
box-shadow: 0 1px 2px rgba(0,0,0,0.05);
}
}

View File

@@ -0,0 +1,26 @@
import { Component, input, output } from '@angular/core';
import { CommonModule } from '@angular/common';
import { TranslateModule } from '@ngx-translate/core';
export interface ToggleOption {
label: string;
value: any;
}
@Component({
selector: 'app-toggle-selector',
standalone: true,
imports: [CommonModule, TranslateModule],
templateUrl: './app-toggle-selector.component.html',
styleUrl: './app-toggle-selector.component.scss'
})
export class AppToggleSelectorComponent {
options = input.required<ToggleOption[]>();
selectedValue = input.required<any>();
selectionChange = output<any>();
selectOption(value: any) {
this.selectionChange.emit(value);
}
}

View File

@@ -107,8 +107,8 @@
"SUBTITLE": "We have two locations to serve you better. Select a location to see details.", "SUBTITLE": "We have two locations to serve you better. Select a location to see details.",
"TICINO": "Ticino", "TICINO": "Ticino",
"BIENNE": "Bienne", "BIENNE": "Bienne",
"ADDRESS_TICINO": "Ticino Office, Switzerland", "ADDRESS_TICINO": "Via G. Pioda 29a, 6710 Biasca (TI)",
"ADDRESS_BIENNE": "Bienne Office, Switzerland", "ADDRESS_BIENNE": "Lyss-strasse 71, Nidau 2560 Bienne",
"CONTACT_US": "Contact Us" "CONTACT_US": "Contact Us"
}, },
"LEGAL": { "LEGAL": {

View File

@@ -102,7 +102,7 @@
"PROCESSING": "Elaborazione...", "PROCESSING": "Elaborazione...",
"NOTES_PLACEHOLDER": "Istruzioni specifiche...", "NOTES_PLACEHOLDER": "Istruzioni specifiche...",
"SETUP_NOTE": "* Include {{cost}} Costo di Setup", "SETUP_NOTE": "* Include {{cost}} Costo di Setup",
"STEP_WARNING": "La visualizzazione 3D non è compatibile con i file step e 3mf, ma il calcolatore funziona." "STEP_WARNING": "La visualizzazione 3D non è compatibile con i file step e 3mf"
}, },
"QUOTE": { "QUOTE": {
"PROCEED_ORDER": "Procedi con l'ordine", "PROCEED_ORDER": "Procedi con l'ordine",
@@ -170,8 +170,8 @@
"SUBTITLE": "Siamo presenti in due sedi per coprire meglio il territorio. Seleziona la sede per vedere i dettagli.", "SUBTITLE": "Siamo presenti in due sedi per coprire meglio il territorio. Seleziona la sede per vedere i dettagli.",
"TICINO": "Ticino", "TICINO": "Ticino",
"BIENNE": "Bienne", "BIENNE": "Bienne",
"ADDRESS_TICINO": "Sede Ticino, Svizzera", "ADDRESS_TICINO": "Via G. Pioda 29a, 6710 Biasca (TI)",
"ADDRESS_BIENNE": "Sede Bienne, Svizzera", "ADDRESS_BIENNE": "Lyss-strasse 71, Nidau 2560 Bienne",
"CONTACT_US": "Contattaci" "CONTACT_US": "Contattaci"
}, },
"LEGAL": { "LEGAL": {