From e677170fd202c5e9f0c64224dbd8d6d7dd60fe61 Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 6 Nov 2025 14:03:10 +0100 Subject: [PATCH 1/3] inital structure of docs --- web/README.md | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/web/README.md b/web/README.md index e215bc4..e9ed160 100644 --- a/web/README.md +++ b/web/README.md @@ -1,36 +1,10 @@ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -## Getting Started +# Phantom Air/Hotels -First, run the development server: +Design Discovery Documentation: https://github.com/velocitatem/PHANTOM/wiki/Design-Discovery -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` +> This webapp serves two modes `{HOTEL,AIRLINE}` which are given by an env variable -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +The webapp should serve under the / route the landing page which for both platforms is very similar. We define a set of components like Hero, Card, Button, Link ... This we can then pass to specific components each mode might demand that makes it behave differently, hotel cards showing hotel rooms from database and airline cards showing flights from database and each fetching prices from the pricing provider with a different HTTP parameter. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. From 5777437540e5ef09c23db0840ea970861c6a2a1c Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 6 Nov 2025 14:12:18 +0100 Subject: [PATCH 2/3] init styles and docs --- web/README.md | 4 ++++ web/src/styles/airline.css | 0 web/src/styles/hotel.css | 0 3 files changed, 4 insertions(+) create mode 100644 web/src/styles/airline.css create mode 100644 web/src/styles/hotel.css diff --git a/web/README.md b/web/README.md index e9ed160..12fff92 100644 --- a/web/README.md +++ b/web/README.md @@ -8,3 +8,7 @@ Design Discovery Documentation: https://github.com/velocitatem/PHANTOM/wiki/Desi The webapp should serve under the / route the landing page which for both platforms is very similar. We define a set of components like Hero, Card, Button, Link ... This we can then pass to specific components each mode might demand that makes it behave differently, hotel cards showing hotel rooms from database and airline cards showing flights from database and each fetching prices from the pricing provider with a different HTTP parameter. +- globally we define a middleware.ts which is our switcher for modes. +- /app will have (airline) and (hotel) children which each have a layout.tsx and page.tsx where /app also has a parent layout defining layout.tsx and globals.css for any shared styling to avoid repretition. +- /components/ is gonna have ui/ which defines things like Button, Card, DatePicker with generic definitions and any tracking or observation code. We then define feats/airline/ and feats/hotel/ as children of components with specific components like AirlineHero and HotelCard. +- in /styles/ we define airline.css and hotel.css to tailor accents and styling for each. diff --git a/web/src/styles/airline.css b/web/src/styles/airline.css new file mode 100644 index 0000000..e69de29 diff --git a/web/src/styles/hotel.css b/web/src/styles/hotel.css new file mode 100644 index 0000000..e69de29 From aa98b2d1691bae27f52c328fa57a6d9f80121b2a Mon Sep 17 00:00:00 2001 From: Daniel Rosel Date: Thu, 6 Nov 2025 14:15:50 +0100 Subject: [PATCH 3/3] basic style implementation --- web/src/app/globals.css | 74 ++++++- web/src/styles/airline.css | 302 ++++++++++++++++++++++++++++ web/src/styles/hotel.css | 400 +++++++++++++++++++++++++++++++++++++ 3 files changed, 775 insertions(+), 1 deletion(-) diff --git a/web/src/app/globals.css b/web/src/app/globals.css index a2dc41e..eba0bfc 100644 --- a/web/src/app/globals.css +++ b/web/src/app/globals.css @@ -3,6 +3,15 @@ :root { --background: #ffffff; --foreground: #171717; + --bg-primary: #ffffff; + --bg-secondary: #f5f5f5; + --text-primary: #333333; + --text-secondary: #666666; + --spacing-sm: 8px; + --spacing-md: 16px; + --spacing-lg: 32px; + --border-radius: 8px; + --shadow-card: 0 2px 8px rgba(0, 0, 0, 0.1); } @theme inline { @@ -19,8 +28,71 @@ } } +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + line-height: 1.6; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: 700; + color: var(--text-primary); + line-height: 1.2; +} + +h1 { font-size: 2.5rem; } +h2 { font-size: 2rem; } +h3 { font-size: 1.5rem; } + +button { + cursor: pointer; + border: none; + outline: none; + font-family: inherit; + transition: all 0.2s ease; +} + +input, select, textarea { + font-family: inherit; + font-size: 1rem; + outline: none; +} + +.container { + max-width: 1200px; + margin: 0 auto; + padding: 0 var(--spacing-md); +} + +.card { + background: var(--bg-primary); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); + overflow: hidden; +} + +.btn-primary { + padding: 12px 24px; + font-weight: 600; + font-size: 1rem; + border-radius: var(--border-radius); + transition: all 0.2s ease; +} + +.btn-primary:hover { + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); +} + +.section-spacing { + margin-bottom: var(--spacing-lg); } diff --git a/web/src/styles/airline.css b/web/src/styles/airline.css index e69de29..33f5cb9 100644 --- a/web/src/styles/airline.css +++ b/web/src/styles/airline.css @@ -0,0 +1,302 @@ +/* Airline Platform - Sky Blue Theme */ + +:root[data-mode="airline"] { + --accent-primary: #007aff; + --accent-secondary: #4caf50; + --accent-warning: #ff3b30; + --accent-primary-hover: #0051d5; + --accent-primary-light: #e6f2ff; + --text-accent: #007aff; +} + +[data-mode="airline"] { + --primary-color: var(--accent-primary); +} + +[data-mode="airline"] .btn-primary { + background-color: var(--accent-primary); + color: #ffffff; +} + +[data-mode="airline"] .btn-primary:hover { + background-color: var(--accent-primary-hover); +} + +[data-mode="airline"] .btn-secondary { + background-color: transparent; + color: var(--accent-primary); + border: 2px solid var(--accent-primary); +} + +[data-mode="airline"] .btn-secondary:hover { + background-color: var(--accent-primary-light); +} + +[data-mode="airline"] .badge-value { + background-color: var(--accent-secondary); + color: #ffffff; + padding: 4px 8px; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; +} + +[data-mode="airline"] .badge-warning { + background-color: var(--accent-warning); + color: #ffffff; + padding: 4px 8px; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; +} + +[data-mode="airline"] .search-form { + background: var(--bg-primary); + padding: var(--spacing-lg); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); +} + +[data-mode="airline"] .flight-card { + display: grid; + grid-template-columns: 2fr 3fr 2fr; + gap: var(--spacing-md); + padding: var(--spacing-md); + background: var(--bg-primary); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); + margin-bottom: var(--spacing-md); + transition: box-shadow 0.2s ease; +} + +[data-mode="airline"] .flight-card:hover { + box-shadow: 0 4px 16px rgba(0, 122, 255, 0.15); +} + +[data-mode="airline"] .flight-timing { + display: flex; + flex-direction: column; + justify-content: center; +} + +[data-mode="airline"] .flight-time { + font-size: 1.5rem; + font-weight: 700; + color: var(--text-primary); +} + +[data-mode="airline"] .flight-airport { + font-size: 0.875rem; + color: var(--text-secondary); + font-weight: 500; +} + +[data-mode="airline"] .flight-route { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + text-align: center; +} + +[data-mode="airline"] .flight-duration { + font-size: 0.875rem; + color: var(--text-secondary); + margin-bottom: 4px; +} + +[data-mode="airline"] .flight-stops { + font-size: 0.875rem; + color: var(--text-secondary); + font-weight: 600; +} + +[data-mode="airline"] .flight-pricing { + display: flex; + flex-direction: column; + justify-content: center; + gap: var(--spacing-sm); +} + +[data-mode="airline"] .fare-option { + display: flex; + justify-content: space-between; + align-items: center; + padding: var(--spacing-sm); + border: 1px solid #e0e0e0; + border-radius: 6px; + transition: all 0.2s ease; +} + +[data-mode="airline"] .fare-option:hover { + border-color: var(--accent-primary); + background-color: var(--accent-primary-light); +} + +[data-mode="airline"] .fare-class { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +[data-mode="airline"] .fare-price { + font-size: 1.125rem; + font-weight: 700; + color: var(--accent-primary); +} + +[data-mode="airline"] .date-price-bar { + display: flex; + overflow-x: auto; + gap: var(--spacing-sm); + padding: var(--spacing-md) 0; + margin-bottom: var(--spacing-lg); +} + +[data-mode="airline"] .date-option { + min-width: 100px; + padding: var(--spacing-sm); + text-align: center; + border: 2px solid #e0e0e0; + border-radius: 6px; + cursor: pointer; + transition: all 0.2s ease; +} + +[data-mode="airline"] .date-option:hover { + border-color: var(--accent-primary); +} + +[data-mode="airline"] .date-option.active { + border-color: var(--accent-primary); + background-color: var(--accent-primary-light); +} + +[data-mode="airline"] .date-label { + font-size: 0.75rem; + color: var(--text-secondary); + margin-bottom: 4px; +} + +[data-mode="airline"] .date-price { + font-size: 1rem; + font-weight: 700; + color: var(--accent-primary); +} + +[data-mode="airline"] .progress-wizard { + display: flex; + justify-content: space-between; + align-items: center; + max-width: 800px; + margin: var(--spacing-lg) auto; + padding: 0 var(--spacing-md); +} + +[data-mode="airline"] .wizard-step { + display: flex; + flex-direction: column; + align-items: center; + flex: 1; + position: relative; +} + +[data-mode="airline"] .wizard-step::after { + content: ''; + position: absolute; + top: 20px; + left: 50%; + width: 100%; + height: 2px; + background: #e0e0e0; + z-index: -1; +} + +[data-mode="airline"] .wizard-step:last-child::after { + display: none; +} + +[data-mode="airline"] .wizard-number { + width: 40px; + height: 40px; + border-radius: 50%; + background: #e0e0e0; + color: var(--text-secondary); + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + margin-bottom: var(--spacing-sm); +} + +[data-mode="airline"] .wizard-step.active .wizard-number { + background: var(--accent-primary); + color: #ffffff; +} + +[data-mode="airline"] .wizard-step.completed .wizard-number { + background: var(--accent-secondary); + color: #ffffff; +} + +[data-mode="airline"] .wizard-label { + font-size: 0.875rem; + color: var(--text-secondary); + text-align: center; +} + +[data-mode="airline"] .wizard-step.active .wizard-label { + color: var(--accent-primary); + font-weight: 600; +} + +[data-mode="airline"] a { + color: var(--accent-primary); + text-decoration: none; +} + +[data-mode="airline"] a:hover { + text-decoration: underline; +} + +[data-mode="airline"] .input-field { + border: 2px solid #e0e0e0; + border-radius: 6px; + padding: 12px; + transition: border-color 0.2s ease; +} + +[data-mode="airline"] .input-field:focus { + border-color: var(--accent-primary); + box-shadow: 0 0 0 3px var(--accent-primary-light); +} + +[data-mode="airline"] .filter-sidebar { + background: var(--bg-primary); + padding: var(--spacing-md); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); +} + +[data-mode="airline"] .filter-section { + margin-bottom: var(--spacing-lg); +} + +[data-mode="airline"] .filter-title { + font-size: 1rem; + font-weight: 700; + color: var(--text-primary); + margin-bottom: var(--spacing-sm); +} + +[data-mode="airline"] .checkbox-label { + display: flex; + align-items: center; + gap: var(--spacing-sm); + padding: var(--spacing-sm) 0; + cursor: pointer; +} + +[data-mode="airline"] .checkbox-label:hover { + color: var(--accent-primary); +} diff --git a/web/src/styles/hotel.css b/web/src/styles/hotel.css index e69de29..8c67285 100644 --- a/web/src/styles/hotel.css +++ b/web/src/styles/hotel.css @@ -0,0 +1,400 @@ +/* Hotel Platform - Action Blue Theme */ + +:root[data-mode="hotel"] { + --accent-primary: #007aff; + --accent-secondary: #4caf50; + --accent-warning: #d9534f; + --accent-primary-hover: #0051d5; + --accent-primary-light: #e6f2ff; + --text-accent: #007aff; + --bg-tertiary: #f5f5f7; +} + +[data-mode="hotel"] { + --primary-color: var(--accent-primary); +} + +[data-mode="hotel"] .btn-primary { + background-color: var(--accent-primary); + color: #ffffff; +} + +[data-mode="hotel"] .btn-primary:hover { + background-color: var(--accent-primary-hover); +} + +[data-mode="hotel"] .btn-secondary { + background-color: transparent; + color: var(--accent-primary); + border: 2px solid var(--accent-primary); +} + +[data-mode="hotel"] .btn-secondary:hover { + background-color: var(--accent-primary-light); +} + +[data-mode="hotel"] .badge-value { + background-color: var(--accent-secondary); + color: #ffffff; + padding: 4px 10px; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; +} + +[data-mode="hotel"] .badge-warning { + background-color: var(--accent-warning); + color: #ffffff; + padding: 4px 10px; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 600; +} + +[data-mode="hotel"] .badge-rating { + background-color: var(--accent-primary); + color: #ffffff; + padding: 6px 10px; + border-radius: 4px; + font-size: 0.875rem; + font-weight: 700; +} + +[data-mode="hotel"] .search-form { + background: var(--bg-primary); + padding: var(--spacing-lg); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); + max-width: 900px; + margin: 0 auto; +} + +[data-mode="hotel"] .hotel-card { + display: grid; + grid-template-columns: 300px 1fr auto; + gap: var(--spacing-md); + background: var(--bg-primary); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); + margin-bottom: var(--spacing-md); + overflow: hidden; + transition: box-shadow 0.2s ease; +} + +[data-mode="hotel"] .hotel-card:hover { + box-shadow: 0 4px 16px rgba(0, 122, 255, 0.15); +} + +[data-mode="hotel"] .hotel-image { + position: relative; + width: 100%; + height: 100%; + min-height: 220px; + overflow: hidden; +} + +[data-mode="hotel"] .hotel-image img { + width: 100%; + height: 100%; + object-fit: cover; +} + +[data-mode="hotel"] .image-carousel { + position: relative; +} + +[data-mode="hotel"] .carousel-nav { + position: absolute; + bottom: var(--spacing-sm); + left: 50%; + transform: translateX(-50%); + display: flex; + gap: 6px; +} + +[data-mode="hotel"] .carousel-dot { + width: 8px; + height: 8px; + border-radius: 50%; + background: rgba(255, 255, 255, 0.5); + cursor: pointer; +} + +[data-mode="hotel"] .carousel-dot.active { + background: #ffffff; +} + +[data-mode="hotel"] .hotel-info { + padding: var(--spacing-md); + display: flex; + flex-direction: column; + gap: var(--spacing-sm); +} + +[data-mode="hotel"] .hotel-name { + font-size: 1.25rem; + font-weight: 700; + color: var(--text-primary); + margin: 0; +} + +[data-mode="hotel"] .hotel-location { + font-size: 0.875rem; + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 4px; +} + +[data-mode="hotel"] .hotel-rating { + display: flex; + align-items: center; + gap: var(--spacing-sm); +} + +[data-mode="hotel"] .rating-text { + font-size: 0.875rem; + font-weight: 600; + color: var(--text-primary); +} + +[data-mode="hotel"] .hotel-features { + display: flex; + flex-wrap: wrap; + gap: var(--spacing-sm); + margin-top: var(--spacing-sm); +} + +[data-mode="hotel"] .feature-tag { + padding: 4px 8px; + background: var(--bg-tertiary); + color: var(--text-secondary); + font-size: 0.75rem; + border-radius: 4px; +} + +[data-mode="hotel"] .hotel-pricing { + padding: var(--spacing-md); + display: flex; + flex-direction: column; + justify-content: space-between; + align-items: flex-end; + min-width: 200px; +} + +[data-mode="hotel"] .price-wrapper { + text-align: right; +} + +[data-mode="hotel"] .price-label { + font-size: 0.75rem; + color: var(--text-secondary); + margin-bottom: 4px; +} + +[data-mode="hotel"] .price-amount { + font-size: 1.75rem; + font-weight: 700; + color: var(--accent-primary); +} + +[data-mode="hotel"] .price-unit { + font-size: 0.875rem; + color: var(--text-secondary); +} + +[data-mode="hotel"] .price-original { + text-decoration: line-through; + color: var(--text-secondary); + font-size: 1rem; + margin-right: var(--spacing-sm); +} + +[data-mode="hotel"] .urgency-message { + font-size: 0.75rem; + color: var(--accent-warning); + font-weight: 600; + margin-top: 4px; +} + +[data-mode="hotel"] .free-cancellation { + font-size: 0.75rem; + color: var(--accent-secondary); + font-weight: 600; + margin-top: 4px; +} + +[data-mode="hotel"] .filter-sidebar { + background: var(--bg-primary); + padding: var(--spacing-md); + border-radius: var(--border-radius); + box-shadow: var(--shadow-card); +} + +[data-mode="hotel"] .filter-section { + margin-bottom: var(--spacing-lg); + padding-bottom: var(--spacing-md); + border-bottom: 1px solid #e0e0e0; +} + +[data-mode="hotel"] .filter-section:last-child { + border-bottom: none; +} + +[data-mode="hotel"] .filter-title { + font-size: 1rem; + font-weight: 700; + color: var(--text-primary); + margin-bottom: var(--spacing-md); +} + +[data-mode="hotel"] .checkbox-label { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--spacing-sm) 0; + cursor: pointer; +} + +[data-mode="hotel"] .checkbox-label:hover { + color: var(--accent-primary); +} + +[data-mode="hotel"] .checkbox-count { + font-size: 0.875rem; + color: var(--text-secondary); +} + +[data-mode="hotel"] .price-slider { + margin-top: var(--spacing-md); +} + +[data-mode="hotel"] .slider-track { + width: 100%; + height: 6px; + background: #e0e0e0; + border-radius: 3px; + position: relative; +} + +[data-mode="hotel"] .slider-range { + height: 100%; + background: var(--accent-primary); + border-radius: 3px; +} + +[data-mode="hotel"] .slider-values { + display: flex; + justify-content: space-between; + margin-top: var(--spacing-sm); + font-size: 0.875rem; + color: var(--text-secondary); +} + +[data-mode="hotel"] .map-toggle { + background: var(--bg-primary); + border: 2px solid var(--accent-primary); + color: var(--accent-primary); + padding: 12px 24px; + border-radius: var(--border-radius); + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +[data-mode="hotel"] .map-toggle:hover { + background: var(--accent-primary); + color: #ffffff; +} + +[data-mode="hotel"] .results-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--spacing-lg); + padding: var(--spacing-md); + background: var(--bg-primary); + border-radius: var(--border-radius); +} + +[data-mode="hotel"] .sort-dropdown { + padding: 8px 12px; + border: 2px solid #e0e0e0; + border-radius: 6px; + background: var(--bg-primary); + cursor: pointer; + font-size: 0.875rem; +} + +[data-mode="hotel"] .sort-dropdown:focus { + border-color: var(--accent-primary); +} + +[data-mode="hotel"] .view-toggle { + display: flex; + gap: var(--spacing-sm); +} + +[data-mode="hotel"] .view-button { + padding: 8px 12px; + background: transparent; + border: 2px solid #e0e0e0; + border-radius: 6px; + cursor: pointer; + transition: all 0.2s ease; +} + +[data-mode="hotel"] .view-button.active { + background: var(--accent-primary); + color: #ffffff; + border-color: var(--accent-primary); +} + +[data-mode="hotel"] a { + color: var(--accent-primary); + text-decoration: none; +} + +[data-mode="hotel"] a:hover { + text-decoration: underline; +} + +[data-mode="hotel"] .input-field { + border: 2px solid #e0e0e0; + border-radius: 6px; + padding: 12px; + width: 100%; + transition: border-color 0.2s ease; +} + +[data-mode="hotel"] .input-field:focus { + border-color: var(--accent-primary); + box-shadow: 0 0 0 3px var(--accent-primary-light); +} + +[data-mode="hotel"] .tab-navigation { + display: flex; + gap: 0; + margin-bottom: var(--spacing-lg); + border-bottom: 2px solid #e0e0e0; +} + +[data-mode="hotel"] .tab-item { + padding: 12px 24px; + background: transparent; + border: none; + border-bottom: 3px solid transparent; + color: var(--text-secondary); + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +[data-mode="hotel"] .tab-item:hover { + color: var(--accent-primary); +} + +[data-mode="hotel"] .tab-item.active { + color: var(--accent-primary); + border-bottom-color: var(--accent-primary); +}