diff --git a/.gitignore b/.gitignore
index 8fa8bca90..f3dd54fba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ node_modules/
# build output
**/dist/
.next/
+**/out/
coverage/
*.tsbuildinfo
**/*.tsbuildinfo
diff --git a/CLAUDE.md b/CLAUDE.md
index a20fbaee7..f070313f6 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -32,6 +32,7 @@ copying a pattern across:
| `edr-passenger-web/portal` | `@edr/passenger-portal` | **Next.js** | 5174 |
| `edr-passenger-web/backoffice` | `@edr/passenger-backoffice` | **Next.js** | 5184 |
| `edr-payment-api` | `@edr/payment-api` | NestJS + **TypeORM** | 3003 |
+| `edr-landing` | `@edr/landing` | **Next.js** static export | 5163 |
Those are the **fallbacks compiled into the code**, not what you will be running. Every
port is overridden by `PORT` in the app's `.env` / `.env.development`; the freight vite
@@ -43,8 +44,16 @@ the whole team and the low ports are contested — see the workspace root `CLAUD
Each holds a `portal/` and `backoffice/` sub-app, both independent pnpm workspace
packages (see `pnpm-workspace.yaml`).
-`apps/edr-landing/` exists on disk but has **no `package.json`** — it is not a workspace
-package and is not built, linted, or type-checked. Leave it alone unless asked.
+`apps/edr-landing/` is the public front door at `edrsc.com`: one static page that routes
+visitors to the passenger or freight app. It is a Next.js **static export**
+(`output: 'export'`), so its build artifact is `out/`, not `.next/`. It depends on no
+workspace package — not even `@edr/ui-common`, whose Tailwind 4 tokens do not fit its
+Tailwind 3 setup.
+
+Its two destinations come from `NEXT_PUBLIC_PASSENGER_URL` and `NEXT_PUBLIC_FREIGHT_URL`
+(each an origin; the entry path is appended in `src/lib/apps.ts`). A static export inlines
+those at **build** time, so they must be passed as Docker build args — setting them in the
+runtime environment does nothing.
`apps/edr-gps-tracker/` is a separate service with its own `.env.example`.
diff --git a/apps/edr-landing/next-env.d.ts b/apps/edr-landing/next-env.d.ts
index c4b7818fb..40c3d6809 100644
--- a/apps/edr-landing/next-env.d.ts
+++ b/apps/edr-landing/next-env.d.ts
@@ -1,6 +1,5 @@
///
///
-import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
-// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
+// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
diff --git a/apps/edr-landing/next.config.js b/apps/edr-landing/next.config.js
new file mode 100644
index 000000000..656afb70d
--- /dev/null
+++ b/apps/edr-landing/next.config.js
@@ -0,0 +1,13 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ // Static HTML at build time — the page has no server work to do, and
+ // infrastructure/docker/Dockerfile.web already serves an `out/` directory
+ // through nginx. Also means NEXT_PUBLIC_* is inlined at build time, so the
+ // two destination URLs must be passed as Docker build args, not runtime env.
+ output: 'export',
+ reactStrictMode: true,
+ // No image optimizer exists behind a static export.
+ images: { unoptimized: true },
+};
+
+export default nextConfig;
diff --git a/apps/edr-landing/package.json b/apps/edr-landing/package.json
new file mode 100644
index 000000000..b8baf027b
--- /dev/null
+++ b/apps/edr-landing/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@edr/landing",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "next dev -p 5163",
+ "build": "next build",
+ "start": "next start -p 5163",
+ "lint": "next lint",
+ "type-check": "tsc --noEmit"
+ },
+ "dependencies": {
+ "lucide-react": "^0.446.0",
+ "next": "^14.2.0",
+ "react": "^18.3.1",
+ "react-dom": "^18.3.1"
+ },
+ "devDependencies": {
+ "@types/node": "^20.0.0",
+ "@types/react": "^18.3.11",
+ "@types/react-dom": "^18.3.0",
+ "autoprefixer": "^10.4.20",
+ "postcss": "^8.4.47",
+ "tailwindcss": "^3.4.13",
+ "typescript": "^5.5.4"
+ }
+}
diff --git a/apps/edr-landing/postcss.config.js b/apps/edr-landing/postcss.config.js
new file mode 100644
index 000000000..2aa7205d4
--- /dev/null
+++ b/apps/edr-landing/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/apps/edr-landing/public/edr-logo.png b/apps/edr-landing/public/edr-logo.png
new file mode 100644
index 000000000..3966c9e80
Binary files /dev/null and b/apps/edr-landing/public/edr-logo.png differ
diff --git a/apps/edr-landing/src/app/globals.css b/apps/edr-landing/src/app/globals.css
new file mode 100644
index 000000000..6aa66fed6
--- /dev/null
+++ b/apps/edr-landing/src/app/globals.css
@@ -0,0 +1,174 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ body {
+ @apply bg-white text-slate-900 antialiased;
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* Motion — lifted from the freight portal's landing page so the two */
+/* public surfaces share one visual language. */
+/* ------------------------------------------------------------------ */
+
+.edr-reveal {
+ transition:
+ opacity 0.7s cubic-bezier(0.16, 1, 0.3, 1),
+ transform 0.7s cubic-bezier(0.16, 1, 0.3, 1);
+}
+
+/* The hidden state is scoped to `html.js`, set by an inline script in the
+ layout. Without it a reader with JavaScript off would get a blank page —
+ fatal for a front door whose whole job is two links. */
+.js .edr-reveal {
+ opacity: 0;
+ transform: translateY(24px);
+}
+.js .edr-reveal.is-visible {
+ opacity: 1;
+ transform: none;
+}
+
+/* Hero: rail streaks racing toward the horizon */
+.edr-rails {
+ position: absolute;
+ inset: 0;
+ background-image: repeating-linear-gradient(
+ 100deg,
+ transparent 0 46px,
+ rgba(52, 211, 153, 0.16) 46px 48px
+ );
+ mask-image: radial-gradient(120% 90% at 78% 40%, #000 0%, transparent 70%);
+ -webkit-mask-image: radial-gradient(120% 90% at 78% 40%, #000 0%, transparent 70%);
+ animation: edr-rails 9s linear infinite;
+}
+@keyframes edr-rails {
+ to {
+ background-position: 480px 0;
+ }
+}
+
+.edr-hero-glow {
+ position: absolute;
+ inset: 0;
+ background:
+ radial-gradient(46% 60% at 12% 22%, rgba(10, 138, 95, 0.45), transparent 70%),
+ radial-gradient(40% 55% at 88% 82%, rgba(242, 165, 22, 0.22), transparent 70%);
+ animation: edr-breathe 11s ease-in-out infinite;
+}
+@keyframes edr-breathe {
+ 0%,
+ 100% {
+ opacity: 0.75;
+ transform: scale(1);
+ }
+ 50% {
+ opacity: 1;
+ transform: scale(1.06);
+ }
+}
+
+/* Pulsing live dot */
+.edr-pulse-dot {
+ position: relative;
+ display: inline-block;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 9999px;
+ background: #34d399;
+}
+.edr-pulse-dot::after {
+ content: "";
+ position: absolute;
+ inset: -3px;
+ border-radius: 9999px;
+ border: 2px solid currentColor;
+ color: inherit;
+ background: inherit;
+ opacity: 0.55;
+ animation: edr-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
+}
+@keyframes edr-ping {
+ 0% {
+ transform: scale(0.6);
+ opacity: 0.6;
+ }
+ 80%,
+ 100% {
+ transform: scale(2.1);
+ opacity: 0;
+ }
+}
+
+/* The corridor, with a train light running its length */
+.edr-corridor {
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 18px;
+}
+.edr-corridor::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ right: 0;
+ top: 50%;
+ height: 2px;
+ transform: translateY(-50%);
+ background: rgba(52, 211, 153, 0.28);
+}
+.edr-corridor-node {
+ position: relative;
+ z-index: 1;
+ width: 10px;
+ height: 10px;
+ border-radius: 9999px;
+ background: #10202f;
+ border: 2px solid #34d399;
+}
+.edr-corridor-node.is-hub {
+ width: 16px;
+ height: 16px;
+ background: #34d399;
+}
+.edr-corridor-node.is-border {
+ background: #f2a516;
+ border-color: #f2a516;
+}
+.edr-corridor-train {
+ position: absolute;
+ top: 50%;
+ left: 0;
+ width: 74px;
+ height: 4px;
+ transform: translateY(-50%);
+ border-radius: 9999px;
+ background: linear-gradient(90deg, transparent, #f2a516);
+ box-shadow: 0 0 18px 3px rgba(242, 165, 22, 0.55);
+ animation: edr-run 12s linear infinite;
+}
+@keyframes edr-run {
+ 0% {
+ left: -6%;
+ }
+ 100% {
+ left: 100%;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .js .edr-reveal {
+ opacity: 1;
+ transform: none;
+ transition: none;
+ }
+ .edr-rails,
+ .edr-hero-glow,
+ .edr-pulse-dot::after,
+ .edr-corridor-train {
+ animation: none !important;
+ }
+}
diff --git a/apps/edr-landing/src/app/layout.tsx b/apps/edr-landing/src/app/layout.tsx
new file mode 100644
index 000000000..60510d9f7
--- /dev/null
+++ b/apps/edr-landing/src/app/layout.tsx
@@ -0,0 +1,106 @@
+import type { Metadata, Viewport } from "next";
+
+import "./globals.css";
+
+const siteUrl = process.env.NEXT_PUBLIC_SITE_URL ?? "https://edrsc.com";
+
+export const metadata: Metadata = {
+ metadataBase: new URL(siteUrl),
+ title: "Ethio-Djibouti Railway — Passenger & Freight",
+ description:
+ "The Ethio-Djibouti Railway online. Book a train ticket on the passenger portal, or book and track cargo on the freight portal — 752 km of electrified standard-gauge railway between Addis Ababa and Djibouti.",
+ applicationName: "Ethio-Djibouti Railway",
+ authors: [{ name: "EDR — Ethio-Djibouti Railway" }],
+ creator: "EDR — Ethio-Djibouti Railway",
+ publisher: "EDR — Ethio-Djibouti Railway",
+ alternates: { canonical: "/" },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: { index: true, follow: true, "max-image-preview": "large" },
+ },
+ openGraph: {
+ type: "website",
+ locale: "en_US",
+ url: siteUrl,
+ siteName: "Ethio-Djibouti Railway",
+ title: "Ethio-Djibouti Railway — Passenger & Freight",
+ description:
+ "Book a seat or ship a container on the same railway. Pick the service you need.",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "Ethio-Djibouti Railway — Passenger & Freight",
+ description:
+ "Book a seat or ship a container on the same railway. Pick the service you need.",
+ },
+ icons: {
+ icon: "/edr-logo.png",
+ shortcut: "/edr-logo.png",
+ apple: "/edr-logo.png",
+ },
+};
+
+export const viewport: Viewport = {
+ width: "device-width",
+ initialScale: 1,
+ themeColor: "#0c1a2b",
+};
+
+const organizationSchema = {
+ "@context": "https://schema.org",
+ "@type": "Organization",
+ name: "Ethio-Djibouti Railway (EDR)",
+ url: siteUrl,
+ logo: `${siteUrl}/edr-logo.png`,
+ contactPoint: {
+ "@type": "ContactPoint",
+ telephone: "9546",
+ email: "edr_@edrsc.com",
+ contactType: "customer service",
+ availableLanguage: ["English", "Amharic"],
+ },
+ address: {
+ "@type": "PostalAddress",
+ addressLocality: "Addis Ababa",
+ addressCountry: "ET",
+ },
+};
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+ {/* Loaded as a stylesheet rather than next/font, which fetches from
+ fonts.googleapis.com during `next build` and fails the whole build
+ on a machine without network. Matches the freight portal. */}
+
+
+
+ {/* Gates the reveal animation's hidden state (see globals.css) so the
+ page is fully readable with JavaScript off. */}
+
+
+
+ {children}
+
+ );
+}
diff --git a/apps/edr-landing/src/app/page.tsx b/apps/edr-landing/src/app/page.tsx
new file mode 100644
index 000000000..4ac3e506f
--- /dev/null
+++ b/apps/edr-landing/src/app/page.tsx
@@ -0,0 +1,395 @@
+import {
+ ArrowRight,
+ Container,
+ Mail,
+ MapPin,
+ Phone,
+ TrainFront,
+} from "lucide-react";
+
+import { CountUp, Reveal } from "@/components/motion";
+import { FREIGHT_URL, PASSENGER_URL } from "@/lib/apps";
+
+/* ------------------------------------------------------------------ */
+/* Content */
+/* ------------------------------------------------------------------ */
+
+const doors = [
+ {
+ key: "passenger",
+ icon: TrainFront,
+ name: "Passenger",
+ lede: "Travelling yourself.",
+ href: PASSENGER_URL,
+ cta: "Open Passenger",
+ points: [
+ "Search trains and book a seat",
+ "Choose your class and seat",
+ "Pay with Telebirr or CBE Birr",
+ "Manage or reschedule a trip",
+ ],
+ },
+ {
+ key: "freight",
+ icon: Container,
+ name: "Freight",
+ lede: "Shipping goods.",
+ href: FREIGHT_URL,
+ cta: "Open Freight",
+ points: [
+ "Book containers or bulk cargo",
+ "Track every wagon live",
+ "Customs-ready documents",
+ "Invoices and settlement",
+ ],
+ },
+] as const;
+
+const stations = [
+ { name: "Addis Ababa", kind: "hub" },
+ { name: "Mojo", kind: "stop" },
+ { name: "Adama", kind: "stop" },
+ { name: "Awash", kind: "stop" },
+ { name: "Dire Dawa", kind: "hub" },
+ { name: "Dewele", kind: "border" },
+ { name: "Djibouti Port", kind: "hub" },
+] as const;
+
+const stats = [
+ { value: 752, suffix: " km", label: "Addis Ababa to Djibouti, electrified standard gauge" },
+ { value: 16, prefix: "~", suffix: " h", label: "Mojo Dry Port to Djibouti Port transit" },
+ { value: 2, label: "Countries connected by one railway" },
+ { text: "24/7", label: "Booking and tracking online" },
+] as const;
+
+/* ------------------------------------------------------------------ */
+/* Page */
+/* ------------------------------------------------------------------ */
+
+export default function Home() {
+ return (
+ <>
+
+ Skip to the services
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+function Wordmark({ subtitle = true }: { subtitle?: boolean }) {
+ return (
+
+
+
+
+
+
+ EDR
+ {subtitle ? (
+
+ Ethio-Djibouti Railway
+
+ ) : null}
+
+
+ );
+}
+
+function Header() {
+ return (
+
+ );
+}
+
+function Hero() {
+ return (
+
+
+
+
+
+
+
+
+
+ Addis Ababa – Djibouti · 752 km electrified corridor
+
+
+
+
+
+ The Ethio-Djibouti Railway,
+ online.
+
+
+
+
+
+ Book a seat or ship a container on the same railway. Pick the
+ service you need.
+
+
+
+
+ {/* The two doors — the reason this page exists. */}
+
+ {doors.map((door, index) => (
+
+
+
+ ))}
+
+
+
+
+ Travelling yourself?{" "}
+ Passenger. {" "}
+ Shipping goods?{" "}
+ Freight.
+
+
+
+
+ );
+}
+
+function Door({
+ icon: Icon,
+ name,
+ lede,
+ href,
+ cta,
+ points,
+}: (typeof doors)[number]) {
+ return (
+
+
+
+
+
+
+ {name}
+
+ {lede}
+
+
+ {points.map((point) => (
+
+
+ {point}
+
+ ))}
+
+
+
+ {cta}
+
+
+
+ );
+}
+
+function Corridor() {
+ return (
+
+
+
+
+
+
+
+
+ {stations.map((station, index) => {
+ const isFirst = index === 0;
+ const isLast = index === stations.length - 1;
+
+ return (
+
+
+
+ {/* Absolute so a long label never widens its column — the
+ nodes have to stay evenly spaced along the rail. */}
+
+ {station.name}
+ {station.kind === "border" ? " (border)" : ""}
+
+
+ );
+ })}
+
+
+
+
+
+ );
+}
+
+function Stats() {
+ return (
+
+
+
+
+ {stats.map((stat) => (
+
+
+ {"text" in stat ? (
+ stat.text
+ ) : (
+
+ )}
+
+
+ {stat.label}
+
+
+ ))}
+
+
+
+
+ );
+}
+
+function Footer() {
+ return (
+
+ );
+}
diff --git a/apps/edr-landing/src/components/motion.tsx b/apps/edr-landing/src/components/motion.tsx
new file mode 100644
index 000000000..a009cfd49
--- /dev/null
+++ b/apps/edr-landing/src/components/motion.tsx
@@ -0,0 +1,116 @@
+"use client";
+
+import { useEffect, useRef, useState } from "react";
+
+/** Fires once when the element first scrolls into view. */
+function useReveal() {
+ const ref = useRef(null);
+ const [shown, setShown] = useState(false);
+
+ useEffect(() => {
+ const node = ref.current;
+ if (!node) return;
+
+ if (typeof IntersectionObserver === "undefined") {
+ setShown(true);
+ return;
+ }
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ if (entries.some((entry) => entry.isIntersecting)) {
+ setShown(true);
+ observer.disconnect();
+ }
+ },
+ { rootMargin: "0px 0px -12% 0px", threshold: 0.15 },
+ );
+
+ observer.observe(node);
+ return () => observer.disconnect();
+ }, []);
+
+ return { ref, shown };
+}
+
+/**
+ * Fades and lifts its children in on first view.
+ *
+ * The hidden state lives behind `html.js` in globals.css, so with JavaScript
+ * off the content renders plainly instead of staying at opacity 0 forever.
+ */
+export function Reveal({
+ children,
+ delay = 0,
+ className = "",
+}: {
+ children: React.ReactNode;
+ delay?: number;
+ className?: string;
+}) {
+ const { ref, shown } = useReveal();
+
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * Counts up to `value` once scrolled into view.
+ *
+ * Starts at the final value so the static export and any no-JS reader show the
+ * real number, then arms itself back to 0 on mount. The stats row is below the
+ * fold, so that reset happens off-screen and is never seen.
+ */
+export function CountUp({
+ value,
+ prefix = "",
+ suffix = "",
+ duration = 1400,
+}: {
+ value: number;
+ prefix?: string;
+ suffix?: string;
+ duration?: number;
+}) {
+ const { ref, shown } = useReveal();
+ const [display, setDisplay] = useState(value);
+ const [armed, setArmed] = useState(false);
+
+ useEffect(() => {
+ if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
+ setDisplay(0);
+ setArmed(true);
+ }, []);
+
+ useEffect(() => {
+ if (!armed || !shown) return;
+
+ let frame = 0;
+ const start = performance.now();
+
+ const tick = (now: number) => {
+ const progress = Math.min(1, (now - start) / duration);
+ // easeOutCubic
+ setDisplay(value * (1 - Math.pow(1 - progress, 3)));
+ if (progress < 1) frame = requestAnimationFrame(tick);
+ };
+
+ frame = requestAnimationFrame(tick);
+ return () => cancelAnimationFrame(frame);
+ }, [armed, shown, value, duration]);
+
+ return (
+
+ {prefix}
+ {Math.round(display)}
+ {suffix}
+
+ );
+}
diff --git a/apps/edr-landing/src/lib/apps.ts b/apps/edr-landing/src/lib/apps.ts
new file mode 100644
index 000000000..06d34a67f
--- /dev/null
+++ b/apps/edr-landing/src/lib/apps.ts
@@ -0,0 +1,16 @@
+// The two destinations this page exists to route to. Set NEXT_PUBLIC_* to each
+// app's origin; the entry path is appended here so no deploy has to remember
+// it. Inlined at build time by `output: 'export'`, so these are passed as
+// Docker build args (see infrastructure/docker/Dockerfile.web), not runtime env.
+const origin = (url: string) => url.replace(/\/+$/, "");
+
+// The passenger app serves its booking search at `/` — that is already the app.
+export const PASSENGER_URL = origin(
+ process.env.NEXT_PUBLIC_PASSENGER_URL ?? "https://passenger.edrsc.com",
+);
+
+// The freight app serves its own marketing landing at `/`, so link past it:
+// `/portal` is the app itself, and redirects to /login when signed out.
+export const FREIGHT_URL = `${origin(
+ process.env.NEXT_PUBLIC_FREIGHT_URL ?? "https://freight.edrsc.com",
+)}/portal`;
diff --git a/apps/edr-landing/tailwind.config.js b/apps/edr-landing/tailwind.config.js
new file mode 100644
index 000000000..cf24e4a93
--- /dev/null
+++ b/apps/edr-landing/tailwind.config.js
@@ -0,0 +1,24 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
+ theme: {
+ extend: {
+ colors: {
+ // Palette lifted from the freight landing page so the two public
+ // surfaces read as one railway.
+ edr: {
+ ink: "#0c1a2b",
+ "ink-2": "#10202f",
+ primary: "#0a8a5f",
+ "primary-dark": "#087049",
+ light: "#34d399",
+ amber: "#f2a516",
+ },
+ },
+ fontFamily: {
+ sans: ["Inter", "ui-sans-serif", "system-ui", "sans-serif"],
+ },
+ },
+ },
+ plugins: [],
+};
diff --git a/apps/edr-landing/tsconfig.json b/apps/edr-landing/tsconfig.json
new file mode 100644
index 000000000..0416e37cb
--- /dev/null
+++ b/apps/edr-landing/tsconfig.json
@@ -0,0 +1,12 @@
+{
+ "extends": "../../packages/config/tsconfig/nextjs.json",
+ "compilerOptions": {
+ "paths": {
+ "@/*": ["./src/*"]
+ },
+ "plugins": [{ "name": "next" }],
+ "strictNullChecks": true
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/infrastructure/docker/Dockerfile.web b/infrastructure/docker/Dockerfile.web
index 714e49d33..4860ed938 100644
--- a/infrastructure/docker/Dockerfile.web
+++ b/infrastructure/docker/Dockerfile.web
@@ -27,6 +27,8 @@ ARG VITE_API_URL
ARG VITE_BASE_API_URL
ARG VITE_USER_MANAGEMENT_BASE
ARG NEXT_PUBLIC_API_URL
+ARG NEXT_PUBLIC_PASSENGER_URL
+ARG NEXT_PUBLIC_FREIGHT_URL
ARG VITE_GOOGLE_MAPS_API_KEY
ARG VITE_POSTHOG_KEY
ARG VITE_POSTHOG_HOST
@@ -35,13 +37,19 @@ ENV VITE_API_URL=${VITE_API_URL}
ENV VITE_BASE_API_URL=${VITE_BASE_API_URL}
ENV VITE_USER_MANAGEMENT_BASE=${VITE_USER_MANAGEMENT_BASE}
ENV NEXT_PUBLIC_API_URL=${NEXT_PUBLIC_API_URL}
+# @edr/landing is a static export; these are inlined at build time.
+ENV NEXT_PUBLIC_PASSENGER_URL=${NEXT_PUBLIC_PASSENGER_URL}
+ENV NEXT_PUBLIC_FREIGHT_URL=${NEXT_PUBLIC_FREIGHT_URL}
ENV VITE_GOOGLE_MAPS_API_KEY=${VITE_GOOGLE_MAPS_API_KEY}
ENV VITE_POSTHOG_KEY=${VITE_POSTHOG_KEY}
ENV VITE_POSTHOG_HOST=${VITE_POSTHOG_HOST}
# dev|staging only — gates the Fayda/OTP bypass. Unset in prod.
ENV VITE_ENV=${VITE_ENV}
-RUN if [ -z "$VITE_API_URL" ] || [ -z "$VITE_BASE_API_URL" ] || [ -z "$VITE_USER_MANAGEMENT_BASE" ]; then \
+# Guards the freight Vite apps. @edr/landing is a Next static export that
+# reads none of these, so requiring them would block its build outright.
+RUN if [ "${TURBO_FILTER}" != "@edr/landing" ] && \
+ { [ -z "$VITE_API_URL" ] || [ -z "$VITE_BASE_API_URL" ] || [ -z "$VITE_USER_MANAGEMENT_BASE" ]; }; then \
echo "ERROR: VITE_API_URL, VITE_BASE_API_URL, and VITE_USER_MANAGEMENT_BASE must all be set" && \
exit 1; \
fi
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 6e9da8bbc..74cb43f20 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -607,7 +607,7 @@ importers:
version: 5.101.0(react@19.2.6)
'@tria-plc/iamui':
specifier: file:../../../local-packages/tria-plc-iamui-0.1.1.tgz
- version: file:local-packages/tria-plc-iamui-0.1.1.tgz(0ce39b7e349029277dcd938d06eeb0f7)
+ version: file:local-packages/tria-plc-iamui-0.1.1.tgz(a5d0bdee55164ae56064fb273b530e00)
'@vis.gl/react-google-maps':
specifier: ^1.8.3
version: 1.8.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
@@ -779,6 +779,43 @@ importers:
specifier: ^5.5.4
version: 5.9.3
+ apps/edr-landing:
+ dependencies:
+ lucide-react:
+ specifier: ^0.446.0
+ version: 0.446.0(react@18.3.1)
+ next:
+ specifier: ^14.2.0
+ version: 14.2.35(@playwright/test@1.61.1)(babel-plugin-macros@3.1.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react:
+ specifier: ^18.3.1
+ version: 18.3.1
+ react-dom:
+ specifier: ^18.3.1
+ version: 18.3.1(react@18.3.1)
+ devDependencies:
+ '@types/node':
+ specifier: ^20.0.0
+ version: 20.19.42
+ '@types/react':
+ specifier: ^18.3.11
+ version: 18.3.31
+ '@types/react-dom':
+ specifier: ^18.3.0
+ version: 18.3.7(@types/react@18.3.31)
+ autoprefixer:
+ specifier: ^10.4.20
+ version: 10.5.0(postcss@8.5.15)
+ postcss:
+ specifier: ^8.4.47
+ version: 8.5.15
+ tailwindcss:
+ specifier: ^3.4.13
+ version: 3.4.19(yaml@2.9.0)
+ typescript:
+ specifier: ^5.5.4
+ version: 5.9.3
+
apps/edr-passenger-api:
dependencies:
'@edr/iam-seed':
@@ -13087,11 +13124,11 @@ snapshots:
'@babel/helpers': 7.29.7
'@babel/parser': 7.29.7
'@babel/template': 7.29.7
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
'@babel/types': 7.29.7
'@jridgewell/remapping': 2.3.5
convert-source-map: 2.0.0
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
gensync: 1.0.0-beta.2
json5: 2.2.3
semver: 6.3.1
@@ -13126,7 +13163,7 @@ snapshots:
'@babel/helper-optimise-call-expression': 7.29.7
'@babel/helper-replace-supers': 7.29.7(@babel/core@7.29.7)
'@babel/helper-skip-transparent-expression-wrappers': 7.29.7
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
semver: 6.3.1
transitivePeerDependencies:
- supports-color
@@ -13135,7 +13172,14 @@ snapshots:
'@babel/helper-member-expression-to-functions@7.29.7':
dependencies:
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.29.7':
+ dependencies:
+ '@babel/traverse': 7.29.7
'@babel/types': 7.29.7
transitivePeerDependencies:
- supports-color
@@ -13150,9 +13194,9 @@ snapshots:
'@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
dependencies:
'@babel/core': 7.29.7
- '@babel/helper-module-imports': 7.29.7(supports-color@5.5.0)
+ '@babel/helper-module-imports': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
transitivePeerDependencies:
- supports-color
@@ -13167,13 +13211,13 @@ snapshots:
'@babel/core': 7.29.7
'@babel/helper-member-expression-to-functions': 7.29.7
'@babel/helper-optimise-call-expression': 7.29.7
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
transitivePeerDependencies:
- supports-color
'@babel/helper-skip-transparent-expression-wrappers@7.29.7':
dependencies:
- '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7
'@babel/types': 7.29.7
transitivePeerDependencies:
- supports-color
@@ -13326,6 +13370,18 @@ snapshots:
'@babel/parser': 7.29.7
'@babel/types': 7.29.7
+ '@babel/traverse@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-globals': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+ debug: 4.4.3(supports-color@8.1.1)
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/traverse@7.29.7(supports-color@5.5.0)':
dependencies:
'@babel/code-frame': 7.29.7
@@ -13810,7 +13866,7 @@ snapshots:
'@emotion/babel-plugin@11.13.5':
dependencies:
- '@babel/helper-module-imports': 7.29.7(supports-color@5.5.0)
+ '@babel/helper-module-imports': 7.29.7
'@babel/runtime': 7.29.7
'@emotion/hash': 0.9.2
'@emotion/memoize': 0.9.0
@@ -13976,7 +14032,7 @@ snapshots:
'@eslint/eslintrc@2.1.4':
dependencies:
ajv: 6.15.0
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
espree: 9.6.1
globals: 13.24.0
ignore: 5.3.2
@@ -14136,7 +14192,7 @@ snapshots:
'@humanwhocodes/config-array@0.13.0':
dependencies:
'@humanwhocodes/object-schema': 2.0.3
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
minimatch: 3.1.5
transitivePeerDependencies:
- supports-color
@@ -15735,7 +15791,7 @@ snapshots:
'@puppeteer/browsers@2.13.2':
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
extract-zip: 2.0.1
progress: 2.0.3
proxy-agent: 6.5.0
@@ -17809,7 +17865,7 @@ snapshots:
'@tokenizer/inflate@0.4.1':
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
token-types: 6.1.2
transitivePeerDependencies:
- supports-color
@@ -18096,6 +18152,130 @@ snapshots:
- utf-8-validate
- vite
+ '@tria-plc/iamui@file:local-packages/tria-plc-iamui-0.1.1.tgz(a5d0bdee55164ae56064fb273b530e00)':
+ dependencies:
+ '@emotion/react': 11.14.0(@types/react@18.3.31)(react@19.2.6)
+ '@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@18.3.31)(react@19.2.6))(@types/react@18.3.31)(react@19.2.6)
+ '@hookform/resolvers': 5.4.0(react-hook-form@7.77.0(react@19.2.6))
+ '@lottiefiles/react-lottie-player': 3.6.0(react@19.2.6)
+ '@mantine/charts': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@7.17.8(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(recharts@3.8.1(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)(redux@5.0.1))
+ '@mantine/core': 7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@mantine/dates': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@7.17.8(react@19.2.6))(dayjs@1.11.21)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@mantine/hooks': 7.17.8(react@19.2.6)
+ '@mantine/notifications': 7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@7.17.8(react@19.2.6))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-accordion': 1.2.13(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-alert-dialog': 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-avatar': 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-checkbox': 1.3.4(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-context-menu': 2.3.0(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-dialog': 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-dropdown-menu': 2.1.17(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-hover-card': 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-label': 2.1.9(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-navigation-menu': 1.2.15(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-popover': 1.1.16(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-progress': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-radio-group': 1.4.0(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-scroll-area': 1.2.11(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-select': 2.3.0(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-separator': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-slot': 1.2.5(@types/react@18.3.31)(react@19.2.6)
+ '@radix-ui/react-switch': 1.3.0(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-tabs': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-toast': 1.2.16(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@radix-ui/react-tooltip': 1.2.9(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@react-pdf-viewer/core': 3.12.0(pdfjs-dist@5.4.296)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@react-pdf-viewer/default-layout': 3.12.0(pdfjs-dist@5.4.296)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@react-pdf/renderer': 4.5.1(react@19.2.6)
+ '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@18.3.31)(react@19.2.6)(redux@5.0.1))(react@19.2.6)
+ '@tabler/icons-react': 3.44.0(react@19.2.6)
+ '@tailwindcss/vite': 4.3.0(vite@5.4.21(@types/node@24.13.1)(lightningcss@1.32.0)(terser@5.48.0))
+ '@tanstack/react-query': 5.101.0(react@19.2.6)
+ '@tanstack/react-query-devtools': 5.101.0(@tanstack/react-query@5.101.0(react@19.2.6))(react@19.2.6)
+ '@tanstack/react-table': 8.21.3(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ '@tinymce/tinymce-react': 6.3.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(tinymce@7.9.3)
+ '@types/dompurify': 3.2.0
+ '@types/node': 24.13.1
+ '@types/tinymce': 4.6.9
+ axios: 1.17.0
+ class-variance-authority: 0.7.1
+ clsx: 2.1.1
+ cmdk: 1.1.1(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ date-fns: 3.6.0
+ dayjs: 1.11.21
+ dompurify: 3.4.8
+ ethiopian-calendar-date-converter: 2.1.6
+ ethiopian-calendar-new: 1.1.0
+ file-type: 18.7.0
+ framer-motion: 12.40.0(@emotion/is-prop-valid@1.4.0)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ html2canvas: 1.4.1
+ i18next: 25.10.10(typescript@5.9.3)
+ i18next-browser-languagedetector: 8.2.1
+ jquery: 3.7.1
+ js-cookie: 3.0.8
+ jspdf: 3.0.4
+ lodash: 4.18.1
+ lucide-react: 0.513.0(react@19.2.6)
+ mantine-react-table: 2.0.0-beta.9(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/dates@7.17.8(@mantine/core@7.17.8(@mantine/hooks@7.17.8(react@19.2.6))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@7.17.8(react@19.2.6))(dayjs@1.11.21)(react-dom@19.2.6(react@19.2.6))(react@19.2.6))(@mantine/hooks@7.17.8(react@19.2.6))(@tabler/icons-react@3.44.0(react@19.2.6))(clsx@2.1.1)(dayjs@1.11.21)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ mui-ethiopian-datepicker: 0.3.2(4b3af212eafdf0059f009b005d7e343d)
+ next-themes: 0.4.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ path: 0.12.7
+ pdf-lib: 1.17.1
+ qs: 6.15.2
+ react: 19.2.6
+ react-cookie: 8.1.2(@types/react@18.3.31)(react@19.2.6)
+ react-css-nocode-editor: 1.0.13(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)
+ react-day-picker: 8.10.2(date-fns@3.6.0)(react@19.2.6)
+ react-dom: 19.2.6(react@19.2.6)
+ react-dropzone: 14.4.1(react@19.2.6)
+ react-hook-form: 7.77.0(react@19.2.6)
+ react-i18next: 15.7.4(i18next@25.10.10(typescript@5.9.3))(react-dom@19.2.6(react@19.2.6))(react@19.2.6)(typescript@5.9.3)
+ react-icons: 5.6.0(react@19.2.6)
+ react-image-crop: 11.0.10(react@19.2.6)
+ react-intersection-observer: 9.16.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react-pdf: 10.4.1(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react-pdf-html: 2.1.5(@react-pdf/renderer@4.5.1(react@19.2.6))(react@19.2.6)
+ react-redux: 9.3.0(@types/react@18.3.31)(react@19.2.6)(redux@5.0.1)
+ react-resizable-panels: 3.0.6(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react-router-dom: 7.17.0(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ react-signature-canvas: 1.1.0-alpha.2(@types/prop-types@15.7.15)(@types/react@18.3.31)(prop-types@15.8.1)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ recharts: 3.8.1(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)(redux@5.0.1)
+ rollup-plugin-visualizer: 7.0.1(rollup@4.61.1)
+ socket.io-client: 4.8.3
+ sonner: 2.0.7(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ tailwind-merge: 3.6.0
+ tailwind-scrollbar-hide: 4.0.0(tailwindcss@4.3.0)
+ tailwindcss: 4.3.0
+ tailwindcss-animate: 1.0.7(tailwindcss@4.3.0)
+ tinymce: 7.9.3
+ url: 0.11.4
+ vaul: 1.1.2(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@19.2.6(react@19.2.6))(react@19.2.6)
+ xlsx: 0.18.5
+ zod: 3.25.76
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@emotion/is-prop-valid'
+ - '@mui/icons-material'
+ - '@mui/material'
+ - '@mui/x-date-pickers'
+ - '@types/prop-types'
+ - '@types/react'
+ - '@types/react-dom'
+ - bufferutil
+ - debug
+ - pdfjs-dist
+ - prop-types
+ - react-is
+ - react-native
+ - redux
+ - rolldown
+ - rollup
+ - supports-color
+ - typescript
+ - utf-8-validate
+ - vite
+
'@ts-morph/common@0.27.0':
dependencies:
fast-glob: 3.3.3
@@ -18493,7 +18673,7 @@ snapshots:
'@typescript-eslint/types': 8.60.1
'@typescript-eslint/typescript-estree': 8.60.1(typescript@5.9.3)
'@typescript-eslint/visitor-keys': 8.60.1
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
eslint: 8.57.1
typescript: 5.9.3
transitivePeerDependencies:
@@ -18503,7 +18683,7 @@ snapshots:
dependencies:
'@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.9.3)
'@typescript-eslint/types': 8.60.1
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
typescript: 5.9.3
transitivePeerDependencies:
- supports-color
@@ -18522,7 +18702,7 @@ snapshots:
'@typescript-eslint/types': 8.60.1
'@typescript-eslint/typescript-estree': 8.60.1(typescript@5.9.3)
'@typescript-eslint/utils': 8.60.1(eslint@8.57.1)(typescript@5.9.3)
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
eslint: 8.57.1
ts-api-utils: 2.5.0(typescript@5.9.3)
typescript: 5.9.3
@@ -18537,7 +18717,7 @@ snapshots:
'@typescript-eslint/tsconfig-utils': 8.60.1(typescript@5.9.3)
'@typescript-eslint/types': 8.60.1
'@typescript-eslint/visitor-keys': 8.60.1
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
minimatch: 10.2.5
semver: 7.8.2
tinyglobby: 0.2.17
@@ -18826,7 +19006,7 @@ snapshots:
agent-base@6.0.2:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -19335,6 +19515,16 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ babel-plugin-styled-components@2.3.0(styled-components@5.3.11(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6))(supports-color@5.5.0):
+ dependencies:
+ '@babel/helper-annotate-as-pure': 7.29.7
+ '@babel/helper-module-imports': 7.29.7(supports-color@5.5.0)
+ '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7)
+ picomatch: 4.0.4
+ styled-components: 5.3.11(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)
+ transitivePeerDependencies:
+ - supports-color
+
babel-polyfill@6.26.0:
dependencies:
babel-runtime: 6.26.0
@@ -19490,7 +19680,7 @@ snapshots:
dependencies:
bytes: 3.1.2
content-type: 1.0.5
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
http-errors: 2.0.1
iconv-lite: 0.7.2
on-finished: 2.4.1
@@ -20539,7 +20729,7 @@ snapshots:
engine.io-client@6.6.5:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
engine.io-parser: 5.2.3
ws: 8.20.1
xmlhttprequest-ssl: 2.1.2
@@ -20559,7 +20749,7 @@ snapshots:
base64id: 2.0.0
cookie: 0.7.2
cors: 2.8.6
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
engine.io-parser: 5.2.3
ws: 8.21.0
transitivePeerDependencies:
@@ -20790,7 +20980,7 @@ snapshots:
eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.60.1(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1))(eslint@8.57.1):
dependencies:
'@nolyfill/is-core-module': 1.0.39
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
eslint: 8.57.1
get-tsconfig: 4.14.0
is-bun-module: 2.0.0
@@ -20918,7 +21108,7 @@ snapshots:
ajv: 6.15.0
chalk: 4.1.2
cross-spawn: 7.0.6
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
doctrine: 3.0.0
escape-string-regexp: 4.0.0
eslint-scope: 7.2.2
@@ -21155,7 +21345,7 @@ snapshots:
content-type: 1.0.5
cookie: 0.7.2
cookie-signature: 1.2.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
depd: 2.0.0
encodeurl: 2.0.0
escape-html: 1.0.3
@@ -21208,7 +21398,7 @@ snapshots:
extract-zip@2.0.1:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
get-stream: 5.2.0
yauzl: 2.10.0
optionalDependencies:
@@ -21363,7 +21553,7 @@ snapshots:
finalhandler@2.1.1:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
encodeurl: 2.0.0
escape-html: 1.0.3
on-finished: 2.4.1
@@ -21611,7 +21801,7 @@ snapshots:
dependencies:
basic-ftp: 5.3.1
data-uri-to-buffer: 6.0.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -21920,7 +22110,7 @@ snapshots:
http-proxy-agent@7.0.2:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -21933,14 +22123,14 @@ snapshots:
https-proxy-agent@5.0.1:
dependencies:
agent-base: 6.0.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
https-proxy-agent@7.0.6:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -22380,7 +22570,7 @@ snapshots:
istanbul-lib-source-maps@4.0.1:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
istanbul-lib-coverage: 3.2.2
source-map: 0.6.1
transitivePeerDependencies:
@@ -23040,7 +23230,7 @@ snapshots:
dependencies:
chalk: 5.6.2
commander: 13.1.0
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
execa: 8.0.1
lilconfig: 3.1.3
listr2: 8.3.3
@@ -23727,7 +23917,7 @@ snapshots:
micromark@4.0.2:
dependencies:
'@types/debug': 4.1.13
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
decode-named-character-reference: 1.3.0
devlop: 1.1.0
micromark-core-commonmark: 2.0.3
@@ -24253,7 +24443,7 @@ snapshots:
dependencies:
'@tootallnate/quickjs-emscripten': 0.23.0
agent-base: 7.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
get-uri: 6.0.5
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
@@ -24603,7 +24793,7 @@ snapshots:
proxy-agent@6.5.0:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
http-proxy-agent: 7.0.2
https-proxy-agent: 7.0.6
lru-cache: 7.18.3
@@ -24632,7 +24822,7 @@ snapshots:
dependencies:
'@puppeteer/browsers': 2.13.2
chromium-bidi: 14.0.0(devtools-protocol@0.0.1608973)
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
devtools-protocol: 0.0.1608973
typed-query-selector: 2.12.2
webdriver-bidi-protocol: 0.4.1
@@ -24885,6 +25075,15 @@ snapshots:
- '@babel/core'
- react-is
+ react-css-nocode-editor@1.0.13(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6):
+ dependencies:
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ styled-components: 5.3.11(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6)
+ transitivePeerDependencies:
+ - '@babel/core'
+ - react-is
+
react-day-picker@8.10.2(date-fns@3.6.0)(react@19.2.6):
dependencies:
date-fns: 3.6.0
@@ -25495,7 +25694,7 @@ snapshots:
router@2.2.0:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
depd: 2.0.0
is-promise: 4.0.0
parseurl: 1.3.3
@@ -25617,7 +25816,7 @@ snapshots:
send@1.2.1:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
encodeurl: 2.0.0
escape-html: 1.0.3
etag: 1.8.1
@@ -25833,7 +26032,7 @@ snapshots:
socket.io-adapter@2.5.8:
dependencies:
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
ws: 8.21.0
transitivePeerDependencies:
- bufferutil
@@ -25843,7 +26042,7 @@ snapshots:
socket.io-client@4.8.3:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
engine.io-client: 6.6.5
socket.io-parser: 4.2.6
transitivePeerDependencies:
@@ -25854,7 +26053,7 @@ snapshots:
socket.io-parser@4.2.6:
dependencies:
'@socket.io/component-emitter': 3.1.2
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
transitivePeerDependencies:
- supports-color
@@ -25863,7 +26062,7 @@ snapshots:
accepts: 1.3.8
base64id: 2.0.0
cors: 2.8.6
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
engine.io: 6.6.9
socket.io-adapter: 2.5.8
socket.io-parser: 4.2.6
@@ -25875,7 +26074,7 @@ snapshots:
socks-proxy-agent@8.0.5:
dependencies:
agent-base: 7.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
socks: 2.8.9
transitivePeerDependencies:
- supports-color
@@ -26170,6 +26369,24 @@ snapshots:
transitivePeerDependencies:
- '@babel/core'
+ styled-components@5.3.11(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6):
+ dependencies:
+ '@babel/helper-module-imports': 7.29.7(supports-color@5.5.0)
+ '@babel/traverse': 7.29.7(supports-color@5.5.0)
+ '@emotion/is-prop-valid': 1.4.0
+ '@emotion/stylis': 0.8.5
+ '@emotion/unitless': 0.7.5
+ babel-plugin-styled-components: 2.3.0(styled-components@5.3.11(react-dom@19.2.6(react@19.2.6))(react-is@19.2.7)(react@19.2.6))(supports-color@5.5.0)
+ css-to-react-native: 3.2.0
+ hoist-non-react-statics: 3.3.2
+ react: 19.2.6
+ react-dom: 19.2.6(react@19.2.6)
+ react-is: 19.2.7
+ shallowequal: 1.1.0
+ supports-color: 5.5.0
+ transitivePeerDependencies:
+ - '@babel/core'
+
styled-jsx@5.1.1(babel-plugin-macros@3.1.0)(react@18.3.1):
dependencies:
client-only: 0.0.1
@@ -26195,7 +26412,7 @@ snapshots:
dependencies:
component-emitter: 1.3.1
cookiejar: 2.1.4
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
fast-safe-stringify: 2.1.1
form-data: 4.0.5
formidable: 3.5.4
@@ -26708,7 +26925,7 @@ snapshots:
app-root-path: 3.1.0
buffer: 6.0.3
dayjs: 1.11.21
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
dedent: 1.7.2(babel-plugin-macros@3.1.0)
dotenv: 16.6.1
glob: 10.5.0
@@ -26732,7 +26949,7 @@ snapshots:
app-root-path: 3.1.0
buffer: 6.0.3
dayjs: 1.11.21
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
dedent: 1.7.2(babel-plugin-macros@3.1.0)
dotenv: 16.6.1
glob: 10.5.0
@@ -27093,7 +27310,7 @@ snapshots:
vite-node@2.1.9(@types/node@22.20.1)(lightningcss@1.32.0)(terser@5.48.0):
dependencies:
cac: 6.7.14
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
es-module-lexer: 1.7.0
pathe: 1.1.2
vite: 5.4.21(@types/node@22.20.1)(lightningcss@1.32.0)(terser@5.48.0)
@@ -27111,7 +27328,7 @@ snapshots:
vite-node@2.1.9(@types/node@24.13.1)(lightningcss@1.32.0)(terser@5.48.0):
dependencies:
cac: 6.7.14
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
es-module-lexer: 1.7.0
pathe: 1.1.2
vite: 5.4.21(@types/node@24.13.1)(lightningcss@1.32.0)(terser@5.48.0)
@@ -27158,7 +27375,7 @@ snapshots:
'@vitest/spy': 2.1.9
'@vitest/utils': 2.1.9
chai: 5.3.3
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 1.1.2
@@ -27194,7 +27411,7 @@ snapshots:
'@vitest/spy': 2.1.9
'@vitest/utils': 2.1.9
chai: 5.3.3
- debug: 4.4.3(supports-color@5.5.0)
+ debug: 4.4.3(supports-color@8.1.1)
expect-type: 1.3.0
magic-string: 0.30.21
pathe: 1.1.2
diff --git a/turbo.json b/turbo.json
index 18f5d7459..9ab26ab9c 100644
--- a/turbo.json
+++ b/turbo.json
@@ -4,7 +4,7 @@
"tasks": {
"build": {
"dependsOn": ["^build"],
- "outputs": ["dist/**", "build/**", ".next/**"]
+ "outputs": ["dist/**", "build/**", ".next/**", "out/**"]
},
"dev": {
"cache": false,