Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions src/components/ui/app-header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
} from "@/components/ui/dropdown-menu";
import { cn } from "@/lib/utils";
import { useAuth } from "@/contexts/AuthContext";
import { BrandLogo } from "@/components/ui/brand-logo";

interface NavItem {
path: string;
Expand Down Expand Up @@ -94,13 +95,8 @@ export function AppHeader({ className }: AppHeaderProps) {
>
<div className="flex h-16 items-center px-4 md:px-6">
{/* Logo and Brand */}
<div className="flex items-center gap-3 mr-8">
<div className="flex h-10 w-10 items-center justify-center rounded-lg bg-white/20 text-white font-bold text-lg">
SM
</div>
<span className="hidden md:block text-lg font-bold text-white tracking-tight">
Sheet Metal Connect
</span>
<div className="mr-8">
<BrandLogo size="lg" showName={true} variant="light" />
</div>

{/* Navigation Links */}
Expand Down
110 changes: 110 additions & 0 deletions src/components/ui/brand-logo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as React from "react";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils";
import { useAuth } from "@/contexts/AuthContext";

interface BrandLogoProps {
/** Size variant */
size?: "sm" | "md" | "lg";
/** Show the text name alongside the logo */
showName?: boolean;
/** Additional className for the container */
className?: string;
/** Override default color scheme (for headers with specific backgrounds) */
variant?: "default" | "light" | "dark";
}

const sizeConfig = {
sm: {
logo: "h-8 w-8",
text: "text-sm",
fontSize: "text-xs",
},
md: {
logo: "h-9 w-9",
text: "text-lg",
fontSize: "text-sm",
},
lg: {
logo: "h-10 w-10",
text: "text-xl",
fontSize: "text-base",
},
};

export const BrandLogo: React.FC<BrandLogoProps> = ({
size = "md",
showName = true,
className,
variant = "default",
}) => {
const { t } = useTranslation();
const { tenant } = useAuth();

const config = sizeConfig[size];

// Check if whitelabeling is enabled and tenant has custom settings
const isWhitelabeled = tenant?.whitelabel_enabled === true;
const customLogo = isWhitelabeled ? tenant?.whitelabel_logo_url : null;
const customAppName = isWhitelabeled ? tenant?.whitelabel_app_name : null;
const customPrimaryColor = isWhitelabeled ? tenant?.whitelabel_primary_color : null;

// Determine the app name to display
const displayName = customAppName || t("app.name");

// Get text color based on variant
const textColorClass = variant === "light"
? "text-white"
: variant === "dark"
? "text-foreground"
: "text-foreground";

// Default logo gradient or custom primary color
const defaultGradient = "bg-gradient-to-br from-[#3a4656] to-[#0080ff]";
const customBgStyle = customPrimaryColor
? { backgroundColor: customPrimaryColor }
: undefined;

return (
<div className={cn("flex items-center gap-3", className)}>
{/* Logo */}
{customLogo ? (
// Custom logo from whitelabeling
<img
src={customLogo}
alt={displayName}
className={cn(config.logo, "rounded-lg object-contain")}
/>
) : (
// Default logo badge with initials
<div
className={cn(
config.logo,
"rounded-lg flex items-center justify-center",
!customPrimaryColor && defaultGradient,
"text-white font-bold",
config.fontSize
)}
style={customBgStyle}
>
SM
</div>
)}

{/* Brand Name */}
{showName && (
<span
className={cn(
"hidden sm:block font-bold tracking-tight",
config.text,
textColorClass
)}
>
{displayName}
</span>
)}
</div>
);
};

export default BrandLogo;
8 changes: 7 additions & 1 deletion src/contexts/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,14 @@ interface TenantInfo {
id: string;
name: string;
company_name: string | null;
plan: "free" | "pro" | "premium";
plan: "free" | "pro" | "premium" | "enterprise";
status: "active" | "cancelled" | "suspended" | "trial";
// Whitelabeling fields (premium feature)
whitelabel_enabled: boolean;
whitelabel_logo_url: string | null;
whitelabel_app_name: string | null;
whitelabel_primary_color: string | null;
whitelabel_favicon_url: string | null;
}

interface AuthContextType {
Expand Down
20 changes: 20 additions & 0 deletions src/integrations/supabase/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2272,6 +2272,11 @@ export type Database = {
trial_ends_at: string | null
updated_at: string | null
vat_number: string | null
whitelabel_app_name: string | null
whitelabel_enabled: boolean | null
whitelabel_favicon_url: string | null
whitelabel_logo_url: string | null
whitelabel_primary_color: string | null
working_days_mask: number | null
}
Insert: {
Expand Down Expand Up @@ -2313,6 +2318,11 @@ export type Database = {
trial_ends_at?: string | null
updated_at?: string | null
vat_number?: string | null
whitelabel_app_name?: string | null
whitelabel_enabled?: boolean | null
whitelabel_favicon_url?: string | null
whitelabel_logo_url?: string | null
whitelabel_primary_color?: string | null
working_days_mask?: number | null
}
Update: {
Expand Down Expand Up @@ -2354,6 +2364,11 @@ export type Database = {
trial_ends_at?: string | null
updated_at?: string | null
vat_number?: string | null
whitelabel_app_name?: string | null
whitelabel_enabled?: boolean | null
whitelabel_favicon_url?: string | null
whitelabel_logo_url?: string | null
whitelabel_primary_color?: string | null
working_days_mask?: number | null
}
Relationships: []
Expand Down Expand Up @@ -2870,6 +2885,11 @@ export type Database = {
name: string
plan: Database["public"]["Enums"]["subscription_plan"]
status: Database["public"]["Enums"]["subscription_status"]
whitelabel_enabled: boolean
whitelabel_logo_url: string | null
whitelabel_app_name: string | null
whitelabel_primary_color: string | null
whitelabel_favicon_url: string | null
}[]
}
get_tenant_quota: {
Expand Down
10 changes: 2 additions & 8 deletions src/layouts/AdminLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import { LanguageSwitcher } from "@/components/LanguageSwitcher";
import { ROUTES } from "@/routes";
import { useTranslation } from "react-i18next";
import AnimatedBackground from "@/components/AnimatedBackground";
import { BrandLogo } from "@/components/ui/brand-logo";
import SessionTrackingBar from "@/components/SessionTrackingBar";

interface AdminLayoutProps {
Expand Down Expand Up @@ -264,14 +265,7 @@ export default function AdminLayout({ children }: AdminLayoutProps) {
<div className="flex h-full flex-col">
{/* Logo/Brand */}
<div className="flex h-16 items-center border-b px-6">
<div className="flex items-center gap-2">
<Factory className="h-8 w-8 text-primary" strokeWidth={1.5} />
{!collapsed && (
<span className="text-lg font-bold hero-title">
Eryxon Flow
</span>
)}
</div>
<BrandLogo size="md" showName={!collapsed} />
</div>

{/* Navigation */}
Expand Down
16 changes: 2 additions & 14 deletions src/layouts/OperatorLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import CurrentlyTimingWidget from "@/components/operator/CurrentlyTimingWidget";
import { LanguageSwitcher } from "@/components/LanguageSwitcher";
import { AppTour } from "@/components/onboarding";
import { ROUTES } from "@/routes";
import { BrandLogo } from "@/components/ui/brand-logo";
import SessionTrackingBar from "@/components/SessionTrackingBar";

interface OperatorLayoutProps {
Expand Down Expand Up @@ -68,20 +69,7 @@ export const OperatorLayout: React.FC<OperatorLayoutProps> = ({ children }) => {
>
<div className="flex h-14 sm:h-16 items-center justify-between px-4 sm:px-6">
{/* Logo/Brand */}
<div className="flex items-center gap-3">
<div
className={cn(
"h-9 w-9 rounded-lg flex items-center justify-center",
"bg-gradient-to-br from-[#3a4656] to-[#0080ff]",
"text-white font-bold text-sm"
)}
>
SM
</div>
<span className="hidden sm:block text-lg font-bold text-foreground tracking-tight">
{t("app.name")}
</span>
</div>
<BrandLogo size="md" showName={true} />

{/* Right Side Actions */}
<div className="flex items-center gap-2">
Expand Down
Loading
Loading