Files
carmanagement/apps/homepage/src/components/WorkspaceTabs.tsx
T
root 256ff0814e
Build & Deploy / Build & Push Docker Image (push) Failing after 44s
Build & Deploy / Deploy to VPS (push) Has been skipped
Test / API Unit Tests (push) Failing after 5m0s
Test / Marketplace Unit Tests (push) Failing after 4m51s
Test / Admin Unit Tests (push) Successful in 9m31s
Test / Dashboard Unit Tests (push) Successful in 9m37s
Test / API Integration Tests (push) Successful in 9m54s
refactor: split marketplace into homepage and storefront apps
- Remove apps/marketplace entirely
- Create apps/homepage (port 3000): landing/marketing pages (home, features, pricing, platform-ops, review, legal)
- Create apps/storefront (port 3004): vehicle browsing (explore) and renter account (dashboard, profile, notifications)
- Duplicate shared components/libs into each app
- Update homepage header nav: Home, Features, Pricing instead of Home, Explore
- Fix all /explore cross-references in homepage to point to /features
- Update docker-compose.dev.yml: new homepage and storefront services, remove marketplace
- Update docker-compose.production.yml: split into homepage (main domain) and storefront (path-based routes)
- Update Dockerfiles: EXPOSE 3004 instead of 3003
- Update root package.json scripts: homepage/storefront profiles, tests, prod scripts
- Add docker-prod-up-homepage.sh and docker-prod-up-storefront.sh, remove marketplace script
2026-06-25 21:09:16 -04:00

235 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useMarketplacePreferences } from '@/components/MarketplaceShell'
import Image from 'next/image'
import Link from 'next/link'
import { resolveBrowserAppUrl } from '@/lib/appUrls'
export default function WorkspaceTabs() {
const { language, theme } = useMarketplacePreferences()
const dashboardUrl = resolveBrowserAppUrl(process.env.NEXT_PUBLIC_DASHBOARD_URL ?? 'http://localhost:3000/dashboard')
const copy = {
en: {
badge: 'Unified Workspace',
title: 'One launch point on port 3000 for marketplace discovery, company operations, and platform admin work.',
intro:
'The apps still run as separate Next.js surfaces internally, but the navbar now takes you straight to each workspace area without hunting for ports.',
sectionBadge: 'RentalDriveGo Marketplace',
sectionTitle: 'Browse local rental fleets without losing the company behind the wheel.',
sectionBodyA: 'Discovery happens here. Booking and payment happen on each rental company\'s own branded site.',
sectionBodyB: 'Customers only reach that branded booking site after selecting a vehicle from a company\'s fleet.',
exploreVehicles: 'Explore vehicles',
viewPricing: 'View pricing',
nowServing: 'Now serving',
promoTitle: 'Discovery, pricing, owner sign-in, and booking entry all stay anchored on the marketplace surface.',
promoBody:
'Company Workspace and Platform Operations stay available from the navbar, while branded booking starts after a renter picks a vehicle.',
customerPaths: 'Customer Paths',
ownerSignIn: 'Owner sign in',
pricing: 'Pricing',
workspaceAreas: 'Workspace Areas',
workspaceA: 'Company Workspace for fleet operations, team, billing, and analytics.',
workspaceB: 'Platform Operations for platform-level support and tenant oversight.',
workspaceC: 'Branded booking handoff begins only after a renter chooses a specific vehicle.',
},
fr: {
badge: 'Espace unifié',
title: 'Un point dentrée sur le port 3000 pour la découverte marketplace, les opérations de lentreprise et ladministration de la plateforme.',
intro:
'Les applications restent des interfaces Next.js séparées en interne, mais la barre de navigation mène directement à chaque espace sans avoir à changer de port manuellement.',
sectionBadge: 'Marketplace RentalDriveGo',
sectionTitle: 'Parcourez les flottes locales sans perdre lidentité de lentreprise qui gère la location.',
sectionBodyA: 'La découverte commence ici. La réservation et le paiement se font sur le site de réservation propre à chaque entreprise.',
sectionBodyB: 'Les clients accèdent à ce site de réservation seulement après avoir choisi un véhicule dans la flotte dune entreprise.',
exploreVehicles: 'Explorer les véhicules',
viewPricing: 'Voir les tarifs',
nowServing: 'Disponible',
promoTitle: 'La découverte, les tarifs, la connexion propriétaire et laccès à la réservation restent ancrés dans la marketplace.',
promoBody:
'Lespace entreprise et les opérations de la plateforme restent accessibles depuis la navigation, tandis que la réservation de marque commence après le choix dun véhicule.',
customerPaths: 'Parcours client',
ownerSignIn: 'Connexion propriétaire',
pricing: 'Tarifs',
workspaceAreas: 'Espaces de travail',
workspaceA: 'Espace entreprise pour la flotte, l’équipe, la facturation et lanalyse.',
workspaceB: 'Opérations plateforme pour le support global et la supervision multi-entreprises.',
workspaceC: 'Le transfert vers la réservation de marque commence seulement après le choix dun véhicule précis.',
},
ar: {
badge: 'مساحة موحدة',
title: 'نقطة دخول واحدة على المنفذ 3000 لاكتشاف السوق وعمليات الشركات وإدارة المنصة.',
intro:
'ما زالت التطبيقات تعمل كواجهات Next.js منفصلة داخلياً، لكن شريط التنقل ينقلك مباشرة إلى كل مساحة عمل من دون الحاجة إلى البحث عن المنافذ.',
sectionBadge: 'سوق RentalDriveGo',
sectionTitle: 'تصفح أساطيل التأجير المحلية مع الحفاظ على هوية الشركة التي تدير الحجز.',
sectionBodyA: 'الاكتشاف يبدأ هنا. الحجز والدفع يتمان على موقع الحجز الخاص بكل شركة.',
sectionBodyB: 'لا يصل العميل إلى موقع الحجز الخاص بالشركة إلا بعد اختيار سيارة من أسطول تلك الشركة.',
exploreVehicles: 'استكشف السيارات',
viewPricing: 'عرض الأسعار',
nowServing: 'المتاح الآن',
promoTitle: 'الاكتشاف والأسعار وتسجيل دخول المالك وبداية الحجز تبقى كلها داخل واجهة السوق.',
promoBody:
'تبقى مساحة الشركة وعمليات المنصة متاحتين من شريط التنقل، بينما يبدأ الحجز المرتبط بالعلامة التجارية بعد اختيار المستأجر لسيارة.',
customerPaths: 'مسارات العملاء',
ownerSignIn: 'دخول المالك',
pricing: 'الأسعار',
workspaceAreas: 'مساحات العمل',
workspaceA: 'مساحة الشركة لإدارة الأسطول والفريق والفوترة والتحليلات.',
workspaceB: 'عمليات المنصة للدعم والإشراف على الشركات على مستوى المنصة.',
workspaceC: 'الانتقال إلى موقع الحجز الخاص بالشركة يبدأ فقط بعد اختيار سيارة محددة.',
},
}[language]
return (
<main className={`min-h-screen transition-colors ${
theme === 'dark'
? 'bg-[radial-gradient(circle_at_top,rgba(234,88,12,0.20),transparent_28%),linear-gradient(180deg,#0f1f4a,#112d6e)]'
: 'bg-[radial-gradient(circle_at_top,rgba(234,88,12,0.08),transparent_28%),linear-gradient(180deg,#f5f8ff,white)]'
}`}>
<div className="shell py-16 sm:py-20">
<div className="max-w-4xl">
<div className="flex items-center gap-4">
<Link href="/" aria-label="RentalDriveGo home">
<Image
src="/rentalcardrive.png"
alt="RentalDriveGo"
width={72}
height={72}
className={`h-16 w-16 rounded-2xl object-contain p-1 shadow-sm ${
theme === 'dark' ? 'border border-orange-500/40 bg-blue-950' : 'border border-orange-200 bg-white'
}`}
/>
</Link>
<p className={`text-sm font-semibold uppercase tracking-[0.24em] ${
theme === 'dark' ? 'text-orange-300' : 'text-orange-700'
}`}>
{copy.badge}
</p>
</div>
<h1 className={`mt-5 text-4xl font-black tracking-tight sm:text-6xl ${
theme === 'dark' ? 'text-slate-100' : 'text-blue-900'
}`}>
{copy.title}
</h1>
<p className={`mt-6 max-w-3xl text-lg leading-8 ${
theme === 'dark' ? 'text-stone-300' : 'text-stone-600'
}`}>
{copy.intro}
</p>
</div>
<section className={`mt-10 overflow-hidden rounded-[2rem] border shadow-xl transition-colors ${
theme === 'dark'
? 'border-blue-900 bg-blue-950 shadow-black/20'
: 'border-stone-200 bg-white shadow-stone-200/40'
}`}>
<div className={`px-6 py-6 ${theme === 'dark' ? 'border-b border-stone-800' : 'border-b border-stone-200'}`}>
<p className={`text-xs font-semibold uppercase tracking-[0.18em] ${
theme === 'dark' ? 'text-orange-300' : 'text-orange-700'
}`}>
{copy.sectionBadge}
</p>
<div className="mt-3 flex flex-col gap-4 lg:flex-row lg:items-end lg:justify-between">
<div className="max-w-3xl">
<h2 className={`text-2xl font-black tracking-tight ${
theme === 'dark' ? 'text-slate-100' : 'text-blue-900'
}`}>
{copy.sectionTitle}
</h2>
<p className={`mt-3 text-sm leading-7 ${
theme === 'dark' ? 'text-stone-300' : 'text-stone-600'
}`}>
{copy.sectionBodyA}
</p>
<p className={`mt-3 text-sm leading-7 ${
theme === 'dark' ? 'text-stone-300' : 'text-stone-600'
}`}>
{copy.sectionBodyB}
</p>
</div>
<div className="flex flex-wrap gap-3">
<Link
href="/features"
className={`rounded-full px-5 py-2.5 text-sm font-semibold transition ${
theme === 'dark'
? 'bg-orange-400 text-blue-950 hover:bg-orange-300'
: 'bg-blue-950 text-white hover:bg-blue-900/40'
}`}
>
{copy.exploreVehicles}
</Link>
<Link
href="/pricing"
className={`rounded-full border px-5 py-2.5 text-sm font-semibold transition ${
theme === 'dark'
? 'border-blue-800 text-slate-200 hover:border-blue-600 hover:bg-blue-900/40'
: 'border-stone-300 text-stone-700 hover:border-stone-400 hover:bg-stone-50'
}`}
>
{copy.viewPricing}
</Link>
</div>
</div>
</div>
<div className={`grid gap-6 px-6 py-8 lg:grid-cols-[1.4fr_0.9fr] ${
theme === 'dark' ? 'bg-blue-950' : 'bg-stone-50'
}`}>
<div className="rounded-[1.5rem] bg-[linear-gradient(135deg,#06132e,#0d1b38)] p-8 text-white">
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-orange-300">{copy.nowServing}</p>
<h3 className="mt-4 text-3xl font-black tracking-tight">
{copy.promoTitle}
</h3>
<p className="mt-4 max-w-2xl text-sm leading-7 text-stone-200">
{copy.promoBody}
</p>
</div>
<div className="grid gap-4">
<div className={`rounded-[1.5rem] border p-5 ${
theme === 'dark' ? 'border-blue-900 bg-blue-950' : 'border-stone-200 bg-white'
}`}>
<p className={`text-xs font-semibold uppercase tracking-[0.18em] ${
theme === 'dark' ? 'text-stone-400' : 'text-stone-500'
}`}>{copy.customerPaths}</p>
<div className="mt-4 flex flex-wrap gap-3">
<Link href="/features" className="rounded-full bg-orange-500 px-4 py-2 text-sm font-semibold text-white">
{copy.exploreVehicles}
</Link>
<a className={`rounded-full border px-4 py-2 text-sm font-semibold ${
theme === 'dark' ? 'border-stone-700 text-stone-200' : 'border-stone-300 text-stone-700'
}`} href={`${dashboardUrl}/sign-in`}>
{copy.ownerSignIn}
</a>
<Link className={`rounded-full border px-4 py-2 text-sm font-semibold ${
theme === 'dark' ? 'border-stone-700 text-stone-200' : 'border-stone-300 text-stone-700'
}`} href="/pricing">
{copy.pricing}
</Link>
</div>
</div>
<div className={`rounded-[1.5rem] border p-5 ${
theme === 'dark' ? 'border-blue-900 bg-blue-950' : 'border-stone-200 bg-white'
}`}>
<p className={`text-xs font-semibold uppercase tracking-[0.18em] ${
theme === 'dark' ? 'text-stone-400' : 'text-stone-500'
}`}>{copy.workspaceAreas}</p>
<ul className={`mt-4 space-y-3 text-sm ${
theme === 'dark' ? 'text-stone-300' : 'text-stone-600'
}`}>
<li>{copy.workspaceA}</li>
<li>{copy.workspaceB}</li>
<li>{copy.workspaceC}</li>
</ul>
</div>
</div>
</div>
</section>
</div>
</main>
)
}