ui frotneedn fixes

This commit is contained in:
IGNY8 VPS (Salman)
2025-11-26 06:47:23 +00:00
parent 451594bd29
commit 4fe68cc271
40 changed files with 1638 additions and 275 deletions

View File

@@ -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"

View File

@@ -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>
);

View File

@@ -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>

View File

@@ -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>
);
}

View File

@@ -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...</>

View File

@@ -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>
);
},
);