Dates Table
import { ChangeDetectionStrategy, Component, computed, signal, Signal } from '@angular/core';
import { CommonModule } from '@angular/common';
/**
* Interface defining a choice for the Kilimanjaro climb options.
*/
interface ClimbOption {
id: string;
name: string;
minDays: number;
maxDays: number;
description: string;
}
/**
* Interface defining a choice for the Safari options.
*/
interface SafariOption {
id: string;
name: string;
description: string;
popularDurationDays: number;
}
/**
* Main application component for the itinerary builder.
*/
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule],
template: `
Tanzania Custom Adventure
Design Your Dream Climb & Safari Itinerary
@for (step of steps; track step; let i = $index) {
{{ i + 1 }}
{{ step.name }}
}
@if (currentStep() === 'climb') {
1. Choose Your Climb
Select a Kilimanjaro route that fits your challenge level and schedule.
{{ climb.name }}
{{ climb.minDays }}-{{ climb.maxDays }} Days
{{ climb.description }}
}
@if (selectedClimb()) {
{{ climbDays() }} days
We recommend more days for better acclimatization and a higher chance of success.
}
}
@if (currentStep() === 'safari') {
2. Design Your Safari
Explore the famous Northern Circuit or the secluded South.
{{ safari.name }}
Recommended: {{ safari.popularDurationDays }} Days
{{ safari.description }}
}
@if (selectedSafari()) {
{{ safariDays() }} days
A minimum of 3 days is required, but 5-7 days allows you to cover more parks and wildlife.
}
}
@if (currentStep() === 'summary') {
3. Your Complete Itinerary
The Climb: {{ selectedClimb()?.name || 'Not Selected' }}
@if (selectedClimb()) {
{{ climbDays() }} Days
{{ selectedClimb()!.description }}
} @else {
Please select a Kilimanjaro route.
}
The Safari: {{ selectedSafari()?.name || 'Not Selected' }}
@if (selectedSafari()) {
{{ safariDays() }} Days
{{ selectedSafari()!.description }}
} @else {
Please select a Safari circuit.
}
Next Steps:
{{ itinerarySummary() }}
}
@if (showModal()) {
Itinerary Submitted!
Thank you for building your dream adventure.
Summary:
{{ totalDays() }} Days: {{ selectedClimb()!.name }} Climb + {{ selectedSafari()!.name }} Safari.
Our team will be in touch within 24 hours to finalize details and provide a custom quote for your adventure.
}
`,
styles: `
/* Custom style for the range input thumb */
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
background: #3B82F6; /* Blue 500 */
border-radius: 50%;
cursor: pointer;
transition: background 0.15s ease-in-out;
}
input[type=range]:hover::-webkit-slider-thumb {
background: #1D4ED8; /* Blue 700 */
}
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class App {
// --- Data Definitions ---
readonly climbOptions: ClimbOption[] = [
{ id: 'lemosho', name: 'Lemosho Route', minDays: 7, maxDays: 9, description: 'The most scenic route with the highest summit success rate due to excellent acclimatization.' },
{ id: 'machame', name: 'Machame Route', minDays: 6, maxDays: 8, description: 'The "Whiskey Route." Very popular, challenging, and offering stunning views. Best done in 7 days.' },
{ id: 'marangu', name: 'Marangu Route', minDays: 5, maxDays: 6, description: 'The "Coca-Cola Route." The only one with hut accommodation, but has a lower success rate.' },
{ id: 'rongai', name: 'Rongai Route', minDays: 6, maxDays: 7, description: 'The only northern approach, drier conditions, and less crowded. Excellent for less experienced hikers.' },
];
readonly safariOptions: SafariOption[] = [
{ id: 'north', name: 'Northern Circuit Deluxe', description: 'Serengeti, Ngorongoro Crater, Tarangire, Lake Manyara. The ultimate Big Five experience.', popularDurationDays: 6 },
{ id: 'south', name: 'Southern Wilderness', description: 'Ruaha National Park & Nyerere (Selous). Exclusive, rugged, and less crowded game viewing.', popularDurationDays: 7 },
{ id: 'west', name: 'Western Chimpanzee Trek', description: 'Gombe Stream and Mahale Mountains. Focus on primate viewing and lake life.', popularDurationDays: 4 },
];
// --- State Management (Signals) ---
currentStep = signal<'climb' | 'safari' | 'summary'>('climb');
showModal = signal(false);
// Climb State
selectedClimb = signal
climbDays = signal(0);
// Safari State
selectedSafari = signal
safariDays = signal(0);
// List of steps for the indicator
readonly steps = [
{ key: 'climb', name: 'Climb' },
{ key: 'safari', name: 'Safari' },
{ key: 'summary', name: 'Summary' },
];
// --- Computed Signals ---
/** Calculates the total duration of the entire trip (climb + safari). */
totalDays: Signal
return this.climbDays() + this.safariDays();
});
/** Generates a conversational summary of the chosen itinerary. */
itinerarySummary: Signal
const climb = this.selectedClimb();
const safari = this.selectedSafari();
const total = this.totalDays();
if (!climb || !safari) {
return "Please complete both the Climb and Safari selections to generate your personalized summary and request a quote.";
}
return You have created a spectacular ${total}-day Tanzania Adventure! It includes a ${this.climbDays()}-day trek on the ${climb.name} (known for its excellent views and high success rate) followed by a ${this.safariDays()}-day immersion in the ${safari.name}. This combination provides a perfect blend of high-altitude challenge and world-class wildlife viewing. We are ready to start planning your perfect departure date.;
});
// --- Methods ---
/**
* Selects a climb option and sets the default days.
* @param climb The selected ClimbOption.
*/
selectClimb(climb: ClimbOption): void {
this.selectedClimb.set(climb);
// Set a good default: 7 days is a good balance for most routes
this.climbDays.set(Math.min(climb.maxDays, 7));
}
/**
* Sets the number of days for the climb from the range slider.
* @param event The input event from the range slider.
*/
setClimbDays(event: Event): void {
const value = parseInt((event.target as HTMLInputElement).value, 10);
this.climbDays.set(value);
}
/**
* Selects a safari option and sets the default days.
* @param safari The selected SafariOption.
*/
selectSafari(safari: SafariOption): void {
this.selectedSafari.set(safari);
this.safariDays.set(safari.popularDurationDays);
}
/**
* Sets the number of days for the safari from the range slider.
* @param event The input event from the range slider.
*/
setSafariDays(event: Event): void {
const value = parseInt((event.target as HTMLInputElement).value, 10);
this.safariDays.set(value);
}
/**
* Moves to the next step if the current step is complete.
*/
nextStep(): void {
if (this.currentStep() === 'climb' && this.selectedClimb()) {
this.currentStep.set('safari');
} else if (this.currentStep() === 'safari' && this.selectedSafari()) {
this.currentStep.set('summary');
}
}
/**
* Moves to the previous step.
*/
prevStep(): void {
if (this.currentStep() === 'safari') {
this.currentStep.set('climb');
} else if (this.currentStep() === 'summary') {
this.currentStep.set('safari');
}
}
/**
* Allows jumping to a specific step based on index.
* @param index The index of the step to go to.
*/
goToStep(index: number): void {
this.currentStep.set(this.steps[index].key as 'climb' | 'safari' | 'summary');
}
/**
* Generates the CSS classes for the step indicator circles.
* @param key The key of the step.
* @returns The combined CSS classes.
*/
getStepClass(key: 'climb' | 'safari' | 'summary'): string {
const isCurrent = this.currentStep() === key;
const isComplete = (key === 'climb' && this.selectedClimb()) || (key === 'safari' && this.selectedSafari());
if (isCurrent) {
return 'bg-indigo-600 ring-4 ring-indigo-300';
} else if (isComplete) {
return 'bg-indigo-400 hover:bg-indigo-500';
} else {
return 'bg-gray-400';
}
}
/**
* Shows the modal confirmation for quote request.
*/
requestQuote(): void {
if (this.totalDays() > 0) {
this.showModal.set(true);
}
}
}





