COmpoeentes standardization 2
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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 */}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user