ui frotneedn fixes
This commit is contained in:
@@ -386,12 +386,13 @@ export default function ImageGenerationCard({
|
||||
<Button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating || !prompt.trim()}
|
||||
className="inline-flex items-center gap-2 px-6 py-2.5"
|
||||
variant="primary"
|
||||
size="md"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<>
|
||||
<svg
|
||||
className="h-4 w-4 animate-spin"
|
||||
className="h-4 w-4 animate-spin mr-2"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
|
||||
@@ -19,6 +19,7 @@ interface PageHeaderProps {
|
||||
color: 'blue' | 'green' | 'purple' | 'orange' | 'red' | 'indigo';
|
||||
};
|
||||
hideSiteSector?: boolean; // Hide site/sector selector and info for global pages
|
||||
navigation?: ReactNode; // Module navigation tabs
|
||||
}
|
||||
|
||||
export default function PageHeader({
|
||||
@@ -29,6 +30,7 @@ export default function PageHeader({
|
||||
className = "",
|
||||
badge,
|
||||
hideSiteSector = false,
|
||||
navigation,
|
||||
}: PageHeaderProps) {
|
||||
const { activeSite } = useSiteStore();
|
||||
const { activeSector } = useSectorStore();
|
||||
@@ -44,6 +46,7 @@ export default function PageHeader({
|
||||
|
||||
return (
|
||||
<div className={`flex flex-col sm:flex-row items-start sm:items-center justify-between gap-4 ${className}`}>
|
||||
{/* Left side: Title, badge, and site/sector info */}
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-3">
|
||||
{badge && (
|
||||
@@ -98,16 +101,21 @@ export default function PageHeader({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
{!hideSiteSector && <SiteAndSectorSelector />}
|
||||
{showRefresh && onRefresh && (
|
||||
<button
|
||||
onClick={onRefresh}
|
||||
className="px-4 py-2 text-sm font-medium text-brand-500 hover:text-brand-600 border border-brand-200 rounded-lg hover:bg-brand-50 dark:border-brand-800 dark:hover:bg-brand-500/10 transition-colors"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Right side: Navigation bar stacked above site/sector selector */}
|
||||
<div className="flex flex-col items-end gap-3">
|
||||
{navigation && <div>{navigation}</div>}
|
||||
<div className="flex items-center gap-3">
|
||||
{!hideSiteSector && <SiteAndSectorSelector />}
|
||||
{showRefresh && onRefresh && (
|
||||
<button
|
||||
onClick={onRefresh}
|
||||
className="px-4 py-2 text-sm font-medium text-brand-500 hover:text-brand-600 border border-brand-200 rounded-lg hover:bg-brand-50 dark:border-brand-800 dark:hover:bg-brand-500/10 transition-colors"
|
||||
>
|
||||
Refresh
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -228,8 +228,7 @@ export default function SiteIntegrationsSection({ siteId }: SiteIntegrationsSect
|
||||
Connect your sites to external platforms (WordPress, Shopify, Custom APIs)
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={handleAdd} variant="primary">
|
||||
<PlusIcon className="w-4 h-4 mr-2" />
|
||||
<Button onClick={handleAdd} variant="primary" startIcon={<PlusIcon className="w-4 h-4" />}>
|
||||
Add Integration
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Link, useLocation } from 'react-router';
|
||||
import { Tabs, TabList, Tab } from '../ui/tabs/Tabs';
|
||||
import Button from '../ui/button/Button';
|
||||
|
||||
export interface NavigationTab {
|
||||
label: string;
|
||||
@@ -32,34 +32,22 @@ export default function ModuleNavigationTabs({ tabs, className = '' }: ModuleNav
|
||||
const activeTabId = activeTab?.path || tabs[0]?.path || '';
|
||||
|
||||
return (
|
||||
<div className={`mb-6 ${className}`}>
|
||||
<Tabs defaultTab={activeTabId}>
|
||||
{(activeTabId, setActiveTab) => (
|
||||
<TabList className="bg-gray-100 dark:bg-gray-900">
|
||||
{tabs.map((tab) => {
|
||||
const isActive = activeTabId === tab.path || location.pathname.startsWith(tab.path + '/');
|
||||
|
||||
return (
|
||||
<Link
|
||||
key={tab.path}
|
||||
to={tab.path}
|
||||
onClick={() => setActiveTab(tab.path)}
|
||||
className="flex-1"
|
||||
>
|
||||
<Tab
|
||||
tabId={tab.path}
|
||||
isActive={isActive}
|
||||
className="flex items-center justify-center gap-2"
|
||||
>
|
||||
{tab.icon && <span className="flex-shrink-0">{tab.icon}</span>}
|
||||
<span>{tab.label}</span>
|
||||
</Tab>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</TabList>
|
||||
)}
|
||||
</Tabs>
|
||||
<div className={`inline-flex gap-2 p-2 rounded-lg bg-gray-50 dark:bg-gray-800/50 shadow-sm border border-gray-200 dark:border-gray-700 ${className}`}>
|
||||
{tabs.map((tab) => {
|
||||
const isActive = activeTabId === tab.path || location.pathname.startsWith(tab.path + '/');
|
||||
|
||||
return (
|
||||
<Link key={tab.path} to={tab.path}>
|
||||
<Button
|
||||
variant={isActive ? 'primary' : 'secondary'}
|
||||
size="sm"
|
||||
startIcon={tab.icon}
|
||||
>
|
||||
{tab.label}
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -411,14 +411,8 @@ export default function WorkflowGuide({ onSiteAdded }: WorkflowGuideProps) {
|
||||
<Button
|
||||
onClick={handleAddSite}
|
||||
disabled={!siteName || !siteName.trim() || !selectedIndustry || selectedSectors.length === 0 || isCreatingSite}
|
||||
variant="solid"
|
||||
tone="brand"
|
||||
variant="primary"
|
||||
size="lg"
|
||||
className={`text-lg font-semibold px-8 py-4 h-auto transition-all duration-200 ${
|
||||
!siteName || !siteName.trim() || !selectedIndustry || selectedSectors.length === 0 || isCreatingSite
|
||||
? ''
|
||||
: 'hover:shadow-lg hover:scale-[1.01] active:scale-[0.99]'
|
||||
}`}
|
||||
>
|
||||
{isCreatingSite ? (
|
||||
<>Creating Site...</>
|
||||
|
||||
@@ -7,13 +7,12 @@
|
||||
import { ReactNode, forwardRef } from "react";
|
||||
import clsx from "clsx";
|
||||
import { twMerge } from "tailwind-merge";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type ButtonSize = "xs" | "sm" | "md" | "lg";
|
||||
type ButtonVariant = "solid" | "soft" | "outline" | "ghost" | "gradient";
|
||||
type ButtonSize = "xs" | "sm" | "md" | "lg" | "xl" | "2xl";
|
||||
type ButtonVariant = "primary" | "secondary" | "outline" | "ghost" | "gradient";
|
||||
type ButtonTone = "brand" | "success" | "warning" | "danger" | "neutral";
|
||||
type ButtonShape = "rounded" | "pill";
|
||||
type ButtonElement = HTMLButtonElement | HTMLAnchorElement;
|
||||
type ButtonElement = HTMLButtonElement;
|
||||
|
||||
interface ButtonProps {
|
||||
children: ReactNode; // Button text or content
|
||||
@@ -28,26 +27,22 @@ interface ButtonProps {
|
||||
fullWidth?: boolean; // Stretch to parent width
|
||||
className?: string; // Additional classes
|
||||
type?: "button" | "submit" | "reset"; // Button type
|
||||
as?: "button" | "a" | typeof Link;
|
||||
href?: string;
|
||||
to?: string; // For React Router Link
|
||||
target?: string;
|
||||
rel?: string;
|
||||
}
|
||||
|
||||
const toneMap: Record<
|
||||
ButtonTone,
|
||||
{
|
||||
solid: string;
|
||||
soft: string;
|
||||
primary: string;
|
||||
secondary: string;
|
||||
outline: string;
|
||||
ghost: string;
|
||||
ring: string;
|
||||
}
|
||||
> = {
|
||||
brand: {
|
||||
solid: "bg-brand-500 text-white hover:bg-brand-600",
|
||||
soft: "bg-brand-50 text-brand-600 hover:bg-brand-100",
|
||||
primary: "bg-brand-500 text-white hover:bg-brand-600",
|
||||
secondary:
|
||||
"bg-[#64748b] text-white hover:bg-[#475569] dark:bg-[#64748b] dark:hover:bg-[#475569]",
|
||||
outline:
|
||||
"text-brand-600 ring-1 ring-brand-200 hover:bg-brand-25 dark:ring-brand-500/40 dark:text-brand-300 dark:hover:bg-brand-500/[0.08]",
|
||||
ghost:
|
||||
@@ -55,8 +50,9 @@ const toneMap: Record<
|
||||
ring: "focus-visible:ring-brand-500",
|
||||
},
|
||||
success: {
|
||||
solid: "bg-success-500 text-white hover:bg-success-600",
|
||||
soft: "bg-success-50 text-success-600 hover:bg-success-100",
|
||||
primary: "bg-success-500 text-white hover:bg-success-600",
|
||||
secondary:
|
||||
"bg-[#64748b] text-white hover:bg-[#475569] dark:bg-[#64748b] dark:hover:bg-[#475569]",
|
||||
outline:
|
||||
"text-success-600 ring-1 ring-success-200 hover:bg-success-25 dark:ring-success-500/40 dark:text-success-300",
|
||||
ghost:
|
||||
@@ -64,8 +60,9 @@ const toneMap: Record<
|
||||
ring: "focus-visible:ring-success-500",
|
||||
},
|
||||
warning: {
|
||||
solid: "bg-warning-500 text-white hover:bg-warning-600",
|
||||
soft: "bg-warning-50 text-warning-600 hover:bg-warning-100",
|
||||
primary: "bg-warning-500 text-white hover:bg-warning-600",
|
||||
secondary:
|
||||
"bg-[#64748b] text-white hover:bg-[#475569] dark:bg-[#64748b] dark:hover:bg-[#475569]",
|
||||
outline:
|
||||
"text-warning-600 ring-1 ring-warning-200 hover:bg-warning-25 dark:ring-warning-500/40 dark:text-warning-300",
|
||||
ghost:
|
||||
@@ -73,8 +70,9 @@ const toneMap: Record<
|
||||
ring: "focus-visible:ring-warning-500",
|
||||
},
|
||||
danger: {
|
||||
solid: "bg-error-500 text-white hover:bg-error-600",
|
||||
soft: "bg-error-50 text-error-600 hover:bg-error-100",
|
||||
primary: "bg-error-500 text-white hover:bg-error-600",
|
||||
secondary:
|
||||
"bg-[#64748b] text-white hover:bg-[#475569] dark:bg-[#64748b] dark:hover:bg-[#475569]",
|
||||
outline:
|
||||
"text-error-600 ring-1 ring-error-200 hover:bg-error-25 dark:ring-error-500/40 dark:text-error-300",
|
||||
ghost:
|
||||
@@ -82,10 +80,10 @@ const toneMap: Record<
|
||||
ring: "focus-visible:ring-error-500",
|
||||
},
|
||||
neutral: {
|
||||
solid:
|
||||
primary:
|
||||
"bg-gray-900 text-white hover:bg-gray-800 dark:bg-white/10 dark:hover:bg-white/20",
|
||||
soft:
|
||||
"bg-gray-100 text-gray-900 hover:bg-gray-200 dark:bg-white/[0.08] dark:text-white dark:hover:bg-white/[0.12]",
|
||||
secondary:
|
||||
"bg-[#64748b] text-white hover:bg-[#475569] dark:bg-[#64748b] dark:hover:bg-[#475569]",
|
||||
outline:
|
||||
"text-gray-700 ring-1 ring-gray-300 hover:bg-gray-50 dark:text-gray-200 dark:ring-white/[0.08] dark:hover:bg-white/[0.04]",
|
||||
ghost:
|
||||
@@ -112,6 +110,8 @@ const sizeClasses: Record<ButtonSize, string> = {
|
||||
sm: "h-9 px-3 text-sm",
|
||||
md: "h-10 px-4 text-sm",
|
||||
lg: "h-12 px-5 text-base",
|
||||
xl: "h-14 px-6 text-lg",
|
||||
"2xl": "h-auto px-8 py-4 text-lg font-semibold",
|
||||
};
|
||||
|
||||
const Button = forwardRef<ButtonElement, ButtonProps>(
|
||||
@@ -119,7 +119,7 @@ const Button = forwardRef<ButtonElement, ButtonProps>(
|
||||
{
|
||||
children,
|
||||
size = "md",
|
||||
variant = "solid",
|
||||
variant = "primary",
|
||||
tone = "brand",
|
||||
shape = "rounded",
|
||||
startIcon,
|
||||
@@ -129,19 +129,14 @@ const Button = forwardRef<ButtonElement, ButtonProps>(
|
||||
disabled = false,
|
||||
fullWidth = false,
|
||||
type = "button",
|
||||
as = "button",
|
||||
href,
|
||||
to,
|
||||
target,
|
||||
rel,
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const toneStyles = toneMap[tone];
|
||||
|
||||
const variantClasses: Record<ButtonVariant, string> = {
|
||||
solid: toneStyles.solid,
|
||||
soft: toneStyles.soft,
|
||||
primary: toneStyles.primary,
|
||||
secondary: toneStyles.secondary,
|
||||
outline: clsx(
|
||||
"bg-transparent transition-colors",
|
||||
toneStyles.outline,
|
||||
@@ -173,34 +168,18 @@ const Button = forwardRef<ButtonElement, ButtonProps>(
|
||||
),
|
||||
);
|
||||
|
||||
const Component = as === "a" ? "a" : as === Link ? Link : "button";
|
||||
|
||||
return (
|
||||
<Component
|
||||
<button
|
||||
ref={ref}
|
||||
className={computedClass}
|
||||
onClick={onClick}
|
||||
{...(as === "button"
|
||||
? {
|
||||
type,
|
||||
disabled,
|
||||
}
|
||||
: as === Link
|
||||
? {
|
||||
to,
|
||||
"aria-disabled": disabled,
|
||||
}
|
||||
: {
|
||||
href,
|
||||
target,
|
||||
rel,
|
||||
"aria-disabled": disabled,
|
||||
})}
|
||||
type={type}
|
||||
disabled={disabled}
|
||||
>
|
||||
{startIcon && <span className="flex items-center">{startIcon}</span>}
|
||||
<span className="whitespace-nowrap">{children}</span>
|
||||
{endIcon && <span className="flex items-center">{endIcon}</span>}
|
||||
</Component>
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user