COmpoeentes standardization 2

This commit is contained in:
IGNY8 VPS (Salman)
2026-01-02 00:27:27 +00:00
parent a4691ad2da
commit f28f641fd5
50 changed files with 622 additions and 490 deletions

View File

@@ -97,8 +97,7 @@ export default function PaymentHistory() {
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
Payment History
</h2>
<Button onClick={loadPayments} variant="outline" size="sm">
<RefreshCwIcon className="w-4 h-4 mr-2" />
<Button onClick={loadPayments} variant="outline" size="sm" startIcon={<RefreshCwIcon className="w-4 h-4" />}>
Refresh
</Button>
</div>

View File

@@ -3,7 +3,9 @@ import Switch from '../form/switch/Switch';
import Button from '../ui/button/Button';
import Badge from '../ui/badge/Badge';
import SiteSetupChecklist from '../sites/SiteSetupChecklist';
import SiteTypeBadge from '../sites/SiteTypeBadge';
import { Site } from '../../services/api';
import { BoxCubeIcon as SettingsIcon, EyeIcon, FileIcon } from '../../icons';
interface SiteCardProps {
site: Site;
@@ -68,16 +70,17 @@ export default function SiteCard({
</p>
)}
<div className="flex items-center gap-2 mb-2 flex-wrap">
<SiteTypeBadge hostingType={site.hosting_type} />
{site.industry_name && (
<Badge variant="light" color="info" className="text-xs">
<Badge variant="soft" color="warning" size="sm">
{site.industry_name}
</Badge>
)}
<Badge variant="light" color="info" className="text-xs">
<Badge variant="soft" color="neutral" size="sm">
{site.active_sectors_count} / 5 Sectors
</Badge>
{site.status && (
<Badge variant="light" color={site.status === 'active' ? 'success' : 'dark'} className="text-xs">
<Badge variant={site.is_active ? 'solid' : 'soft'} color={site.status === 'active' ? 'success' : 'neutral'} size="sm">
{site.status}
</Badge>
)}
@@ -105,46 +108,34 @@ export default function SiteCard({
/>
</div>
</div>
<div className="flex items-center justify-between border-t border-gray-200 p-5 dark:border-gray-800">
<div className="flex gap-3">
<div className="flex items-center justify-between border-t border-gray-200 p-4 dark:border-gray-800">
<div className="flex gap-2 flex-1">
<Button
variant="outline"
size="md"
onClick={() => onSettings(site)}
title="Configure Site - Update connection details and publishing settings"
className="shadow-theme-xs inline-flex h-11 w-11 items-center justify-center rounded-lg border border-gray-300 text-gray-700 dark:border-gray-700 dark:text-gray-400"
variant="primary"
tone="brand"
size="sm"
onClick={() => onDetails(site)}
startIcon={<EyeIcon className="w-4 h-4" />}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 20 20"
fill="none"
>
<path
d="M5.64615 4.59906C5.05459 4.25752 4.29808 4.46015 3.95654 5.05171L2.69321 7.23986C2.35175 7.83128 2.5544 8.58754 3.14582 8.92899C3.97016 9.40493 3.97017 10.5948 3.14583 11.0707C2.55441 11.4122 2.35178 12.1684 2.69323 12.7598L3.95657 14.948C4.2981 15.5395 5.05461 15.7422 5.64617 15.4006C6.4706 14.9247 7.50129 15.5196 7.50129 16.4715C7.50129 17.1545 8.05496 17.7082 8.73794 17.7082H11.2649C11.9478 17.7082 12.5013 17.1545 12.5013 16.4717C12.5013 15.5201 13.5315 14.9251 14.3556 15.401C14.9469 15.7423 15.7029 15.5397 16.0443 14.9485L17.3079 12.7598C17.6494 12.1684 17.4467 11.4121 16.8553 11.0707C16.031 10.5948 16.031 9.40494 16.8554 8.92902C17.4468 8.58757 17.6494 7.83133 17.3079 7.23992L16.0443 5.05123C15.7029 4.45996 14.9469 4.25737 14.3556 4.59874C13.5315 5.07456 12.5013 4.47961 12.5013 3.52798C12.5013 2.84515 11.9477 2.2915 11.2649 2.2915L8.73795 2.2915C8.05496 2.2915 7.50129 2.84518 7.50129 3.52816C7.50129 4.48015 6.47059 5.07505 5.64615 4.59906Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M12.5714 9.99977C12.5714 11.4196 11.4204 12.5706 10.0005 12.5706C8.58069 12.5706 7.42969 11.4196 7.42969 9.99977C7.42969 8.57994 8.58069 7.42894 10.0005 7.42894C11.4204 7.42894 12.5714 8.57994 12.5714 9.99977Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
Dashboard
</Button>
<Button
variant="secondary"
tone="neutral"
size="sm"
onClick={() => onDetails(site)}
startIcon={<FileIcon className="w-4 h-4" />}
>
Content
</Button>
<Button
variant="outline"
size="md"
onClick={() => onDetails(site)}
title="View Site Details - See all information about this website"
className="shadow-theme-xs inline-flex h-11 items-center justify-center rounded-lg border border-gray-300 px-4 py-3 text-sm font-medium text-gray-700 dark:border-gray-700 dark:text-gray-400"
tone="neutral"
size="sm"
onClick={() => onSettings(site)}
startIcon={<SettingsIcon className="w-4 h-4" />}
>
View Details
Settings
</Button>
</div>
<Switch

View File

@@ -6,13 +6,6 @@
import { useNavigate } from 'react-router-dom';
import Button from '../ui/button/Button';
import {
ListIcon,
GroupIcon,
BoltIcon,
FileTextIcon,
FileIcon,
CheckCircleIcon,
PaperPlaneIcon,
HelpCircleIcon,
} from '../../icons';
@@ -34,75 +27,75 @@ interface QuickActionsWidgetProps {
const workflowSteps = [
{
num: 1,
icon: ListIcon,
title: 'Add Keywords',
description: 'Import your target keywords manually or from CSV',
href: '/planner/keyword-opportunities',
actionLabel: 'Add',
gradient: 'from-brand-500 to-brand-600',
buttonTone: 'brand' as const,
},
{
num: 2,
icon: GroupIcon,
title: 'Auto Cluster',
description: 'AI groups related keywords into content clusters',
href: '/planner/clusters',
href: '/planner/keyword-opportunities', // Clustering runs from keywords page
actionLabel: 'Cluster',
gradient: 'from-purple-500 to-purple-600',
buttonTone: 'brand' as const,
},
{
num: 3,
icon: BoltIcon,
title: 'Generate Ideas',
description: 'Create content ideas from your keyword clusters',
href: '/planner/ideas',
actionLabel: 'Ideas',
gradient: 'from-warning-500 to-warning-600',
buttonTone: 'warning' as const,
},
{
num: 4,
icon: CheckCircleIcon,
title: 'Create Tasks',
description: 'Convert approved ideas into content tasks',
href: '/writer/tasks',
actionLabel: 'Tasks',
gradient: 'from-brand-500 to-brand-600',
buttonTone: 'brand' as const,
},
{
num: 5,
icon: FileTextIcon,
title: 'Generate Content',
description: 'AI writes SEO-optimized articles from tasks',
href: '/writer/content',
actionLabel: 'Write',
gradient: 'from-success-500 to-success-600',
buttonTone: 'success' as const,
},
{
num: 6,
icon: FileIcon,
title: 'Generate Images',
description: 'Create featured images and media for articles',
href: '/writer/images',
actionLabel: 'Images',
gradient: 'from-purple-500 to-purple-600',
buttonTone: 'brand' as const,
},
{
num: 7,
icon: CheckCircleIcon,
title: 'Review & Approve',
description: 'Quality check and approve generated content',
href: '/writer/review',
actionLabel: 'Review',
gradient: 'from-warning-500 to-warning-600',
buttonTone: 'warning' as const,
},
{
num: 8,
icon: PaperPlaneIcon,
title: 'Publish to WP',
description: 'Push approved content to your WordPress site',
href: '/writer/published',
actionLabel: 'Publish',
gradient: 'from-success-500 to-success-600',
buttonTone: 'success' as const,
},
];
@@ -132,7 +125,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{/* Column 1: Steps 1-3 */}
<div className="space-y-2.5">
{workflowSteps.slice(0, 3).map((step) => {
const Icon = step.icon;
return (
<div
key={step.num}
@@ -143,11 +135,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{step.num}
</span>
{/* Icon with solid gradient background */}
<div className={`flex-shrink-0 p-1.5 rounded-lg bg-gradient-to-br ${step.gradient} shadow-sm`}>
<Icon className="w-4 h-4 text-white" />
</div>
{/* Text Content */}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
@@ -158,13 +145,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
</p>
</div>
{/* Action Button */}
{/* Action Button - matches step number color */}
<Button
size="xs"
variant="outline"
tone="brand"
variant="primary"
tone={step.buttonTone}
onClick={() => navigate(step.href)}
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
className="flex-shrink-0"
>
{step.actionLabel}
</Button>
@@ -176,7 +163,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{/* Column 2: Steps 4-6 */}
<div className="space-y-2.5">
{workflowSteps.slice(3, 6).map((step) => {
const Icon = step.icon;
return (
<div
key={step.num}
@@ -187,11 +173,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{step.num}
</span>
{/* Icon with solid gradient background */}
<div className={`flex-shrink-0 p-1.5 rounded-lg bg-gradient-to-br ${step.gradient} shadow-sm`}>
<Icon className="w-4 h-4 text-white" />
</div>
{/* Text Content */}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
@@ -202,13 +183,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
</p>
</div>
{/* Action Button */}
{/* Action Button - matches step number color */}
<Button
size="xs"
variant="outline"
tone="brand"
variant="primary"
tone={step.buttonTone}
onClick={() => navigate(step.href)}
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
className="flex-shrink-0"
>
{step.actionLabel}
</Button>
@@ -220,7 +201,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{/* Column 3: Steps 7-8 */}
<div className="space-y-2.5">
{workflowSteps.slice(6, 8).map((step) => {
const Icon = step.icon;
return (
<div
key={step.num}
@@ -231,11 +211,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
{step.num}
</span>
{/* Icon with solid gradient background */}
<div className={`flex-shrink-0 p-1.5 rounded-lg bg-gradient-to-br ${step.gradient} shadow-sm`}>
<Icon className="w-4 h-4 text-white" />
</div>
{/* Text Content */}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
@@ -246,13 +221,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
</p>
</div>
{/* Action Button */}
{/* Action Button - matches step number color */}
<Button
size="xs"
variant="outline"
tone="brand"
variant="primary"
tone={step.buttonTone}
onClick={() => navigate(step.href)}
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
className="flex-shrink-0"
>
{step.actionLabel}
</Button>

View File

@@ -27,9 +27,7 @@ export default function DemographicCard() {
</p>
</div>
<div className="relative inline-block">
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
</IconButton>
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options" icon={<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />} />
<Dropdown
isOpen={isOpen}
onClose={closeDropdown}

View File

@@ -108,9 +108,7 @@ export default function MonthlySalesChart() {
Monthly Sales
</h3>
<div className="relative inline-block">
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
</IconButton>
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options" icon={<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />} />
<Dropdown
isOpen={isOpen}
onClose={closeDropdown}

View File

@@ -77,9 +77,7 @@ export default function MonthlyTarget() {
</p>
</div>
<div className="relative inline-block">
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
</IconButton>
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options" icon={<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />} />
<Dropdown
isOpen={isOpen}
onClose={closeDropdown}

View File

@@ -19,15 +19,15 @@ const Checkbox: React.FC<CheckboxProps> = ({
}) => {
return (
<label
className={`flex items-center space-x-3 group cursor-pointer ${
className={`flex items-center gap-2.5 group cursor-pointer ${
disabled ? "cursor-not-allowed opacity-60" : ""
}`}
>
<div className="relative w-4 h-4">
<div className="relative flex items-center justify-center w-5 h-5">
<input
id={id}
type="checkbox"
className={`w-4 h-4 appearance-none cursor-pointer dark:border-gray-700 border border-gray-300 checked:border-transparent rounded-md checked:bg-brand-500 disabled:opacity-60
className={`w-5 h-5 appearance-none cursor-pointer dark:border-gray-700 border border-gray-300 checked:border-transparent rounded-md checked:bg-brand-500 disabled:opacity-60
${className}`}
checked={checked}
onChange={(e) => onChange(e.target.checked)}
@@ -35,35 +35,31 @@ const Checkbox: React.FC<CheckboxProps> = ({
/>
{checked && (
<svg
className="absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-none top-1/2 left-1/2"
className="absolute w-3 h-3 pointer-events-none text-white"
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
>
<path
d="M11.6666 3.5L5.24992 9.91667L2.33325 7"
stroke="white"
strokeWidth="1.94437"
d="M10 3L4.5 8.5L2 6"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
)}
{disabled && (
{disabled && !checked && (
<svg
className="absolute transform -translate-x-1/2 -translate-y-1/2 pointer-events-none top-1/2 left-1/2"
className="absolute w-3 h-3 pointer-events-none text-gray-300"
xmlns="http://www.w3.org/2000/svg"
width="12"
height="12"
viewBox="0 0 12 12"
fill="none"
>
<path
d="M11.6666 3.5L5.24992 9.91667L2.33325 7"
stroke="#E4E7EC"
strokeWidth="2.33333"
d="M10 3L4.5 8.5L2 6"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>

View File

@@ -128,13 +128,30 @@ export default function NotificationDropdown() {
return (
<div className="relative">
<IconButton
ref={buttonRef as React.RefObject<HTMLButtonElement>}
variant="outline"
className="relative dropdown-toggle h-11 w-11"
onClick={handleClick}
aria-label={`Notifications ${unreadCount > 0 ? `(${unreadCount} unread)` : ''}`}
>
<div className="relative">
<IconButton
ref={buttonRef as React.RefObject<HTMLButtonElement>}
variant="outline"
tone="neutral"
size="md"
icon={
<svg
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.75 2.29248C10.75 1.87827 10.4143 1.54248 10 1.54248C9.58583 1.54248 9.25004 1.87827 9.25004 2.29248V2.83613C6.08266 3.20733 3.62504 5.9004 3.62504 9.16748V14.4591H3.33337C2.91916 14.4591 2.58337 14.7949 2.58337 15.2091C2.58337 15.6234 2.91916 15.9591 3.33337 15.9591H4.37504H15.625H16.6667C17.0809 15.9591 17.4167 15.6234 17.4167 15.2091C17.4167 14.7949 17.0809 14.4591 16.6667 14.4591H16.375V9.16748C16.375 5.9004 13.9174 3.20733 10.75 2.83613V2.29248ZM14.875 14.4591V9.16748C14.875 6.47509 12.6924 4.29248 10 4.29248C7.30765 4.29248 5.12504 6.47509 5.12504 9.16748V14.4591H14.875ZM8.00004 17.7085C8.00004 18.1228 8.33583 18.4585 8.75004 18.4585H11.25C11.6643 18.4585 12 18.1228 12 17.7085C12 17.2943 11.6643 16.9585 11.25 16.9585H8.75004C8.33583 16.9585 8.00004 17.2943 8.00004 17.7085Z"
/>
</svg>
}
onClick={handleClick}
aria-label={`Notifications ${unreadCount > 0 ? `(${unreadCount} unread)` : ''}`}
/>
{/* Notification badge */}
{unreadCount > 0 && (
<span className="absolute -right-0.5 -top-0.5 z-10 flex h-5 w-5 items-center justify-center rounded-full bg-warning-500 text-[10px] font-semibold text-white">
@@ -142,21 +159,7 @@ export default function NotificationDropdown() {
<span className="absolute inline-flex w-full h-full bg-warning-400 rounded-full opacity-75 animate-ping"></span>
</span>
)}
<svg
className="fill-current"
width="20"
height="20"
viewBox="0 0 20 20"
xmlns="http://www.w3.org/2000/svg"
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.75 2.29248C10.75 1.87827 10.4143 1.54248 10 1.54248C9.58583 1.54248 9.25004 1.87827 9.25004 2.29248V2.83613C6.08266 3.20733 3.62504 5.9004 3.62504 9.16748V14.4591H3.33337C2.91916 14.4591 2.58337 14.7949 2.58337 15.2091C2.58337 15.6234 2.91916 15.9591 3.33337 15.9591H4.37504H15.625H16.6667C17.0809 15.9591 17.4167 15.6234 17.4167 15.2091C17.4167 14.7949 17.0809 14.4591 16.6667 14.4591H16.375V9.16748C16.375 5.9004 13.9174 3.20733 10.75 2.83613V2.29248ZM14.875 14.4591V9.16748C14.875 6.47509 12.6924 4.29248 10 4.29248C7.30765 4.29248 5.12504 6.47509 5.12504 9.16748V14.4591H14.875ZM8.00004 17.7085C8.00004 18.1228 8.33583 18.4585 8.75004 18.4585H11.25C11.6643 18.4585 12 18.1228 12 17.7085C12 17.2943 11.6643 16.9585 11.25 16.9585H8.75004C8.33583 16.9585 8.00004 17.2943 8.00004 17.7085Z"
fill="currentColor"
/>
</svg>
</IconButton>
</div>
<Dropdown
isOpen={isOpen}

View File

@@ -2,7 +2,6 @@ import { useState, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { DropdownItem } from "../ui/dropdown/DropdownItem";
import { Dropdown } from "../ui/dropdown/Dropdown";
import { Link } from "react-router-dom";
import { useAuthStore } from "../../store/authStore";
import Button from "../ui/button/Button";
@@ -26,33 +25,28 @@ export default function UserDropdown() {
closeDropdown();
};
return (
<div className="relative">
<Button
<div className="relative flex-shrink-0">
<button
ref={buttonRef}
onClick={toggleDropdown}
variant="ghost"
tone="neutral"
className="flex items-center text-gray-700 dropdown-toggle dark:text-gray-400"
className="flex items-center gap-2 px-2 py-1.5 rounded-lg text-gray-700 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
>
<span className="mr-3 overflow-hidden rounded-full h-11 w-11 bg-brand-500 flex items-center justify-center">
<span className="overflow-hidden rounded-full h-9 w-9 bg-brand-500 flex items-center justify-center flex-shrink-0">
{user?.email ? (
<span className="text-white font-semibold text-sm">
{user.email.charAt(0).toUpperCase()}
</span>
) : (
<img src="/images/user/owner.jpg" alt="User" />
<img src="/images/user/owner.jpg" alt="User" className="h-full w-full object-cover" />
)}
</span>
<span className="block mr-1 font-medium text-theme-sm">
<span className="font-medium text-sm whitespace-nowrap">
{user?.username || user?.email?.split("@")[0] || "User"}
</span>
<svg
className={`stroke-gray-500 dark:stroke-gray-400 transition-transform duration-200 ${
className={`w-4 h-4 text-gray-500 dark:text-gray-400 transition-transform duration-200 flex-shrink-0 ${
isOpen ? "rotate-180" : ""
}`}
width="18"
height="20"
viewBox="0 0 18 20"
fill="none"
xmlns="http://www.w3.org/2000/svg"
@@ -65,7 +59,7 @@ export default function UserDropdown() {
strokeLinejoin="round"
/>
</svg>
</Button>
</button>
<Dropdown
isOpen={isOpen}

View File

@@ -301,8 +301,8 @@ export default function SiteIntegrationsSection({ siteId }: SiteIntegrationsSect
size="sm"
onClick={() => handleSync(integration)}
className="flex-1"
startIcon={<RefreshCwIcon className="w-4 h-4" />}
>
<RefreshCwIcon className="w-4 h-4 mr-1" />
Sync
</Button>
)}

View File

@@ -7,6 +7,7 @@ import React, { useState, useCallback, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Card } from '../ui/card';
import Button from '../ui/button/Button';
import IconButton from '../ui/button/IconButton';
import {
ArrowRightIcon,
ArrowLeftIcon,
@@ -209,14 +210,13 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar
</p>
</div>
</div>
<Button
<IconButton
variant="ghost"
size="sm"
onClick={handleSkipAll}
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
>
<CloseIcon className="w-5 h-5" />
</Button>
icon={<CloseIcon className="w-5 h-5" />}
/>
</div>
{/* Progress Bar */}

View File

@@ -14,7 +14,7 @@ import {
CopyIcon,
CheckCircleIcon,
TimeIcon,
ArrowUpIcon,
DownloadIcon,
} from '../../../icons';
import { integrationApi } from '../../../services/integration.api';
import { useToast } from '../../ui/toast/ToastContainer';
@@ -115,8 +115,12 @@ export default function Step3ConnectIntegration({
title: 'Download the Plugin',
description: 'Get the IGNY8 Bridge plugin from our dashboard',
action: (
<Button variant="outline" size="sm" className="gap-1">
<ArrowUpIcon className="w-3 h-3" />
<Button
variant="outline"
tone="brand"
size="sm"
startIcon={<DownloadIcon className="w-3 h-3" />}
>
Download Plugin
</Button>
),
@@ -174,9 +178,9 @@ export default function Step3ConnectIntegration({
variant="outline"
size="sm"
onClick={handleCopyApiKey}
className="gap-1 flex-shrink-0"
className="flex-shrink-0"
startIcon={<CopyIcon className="w-4 h-4" />}
>
<CopyIcon className="w-4 h-4" />
Copy
</Button>
</div>

View File

@@ -167,13 +167,12 @@ export default function Step4AddKeywords({
className="pl-10"
/>
</div>
<Button
<IconButton
variant="outline"
onClick={handleAddKeyword}
disabled={!inputValue.trim()}
>
<PlusIcon className="w-4 h-4" />
</Button>
icon={<PlusIcon className="w-4 h-4" />}
/>
</div>
<p className="text-xs text-gray-500 mt-1">
Tip: Paste a comma-separated list or one keyword per line

View File

@@ -118,9 +118,8 @@ export default function PublishingRules({ rules, onChange }: PublishingRulesProp
Example: Publish blog posts to WordPress but guides to your main site
</p>
</div>
<Button onClick={handleAddRule} variant="primary" size="sm">
<PlusIcon className="w-4 h-4 mr-2" />
+ Add a Publishing Rule
<Button onClick={handleAddRule} variant="primary" size="sm" startIcon={<PlusIcon className="w-4 h-4" />}>
Add a Publishing Rule
</Button>
</div>

View File

@@ -7,6 +7,7 @@ import React, { useState } from 'react';
import { EyeIcon, XIcon, Maximize2Icon } from '../../icons';
import { Card } from '../../ui/card';
import Button from '../../ui/button/Button';
import IconButton from '../../ui/button/IconButton';
export interface LayoutPreviewProps {
layoutId: string;
@@ -145,15 +146,12 @@ export default function LayoutPreview({ layoutId, layoutName, onClose, onSelect
{isFullscreen ? 'Exit' : 'Fullscreen'}
</Button>
{onSelect && (
<Button variant="primary" size="sm" onClick={onSelect}>
<EyeIcon className="w-4 h-4 mr-2" />
<Button variant="primary" size="sm" onClick={onSelect} startIcon={<EyeIcon className="w-4 h-4" />}>
Select Layout
</Button>
)}
{onClose && (
<Button variant="ghost" size="sm" onClick={onClose}>
<XIcon className="w-4 h-4" />
</Button>
<IconButton variant="ghost" size="sm" onClick={onClose} icon={<XIcon className="w-4 h-4" />} />
)}
</div>
</div>

View File

@@ -9,9 +9,10 @@ import Badge from '../ui/badge/Badge';
interface SiteTypeBadgeProps {
hostingType: string;
className?: string;
size?: 'sm' | 'md' | 'lg';
}
export default function SiteTypeBadge({ hostingType, className = '' }: SiteTypeBadgeProps) {
export default function SiteTypeBadge({ hostingType, className = '', size = 'sm' }: SiteTypeBadgeProps) {
const getTypeInfo = () => {
switch (hostingType) {
case 'igny8_sites':
@@ -41,11 +42,11 @@ export default function SiteTypeBadge({ hostingType, className = '' }: SiteTypeB
<Badge
variant="soft"
color={typeInfo.color}
size={size}
startIcon={typeInfo.icon}
className={className}
>
{typeInfo.label}
</Badge>
);
}
}

View File

@@ -90,14 +90,12 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
</div>
<div className="flex gap-2">
{onReset && (
<Button variant="outline" onClick={onReset}>
<RefreshCwIcon className="w-4 h-4 mr-2" />
<Button variant="outline" onClick={onReset} startIcon={<RefreshCwIcon className="w-4 h-4" />}>
Reset
</Button>
)}
{onSave && (
<Button variant="primary" onClick={onSave}>
<SaveIcon className="w-4 h-4 mr-2" />
<Button variant="primary" onClick={onSave} startIcon={<SaveIcon className="w-4 h-4" />}>
Save Styles
</Button>
)}
@@ -112,8 +110,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
tone="brand"
size="sm"
onClick={() => setActiveTab('css')}
startIcon={<CodeIcon className="w-4 h-4" />}
>
<CodeIcon className="w-4 h-4 mr-2" />
Custom CSS
</Button>
<Button
@@ -121,8 +119,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
tone="brand"
size="sm"
onClick={() => setActiveTab('colors')}
startIcon={<PaletteIcon className="w-4 h-4" />}
>
<PaletteIcon className="w-4 h-4 mr-2" />
Colors
</Button>
<Button
@@ -130,8 +128,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
tone="brand"
size="sm"
onClick={() => setActiveTab('typography')}
startIcon={<TypeIcon className="w-4 h-4" />}
>
<TypeIcon className="w-4 h-4 mr-2" />
Typography
</Button>
<Button

View File

@@ -74,8 +74,8 @@ export default function TemplateCustomizer({
tone="brand"
size="sm"
onClick={() => setActiveTab('layout')}
startIcon={<LayoutIcon className="w-4 h-4" />}
>
<LayoutIcon className="w-4 h-4 mr-2" />
Layout
</Button>
<Button
@@ -83,8 +83,8 @@ export default function TemplateCustomizer({
tone="brand"
size="sm"
onClick={() => setActiveTab('colors')}
startIcon={<PaletteIcon className="w-4 h-4" />}
>
<PaletteIcon className="w-4 h-4 mr-2" />
Colors
</Button>
<Button
@@ -92,8 +92,8 @@ export default function TemplateCustomizer({
tone="brand"
size="sm"
onClick={() => setActiveTab('typography')}
startIcon={<TypeIcon className="w-4 h-4" />}
>
<TypeIcon className="w-4 h-4 mr-2" />
Typography
</Button>
<Button
@@ -101,8 +101,8 @@ export default function TemplateCustomizer({
tone="brand"
size="sm"
onClick={() => setActiveTab('spacing')}
startIcon={<SettingsIcon className="w-4 h-4" />}
>
<SettingsIcon className="w-4 h-4 mr-2" />
Spacing
</Button>
</div>

View File

@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom';
import { GlobeIcon, CheckCircleIcon, XCircleIcon, SettingsIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon } from '../../icons';
import { Card } from '../ui/card';
import Button from '../ui/button/Button';
import IconButton from '../ui/button/IconButton';
import Badge from '../ui/badge/Badge';
interface WordPressIntegration {
@@ -175,8 +176,8 @@ export default function WordPressIntegrationCard({
variant="outline"
size="sm"
className="flex-1"
startIcon={<SettingsIcon className="w-4 h-4" />}
>
<SettingsIcon className="w-4 h-4 mr-2" />
Manage
</Button>
{siteId && (
@@ -190,14 +191,13 @@ export default function WordPressIntegrationCard({
</Button>
)}
{onSync && (
<Button
<IconButton
onClick={onSync}
variant="outline"
size="sm"
disabled={loading}
>
<RefreshCwIcon className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
</Button>
icon={<RefreshCwIcon className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />}
/>
)}
</div>
</div>

View File

@@ -286,16 +286,14 @@ export default function WordPressIntegrationForm({
type={apiKeyVisible ? 'text' : 'password'}
value={apiKeyVisible ? apiKey : maskApiKey(apiKey)}
/>
<Button
<IconButton
onClick={handleCopyApiKey}
variant="outline"
tone="neutral"
size="sm"
className="absolute top-1/2 right-0 -translate-y-1/2 rounded-l-none"
>
<CopyIcon className="w-4 h-4" />
Copy
</Button>
icon={<CopyIcon className="w-4 h-4" />}
/>
</div>
<div className="group relative inline-block">
<IconButton
@@ -303,9 +301,8 @@ export default function WordPressIntegrationForm({
disabled={generatingKey}
variant="outline"
title="Regenerate"
>
<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />
</IconButton>
icon={<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />}
/>
<div className="invisible absolute bottom-full left-1/2 z-50 mb-2.5 -translate-x-1/2 opacity-0 transition-opacity duration-300 group-hover:visible group-hover:opacity-100">
<div className="relative">
<div className="rounded-lg bg-white px-3 py-2 text-xs font-medium whitespace-nowrap text-gray-700 shadow-xs dark:bg-gray-800 dark:text-white">
@@ -348,18 +345,16 @@ export default function WordPressIntegrationForm({
disabled={generatingKey}
variant="ghost"
title="Regenerate API key"
>
<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />
</IconButton>
icon={<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />}
/>
<IconButton
onClick={handleRevokeApiKey}
disabled={generatingKey}
variant="ghost"
tone="danger"
title="Revoke API key"
>
<TrashBinIcon className="w-5 h-5" />
</IconButton>
icon={<TrashBinIcon className="w-5 h-5" />}
/>
</div>
</td>
</tr>
@@ -389,8 +384,8 @@ export default function WordPressIntegrationForm({
<Button
onClick={handleDownloadPlugin}
variant="solid"
startIcon={<DownloadIcon className="w-4 h-4" />}
>
<DownloadIcon className="w-4 h-4 mr-2" />
Download Plugin
</Button>
</div>

View File

@@ -24,6 +24,7 @@ interface ButtonGroupItemProps {
isActive?: boolean;
className?: string;
disabled?: boolean;
startIcon?: ReactNode;
}
export const ButtonGroupItem: React.FC<ButtonGroupItemProps> = ({
@@ -32,18 +33,20 @@ export const ButtonGroupItem: React.FC<ButtonGroupItemProps> = ({
isActive = false,
className = "",
disabled = false,
startIcon,
}) => {
return (
<button
onClick={onClick}
disabled={disabled}
className={`px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white disabled:opacity-50 disabled:cursor-not-allowed ${
className={`inline-flex items-center gap-1.5 px-4 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 hover:text-gray-900 dark:text-gray-400 dark:hover:bg-white/5 dark:hover:text-white disabled:opacity-50 disabled:cursor-not-allowed ${
isActive
? "bg-gray-100 text-gray-900 dark:bg-white/10 dark:text-white"
: ""
} ${className}`}
type="button"
>
{startIcon}
{children}
</button>
);

View File

@@ -51,7 +51,7 @@ export const Modal: React.FC<ModalProps> = ({
const contentClasses = isFullscreen
? "w-full h-full"
: "relative w-full rounded-3xl bg-white dark:bg-gray-900";
: "relative w-full max-w-lg mx-4 rounded-3xl bg-white dark:bg-gray-900 shadow-xl";
return (
<div className="fixed inset-0 flex items-center justify-center overflow-y-auto modal z-99999">