diff --git a/apps/edr-freight-web/backoffice/index.css b/apps/edr-freight-web/backoffice/index.css index 132d7d926..fa9eb3e3d 100644 --- a/apps/edr-freight-web/backoffice/index.css +++ b/apps/edr-freight-web/backoffice/index.css @@ -1,6 +1,12 @@ @import "tailwindcss"; @import "@edr/ui-common/theme.css" layer(theme); +/* The app toggles dark mode by setting the `dark` class on (see + main.tsx / FreightDashboardLayout). Without this, Tailwind v4 compiles + `dark:` utilities to `@media (prefers-color-scheme: dark)` and they follow + the OS setting instead of the in-app toggle. */ +@custom-variant dark (&:where(.dark, .dark *)); + /* Bridge the central Mantine theme into Tailwind. freightMantineTheme (createTheme) is the single source of truth; these just alias its generated CSS variables so `bg-edr-*`, `text-edr-*`, `border-edr-*` utilities resolve @@ -17,6 +23,34 @@ --color-edr-soft: var(--mantine-color-edr-soft-6); --color-edr-ink: var(--mantine-color-edr-ink-6); --color-edr-accent: var(--mantine-color-edr-accent-6); + + /* Numeric `primary-*` scale used throughout the vendored IAM UI + (src/user-management, src/shared, …). Aliased to the edr-green Mantine + tuple (freight-brand.ts) so bg-primary-50 … text-primary-900 resolve to + brand shades; without these the utilities are simply not generated. */ + --color-primary-50: var(--mantine-color-edr-green-0); + --color-primary-100: var(--mantine-color-edr-green-1); + --color-primary-200: var(--mantine-color-edr-green-2); + --color-primary-300: var(--mantine-color-edr-green-3); + --color-primary-400: var(--mantine-color-edr-green-4); + --color-primary-500: var(--mantine-color-edr-green-5); + --color-primary-600: var(--mantine-color-edr-green-6); + --color-primary-700: var(--mantine-color-edr-green-7); + --color-primary-800: var(--mantine-color-edr-green-8); + --color-primary-900: var(--mantine-color-edr-green-9); + --color-primary-950: #022c22; +} + +/* Main brand color. NOTE: at runtime TenantConfig.applyTenantTheme() sets + --primary (and --ring/--accent/…) as INLINE styles on from the + per-hostname tenant config, which beats any stylesheet — change the color + there (localhost → #0EA371). This block is the pre-mount fallback and fixes + --primary-foreground (the layered dark default is dark-on-dark). Same value + in both modes so the brand doesn't shift when toggling. */ +:root, +.dark { + --primary: #0EA371; + --primary-foreground: #ffffff; } :root { diff --git a/apps/edr-freight-web/backoffice/src/components/overview/OverviewQuickLinks.tsx b/apps/edr-freight-web/backoffice/src/components/overview/OverviewQuickLinks.tsx index cc2fa6333..d1598403b 100644 --- a/apps/edr-freight-web/backoffice/src/components/overview/OverviewQuickLinks.tsx +++ b/apps/edr-freight-web/backoffice/src/components/overview/OverviewQuickLinks.tsx @@ -24,7 +24,7 @@ const links = [ { title: "User management", description: "Employees, roles, and permissions", - href: "/dashboard/user-management", + href: "/user-management", icon: Users, }, ]; diff --git a/apps/edr-freight-web/backoffice/src/layout/components/TenantConfig.ts b/apps/edr-freight-web/backoffice/src/layout/components/TenantConfig.ts index 956c29603..758e2a75a 100644 --- a/apps/edr-freight-web/backoffice/src/layout/components/TenantConfig.ts +++ b/apps/edr-freight-web/backoffice/src/layout/components/TenantConfig.ts @@ -119,7 +119,7 @@ const tenantConfigs: Record = { appName: "Smart Office", organizationName: "Addis Ababa City Administration", logo: "", - primaryColor: "#115005", + primaryColor: "#0EA371", moduleConfig: { dms: true, performance: true, @@ -543,7 +543,14 @@ export const applyTenantTheme = (config: TenantConfig) => { root.style.setProperty("--primary", primary); root.style.setProperty("--ring", primary); root.style.setProperty("--sidebar-primary", primary); - root.style.setProperty("--accent", primary); + // Accent is a subtle hover/highlight background (dropdown items, menus) that + // must stay readable under --accent-foreground text in BOTH color schemes. + // Full-strength brand color here produced dark-pill-with-dark-text hovers, + // so use a translucent tint of the brand color instead. + root.style.setProperty( + "--accent", + `color-mix(in oklab, ${primary} 15%, transparent)`, + ); root.style.setProperty("--chart-1", primary); if (config.organizationName) { diff --git a/apps/edr-freight-web/backoffice/src/pages/DashboardPage.tsx b/apps/edr-freight-web/backoffice/src/pages/DashboardPage.tsx index 5e0be323b..701400310 100644 --- a/apps/edr-freight-web/backoffice/src/pages/DashboardPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/DashboardPage.tsx @@ -140,7 +140,7 @@ const DashboardPage = () => { diff --git a/apps/edr-freight-web/backoffice/src/shared/common/form/fields/FormFields.tsx b/apps/edr-freight-web/backoffice/src/shared/common/form/fields/FormFields.tsx index 55b5524a1..bbd9b33b9 100644 --- a/apps/edr-freight-web/backoffice/src/shared/common/form/fields/FormFields.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/common/form/fields/FormFields.tsx @@ -448,14 +448,14 @@ export const FormAddableList = ({ {items.map((unit: string, index: number) => (
{unit} handleDelete(index)} />
@@ -503,7 +503,7 @@ export const ClearableSelect = ({ field.onChange("")} /> @@ -784,7 +784,7 @@ export const SelectedItemsChips = ({ {items.map((item) => (
{localizedName(item.name)} diff --git a/apps/edr-freight-web/backoffice/src/shared/common/ui/SliderUI.tsx b/apps/edr-freight-web/backoffice/src/shared/common/ui/SliderUI.tsx index 7278cf97b..99b267bdd 100644 --- a/apps/edr-freight-web/backoffice/src/shared/common/ui/SliderUI.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/common/ui/SliderUI.tsx @@ -43,7 +43,7 @@ export const SliderUI: FC = ({ disabled={disabled} className="relative flex w-full touch-none select-none items-center h-5"> - + diff --git a/apps/edr-freight-web/backoffice/src/shared/common/ui/badge.tsx b/apps/edr-freight-web/backoffice/src/shared/common/ui/badge.tsx index 36a25ce4e..ca5b2bd4e 100644 --- a/apps/edr-freight-web/backoffice/src/shared/common/ui/badge.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/common/ui/badge.tsx @@ -11,9 +11,9 @@ const badgeVariants = cva( variant: { //draft and forward default: - "bg-blue-100 text-blue-800 text-sm font-medium me-2 px-3 py-0.5 rounded-full dark:bg-blue-900 dark:text-blue-300", + "bg-primary-100 text-primary-800 text-sm font-medium me-2 px-3 py-0.5 rounded-full dark:bg-primary-900 dark:text-primary-300", secondary: - "bg-indigo-100 text-indigo-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-indigo-900 dark:text-indigo-300", + "bg-primary-100 text-primary-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-full dark:bg-primary-900 dark:text-primary-300", destructive: "bg-gray-100 text-gray-800 text-sm font-medium me-2 px-2.5 py-0.5 rounded-sm dark:bg-gray-700 dark:text-gray-300", outline: diff --git a/apps/edr-freight-web/backoffice/src/shared/common/ui/button.tsx b/apps/edr-freight-web/backoffice/src/shared/common/ui/button.tsx index f6920a95b..c2801a4d0 100644 --- a/apps/edr-freight-web/backoffice/src/shared/common/ui/button.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/common/ui/button.tsx @@ -37,7 +37,7 @@ const buttonVariants = cva( actionYellow: "bg-amber-50/90 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-900/40 text-amber-700 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-950/40 hover:text-amber-800 dark:hover:text-amber-200 hover:border-amber-300 dark:hover:border-amber-800/60 focus-visible:ring-amber-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300", actionBlue: - "bg-blue-50/90 dark:bg-blue-950/20 border border-blue-200 dark:border-blue-900/40 text-blue-700 dark:text-blue-300 hover:bg-blue-100 dark:hover:bg-blue-950/40 hover:text-blue-800 dark:hover:text-blue-200 hover:border-blue-300 dark:hover:border-blue-800/60 focus-visible:ring-blue-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300", + "bg-primary-50/90 dark:bg-primary-950/20 border border-primary-200 dark:border-primary-900/40 text-primary-700 dark:text-primary-300 hover:bg-primary-100 dark:hover:bg-primary-950/40 hover:text-primary-800 dark:hover:text-primary-200 hover:border-primary-300 dark:hover:border-primary-800/60 focus-visible:ring-primary-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300", actionPurple: "bg-purple-50/90 dark:bg-purple-950/20 border border-purple-200 dark:border-purple-900/40 text-purple-700 dark:text-purple-300 hover:bg-purple-100 dark:hover:bg-purple-950/40 hover:text-purple-800 dark:hover:text-purple-200 hover:border-purple-300 dark:hover:border-purple-800/60 focus-visible:ring-purple-500 rounded-full h-9 px-4 flex items-center gap-2 font-medium shadow-xs hover:shadow-md transition-all duration-300", }, diff --git a/apps/edr-freight-web/backoffice/src/shared/common/ui/treeSelect.tsx b/apps/edr-freight-web/backoffice/src/shared/common/ui/treeSelect.tsx index 74590ac23..9f9af3c52 100644 --- a/apps/edr-freight-web/backoffice/src/shared/common/ui/treeSelect.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/common/ui/treeSelect.tsx @@ -466,7 +466,7 @@ function TreeItem({ {selectedCount !== null && selectedCount > 0 && ( + className="mr-2 bg-primary-100 hover:bg-primary-100 flex-shrink-0"> {selectedCount} selected )} @@ -1053,7 +1053,7 @@ export default function TreeView({ onMouseMove={handleMouseMove}> {isDragging && (
{ type="button" onClick={handleResendCode} disabled={isResendingCode || !email || !phoneNumber || isSubmitting} - className="text-sm text-indigo-600 hover:text-indigo-800 font-medium flex items-center justify-center space-x-1 mx-auto" + className="text-sm text-primary-600 hover:text-primary-800 font-medium flex items-center justify-center space-x-1 mx-auto" > {isResendingCode ? ( <> - + Sending... ) : ( diff --git a/apps/edr-freight-web/backoffice/src/shared/components/activity-log/activity-stats.tsx b/apps/edr-freight-web/backoffice/src/shared/components/activity-log/activity-stats.tsx index 87db50965..020634cf0 100644 --- a/apps/edr-freight-web/backoffice/src/shared/components/activity-log/activity-stats.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/components/activity-log/activity-stats.tsx @@ -38,8 +38,8 @@ export function ActivityStats({ label: t("auditLog.stats.totalActivities"), value: totalActivities.toLocaleString(), icon: Activity, - color: "text-blue-600 dark:text-blue-400", - bgColor: "bg-blue-50 dark:bg-blue-950", + color: "text-primary-600 dark:text-primary-400", + bgColor: "bg-primary-50 dark:bg-primary-950", }, { label: t("auditLog.stats.successRate"), diff --git a/apps/edr-freight-web/backoffice/src/shared/components/activity-log/export-modal.tsx b/apps/edr-freight-web/backoffice/src/shared/components/activity-log/export-modal.tsx index f3657c727..cbda1bced 100644 --- a/apps/edr-freight-web/backoffice/src/shared/components/activity-log/export-modal.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/components/activity-log/export-modal.tsx @@ -50,11 +50,11 @@ export function ExportModal({ const textStrong = 'text-gray-900 dark:text-gray-100' const textMuted = 'text-gray-600 dark:text-gray-300' const textSubtle = 'text-gray-500 dark:text-gray-400' - const primaryBorder = 'border-primary dark:border-sky-600' + const primaryBorder = 'border-primary dark:border-primary-600' const hoverBg = 'hover:bg-accent dark:hover:bg-gray-800' const hoverText = 'hover:text-accent-foreground dark:hover:text-gray-100' const primaryBtn = - 'bg-primary dark:bg-sky-600 hover:bg-primary/90 dark:hover:bg-sky-500 text-white' + 'bg-primary dark:bg-primary-600 hover:bg-primary/90 dark:hover:bg-primary-500 text-white' const handleExport = () => { onExport(options) diff --git a/apps/edr-freight-web/backoffice/src/shared/components/audit-log/AuditLogDetailPanel.tsx b/apps/edr-freight-web/backoffice/src/shared/components/audit-log/AuditLogDetailPanel.tsx index 6acb61239..ee5e128b2 100644 --- a/apps/edr-freight-web/backoffice/src/shared/components/audit-log/AuditLogDetailPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/components/audit-log/AuditLogDetailPanel.tsx @@ -152,7 +152,7 @@ export const AuditLogDetailPanel = ({ {change.to !== undefined && (

To:{" "} - + {JSON.stringify(change.to)}

diff --git a/apps/edr-freight-web/backoffice/src/shared/components/login/ForgotPassword.tsx b/apps/edr-freight-web/backoffice/src/shared/components/login/ForgotPassword.tsx index 81d85be20..e1707310c 100644 --- a/apps/edr-freight-web/backoffice/src/shared/components/login/ForgotPassword.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/components/login/ForgotPassword.tsx @@ -51,8 +51,8 @@ export const ForgotPassword = () => { }; return ( -
-
+
+
@@ -111,7 +111,7 @@ export const ForgotPassword = () => { )}
-
+

diff --git a/apps/edr-freight-web/backoffice/src/shared/hooks/useDarkMode.ts b/apps/edr-freight-web/backoffice/src/shared/hooks/useDarkMode.ts index 8cbc4470a..273064cec 100644 --- a/apps/edr-freight-web/backoffice/src/shared/hooks/useDarkMode.ts +++ b/apps/edr-freight-web/backoffice/src/shared/hooks/useDarkMode.ts @@ -1,11 +1,15 @@ import { useEffect, useState } from "react"; +// Same storage key/values as main.tsx and FreightDashboardLayout so the +// vendored IAM UI toggle and the host dashboard toggle stay in sync. +const THEME_STORAGE_KEY = "edr-theme"; + export const useDarkMode = () => { const [isDarkMode, setIsDarkMode] = useState(() => { // Initialize from localStorage or system preference - const savedTheme = localStorage.getItem("darkMode"); - if (savedTheme) { - return savedTheme === "true"; + const savedTheme = localStorage.getItem(THEME_STORAGE_KEY); + if (savedTheme === "dark" || savedTheme === "light") { + return savedTheme === "dark"; } return window.matchMedia("(prefers-color-scheme: dark)").matches; }); @@ -14,10 +18,10 @@ export const useDarkMode = () => { // Apply dark mode class on mount and when isDarkMode changes if (isDarkMode) { document.documentElement.classList.add("dark"); - localStorage.setItem("darkMode", "true"); + localStorage.setItem(THEME_STORAGE_KEY, "dark"); } else { document.documentElement.classList.remove("dark"); - localStorage.setItem("darkMode", "false"); + localStorage.setItem(THEME_STORAGE_KEY, "light"); } }, [isDarkMode]); diff --git a/apps/edr-freight-web/backoffice/src/shared/pages/audit-log/audit-log-page.tsx b/apps/edr-freight-web/backoffice/src/shared/pages/audit-log/audit-log-page.tsx index 0023f243b..d7cb79045 100644 --- a/apps/edr-freight-web/backoffice/src/shared/pages/audit-log/audit-log-page.tsx +++ b/apps/edr-freight-web/backoffice/src/shared/pages/audit-log/audit-log-page.tsx @@ -239,7 +239,7 @@ export default function AuditLogPageShared({ case "warning": return "bg-yellow-100 text-yellow-800 dark:bg-yellow-950 dark:text-yellow-300"; case "pending": - return "bg-blue-100 text-blue-800 dark:bg-blue-950 dark:text-blue-300"; + return "bg-primary-100 text-primary-800 dark:bg-primary-950 dark:text-primary-300"; default: return "bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-300"; } diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/ActivityTimeline.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/ActivityTimeline.tsx index d080049c9..2b4162126 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/ActivityTimeline.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/ActivityTimeline.tsx @@ -17,11 +17,11 @@ export const ActivityTimeline = ({ activities }: ActivityTimelineProps) => { return (

-
+
    {activities.map((activity) => (
  • -
    +
    {formatDistanceToNow(new Date(activity.timestamp), { addSuffix: true, @@ -32,7 +32,7 @@ export const ActivityTimeline = ({ activities }: ActivityTimelineProps) => {
    By{" "} - + {activity.user}
    diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/HeaderBar.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/HeaderBar.tsx index a46d80c36..8af75c427 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/HeaderBar.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/HeaderBar.tsx @@ -7,7 +7,7 @@ export const HeaderBar = () => {

    {tenantConfig.appName} Dashboard

    -
    diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/OrgTable.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/OrgTable.tsx index 663b63a23..8ee37bbc2 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/OrgTable.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/OrgTable.tsx @@ -38,7 +38,7 @@ export const OrgTable = ({ organizations }: OrgTableProps) => { {organizations.map((org, index) => ( {org.name?.en || "N/A"} diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/StatCard.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/StatCard.tsx index 4431a8b3e..d07f6cb6f 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/StatCard.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/dashboard/components/StatCard.tsx @@ -34,7 +34,7 @@ export const StatCard: React.FC = ({ onClick={onClick} className={cn( "p-4 rounded-xl shadow-sm transition-colors", - variant === "primary" ? "bg-purple-600 text-white" : "bg-white dark:bg-gray-800 dark:border-gray-700", + variant === "primary" ? "bg-primary text-white" : "bg-white dark:bg-gray-800 dark:border-gray-700", onClick && "cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-700" )} > diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ExternalUsersColumnDefn.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ExternalUsersColumnDefn.tsx index 6dcd6392b..64724430f 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ExternalUsersColumnDefn.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ExternalUsersColumnDefn.tsx @@ -43,7 +43,7 @@ export const ExternalUsersColumnDefn = ( className={`px-2 py-1 rounded-full text-xs font-medium ${ userType === "external_organization" ? "bg-purple-100 text-purple-800 dark:bg-purple-900/40 dark:text-purple-300" - : "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300" + : "bg-primary-100 text-primary-800 dark:bg-primary-900/40 dark:text-primary-300" }`} > {displayText} @@ -65,7 +65,7 @@ export const ExternalUsersColumnDefn = ( return "bg-red-100 text-red-600 hover:bg-red-100 dark:bg-red-900/40 dark:text-red-300 dark:hover:bg-red-900/50"; case "pending": default: - return "bg-blue-100 text-blue-600 hover:bg-blue-100 dark:bg-blue-900/40 dark:text-blue-300 dark:hover:bg-blue-900/50"; + return "bg-primary-100 text-primary-600 hover:bg-primary-100 dark:bg-primary-900/40 dark:text-primary-300 dark:hover:bg-primary-900/50"; } }; @@ -117,7 +117,7 @@ export const ExternalUsersColumnDefn = ( {/* */} diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ViewDocuments.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ViewDocuments.tsx index 6ee92f7ca..75ce08d5c 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ViewDocuments.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/externalUsers/ViewDocuments.tsx @@ -186,7 +186,7 @@ const ViewDocument: React.FC = ({ userId }) => {
    - +

    {t("viewDocument.organizationName")} @@ -340,7 +340,7 @@ const ViewDocument: React.FC = ({ userId }) => { diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/organizationAdmins/AssignAdminDialog.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/organizationAdmins/AssignAdminDialog.tsx index 319af7dec..ae088e9d8 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/organizationAdmins/AssignAdminDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/organizationAdmins/AssignAdminDialog.tsx @@ -272,8 +272,8 @@ export function AssignAdminDialog({ className={cn( "w-full flex items-center justify-between px-3 py-2 rounded-md text-left text-sm transition-colors", !selectedUnit - ? "bg-blue-200 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300" - : "hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300" + ? "bg-primary-200 text-primary-800 dark:bg-primary-900/40 dark:text-primary-300" + : "hover:bg-primary-100 dark:hover:bg-primary-900/30 text-gray-700 dark:text-gray-300" )} > @@ -292,8 +292,8 @@ export function AssignAdminDialog({ className={cn( "w-full flex items-center justify-between px-3 py-2 rounded-md text-left text-sm transition-colors", selectedUnit === unit.id - ? "bg-blue-200 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300" - : "hover:bg-blue-100 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-300" + ? "bg-primary-200 text-primary-800 dark:bg-primary-900/40 dark:text-primary-300" + : "hover:bg-primary-100 dark:hover:bg-primary-900/30 text-gray-700 dark:text-gray-300" )} > {localizedName(unit.name)} diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationDetail.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationDetail.tsx index c3d55a010..fc2369962 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationDetail.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationDetail.tsx @@ -118,8 +118,8 @@ export const OrganizationCard: React.FC = ({ id }) => { break; case "woreda": Icon = MapPin; - bgColor = "bg-blue-100 dark:bg-blue-800"; - textColor = "text-blue-800 dark:text-blue-100"; + bgColor = "bg-primary-100 dark:bg-primary-800"; + textColor = "text-primary-800 dark:text-primary-100"; break; case "subcity": Icon = Home; diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationsColumnDefn.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationsColumnDefn.tsx index 86e3b47a1..b15b4766a 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationsColumnDefn.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/organizations/OrganizationsColumnDefn.tsx @@ -91,7 +91,7 @@ export const OrganizationsColumnDefn: ColumnDef[] = [ diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/sites/SitesColumnDefn.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/sites/SitesColumnDefn.tsx index 22caed8da..62c46bc6e 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/sites/SitesColumnDefn.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/sites/SitesColumnDefn.tsx @@ -104,7 +104,7 @@ export const getSitesColumnDefn = ({ {t("sites.actions.view")} onEdit(site)}> - + {t("sites.actions.edit")} onBranding(site)}> diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateCard.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateCard.tsx index 79e212636..5b66b62fc 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateCard.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateCard.tsx @@ -48,7 +48,7 @@ const TemplateCard = () => { templateList.find((t) => t.id === selectedTemplateId) ?? null; return ( - +

    diff --git a/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateEditor.tsx b/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateEditor.tsx index 9b87f432c..b6de030b1 100644 --- a/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateEditor.tsx +++ b/apps/edr-freight-web/backoffice/src/super-admin/components/templates/components/TemplateEditor.tsx @@ -55,12 +55,12 @@ export const TemplateEditor: React.FC = ({ ${hasPlaceholders ? ` .placeholder { ${isDarkMode ? ` - background-color: #1e3a5f; - border: 1px dashed #60a5fa; - color: #93c5fd; + background-color: #065f46; + border: 1px dashed #34d399; + color: #a7f3d0; ` : ` - background-color: #e0f2fe; - border: 1px dashed #38bdf8; + background-color: #ecfdf5; + border: 1px dashed #0EA371; `} padding: 2px 4px; border-radius: 4px; diff --git a/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx b/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx index 0362232e2..0a6d4adf5 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/Applayout.tsx @@ -14,19 +14,23 @@ export const AppLayout = () => { const userRoles = user?.roles?.map((role) => role.key) || []; const isSuperAdmin = userRoles.includes("super_admin"); + // html/body/#root are `overflow: hidden` (index.css) — the host chrome + // scrolls inside FreightDashboardLayout. This subtree renders its own + // full-page layout instead, so it must be its own scroll container or + // nothing scrolls. Top/AppMenuTabs are `fixed`, unaffected by the scroller. if (isAuthPage) { return ( -
    +
    ); } return ( -
    +
    -
    +
    diff --git a/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSampleForm.tsx b/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSampleForm.tsx index f5ba36792..3ca49ae9a 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSampleForm.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSampleForm.tsx @@ -1619,11 +1619,11 @@ export const TemplateSampleForm = ({ {selectedExternalCC.map((unitName, idx) => (
    + className="flex items-center gap-2 px-3 py-1 bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300 rounded-full text-sm"> {unitName}
    @@ -1692,11 +1692,11 @@ export const TemplateSampleForm = ({ return (
    + className="flex items-center gap-2 px-3 py-1 bg-primary-100 dark:bg-primary-900/40 text-primary-700 dark:text-primary-300 rounded-full text-sm"> {name}
    diff --git a/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSamplePreviewPage.tsx b/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSamplePreviewPage.tsx index dd0479d5e..7a643e870 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSamplePreviewPage.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/TemplateSample/TemplateSamplePreviewPage.tsx @@ -79,7 +79,7 @@ export const TemplateSamplePreviewPage = ({ diff --git a/apps/edr-freight-web/backoffice/src/user-management/components/content/PrefixSuffixTable.tsx b/apps/edr-freight-web/backoffice/src/user-management/components/content/PrefixSuffixTable.tsx index b3e12907c..cf41ccec7 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/components/content/PrefixSuffixTable.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/components/content/PrefixSuffixTable.tsx @@ -160,7 +160,7 @@ export const PrefixSuffixTable = ({ return ( ); diff --git a/apps/edr-freight-web/backoffice/src/user-management/components/content/TagBasedReferenceNumbers.tsx b/apps/edr-freight-web/backoffice/src/user-management/components/content/TagBasedReferenceNumbers.tsx index b4054e2db..0709c1a17 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/components/content/TagBasedReferenceNumbers.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/components/content/TagBasedReferenceNumbers.tsx @@ -425,7 +425,7 @@ export function TagBasedReferenceNumbers({ diff --git a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx index 44f298ef0..9af344caf 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PositionLists.tsx @@ -230,7 +230,7 @@ export default function PositionManagement() { value={selectedUnitId} onValueChange={(value) => setSelectedUnitId(value)} > - + diff --git a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PostionTypeMigration.tsx b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PostionTypeMigration.tsx index dfb418d8f..411399fe8 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PostionTypeMigration.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/components/position-management/PostionTypeMigration.tsx @@ -253,10 +253,10 @@ const PositionTypeMigrationModal: React.FC = ({ {/* Display selected from/to */} {fromId && toId && ( -

    +

    {t("migration.migrationPath")}:
    - {fromName} + {fromName} {toName}

    @@ -380,7 +380,7 @@ const PositionTypeMigrationModal: React.FC = ({ @@ -186,7 +186,7 @@ export const EditNews = () => { {/* Category Selection */}

    -
    +
    Category

    @@ -218,7 +218,7 @@ export const EditNews = () => { {/* Title Section */}

    -
    +
    Title

    diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/News/NewsColumn.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/News/NewsColumn.tsx index a79f0e2a9..c0cd4182a 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/News/NewsColumn.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/News/NewsColumn.tsx @@ -35,7 +35,7 @@ export const NewsColumnDefn = ({ ? rowData.newsCategory?.newsCategoryTitle?.am : rowData.newsCategory?.newsCategoryTitle?.en; return ( - + {categoryTitle || "No Category"} ); diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateForm.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateForm.tsx index 5acb225d8..8696fc230 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateForm.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateForm.tsx @@ -346,7 +346,7 @@ export const TemplateForm = () => { {/* Names Section */}

    -
    +
    {t("template.names", "Names")}

    diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateList.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateList.tsx index 54c1b1f11..584a10f6a 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateList.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/TemplateConfiguration/TemplateList.tsx @@ -231,7 +231,7 @@ export const TemplateList = () => { {template.fileInfo?.originalname ? ( - + {template.fileInfo.originalname} ) : ( diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/AddTheme.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/AddTheme.tsx index d503b422b..6090d8364 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/AddTheme.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/AddTheme.tsx @@ -97,7 +97,7 @@ // {/* Title Section */} //
    //

    -//
    +//
    // Color //

    //
    diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/EditTheme.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/EditTheme.tsx index 5953da289..fea88243a 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/EditTheme.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Theme/EditTheme.tsx @@ -128,7 +128,7 @@ export const EditTheme = () => { {/* Title Section */}

    -
    +
    Color

    diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Vision/ViewVision.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Vision/ViewVision.tsx index 7f54e8dbf..7193741d7 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/Vision/ViewVision.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/Vision/ViewVision.tsx @@ -11,10 +11,10 @@ export const ViewVision = () => { const locale = i18n.language; const localizedName = useLocalizedName(); return ( -
    +
    {/* Header */} -
    +
    {"image from goal"} diff --git a/apps/edr-freight-web/backoffice/src/user-management/web-Management/components/FormInputBox.tsx b/apps/edr-freight-web/backoffice/src/user-management/web-Management/components/FormInputBox.tsx index 7362cb18f..d8fc78f47 100644 --- a/apps/edr-freight-web/backoffice/src/user-management/web-Management/components/FormInputBox.tsx +++ b/apps/edr-freight-web/backoffice/src/user-management/web-Management/components/FormInputBox.tsx @@ -40,7 +40,7 @@ export function FormInputBox({ return (

    -
    +
    {label}

    @@ -190,7 +190,7 @@ export const Section = ({ }) => (

    -
    +
    {title}

    {children} @@ -230,9 +230,9 @@ export const ImageUpload = ({ {preview && ( -
    +
    - + {existingName || "Selected Image"} @@ -242,7 +242,7 @@ export const ImageUpload = ({ variant="ghost" size="sm" onClick={onView} - className="text-blue-600 dark:text-blue-400 hover:text-blue-800 dark:hover:text-blue-300" + className="text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-300" > Preview diff --git a/apps/edr-freight-web/portal/index.css b/apps/edr-freight-web/portal/index.css index aa14f97fe..856c46262 100644 --- a/apps/edr-freight-web/portal/index.css +++ b/apps/edr-freight-web/portal/index.css @@ -1,6 +1,11 @@ @import "tailwindcss"; @import "@edr/ui-common/theme.css" layer(theme); +/* Dark mode is toggled via the `dark` class on . Without this, Tailwind + v4 compiles `dark:` utilities to `@media (prefers-color-scheme: dark)` and + they follow the OS setting instead of the in-app toggle. */ +@custom-variant dark (&:where(.dark, .dark *)); + /* Bridge the central Mantine theme into Tailwind. Mantine (createTheme) is the single source of truth; these just alias its generated CSS variables so `bg-edr-*`, `text-edr-*`, `border-edr-*` utilities resolve to the same tokens. */ diff --git a/apps/edr-passenger-api/.env.example b/apps/edr-passenger-api/.env.example index aaed09659..fcca698e9 100644 --- a/apps/edr-passenger-api/.env.example +++ b/apps/edr-passenger-api/.env.example @@ -28,9 +28,8 @@ MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadmin MINIO_BUCKET=edr-dev -# CORS -FRONTEND_URL=http://localhost:5174 -BACK_OFFICE_URL=http://localhost:5184 +# CORS — comma-separated list of allowed origins (add more, comma-separated) +CORS_ORIGINS=http://localhost:5174,http://localhost:5184 # JWT (legacy passenger auth — being replaced by IAM) # REQUIRED in production — use a random 32+ character string (e.g. openssl rand -hex 32) diff --git a/apps/edr-passenger-api/src/app.module.ts b/apps/edr-passenger-api/src/app.module.ts index 7652fc980..cce0f5e7d 100644 --- a/apps/edr-passenger-api/src/app.module.ts +++ b/apps/edr-passenger-api/src/app.module.ts @@ -1,7 +1,5 @@ import { Logger, Module, OnApplicationBootstrap } from "@nestjs/common"; -import { ThrottlerModule } from "@nestjs/throttler"; -import { DynamicThrottlerGuard } from "./common/dynamic-throttler.guard"; -import { APP_GUARD, APP_FILTER } from "@nestjs/core"; +import { APP_FILTER } from "@nestjs/core"; import { ConfigModule, ConfigService } from "@nestjs/config"; import { ScheduleModule } from "@nestjs/schedule"; import { EventEmitterModule } from "@nestjs/event-emitter"; @@ -69,11 +67,6 @@ import { EOtpType } from "@tria-plc/iamapi-common"; @Module({ imports: [ - ThrottlerModule.forRoot([ - { name: "auth", ttl: 60, limit: 100000 }, - { name: "strict", ttl: 60, limit: 100000 }, - { name: "default", ttl: 60, limit: 100000 }, - ]), ConfigModule.forRoot({ isGlobal: true, load: [ @@ -149,9 +142,7 @@ import { EOtpType } from "@tria-plc/iamapi-common"; ConfigurableFareModule, ], providers: [ - { provide: APP_GUARD, useClass: DynamicThrottlerGuard }, { provide: APP_FILTER, useClass: DeleteExceptionFilter }, - DynamicThrottlerGuard, EdrPassengerOrgSeeder, PassengerStaffUsersSeeder, SegmentFareSeeder, diff --git a/apps/edr-passenger-api/src/common/dynamic-throttler.guard.ts b/apps/edr-passenger-api/src/common/dynamic-throttler.guard.ts deleted file mode 100644 index 5ad8232ec..000000000 --- a/apps/edr-passenger-api/src/common/dynamic-throttler.guard.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Injectable, ExecutionContext, Inject } from '@nestjs/common'; -import { Reflector } from '@nestjs/core'; -import { ThrottlerGuard, ThrottlerStorage, getOptionsToken, getStorageToken } from '@nestjs/throttler'; -import { SystemConfigService, CONFIG_KEYS } from '../modules/system-config/system-config.service'; - -// Route-prefix → throttler tier mapping. -// Evaluated in order; first match wins. -const ROUTE_TIERS: Array<{ prefix: string; tier: 'auth' | 'strict' | 'default' }> = [ - { prefix: '/auth', tier: 'auth' }, - { prefix: '/fayda/verification',tier: 'auth' }, - { prefix: '/bookings', tier: 'strict' }, - { prefix: '/passengers', tier: 'strict' }, - { prefix: '/payments', tier: 'strict' }, - { prefix: '/wallet', tier: 'strict' }, -]; - -@Injectable() -export class DynamicThrottlerGuard extends ThrottlerGuard { - constructor( - @Inject(getOptionsToken()) options: any, - @Inject(getStorageToken()) storageService: ThrottlerStorage, - reflector: Reflector, - private readonly systemConfig: SystemConfigService, - ) { - super(options, storageService, reflector); - } - - async canActivate(context: ExecutionContext): Promise { - if (context.getType() !== 'http') { - return true; - } - - const [authLimit, authTtl, strictLimit, strictTtl, defaultLimit, defaultTtl] = - await Promise.all([ - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_AUTH_LIMIT), - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_AUTH_TTL_MS), - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_STRICT_LIMIT), - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_STRICT_TTL_MS), - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_DEFAULT_LIMIT), - this.systemConfig.getNumber(CONFIG_KEYS.THROTTLE_DEFAULT_TTL_MS), - ]); - - const url: string = context.switchToHttp().getRequest<{ url: string }>().url ?? ''; - const matched = ROUTE_TIERS.find(({ prefix }) => url.startsWith(prefix)); - const tier = matched?.tier ?? 'default'; - - if (tier === 'auth') { - this.throttlers = [{ name: 'auth', ttl: authTtl, limit: authLimit }]; - } else if (tier === 'strict') { - this.throttlers = [{ name: 'strict', ttl: strictTtl, limit: strictLimit }]; - } else { - this.throttlers = [{ name: 'default', ttl: defaultTtl, limit: defaultLimit }]; - } - - return super.canActivate(context); - } -} diff --git a/apps/edr-passenger-api/src/main.ts b/apps/edr-passenger-api/src/main.ts index 26f57e506..1715d1ba3 100644 --- a/apps/edr-passenger-api/src/main.ts +++ b/apps/edr-passenger-api/src/main.ts @@ -33,11 +33,16 @@ async function bootstrap() { // version-neutral at their existing paths (e.g. /search, /bookings) — unchanged for the frontend. app.enableVersioning({ type: VersioningType.URI }); + // Allowed CORS origins come from a single comma-separated env var (CORS_ORIGINS), + // e.g. "https://portal.edr.et,https://backoffice.edr.et". Whitespace around each + // entry is trimmed and empties are dropped. Falls back to the local dev ports. + const corsOrigins = (process.env.CORS_ORIGINS ?? "http://localhost:5174,http://localhost:5184") + .split(",") + .map((origin) => origin.trim()) + .filter((origin) => origin.length > 0); + app.enableCors({ - origin: [ - process.env.PORTAL_URL ?? "http://localhost:5174", - process.env.BACK_OFFICE_URL ?? "http://localhost:5184", - ], + origin: corsOrigins, methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'Accept-Language', 'X-Request-ID'], credentials: true, diff --git a/apps/edr-passenger-api/src/modules/auth/auth.controller.ts b/apps/edr-passenger-api/src/modules/auth/auth.controller.ts index 7f180137e..96ad8a178 100644 --- a/apps/edr-passenger-api/src/modules/auth/auth.controller.ts +++ b/apps/edr-passenger-api/src/modules/auth/auth.controller.ts @@ -20,7 +20,6 @@ import { ApiBody, ApiBearerAuth, } from "@nestjs/swagger"; -import { Throttle, SkipThrottle } from "@nestjs/throttler"; import { IsPublic } from "@tria-plc/api-common/modules/auth/decorators/public.decorator"; import { PassengerAuthService } from "./passenger-auth.service"; import { diff --git a/apps/edr-passenger-api/src/modules/bookings/bookings.controller.ts b/apps/edr-passenger-api/src/modules/bookings/bookings.controller.ts index 1d30c4257..854621e0d 100644 --- a/apps/edr-passenger-api/src/modules/bookings/bookings.controller.ts +++ b/apps/edr-passenger-api/src/modules/bookings/bookings.controller.ts @@ -22,7 +22,6 @@ import { ApiBody, } from "@nestjs/swagger"; import { IsPublic } from "@tria-plc/api-common/modules/auth/decorators/public.decorator"; -import { Throttle } from "@nestjs/throttler"; import { BookingsService } from "./bookings.service"; import { GuestBookingService } from "./guest-booking.service"; import { diff --git a/apps/edr-passenger-api/src/modules/health/health.controller.ts b/apps/edr-passenger-api/src/modules/health/health.controller.ts index a918fa76c..02e020d4f 100644 --- a/apps/edr-passenger-api/src/modules/health/health.controller.ts +++ b/apps/edr-passenger-api/src/modules/health/health.controller.ts @@ -1,13 +1,11 @@ import { Controller, Get, HttpStatus, Res } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; -import { SkipThrottle } from '@nestjs/throttler'; import { IsPublic } from '@tria-plc/api-common/modules/auth/decorators/public.decorator'; import { PrismaService } from '../../common/prisma.service'; import { Response } from 'express'; @ApiTags('Health') @Controller('health') -@SkipThrottle() export class HealthController { constructor(private readonly prisma: PrismaService) {} diff --git a/apps/edr-passenger-api/src/modules/passengers/passengers.controller.ts b/apps/edr-passenger-api/src/modules/passengers/passengers.controller.ts index 502d81a73..20de8ec23 100644 --- a/apps/edr-passenger-api/src/modules/passengers/passengers.controller.ts +++ b/apps/edr-passenger-api/src/modules/passengers/passengers.controller.ts @@ -19,7 +19,6 @@ import { ApiResponse, ApiQuery, } from "@nestjs/swagger"; -import { SkipThrottle, Throttle } from "@nestjs/throttler"; import { PassengersService } from "./passengers.service"; import { CreateTravelerProfileDto, diff --git a/apps/edr-passenger-api/src/modules/payments/internal-payments.controller.ts b/apps/edr-passenger-api/src/modules/payments/internal-payments.controller.ts index 04b721f0c..98262c4c3 100644 --- a/apps/edr-passenger-api/src/modules/payments/internal-payments.controller.ts +++ b/apps/edr-passenger-api/src/modules/payments/internal-payments.controller.ts @@ -7,7 +7,6 @@ import { UseGuards, } from "@nestjs/common"; import { ApiOperation, ApiTags } from "@nestjs/swagger"; -import { SkipThrottle } from "@nestjs/throttler"; import { ServiceAuthGuard } from "../../common/guards/service-auth.guard"; import { PaymentEventDto, MarkPaidResponseDto } from "./internal-payments.dto"; import { PaymentsService } from "./payments.service"; @@ -21,7 +20,6 @@ import { PaymentsService } from "./payments.service"; @ApiTags("Internal Payments") @UseGuards(ServiceAuthGuard) @Controller("internal/payments") -@SkipThrottle() export class InternalPaymentsController { constructor(private readonly paymentsService: PaymentsService) {} diff --git a/apps/edr-passenger-api/src/modules/payments/payments.controller.ts b/apps/edr-passenger-api/src/modules/payments/payments.controller.ts index 918c0b8b8..3c3135832 100644 --- a/apps/edr-passenger-api/src/modules/payments/payments.controller.ts +++ b/apps/edr-passenger-api/src/modules/payments/payments.controller.ts @@ -21,7 +21,6 @@ import { ApiProduces, } from "@nestjs/swagger"; -import { SkipThrottle, Throttle } from "@nestjs/throttler"; import { Response } from "express"; import { PaymentsService } from "./payments.service"; import { diff --git a/apps/edr-passenger-api/src/modules/system-config/system-config.controller.ts b/apps/edr-passenger-api/src/modules/system-config/system-config.controller.ts index 6f2bbb73d..0114c03f9 100644 --- a/apps/edr-passenger-api/src/modules/system-config/system-config.controller.ts +++ b/apps/edr-passenger-api/src/modules/system-config/system-config.controller.ts @@ -1,6 +1,5 @@ import { Body, Controller, Get, Patch, SetMetadata, UseGuards } from '@nestjs/common'; import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger'; -import { SkipThrottle } from '@nestjs/throttler'; import { SystemConfigService } from './system-config.service'; import { IamGuard } from '../../common/iam-adapter'; import { Roles } from '../../common/roles.decorator'; @@ -12,7 +11,6 @@ export class SystemConfigController { @Get('fayda-status') @SetMetadata('isPublic', true) - @SkipThrottle() @ApiOperation({ summary: 'Get Fayda verification enabled status (public)' }) getFaydaStatus() { const enabled = process.env.VERIFAYDA_ENABLED !== 'false'; diff --git a/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts b/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts index 878dc6b36..13de29616 100644 --- a/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts +++ b/apps/edr-passenger-api/src/modules/verifayda/verifayda.controller.ts @@ -15,7 +15,6 @@ import { ApiOperation, ApiTags, } from "@nestjs/swagger"; -import { Throttle } from "@nestjs/throttler"; import type { TCurrentUser } from "@tria-plc/api-common/modules/auth/types/current-user.type"; import { IsPublic } from "@tria-plc/api-common/modules/auth/decorators/public.decorator"; import { JwtGuard } from "../../common/jwt.guard"; diff --git a/apps/edr-passenger-api/src/modules/wallet/wallet.controller.ts b/apps/edr-passenger-api/src/modules/wallet/wallet.controller.ts index 40b6ef91f..d539203f1 100644 --- a/apps/edr-passenger-api/src/modules/wallet/wallet.controller.ts +++ b/apps/edr-passenger-api/src/modules/wallet/wallet.controller.ts @@ -10,7 +10,6 @@ import { Query, } from "@nestjs/common"; import { ApiTags, ApiOperation, ApiBearerAuth } from "@nestjs/swagger"; -import { Throttle } from "@nestjs/throttler"; import { WalletService } from "./wallet.service"; import { JwtGuard } from "../../common/jwt.guard"; diff --git a/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx b/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx index 8a92d0e0f..d7fddfac9 100644 --- a/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx +++ b/apps/edr-passenger-web/backoffice/src/components/layout/Sidebar.tsx @@ -104,7 +104,7 @@ const navigationSections: { title: string; items: NavItem[] }[] = [ title: 'Customer Services', items: [ // { name: 'Loyalty Program', href: '/loyalty', icon: Gift, permission: PERMS.passengers.view }, - // { name: 'Support Center', href: '/support', icon: MessageSquare, permission: PERMS.bookings.view }, + { name: 'Support Center', href: '/support', icon: MessageSquare, permission: PERMS.bookings.view }, { name: 'Notifications', href: '/notifications', icon: Bell, permission: PERMS.notifications.send }, ] }, diff --git a/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx b/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx index 6b9820f17..7dccc7bdf 100644 --- a/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx +++ b/apps/edr-passenger-web/portal/src/app/booking/passengers/page.tsx @@ -994,8 +994,20 @@ function PassengersForm() { const onInvalid = () => { // Sections still behind the Fayda verify screen stay collapsed here — they only expand // when the user explicitly clicks "Enter details manually" (shown only when Fayda is - // unavailable). - setSubmitError('Please fix the highlighted errors before continuing.'); + // unavailable). Their name/DOB/gender fields are still empty at this point, which would + // otherwise surface as a generic "fix the highlighted errors" message that doesn't point + // at the actual cause — tell the user to verify with Fayda instead. + const needsFaydaVerification = passengers.some((p, i) => { + const isEthiopian = p?.nationality === 'ETHIOPIAN'; + const isChildPassenger = i >= adultCount; + const isLoggedInAndVerified = i === 0 && isAuthenticated && user?.faydaVerified; + return isEthiopian && faydaEnabled && !p?.formExpanded && !isLoggedInAndVerified && !isChildPassenger; + }); + setSubmitError( + needsFaydaVerification + ? 'Please verify your identity with Fayda before continuing — tap "Verify with Fayda" above to proceed.' + : 'Please fix the highlighted errors before continuing.' + ); }; const onSubmit = async (data: FormData) => { diff --git a/packages/ui-common/src/styles/index.css b/packages/ui-common/src/styles/index.css index 8b5d67a02..0ce8702a9 100644 --- a/packages/ui-common/src/styles/index.css +++ b/packages/ui-common/src/styles/index.css @@ -1,4 +1,4 @@ @import "tailwindcss"; @import "tw-animate-css"; @import "./theme.css"; -@custom-variant dark (&: where(.dark, .dark *)); +@custom-variant dark (&:where(.dark, .dark *));