COmpoeentes standardization 2
@@ -49,6 +49,8 @@ export default tseslint.config(
|
|||||||
'igny8-design-system/no-raw-input': 'warn',
|
'igny8-design-system/no-raw-input': 'warn',
|
||||||
'igny8-design-system/no-raw-select': 'warn',
|
'igny8-design-system/no-raw-select': 'warn',
|
||||||
'igny8-design-system/no-raw-textarea': 'warn',
|
'igny8-design-system/no-raw-textarea': 'warn',
|
||||||
|
// Button icon positioning - icons as children cause vertical stacking, use startIcon/endIcon props
|
||||||
|
'igny8-design-system/no-icon-children': 'warn',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
* 2. no-raw-input - Use <InputField> from components/form/input/InputField
|
* 2. no-raw-input - Use <InputField> from components/form/input/InputField
|
||||||
* 3. no-raw-select - Use <Select> from components/form/Select
|
* 3. no-raw-select - Use <Select> from components/form/Select
|
||||||
* 4. no-raw-textarea - Use <TextArea> from components/form/input/TextArea
|
* 4. no-raw-textarea - Use <TextArea> from components/form/input/TextArea
|
||||||
|
* 5. no-icon-children - Use startIcon/endIcon props instead of icon children in Button
|
||||||
*
|
*
|
||||||
* USAGE: Import in eslint.config.js and enable rules
|
* USAGE: Import in eslint.config.js and enable rules
|
||||||
*/
|
*/
|
||||||
@@ -226,5 +227,144 @@ module.exports = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disallow icon components as direct children of Button, ButtonGroupItem
|
||||||
|
* Icons must be passed via startIcon or endIcon props to ensure proper horizontal layout.
|
||||||
|
* When icons are children, they get wrapped in spans causing vertical stacking.
|
||||||
|
*
|
||||||
|
* WRONG: <Button><PlusIcon />Add Item</Button> -- icon appears ABOVE text
|
||||||
|
* RIGHT: <Button startIcon={<PlusIcon />}>Add Item</Button> -- icon appears LEFT of text
|
||||||
|
*/
|
||||||
|
'no-icon-children': {
|
||||||
|
meta: {
|
||||||
|
type: 'problem',
|
||||||
|
docs: {
|
||||||
|
description: 'Disallow icon components as children of Button. Use startIcon/endIcon props instead.',
|
||||||
|
recommended: true,
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
useIconProps: 'Icon "{{iconName}}" should be passed via startIcon or endIcon prop, not as a child. Icons as children cause vertical stacking. Use: <Button startIcon={<{{iconName}} />}>Text</Button>',
|
||||||
|
useIconPropsGeneric: 'Icons should be passed via startIcon or endIcon prop, not as children. Icons as children cause vertical stacking.',
|
||||||
|
},
|
||||||
|
schema: [],
|
||||||
|
},
|
||||||
|
create(context) {
|
||||||
|
// Components that support startIcon/endIcon props
|
||||||
|
const buttonComponents = ['Button', 'ButtonGroupItem'];
|
||||||
|
|
||||||
|
// Pattern to detect icon component names (ends with Icon or is a known icon)
|
||||||
|
const iconPatterns = [
|
||||||
|
/Icon$/, // Any component ending in "Icon" (PlusIcon, RefreshCwIcon, etc.)
|
||||||
|
/^Icon[A-Z]/, // Icon prefix pattern
|
||||||
|
];
|
||||||
|
|
||||||
|
function isIconComponent(name) {
|
||||||
|
return iconPatterns.some(pattern => pattern.test(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
JSXElement(node) {
|
||||||
|
const openingElement = node.openingElement;
|
||||||
|
|
||||||
|
// Check if this is a Button or ButtonGroupItem
|
||||||
|
if (openingElement.name.type !== 'JSXIdentifier') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const componentName = openingElement.name.name;
|
||||||
|
if (!buttonComponents.includes(componentName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if startIcon or endIcon props are already provided
|
||||||
|
const hasIconProp = openingElement.attributes.some(attr => {
|
||||||
|
return attr.type === 'JSXAttribute' &&
|
||||||
|
attr.name &&
|
||||||
|
(attr.name.name === 'startIcon' || attr.name.name === 'endIcon' || attr.name.name === 'icon');
|
||||||
|
});
|
||||||
|
|
||||||
|
// If icon props are present, likely the component is correctly configured
|
||||||
|
// Still check children for additional icons
|
||||||
|
|
||||||
|
// Check children for icon components
|
||||||
|
const children = node.children || [];
|
||||||
|
for (const child of children) {
|
||||||
|
// Direct JSX element child
|
||||||
|
if (child.type === 'JSXElement') {
|
||||||
|
const childName = child.openingElement.name;
|
||||||
|
if (childName.type === 'JSXIdentifier' && isIconComponent(childName.name)) {
|
||||||
|
context.report({
|
||||||
|
node: child,
|
||||||
|
messageId: 'useIconProps',
|
||||||
|
data: { iconName: childName.name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSX expression containing icon (e.g., {loading ? <Spinner /> : <PlusIcon />})
|
||||||
|
if (child.type === 'JSXExpressionContainer') {
|
||||||
|
const expression = child.expression;
|
||||||
|
|
||||||
|
// Direct icon in expression: {<PlusIcon />}
|
||||||
|
if (expression.type === 'JSXElement') {
|
||||||
|
const exprName = expression.openingElement.name;
|
||||||
|
if (exprName.type === 'JSXIdentifier' && isIconComponent(exprName.name)) {
|
||||||
|
context.report({
|
||||||
|
node: expression,
|
||||||
|
messageId: 'useIconProps',
|
||||||
|
data: { iconName: exprName.name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Conditional expression: {condition ? <IconA /> : <IconB />}
|
||||||
|
if (expression.type === 'ConditionalExpression') {
|
||||||
|
const consequent = expression.consequent;
|
||||||
|
const alternate = expression.alternate;
|
||||||
|
|
||||||
|
if (consequent.type === 'JSXElement') {
|
||||||
|
const consName = consequent.openingElement.name;
|
||||||
|
if (consName.type === 'JSXIdentifier' && isIconComponent(consName.name)) {
|
||||||
|
context.report({
|
||||||
|
node: consequent,
|
||||||
|
messageId: 'useIconProps',
|
||||||
|
data: { iconName: consName.name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alternate.type === 'JSXElement') {
|
||||||
|
const altName = alternate.openingElement.name;
|
||||||
|
if (altName.type === 'JSXIdentifier' && isIconComponent(altName.name)) {
|
||||||
|
context.report({
|
||||||
|
node: alternate,
|
||||||
|
messageId: 'useIconProps',
|
||||||
|
data: { iconName: altName.name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logical expression: {showIcon && <PlusIcon />}
|
||||||
|
if (expression.type === 'LogicalExpression') {
|
||||||
|
const right = expression.right;
|
||||||
|
if (right.type === 'JSXElement') {
|
||||||
|
const rightName = right.openingElement.name;
|
||||||
|
if (rightName.type === 'JSXIdentifier' && isIconComponent(rightName.name)) {
|
||||||
|
context.report({
|
||||||
|
node: right,
|
||||||
|
messageId: 'useIconProps',
|
||||||
|
data: { iconName: rightName.name },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -97,8 +97,7 @@ export default function PaymentHistory() {
|
|||||||
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
|
<h2 className="text-2xl font-semibold text-gray-900 dark:text-white">
|
||||||
Payment History
|
Payment History
|
||||||
</h2>
|
</h2>
|
||||||
<Button onClick={loadPayments} variant="outline" size="sm">
|
<Button onClick={loadPayments} variant="outline" size="sm" startIcon={<RefreshCwIcon className="w-4 h-4" />}>
|
||||||
<RefreshCwIcon className="w-4 h-4 mr-2" />
|
|
||||||
Refresh
|
Refresh
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ import Switch from '../form/switch/Switch';
|
|||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
import Badge from '../ui/badge/Badge';
|
import Badge from '../ui/badge/Badge';
|
||||||
import SiteSetupChecklist from '../sites/SiteSetupChecklist';
|
import SiteSetupChecklist from '../sites/SiteSetupChecklist';
|
||||||
|
import SiteTypeBadge from '../sites/SiteTypeBadge';
|
||||||
import { Site } from '../../services/api';
|
import { Site } from '../../services/api';
|
||||||
|
import { BoxCubeIcon as SettingsIcon, EyeIcon, FileIcon } from '../../icons';
|
||||||
|
|
||||||
interface SiteCardProps {
|
interface SiteCardProps {
|
||||||
site: Site;
|
site: Site;
|
||||||
@@ -68,16 +70,17 @@ export default function SiteCard({
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
||||||
|
<SiteTypeBadge hostingType={site.hosting_type} />
|
||||||
{site.industry_name && (
|
{site.industry_name && (
|
||||||
<Badge variant="light" color="info" className="text-xs">
|
<Badge variant="soft" color="warning" size="sm">
|
||||||
{site.industry_name}
|
{site.industry_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<Badge variant="light" color="info" className="text-xs">
|
<Badge variant="soft" color="neutral" size="sm">
|
||||||
{site.active_sectors_count} / 5 Sectors
|
{site.active_sectors_count} / 5 Sectors
|
||||||
</Badge>
|
</Badge>
|
||||||
{site.status && (
|
{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}
|
{site.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
@@ -105,46 +108,34 @@ export default function SiteCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center justify-between border-t border-gray-200 p-5 dark:border-gray-800">
|
<div className="flex items-center justify-between border-t border-gray-200 p-4 dark:border-gray-800">
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-2 flex-1">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="primary"
|
||||||
size="md"
|
tone="brand"
|
||||||
onClick={() => onSettings(site)}
|
size="sm"
|
||||||
title="Configure Site - Update connection details and publishing settings"
|
onClick={() => onDetails(site)}
|
||||||
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"
|
startIcon={<EyeIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<svg
|
Dashboard
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</Button>
|
||||||
width="20"
|
<Button
|
||||||
height="20"
|
variant="secondary"
|
||||||
viewBox="0 0 20 20"
|
tone="neutral"
|
||||||
fill="none"
|
size="sm"
|
||||||
>
|
onClick={() => onDetails(site)}
|
||||||
<path
|
startIcon={<FileIcon className="w-4 h-4" />}
|
||||||
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"
|
Content
|
||||||
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>
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="md"
|
tone="neutral"
|
||||||
onClick={() => onDetails(site)}
|
size="sm"
|
||||||
title="View Site Details - See all information about this website"
|
onClick={() => onSettings(site)}
|
||||||
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"
|
startIcon={<SettingsIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
View Details
|
Settings
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
|
|||||||
@@ -6,13 +6,6 @@
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
import {
|
import {
|
||||||
ListIcon,
|
|
||||||
GroupIcon,
|
|
||||||
BoltIcon,
|
|
||||||
FileTextIcon,
|
|
||||||
FileIcon,
|
|
||||||
CheckCircleIcon,
|
|
||||||
PaperPlaneIcon,
|
|
||||||
HelpCircleIcon,
|
HelpCircleIcon,
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
|
|
||||||
@@ -34,75 +27,75 @@ interface QuickActionsWidgetProps {
|
|||||||
const workflowSteps = [
|
const workflowSteps = [
|
||||||
{
|
{
|
||||||
num: 1,
|
num: 1,
|
||||||
icon: ListIcon,
|
|
||||||
title: 'Add Keywords',
|
title: 'Add Keywords',
|
||||||
description: 'Import your target keywords manually or from CSV',
|
description: 'Import your target keywords manually or from CSV',
|
||||||
href: '/planner/keyword-opportunities',
|
href: '/planner/keyword-opportunities',
|
||||||
actionLabel: 'Add',
|
actionLabel: 'Add',
|
||||||
gradient: 'from-brand-500 to-brand-600',
|
gradient: 'from-brand-500 to-brand-600',
|
||||||
|
buttonTone: 'brand' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 2,
|
num: 2,
|
||||||
icon: GroupIcon,
|
|
||||||
title: 'Auto Cluster',
|
title: 'Auto Cluster',
|
||||||
description: 'AI groups related keywords into content clusters',
|
description: 'AI groups related keywords into content clusters',
|
||||||
href: '/planner/clusters',
|
href: '/planner/keyword-opportunities', // Clustering runs from keywords page
|
||||||
actionLabel: 'Cluster',
|
actionLabel: 'Cluster',
|
||||||
gradient: 'from-purple-500 to-purple-600',
|
gradient: 'from-purple-500 to-purple-600',
|
||||||
|
buttonTone: 'brand' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 3,
|
num: 3,
|
||||||
icon: BoltIcon,
|
|
||||||
title: 'Generate Ideas',
|
title: 'Generate Ideas',
|
||||||
description: 'Create content ideas from your keyword clusters',
|
description: 'Create content ideas from your keyword clusters',
|
||||||
href: '/planner/ideas',
|
href: '/planner/ideas',
|
||||||
actionLabel: 'Ideas',
|
actionLabel: 'Ideas',
|
||||||
gradient: 'from-warning-500 to-warning-600',
|
gradient: 'from-warning-500 to-warning-600',
|
||||||
|
buttonTone: 'warning' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 4,
|
num: 4,
|
||||||
icon: CheckCircleIcon,
|
|
||||||
title: 'Create Tasks',
|
title: 'Create Tasks',
|
||||||
description: 'Convert approved ideas into content tasks',
|
description: 'Convert approved ideas into content tasks',
|
||||||
href: '/writer/tasks',
|
href: '/writer/tasks',
|
||||||
actionLabel: 'Tasks',
|
actionLabel: 'Tasks',
|
||||||
gradient: 'from-brand-500 to-brand-600',
|
gradient: 'from-brand-500 to-brand-600',
|
||||||
|
buttonTone: 'brand' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 5,
|
num: 5,
|
||||||
icon: FileTextIcon,
|
|
||||||
title: 'Generate Content',
|
title: 'Generate Content',
|
||||||
description: 'AI writes SEO-optimized articles from tasks',
|
description: 'AI writes SEO-optimized articles from tasks',
|
||||||
href: '/writer/content',
|
href: '/writer/content',
|
||||||
actionLabel: 'Write',
|
actionLabel: 'Write',
|
||||||
gradient: 'from-success-500 to-success-600',
|
gradient: 'from-success-500 to-success-600',
|
||||||
|
buttonTone: 'success' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 6,
|
num: 6,
|
||||||
icon: FileIcon,
|
|
||||||
title: 'Generate Images',
|
title: 'Generate Images',
|
||||||
description: 'Create featured images and media for articles',
|
description: 'Create featured images and media for articles',
|
||||||
href: '/writer/images',
|
href: '/writer/images',
|
||||||
actionLabel: 'Images',
|
actionLabel: 'Images',
|
||||||
gradient: 'from-purple-500 to-purple-600',
|
gradient: 'from-purple-500 to-purple-600',
|
||||||
|
buttonTone: 'brand' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 7,
|
num: 7,
|
||||||
icon: CheckCircleIcon,
|
|
||||||
title: 'Review & Approve',
|
title: 'Review & Approve',
|
||||||
description: 'Quality check and approve generated content',
|
description: 'Quality check and approve generated content',
|
||||||
href: '/writer/review',
|
href: '/writer/review',
|
||||||
actionLabel: 'Review',
|
actionLabel: 'Review',
|
||||||
gradient: 'from-warning-500 to-warning-600',
|
gradient: 'from-warning-500 to-warning-600',
|
||||||
|
buttonTone: 'warning' as const,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
num: 8,
|
num: 8,
|
||||||
icon: PaperPlaneIcon,
|
|
||||||
title: 'Publish to WP',
|
title: 'Publish to WP',
|
||||||
description: 'Push approved content to your WordPress site',
|
description: 'Push approved content to your WordPress site',
|
||||||
href: '/writer/published',
|
href: '/writer/published',
|
||||||
actionLabel: 'Publish',
|
actionLabel: 'Publish',
|
||||||
gradient: 'from-success-500 to-success-600',
|
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 */}
|
{/* Column 1: Steps 1-3 */}
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
{workflowSteps.slice(0, 3).map((step) => {
|
{workflowSteps.slice(0, 3).map((step) => {
|
||||||
const Icon = step.icon;
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.num}
|
key={step.num}
|
||||||
@@ -143,11 +135,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
{step.num}
|
{step.num}
|
||||||
</span>
|
</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 */}
|
{/* Text Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
||||||
@@ -158,13 +145,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Button */}
|
{/* Action Button - matches step number color */}
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="outline"
|
variant="primary"
|
||||||
tone="brand"
|
tone={step.buttonTone}
|
||||||
onClick={() => navigate(step.href)}
|
onClick={() => navigate(step.href)}
|
||||||
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
|
className="flex-shrink-0"
|
||||||
>
|
>
|
||||||
{step.actionLabel}
|
{step.actionLabel}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -176,7 +163,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
{/* Column 2: Steps 4-6 */}
|
{/* Column 2: Steps 4-6 */}
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
{workflowSteps.slice(3, 6).map((step) => {
|
{workflowSteps.slice(3, 6).map((step) => {
|
||||||
const Icon = step.icon;
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.num}
|
key={step.num}
|
||||||
@@ -187,11 +173,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
{step.num}
|
{step.num}
|
||||||
</span>
|
</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 */}
|
{/* Text Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
||||||
@@ -202,13 +183,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Button */}
|
{/* Action Button - matches step number color */}
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="outline"
|
variant="primary"
|
||||||
tone="brand"
|
tone={step.buttonTone}
|
||||||
onClick={() => navigate(step.href)}
|
onClick={() => navigate(step.href)}
|
||||||
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
|
className="flex-shrink-0"
|
||||||
>
|
>
|
||||||
{step.actionLabel}
|
{step.actionLabel}
|
||||||
</Button>
|
</Button>
|
||||||
@@ -220,7 +201,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
{/* Column 3: Steps 7-8 */}
|
{/* Column 3: Steps 7-8 */}
|
||||||
<div className="space-y-2.5">
|
<div className="space-y-2.5">
|
||||||
{workflowSteps.slice(6, 8).map((step) => {
|
{workflowSteps.slice(6, 8).map((step) => {
|
||||||
const Icon = step.icon;
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={step.num}
|
key={step.num}
|
||||||
@@ -231,11 +211,6 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
{step.num}
|
{step.num}
|
||||||
</span>
|
</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 */}
|
{/* Text Content */}
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
<p className="text-sm font-medium text-gray-800 dark:text-gray-200">
|
||||||
@@ -246,13 +221,13 @@ export default function QuickActionsWidget({ onAddKeywords }: QuickActionsWidget
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Action Button */}
|
{/* Action Button - matches step number color */}
|
||||||
<Button
|
<Button
|
||||||
size="xs"
|
size="xs"
|
||||||
variant="outline"
|
variant="primary"
|
||||||
tone="brand"
|
tone={step.buttonTone}
|
||||||
onClick={() => navigate(step.href)}
|
onClick={() => navigate(step.href)}
|
||||||
className="flex-shrink-0 opacity-70 group-hover:opacity-100 transition-opacity"
|
className="flex-shrink-0"
|
||||||
>
|
>
|
||||||
{step.actionLabel}
|
{step.actionLabel}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -27,9 +27,7 @@ export default function DemographicCard() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
|
<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" />} />
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
|
|
||||||
</IconButton>
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={closeDropdown}
|
onClose={closeDropdown}
|
||||||
|
|||||||
@@ -108,9 +108,7 @@ export default function MonthlySalesChart() {
|
|||||||
Monthly Sales
|
Monthly Sales
|
||||||
</h3>
|
</h3>
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
|
<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" />} />
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
|
|
||||||
</IconButton>
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={closeDropdown}
|
onClose={closeDropdown}
|
||||||
|
|||||||
@@ -77,9 +77,7 @@ export default function MonthlyTarget() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="relative inline-block">
|
<div className="relative inline-block">
|
||||||
<IconButton variant="ghost" size="sm" onClick={toggleDropdown} aria-label="More options">
|
<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" />} />
|
||||||
<MoreDotIcon className="text-gray-400 hover:text-gray-700 dark:hover:text-gray-300 size-6" />
|
|
||||||
</IconButton>
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
onClose={closeDropdown}
|
onClose={closeDropdown}
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<label
|
<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" : ""
|
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
|
<input
|
||||||
id={id}
|
id={id}
|
||||||
type="checkbox"
|
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}`}
|
${className}`}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={(e) => onChange(e.target.checked)}
|
onChange={(e) => onChange(e.target.checked)}
|
||||||
@@ -35,35 +35,31 @@ const Checkbox: React.FC<CheckboxProps> = ({
|
|||||||
/>
|
/>
|
||||||
{checked && (
|
{checked && (
|
||||||
<svg
|
<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"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 12 12"
|
viewBox="0 0 12 12"
|
||||||
fill="none"
|
fill="none"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M11.6666 3.5L5.24992 9.91667L2.33325 7"
|
d="M10 3L4.5 8.5L2 6"
|
||||||
stroke="white"
|
stroke="currentColor"
|
||||||
strokeWidth="1.94437"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
)}
|
)}
|
||||||
{disabled && (
|
{disabled && !checked && (
|
||||||
<svg
|
<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"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
width="12"
|
|
||||||
height="12"
|
|
||||||
viewBox="0 0 12 12"
|
viewBox="0 0 12 12"
|
||||||
fill="none"
|
fill="none"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M11.6666 3.5L5.24992 9.91667L2.33325 7"
|
d="M10 3L4.5 8.5L2 6"
|
||||||
stroke="#E4E7EC"
|
stroke="currentColor"
|
||||||
strokeWidth="2.33333"
|
strokeWidth="2"
|
||||||
strokeLinecap="round"
|
strokeLinecap="round"
|
||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -128,13 +128,30 @@ export default function NotificationDropdown() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<IconButton
|
<div className="relative">
|
||||||
ref={buttonRef as React.RefObject<HTMLButtonElement>}
|
<IconButton
|
||||||
variant="outline"
|
ref={buttonRef as React.RefObject<HTMLButtonElement>}
|
||||||
className="relative dropdown-toggle h-11 w-11"
|
variant="outline"
|
||||||
onClick={handleClick}
|
tone="neutral"
|
||||||
aria-label={`Notifications ${unreadCount > 0 ? `(${unreadCount} unread)` : ''}`}
|
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 */}
|
{/* Notification badge */}
|
||||||
{unreadCount > 0 && (
|
{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">
|
<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 className="absolute inline-flex w-full h-full bg-warning-400 rounded-full opacity-75 animate-ping"></span>
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
<svg
|
</div>
|
||||||
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>
|
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { useState, useRef } from "react";
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
import { DropdownItem } from "../ui/dropdown/DropdownItem";
|
||||||
import { Dropdown } from "../ui/dropdown/Dropdown";
|
import { Dropdown } from "../ui/dropdown/Dropdown";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { useAuthStore } from "../../store/authStore";
|
import { useAuthStore } from "../../store/authStore";
|
||||||
import Button from "../ui/button/Button";
|
import Button from "../ui/button/Button";
|
||||||
|
|
||||||
@@ -26,33 +25,28 @@ export default function UserDropdown() {
|
|||||||
closeDropdown();
|
closeDropdown();
|
||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative flex-shrink-0">
|
||||||
<Button
|
<button
|
||||||
ref={buttonRef}
|
ref={buttonRef}
|
||||||
onClick={toggleDropdown}
|
onClick={toggleDropdown}
|
||||||
variant="ghost"
|
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"
|
||||||
tone="neutral"
|
|
||||||
className="flex items-center text-gray-700 dropdown-toggle dark:text-gray-400"
|
|
||||||
>
|
>
|
||||||
<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 ? (
|
{user?.email ? (
|
||||||
<span className="text-white font-semibold text-sm">
|
<span className="text-white font-semibold text-sm">
|
||||||
{user.email.charAt(0).toUpperCase()}
|
{user.email.charAt(0).toUpperCase()}
|
||||||
</span>
|
</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>
|
||||||
|
<span className="font-medium text-sm whitespace-nowrap">
|
||||||
<span className="block mr-1 font-medium text-theme-sm">
|
|
||||||
{user?.username || user?.email?.split("@")[0] || "User"}
|
{user?.username || user?.email?.split("@")[0] || "User"}
|
||||||
</span>
|
</span>
|
||||||
<svg
|
<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" : ""
|
isOpen ? "rotate-180" : ""
|
||||||
}`}
|
}`}
|
||||||
width="18"
|
|
||||||
height="20"
|
|
||||||
viewBox="0 0 18 20"
|
viewBox="0 0 18 20"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
@@ -65,7 +59,7 @@ export default function UserDropdown() {
|
|||||||
strokeLinejoin="round"
|
strokeLinejoin="round"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</Button>
|
</button>
|
||||||
|
|
||||||
<Dropdown
|
<Dropdown
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
|
|||||||
@@ -301,8 +301,8 @@ export default function SiteIntegrationsSection({ siteId }: SiteIntegrationsSect
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleSync(integration)}
|
onClick={() => handleSync(integration)}
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
|
startIcon={<RefreshCwIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<RefreshCwIcon className="w-4 h-4 mr-1" />
|
|
||||||
Sync
|
Sync
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import React, { useState, useCallback, useEffect } from 'react';
|
|||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { Card } from '../ui/card';
|
import { Card } from '../ui/card';
|
||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
|
import IconButton from '../ui/button/IconButton';
|
||||||
import {
|
import {
|
||||||
ArrowRightIcon,
|
ArrowRightIcon,
|
||||||
ArrowLeftIcon,
|
ArrowLeftIcon,
|
||||||
@@ -209,14 +210,13 @@ export default function OnboardingWizard({ onComplete, onSkip }: OnboardingWizar
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleSkipAll}
|
onClick={handleSkipAll}
|
||||||
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
className="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
|
||||||
>
|
icon={<CloseIcon className="w-5 h-5" />}
|
||||||
<CloseIcon className="w-5 h-5" />
|
/>
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Progress Bar */}
|
{/* Progress Bar */}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
CopyIcon,
|
CopyIcon,
|
||||||
CheckCircleIcon,
|
CheckCircleIcon,
|
||||||
TimeIcon,
|
TimeIcon,
|
||||||
ArrowUpIcon,
|
DownloadIcon,
|
||||||
} from '../../../icons';
|
} from '../../../icons';
|
||||||
import { integrationApi } from '../../../services/integration.api';
|
import { integrationApi } from '../../../services/integration.api';
|
||||||
import { useToast } from '../../ui/toast/ToastContainer';
|
import { useToast } from '../../ui/toast/ToastContainer';
|
||||||
@@ -115,8 +115,12 @@ export default function Step3ConnectIntegration({
|
|||||||
title: 'Download the Plugin',
|
title: 'Download the Plugin',
|
||||||
description: 'Get the IGNY8 Bridge plugin from our dashboard',
|
description: 'Get the IGNY8 Bridge plugin from our dashboard',
|
||||||
action: (
|
action: (
|
||||||
<Button variant="outline" size="sm" className="gap-1">
|
<Button
|
||||||
<ArrowUpIcon className="w-3 h-3" />
|
variant="outline"
|
||||||
|
tone="brand"
|
||||||
|
size="sm"
|
||||||
|
startIcon={<DownloadIcon className="w-3 h-3" />}
|
||||||
|
>
|
||||||
Download Plugin
|
Download Plugin
|
||||||
</Button>
|
</Button>
|
||||||
),
|
),
|
||||||
@@ -174,9 +178,9 @@ export default function Step3ConnectIntegration({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleCopyApiKey}
|
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
|
Copy
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -167,13 +167,12 @@ export default function Step4AddKeywords({
|
|||||||
className="pl-10"
|
className="pl-10"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<IconButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={handleAddKeyword}
|
onClick={handleAddKeyword}
|
||||||
disabled={!inputValue.trim()}
|
disabled={!inputValue.trim()}
|
||||||
>
|
icon={<PlusIcon className="w-4 h-4" />}
|
||||||
<PlusIcon className="w-4 h-4" />
|
/>
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-500 mt-1">
|
<p className="text-xs text-gray-500 mt-1">
|
||||||
Tip: Paste a comma-separated list or one keyword per line
|
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
|
Example: Publish blog posts to WordPress but guides to your main site
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Button onClick={handleAddRule} variant="primary" size="sm">
|
<Button onClick={handleAddRule} variant="primary" size="sm" startIcon={<PlusIcon className="w-4 h-4" />}>
|
||||||
<PlusIcon className="w-4 h-4 mr-2" />
|
Add a Publishing Rule
|
||||||
+ Add a Publishing Rule
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import React, { useState } from 'react';
|
|||||||
import { EyeIcon, XIcon, Maximize2Icon } from '../../icons';
|
import { EyeIcon, XIcon, Maximize2Icon } from '../../icons';
|
||||||
import { Card } from '../../ui/card';
|
import { Card } from '../../ui/card';
|
||||||
import Button from '../../ui/button/Button';
|
import Button from '../../ui/button/Button';
|
||||||
|
import IconButton from '../../ui/button/IconButton';
|
||||||
|
|
||||||
export interface LayoutPreviewProps {
|
export interface LayoutPreviewProps {
|
||||||
layoutId: string;
|
layoutId: string;
|
||||||
@@ -145,15 +146,12 @@ export default function LayoutPreview({ layoutId, layoutName, onClose, onSelect
|
|||||||
{isFullscreen ? 'Exit' : 'Fullscreen'}
|
{isFullscreen ? 'Exit' : 'Fullscreen'}
|
||||||
</Button>
|
</Button>
|
||||||
{onSelect && (
|
{onSelect && (
|
||||||
<Button variant="primary" size="sm" onClick={onSelect}>
|
<Button variant="primary" size="sm" onClick={onSelect} startIcon={<EyeIcon className="w-4 h-4" />}>
|
||||||
<EyeIcon className="w-4 h-4 mr-2" />
|
|
||||||
Select Layout
|
Select Layout
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{onClose && (
|
{onClose && (
|
||||||
<Button variant="ghost" size="sm" onClick={onClose}>
|
<IconButton variant="ghost" size="sm" onClick={onClose} icon={<XIcon className="w-4 h-4" />} />
|
||||||
<XIcon className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import Badge from '../ui/badge/Badge';
|
|||||||
interface SiteTypeBadgeProps {
|
interface SiteTypeBadgeProps {
|
||||||
hostingType: string;
|
hostingType: string;
|
||||||
className?: 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 = () => {
|
const getTypeInfo = () => {
|
||||||
switch (hostingType) {
|
switch (hostingType) {
|
||||||
case 'igny8_sites':
|
case 'igny8_sites':
|
||||||
@@ -41,11 +42,11 @@ export default function SiteTypeBadge({ hostingType, className = '' }: SiteTypeB
|
|||||||
<Badge
|
<Badge
|
||||||
variant="soft"
|
variant="soft"
|
||||||
color={typeInfo.color}
|
color={typeInfo.color}
|
||||||
|
size={size}
|
||||||
startIcon={typeInfo.icon}
|
startIcon={typeInfo.icon}
|
||||||
className={className}
|
className={className}
|
||||||
>
|
>
|
||||||
{typeInfo.label}
|
{typeInfo.label}
|
||||||
</Badge>
|
</Badge>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,14 +90,12 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{onReset && (
|
{onReset && (
|
||||||
<Button variant="outline" onClick={onReset}>
|
<Button variant="outline" onClick={onReset} startIcon={<RefreshCwIcon className="w-4 h-4" />}>
|
||||||
<RefreshCwIcon className="w-4 h-4 mr-2" />
|
|
||||||
Reset
|
Reset
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{onSave && (
|
{onSave && (
|
||||||
<Button variant="primary" onClick={onSave}>
|
<Button variant="primary" onClick={onSave} startIcon={<SaveIcon className="w-4 h-4" />}>
|
||||||
<SaveIcon className="w-4 h-4 mr-2" />
|
|
||||||
Save Styles
|
Save Styles
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -112,8 +110,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('css')}
|
onClick={() => setActiveTab('css')}
|
||||||
|
startIcon={<CodeIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<CodeIcon className="w-4 h-4 mr-2" />
|
|
||||||
Custom CSS
|
Custom CSS
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -121,8 +119,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('colors')}
|
onClick={() => setActiveTab('colors')}
|
||||||
|
startIcon={<PaletteIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PaletteIcon className="w-4 h-4 mr-2" />
|
|
||||||
Colors
|
Colors
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -130,8 +128,8 @@ export default function StyleEditor({ styleSettings, onChange, onSave, onReset }
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('typography')}
|
onClick={() => setActiveTab('typography')}
|
||||||
|
startIcon={<TypeIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<TypeIcon className="w-4 h-4 mr-2" />
|
|
||||||
Typography
|
Typography
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -74,8 +74,8 @@ export default function TemplateCustomizer({
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('layout')}
|
onClick={() => setActiveTab('layout')}
|
||||||
|
startIcon={<LayoutIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<LayoutIcon className="w-4 h-4 mr-2" />
|
|
||||||
Layout
|
Layout
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -83,8 +83,8 @@ export default function TemplateCustomizer({
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('colors')}
|
onClick={() => setActiveTab('colors')}
|
||||||
|
startIcon={<PaletteIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PaletteIcon className="w-4 h-4 mr-2" />
|
|
||||||
Colors
|
Colors
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -92,8 +92,8 @@ export default function TemplateCustomizer({
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('typography')}
|
onClick={() => setActiveTab('typography')}
|
||||||
|
startIcon={<TypeIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<TypeIcon className="w-4 h-4 mr-2" />
|
|
||||||
Typography
|
Typography
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -101,8 +101,8 @@ export default function TemplateCustomizer({
|
|||||||
tone="brand"
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('spacing')}
|
onClick={() => setActiveTab('spacing')}
|
||||||
|
startIcon={<SettingsIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<SettingsIcon className="w-4 h-4 mr-2" />
|
|
||||||
Spacing
|
Spacing
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { useNavigate } from 'react-router-dom';
|
|||||||
import { GlobeIcon, CheckCircleIcon, XCircleIcon, SettingsIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon } from '../../icons';
|
import { GlobeIcon, CheckCircleIcon, XCircleIcon, SettingsIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon } from '../../icons';
|
||||||
import { Card } from '../ui/card';
|
import { Card } from '../ui/card';
|
||||||
import Button from '../ui/button/Button';
|
import Button from '../ui/button/Button';
|
||||||
|
import IconButton from '../ui/button/IconButton';
|
||||||
import Badge from '../ui/badge/Badge';
|
import Badge from '../ui/badge/Badge';
|
||||||
|
|
||||||
interface WordPressIntegration {
|
interface WordPressIntegration {
|
||||||
@@ -175,8 +176,8 @@ export default function WordPressIntegrationCard({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="flex-1"
|
className="flex-1"
|
||||||
|
startIcon={<SettingsIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<SettingsIcon className="w-4 h-4 mr-2" />
|
|
||||||
Manage
|
Manage
|
||||||
</Button>
|
</Button>
|
||||||
{siteId && (
|
{siteId && (
|
||||||
@@ -190,14 +191,13 @@ export default function WordPressIntegrationCard({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{onSync && (
|
{onSync && (
|
||||||
<Button
|
<IconButton
|
||||||
onClick={onSync}
|
onClick={onSync}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={loading}
|
disabled={loading}
|
||||||
>
|
icon={<RefreshCwIcon className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />}
|
||||||
<RefreshCwIcon className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
|
/>
|
||||||
</Button>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -286,16 +286,14 @@ export default function WordPressIntegrationForm({
|
|||||||
type={apiKeyVisible ? 'text' : 'password'}
|
type={apiKeyVisible ? 'text' : 'password'}
|
||||||
value={apiKeyVisible ? apiKey : maskApiKey(apiKey)}
|
value={apiKeyVisible ? apiKey : maskApiKey(apiKey)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<IconButton
|
||||||
onClick={handleCopyApiKey}
|
onClick={handleCopyApiKey}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
tone="neutral"
|
tone="neutral"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="absolute top-1/2 right-0 -translate-y-1/2 rounded-l-none"
|
className="absolute top-1/2 right-0 -translate-y-1/2 rounded-l-none"
|
||||||
>
|
icon={<CopyIcon className="w-4 h-4" />}
|
||||||
<CopyIcon className="w-4 h-4" />
|
/>
|
||||||
Copy
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="group relative inline-block">
|
<div className="group relative inline-block">
|
||||||
<IconButton
|
<IconButton
|
||||||
@@ -303,9 +301,8 @@ export default function WordPressIntegrationForm({
|
|||||||
disabled={generatingKey}
|
disabled={generatingKey}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
title="Regenerate"
|
title="Regenerate"
|
||||||
>
|
icon={<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />}
|
||||||
<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />
|
/>
|
||||||
</IconButton>
|
|
||||||
<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="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="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">
|
<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}
|
disabled={generatingKey}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
title="Regenerate API key"
|
title="Regenerate API key"
|
||||||
>
|
icon={<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />}
|
||||||
<RefreshCwIcon className={`w-5 h-5 ${generatingKey ? 'animate-spin' : ''}`} />
|
/>
|
||||||
</IconButton>
|
|
||||||
<IconButton
|
<IconButton
|
||||||
onClick={handleRevokeApiKey}
|
onClick={handleRevokeApiKey}
|
||||||
disabled={generatingKey}
|
disabled={generatingKey}
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
tone="danger"
|
tone="danger"
|
||||||
title="Revoke API key"
|
title="Revoke API key"
|
||||||
>
|
icon={<TrashBinIcon className="w-5 h-5" />}
|
||||||
<TrashBinIcon className="w-5 h-5" />
|
/>
|
||||||
</IconButton>
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -389,8 +384,8 @@ export default function WordPressIntegrationForm({
|
|||||||
<Button
|
<Button
|
||||||
onClick={handleDownloadPlugin}
|
onClick={handleDownloadPlugin}
|
||||||
variant="solid"
|
variant="solid"
|
||||||
|
startIcon={<DownloadIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<DownloadIcon className="w-4 h-4 mr-2" />
|
|
||||||
Download Plugin
|
Download Plugin
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ interface ButtonGroupItemProps {
|
|||||||
isActive?: boolean;
|
isActive?: boolean;
|
||||||
className?: string;
|
className?: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
startIcon?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ButtonGroupItem: React.FC<ButtonGroupItemProps> = ({
|
export const ButtonGroupItem: React.FC<ButtonGroupItemProps> = ({
|
||||||
@@ -32,18 +33,20 @@ export const ButtonGroupItem: React.FC<ButtonGroupItemProps> = ({
|
|||||||
isActive = false,
|
isActive = false,
|
||||||
className = "",
|
className = "",
|
||||||
disabled = false,
|
disabled = false,
|
||||||
|
startIcon,
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
disabled={disabled}
|
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
|
isActive
|
||||||
? "bg-gray-100 text-gray-900 dark:bg-white/10 dark:text-white"
|
? "bg-gray-100 text-gray-900 dark:bg-white/10 dark:text-white"
|
||||||
: ""
|
: ""
|
||||||
} ${className}`}
|
} ${className}`}
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
|
{startIcon}
|
||||||
{children}
|
{children}
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export const Modal: React.FC<ModalProps> = ({
|
|||||||
|
|
||||||
const contentClasses = isFullscreen
|
const contentClasses = isFullscreen
|
||||||
? "w-full h-full"
|
? "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 (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center overflow-y-auto modal z-99999">
|
<div className="fixed inset-0 flex items-center justify-center overflow-y-auto modal z-99999">
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
class="fill-current"
|
width="12"
|
||||||
width="12"
|
height="12"
|
||||||
height="12"
|
viewBox="0 0 12 12"
|
||||||
viewBox="0 0 12 12"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M5.31462 10.3761C5.45194 10.5293 5.65136 10.6257 5.87329 10.6257C5.8736 10.6257 5.8739 10.6257 5.87421 10.6257C6.0663 10.6259 6.25845 10.5527 6.40505 10.4062L9.40514 7.4082C9.69814 7.11541 9.69831 6.64054 9.40552 6.34754C9.11273 6.05454 8.63785 6.05438 8.34486 6.34717L6.62329 8.06753L6.62329 1.875C6.62329 1.46079 6.28751 1.125 5.87329 1.125C5.45908 1.125 5.12329 1.46079 5.12329 1.875L5.12329 8.06422L3.40516 6.34719C3.11218 6.05439 2.6373 6.05454 2.3445 6.34752C2.0517 6.64051 2.05185 7.11538 2.34484 7.40818L5.31462 10.3761Z"
|
||||||
d="M5.31462 10.3761C5.45194 10.5293 5.65136 10.6257 5.87329 10.6257C5.8736 10.6257 5.8739 10.6257 5.87421 10.6257C6.0663 10.6259 6.25845 10.5527 6.40505 10.4062L9.40514 7.4082C9.69814 7.11541 9.69831 6.64054 9.40552 6.34754C9.11273 6.05454 8.63785 6.05438 8.34486 6.34717L6.62329 8.06753L6.62329 1.875C6.62329 1.46079 6.28751 1.125 5.87329 1.125C5.45908 1.125 5.12329 1.46079 5.12329 1.875L5.12329 8.06422L3.40516 6.34719C3.11218 6.05439 2.6373 6.05454 2.3445 6.34752C2.0517 6.64051 2.05185 7.11538 2.34484 7.40818L5.31462 10.3761Z"
|
fill="currentColor"
|
||||||
fill=""
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 858 B After Width: | Height: | Size: 735 B |
@@ -1,15 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
className="fill-current"
|
width="20"
|
||||||
width="20"
|
height="20"
|
||||||
height="20"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M17.4175 9.9986C17.4178 10.1909 17.3446 10.3832 17.198 10.53L12.2013 15.5301C11.9085 15.8231 11.4337 15.8233 11.1407 15.5305C10.8477 15.2377 10.8475 14.7629 11.1403 14.4699L14.8604 10.7472L3.33301 10.7472C2.91879 10.7472 2.58301 10.4114 2.58301 9.99715C2.58301 9.58294 2.91879 9.24715 3.33301 9.24715L14.8549 9.24715L11.1403 5.53016C10.8475 5.23717 10.8477 4.7623 11.1407 4.4695C11.4336 4.1767 11.9085 4.17685 12.2013 4.46984L17.1588 9.43049C17.3173 9.568 17.4175 9.77087 17.4175 9.99715C17.4175 9.99763 17.4175 9.99812 17.4175 9.9986Z"
|
||||||
d="M17.4175 9.9986C17.4178 10.1909 17.3446 10.3832 17.198 10.53L12.2013 15.5301C11.9085 15.8231 11.4337 15.8233 11.1407 15.5305C10.8477 15.2377 10.8475 14.7629 11.1403 14.4699L14.8604 10.7472L3.33301 10.7472C2.91879 10.7472 2.58301 10.4114 2.58301 9.99715C2.58301 9.58294 2.91879 9.24715 3.33301 9.24715L14.8549 9.24715L11.1403 5.53016C10.8475 5.23717 10.8477 4.7623 11.1407 4.4695C11.4336 4.1767 11.9085 4.17685 12.2013 4.46984L17.1588 9.43049C17.3173 9.568 17.4175 9.77087 17.4175 9.99715C17.4175 9.99763 17.4175 9.99812 17.4175 9.9986Z"
|
fill="currentColor"
|
||||||
fill=""
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 897 B After Width: | Height: | Size: 742 B |
@@ -1,16 +1,14 @@
|
|||||||
|
<svg
|
||||||
<svg
|
width="13"
|
||||||
className="fill-current"
|
height="12"
|
||||||
width="13"
|
viewBox="0 0 13 12"
|
||||||
height="12"
|
fill="none"
|
||||||
viewBox="0 0 13 12"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
fill="none"
|
>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
<path
|
||||||
>
|
fill-rule="evenodd"
|
||||||
<path
|
clip-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
d="M6.06462 1.62393C6.20193 1.47072 6.40135 1.37432 6.62329 1.37432C6.6236 1.37432 6.62391 1.37432 6.62422 1.37432C6.81631 1.37415 7.00845 1.44731 7.15505 1.5938L10.1551 4.5918C10.4481 4.88459 10.4483 5.35946 10.1555 5.65246C9.86273 5.94546 9.38785 5.94562 9.09486 5.65283L7.37329 3.93247L7.37329 10.125C7.37329 10.5392 7.03751 10.875 6.62329 10.875C6.20908 10.875 5.87329 10.5392 5.87329 10.125L5.87329 3.93578L4.15516 5.65281C3.86218 5.94561 3.3873 5.94546 3.0945 5.65248C2.8017 5.35949 2.80185 4.88462 3.09484 4.59182L6.06462 1.62393Z"
|
||||||
clip-rule="evenodd"
|
fill="currentColor"
|
||||||
d="M6.06462 1.62393C6.20193 1.47072 6.40135 1.37432 6.62329 1.37432C6.6236 1.37432 6.62391 1.37432 6.62422 1.37432C6.81631 1.37415 7.00845 1.44731 7.15505 1.5938L10.1551 4.5918C10.4481 4.88459 10.4483 5.35946 10.1555 5.65246C9.86273 5.94546 9.38785 5.94562 9.09486 5.65283L7.37329 3.93247L7.37329 10.125C7.37329 10.5392 7.03751 10.875 6.62329 10.875C6.20908 10.875 5.87329 10.5392 5.87329 10.125L5.87329 3.93578L4.15516 5.65281C3.86218 5.94561 3.3873 5.94546 3.0945 5.65248C2.8017 5.35949 2.80185 4.88462 3.09484 4.59182L6.06462 1.62393Z"
|
/>
|
||||||
fill=""
|
</svg>
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 926 B After Width: | Height: | Size: 741 B |
@@ -1,14 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
|
width="20"
|
||||||
width="20"
|
height="20"
|
||||||
height="20"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z"
|
||||||
d="M10.0002 13.8619C7.23361 13.8619 4.86803 12.1372 3.92328 9.70241C4.86804 7.26761 7.23361 5.54297 10.0002 5.54297C12.7667 5.54297 15.1323 7.26762 16.0771 9.70243C15.1323 12.1372 12.7667 13.8619 10.0002 13.8619ZM10.0002 4.04297C6.48191 4.04297 3.49489 6.30917 2.4155 9.4593C2.3615 9.61687 2.3615 9.78794 2.41549 9.94552C3.49488 13.0957 6.48191 15.3619 10.0002 15.3619C13.5184 15.3619 16.5055 13.0957 17.5849 9.94555C17.6389 9.78797 17.6389 9.6169 17.5849 9.45932C16.5055 6.30919 13.5184 4.04297 10.0002 4.04297ZM9.99151 7.84413C8.96527 7.84413 8.13333 8.67606 8.13333 9.70231C8.13333 10.7286 8.96527 11.5605 9.99151 11.5605H10.0064C11.0326 11.5605 11.8646 10.7286 11.8646 9.70231C11.8646 8.67606 11.0326 7.84413 10.0064 7.84413H9.99151Z"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 941 B |
@@ -55,6 +55,8 @@ import { ReactComponent as MoreDotIcon } from "./moredot.svg?react";
|
|||||||
import { ReactComponent as AlertHexaIcon } from "./alert-hexa.svg?react";
|
import { ReactComponent as AlertHexaIcon } from "./alert-hexa.svg?react";
|
||||||
import { ReactComponent as ErrorHexaIcon } from "./info-hexa.svg?react";
|
import { ReactComponent as ErrorHexaIcon } from "./info-hexa.svg?react";
|
||||||
import { ReactComponent as CalendarIcon } from "./calendar.svg?react";
|
import { ReactComponent as CalendarIcon } from "./calendar.svg?react";
|
||||||
|
import { ReactComponent as SaveIcon } from "./save.svg?react";
|
||||||
|
import { ReactComponent as UserPlusIcon } from "./user-plus.svg?react";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
ErrorHexaIcon,
|
ErrorHexaIcon,
|
||||||
@@ -114,6 +116,8 @@ export {
|
|||||||
AngleLeftIcon,
|
AngleLeftIcon,
|
||||||
AngleRightIcon,
|
AngleRightIcon,
|
||||||
CalendarIcon,
|
CalendarIcon,
|
||||||
|
SaveIcon,
|
||||||
|
UserPlusIcon,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Aliases for commonly used icon names (lucide-react replacements)
|
// Aliases for commonly used icon names (lucide-react replacements)
|
||||||
@@ -153,7 +157,6 @@ export { FileIcon as FileArchiveIcon }; // File archive alias
|
|||||||
export { PageIcon as ExternalLinkIcon }; // External link alias
|
export { PageIcon as ExternalLinkIcon }; // External link alias
|
||||||
export { PencilIcon as Wand2Icon }; // Wand alias
|
export { PencilIcon as Wand2Icon }; // Wand alias
|
||||||
export { BoxCubeIcon as LayoutIcon }; // Layout alias
|
export { BoxCubeIcon as LayoutIcon }; // Layout alias
|
||||||
export { PencilIcon as SaveIcon }; // Save alias
|
|
||||||
export { BoxCubeIcon as CodeIcon }; // Code alias
|
export { BoxCubeIcon as CodeIcon }; // Code alias
|
||||||
export { GridIcon as SearchIcon }; // Search alias
|
export { GridIcon as SearchIcon }; // Search alias
|
||||||
export { BoxCubeIcon as PaletteIcon }; // Palette alias
|
export { BoxCubeIcon as PaletteIcon }; // Palette alias
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
|
width="20"
|
||||||
width="20"
|
height="20"
|
||||||
height="20"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M4.98481 2.44399C3.11333 1.57147 1.15325 3.46979 1.96543 5.36824L3.82086 9.70527C3.90146 9.89367 3.90146 10.1069 3.82086 10.2953L1.96543 14.6323C1.15326 16.5307 3.11332 18.4291 4.98481 17.5565L16.8184 12.0395C18.5508 11.2319 18.5508 8.76865 16.8184 7.961L4.98481 2.44399ZM3.34453 4.77824C3.0738 4.14543 3.72716 3.51266 4.35099 3.80349L16.1846 9.32051C16.762 9.58973 16.762 10.4108 16.1846 10.68L4.35098 16.197C3.72716 16.4879 3.0738 15.8551 3.34453 15.2223L5.19996 10.8853C5.21944 10.8397 5.23735 10.7937 5.2537 10.7473L9.11784 10.7473C9.53206 10.7473 9.86784 10.4115 9.86784 9.99726C9.86784 9.58304 9.53206 9.24726 9.11784 9.24726L5.25157 9.24726C5.2358 9.20287 5.2186 9.15885 5.19996 9.11528L3.34453 4.77824Z"
|
||||||
d="M4.98481 2.44399C3.11333 1.57147 1.15325 3.46979 1.96543 5.36824L3.82086 9.70527C3.90146 9.89367 3.90146 10.1069 3.82086 10.2953L1.96543 14.6323C1.15326 16.5307 3.11332 18.4291 4.98481 17.5565L16.8184 12.0395C18.5508 11.2319 18.5508 8.76865 16.8184 7.961L4.98481 2.44399ZM3.34453 4.77824C3.0738 4.14543 3.72716 3.51266 4.35099 3.80349L16.1846 9.32051C16.762 9.58973 16.762 10.4108 16.1846 10.68L4.35098 16.197C3.72716 16.4879 3.0738 15.8551 3.34453 15.2223L5.19996 10.8853C5.21944 10.8397 5.23735 10.7937 5.2537 10.7473L9.11784 10.7473C9.53206 10.7473 9.86784 10.4115 9.86784 9.99726C9.86784 9.58304 9.53206 9.24726 9.11784 9.24726L5.25157 9.24726C5.2358 9.20287 5.2186 9.15885 5.19996 9.11528L3.34453 4.77824Z"
|
fill="currentColor"
|
||||||
fill="currentColor"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 917 B |
@@ -1,15 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
class="fill-current"
|
width="12"
|
||||||
width="12"
|
height="12"
|
||||||
height="12"
|
viewBox="0 0 12 12"
|
||||||
viewBox="0 0 12 12"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M5.25012 3C5.25012 2.58579 5.58591 2.25 6.00012 2.25C6.41433 2.25 6.75012 2.58579 6.75012 3V5.25012L9.00034 5.25012C9.41455 5.25012 9.75034 5.58591 9.75034 6.00012C9.75034 6.41433 9.41455 6.75012 9.00034 6.75012H6.75012V9.00034C6.75012 9.41455 6.41433 9.75034 6.00012 9.75034C5.58591 9.75034 5.25012 9.41455 5.25012 9.00034L5.25012 6.75012H3C2.58579 6.75012 2.25 6.41433 2.25 6.00012C2.25 5.58591 2.58579 5.25012 3 5.25012H5.25012V3Z"
|
||||||
d="M5.25012 3C5.25012 2.58579 5.58591 2.25 6.00012 2.25C6.41433 2.25 6.75012 2.58579 6.75012 3V5.25012L9.00034 5.25012C9.41455 5.25012 9.75034 5.58591 9.75034 6.00012C9.75034 6.41433 9.41455 6.75012 9.00034 6.75012H6.75012V9.00034C6.75012 9.41455 6.41433 9.75034 6.00012 9.75034C5.58591 9.75034 5.25012 9.41455 5.25012 9.00034L5.25012 6.75012H3C2.58579 6.75012 2.25 6.41433 2.25 6.00012C2.25 5.58591 2.58579 5.25012 3 5.25012H5.25012V3Z"
|
fill="currentColor"
|
||||||
fill=""
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 640 B |
29
frontend/src/icons/save.svg
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M19 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H16L21 8V19C21 19.5304 20.7893 20.0391 20.4142 20.4142C20.0391 20.7893 19.5304 21 19 21Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M17 21V13H7V21"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M7 3V8H15"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 759 B |
@@ -1,15 +1,14 @@
|
|||||||
<svg
|
<svg
|
||||||
|
width="20"
|
||||||
width="20"
|
height="20"
|
||||||
height="20"
|
viewBox="0 0 20 20"
|
||||||
viewBox="0 0 20 20"
|
fill="none"
|
||||||
fill="none"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
>
|
||||||
>
|
<path
|
||||||
<path
|
fill-rule="evenodd"
|
||||||
fill-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
clip-rule="evenodd"
|
d="M3.04175 9.99984C3.04175 6.15686 6.1571 3.0415 10.0001 3.0415C13.8431 3.0415 16.9584 6.15686 16.9584 9.99984C16.9584 13.8428 13.8431 16.9582 10.0001 16.9582C6.1571 16.9582 3.04175 13.8428 3.04175 9.99984ZM10.0001 1.5415C5.32867 1.5415 1.54175 5.32843 1.54175 9.99984C1.54175 14.6712 5.32867 18.4582 10.0001 18.4582C14.6715 18.4582 18.4584 14.6712 18.4584 9.99984C18.4584 5.32843 14.6715 1.5415 10.0001 1.5415ZM9.99998 10.7498C9.58577 10.7498 9.24998 10.4141 9.24998 9.99984V5.4165C9.24998 5.00229 9.58577 4.6665 9.99998 4.6665C10.4142 4.6665 10.75 5.00229 10.75 5.4165V9.24984H13.3334C13.7476 9.24984 14.0834 9.58562 14.0834 9.99984C14.0834 10.4141 13.7476 10.7498 13.3334 10.7498H10.0001H9.99998Z"
|
||||||
d="M3.04175 9.99984C3.04175 6.15686 6.1571 3.0415 10.0001 3.0415C13.8431 3.0415 16.9584 6.15686 16.9584 9.99984C16.9584 13.8428 13.8431 16.9582 10.0001 16.9582C6.1571 16.9582 3.04175 13.8428 3.04175 9.99984ZM10.0001 1.5415C5.32867 1.5415 1.54175 5.32843 1.54175 9.99984C1.54175 14.6712 5.32867 18.4582 10.0001 18.4582C14.6715 18.4582 18.4584 14.6712 18.4584 9.99984C18.4584 5.32843 14.6715 1.5415 10.0001 1.5415ZM9.99998 10.7498C9.58577 10.7498 9.24998 10.4141 9.24998 9.99984V5.4165C9.24998 5.00229 9.58577 4.6665 9.99998 4.6665C10.4142 4.6665 10.75 5.00229 10.75 5.4165V9.24984H13.3334C13.7476 9.24984 14.0834 9.58562 14.0834 9.99984C14.0834 10.4141 13.7476 10.7498 13.3334 10.7498H10.0001H9.99998Z"
|
fill="currentColor"
|
||||||
fill="currentColor"
|
/>
|
||||||
/>
|
</svg>
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 904 B |
36
frontend/src/icons/user-plus.svg
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<svg
|
||||||
|
width="20"
|
||||||
|
height="20"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M16 21V19C16 17.9391 15.5786 16.9217 14.8284 16.1716C14.0783 15.4214 13.0609 15 12 15H5C3.93913 15 2.92172 15.4214 2.17157 16.1716C1.42143 16.9217 1 17.9391 1 19V21"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M8.5 11C10.7091 11 12.5 9.20914 12.5 7C12.5 4.79086 10.7091 3 8.5 3C6.29086 3 4.5 4.79086 4.5 7C4.5 9.20914 6.29086 11 8.5 11Z"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M20 8V14"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
d="M23 11H17"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="1.5"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 925 B |
@@ -107,14 +107,17 @@ const AppHeader: React.FC = () => {
|
|||||||
{/* Mobile Menu Toggle */}
|
{/* Mobile Menu Toggle */}
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
tone="neutral"
|
||||||
|
size="md"
|
||||||
onClick={toggleApplicationMenu}
|
onClick={toggleApplicationMenu}
|
||||||
className="w-10 h-10 z-99999 lg:hidden"
|
className="z-99999 lg:hidden"
|
||||||
aria-label="Toggle menu"
|
aria-label="Toggle menu"
|
||||||
>
|
icon={
|
||||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fillRule="evenodd" clipRule="evenodd" d="M5.99902 10.4951C6.82745 10.4951 7.49902 11.1667 7.49902 11.9951V12.0051C7.49902 12.8335 6.82745 13.5051 5.99902 13.5051C5.1706 13.5051 4.49902 12.8335 4.49902 12.0051V11.9951C4.49902 11.1667 5.1706 10.4951 5.99902 10.4951ZM17.999 10.4951C18.8275 10.4951 19.499 11.1667 19.499 11.9951V12.0051C19.499 12.8335 18.8275 13.5051 17.999 13.5051C17.1706 13.5051 16.499 12.8335 16.499 12.0051V11.9951C16.499 11.1667 17.1706 10.4951 17.999 10.4951ZM13.499 11.9951C13.499 11.1667 12.8275 10.4951 11.999 10.4951C11.1706 10.4951 10.499 11.1667 10.499 11.9951V12.0051C10.499 12.8335 11.1706 13.5051 11.999 13.5051C12.8275 13.5051 13.499 12.8335 13.499 12.0051V11.9951Z" fill="currentColor" />
|
<path fillRule="evenodd" clipRule="evenodd" d="M5.99902 10.4951C6.82745 10.4951 7.49902 11.1667 7.49902 11.9951V12.0051C7.49902 12.8335 6.82745 13.5051 5.99902 13.5051C5.1706 13.5051 4.49902 12.8335 4.49902 12.0051V11.9951C4.49902 11.1667 5.1706 10.4951 5.99902 10.4951ZM17.999 10.4951C18.8275 10.4951 19.499 11.1667 19.499 11.9951V12.0051C19.499 12.8335 18.8275 13.5051 17.999 13.5051C17.1706 13.5051 16.499 12.8335 16.499 12.0051V11.9951C16.499 11.1667 17.1706 10.4951 17.999 10.4951ZM13.499 11.9951C13.499 11.1667 12.8275 10.4951 11.999 10.4951C11.1706 10.4951 10.499 11.1667 10.499 11.9951V12.0051C10.499 12.8335 11.1706 13.5051 11.999 13.5051C12.8275 13.5051 13.499 12.8335 13.499 12.0051V11.9951Z" fill="currentColor" />
|
||||||
</svg>
|
</svg>
|
||||||
</IconButton>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Page Title with Badge - Desktop */}
|
{/* Page Title with Badge - Desktop */}
|
||||||
{pageInfo && (
|
{pageInfo && (
|
||||||
@@ -122,20 +125,22 @@ const AppHeader: React.FC = () => {
|
|||||||
{/* Sidebar Toggle Button - Always visible on desktop */}
|
{/* Sidebar Toggle Button - Always visible on desktop */}
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="xs"
|
tone="neutral"
|
||||||
|
size="sm"
|
||||||
|
shape="circle"
|
||||||
onClick={toggleSidebar}
|
onClick={toggleSidebar}
|
||||||
className="w-6 h-6 rounded-full"
|
|
||||||
aria-label={isExpanded ? "Collapse Sidebar" : "Expand Sidebar"}
|
aria-label={isExpanded ? "Collapse Sidebar" : "Expand Sidebar"}
|
||||||
>
|
icon={
|
||||||
<svg
|
<svg
|
||||||
className={`w-3.5 h-3.5 text-gray-500 dark:text-gray-400 transition-transform duration-300 ${isExpanded ? 'rotate-180' : ''}`}
|
className={`w-3.5 h-3.5 transition-transform duration-300 ${isExpanded ? 'rotate-180' : ''}`}
|
||||||
fill="none"
|
fill="none"
|
||||||
stroke="currentColor"
|
stroke="currentColor"
|
||||||
viewBox="0 0 24 24"
|
viewBox="0 0 24 24"
|
||||||
>
|
>
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
|
||||||
</svg>
|
</svg>
|
||||||
</IconButton>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{pageInfo.badge && (
|
{pageInfo.badge && (
|
||||||
<div className={`flex items-center justify-center w-8 h-8 rounded-lg ${badgeColors[pageInfo.badge.color]?.bg || 'bg-gray-600'} flex-shrink-0`}>
|
<div className={`flex items-center justify-center w-8 h-8 rounded-lg ${badgeColors[pageInfo.badge.color]?.bg || 'bg-gray-600'} flex-shrink-0`}>
|
||||||
@@ -188,15 +193,17 @@ const AppHeader: React.FC = () => {
|
|||||||
{/* Search Icon */}
|
{/* Search Icon */}
|
||||||
<IconButton
|
<IconButton
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
|
tone="neutral"
|
||||||
|
size="md"
|
||||||
onClick={() => setIsSearchOpen(true)}
|
onClick={() => setIsSearchOpen(true)}
|
||||||
className="w-10 h-10"
|
|
||||||
title="Search (⌘K)"
|
title="Search (⌘K)"
|
||||||
aria-label="Search"
|
aria-label="Search"
|
||||||
>
|
icon={
|
||||||
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
</IconButton>
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* Dark Mode Toggler */}
|
{/* Dark Mode Toggler */}
|
||||||
<ThemeToggleButton />
|
<ThemeToggleButton />
|
||||||
|
|||||||
@@ -176,18 +176,9 @@ export default function LinkerContentList() {
|
|||||||
disabled={isProcessing || processing === -1}
|
disabled={isProcessing || processing === -1}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
startIcon={isProcessing ? <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" /> : <PlugInIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
{isProcessing ? (
|
{isProcessing ? 'Processing...' : 'Add Links'}
|
||||||
<>
|
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
||||||
Processing...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<PlugInIcon className="w-4 h-4" />
|
|
||||||
Add Links
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -171,18 +171,9 @@ export default function OptimizerContentSelector() {
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={handleBatchOptimize}
|
onClick={handleBatchOptimize}
|
||||||
disabled={selectedIds.length === 0 || processing.length > 0}
|
disabled={selectedIds.length === 0 || processing.length > 0}
|
||||||
|
startIcon={processing.length > 0 ? <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" /> : <BoltIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
{processing.length > 0 ? (
|
{processing.length > 0 ? 'Optimizing...' : `Optimize Selected (${selectedIds.length})`}
|
||||||
<>
|
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
||||||
Optimizing...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<BoltIcon className="w-4 h-4" />
|
|
||||||
Optimize Selected ({selectedIds.length})
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -270,18 +261,9 @@ export default function OptimizerContentSelector() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleOptimize(item.id)}
|
onClick={() => handleOptimize(item.id)}
|
||||||
disabled={isProcessing || processing.length > 0}
|
disabled={isProcessing || processing.length > 0}
|
||||||
|
startIcon={isProcessing ? <div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" /> : <BoltIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
{isProcessing ? (
|
{isProcessing ? 'Optimizing...' : 'Optimize'}
|
||||||
<>
|
|
||||||
<div className="w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin" />
|
|
||||||
Optimizing...
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<BoltIcon className="w-4 h-4" />
|
|
||||||
Optimize
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -157,9 +157,8 @@ export default function ClusterDetail() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => navigate('/planner/clusters')}
|
onClick={() => navigate('/planner/clusters')}
|
||||||
className="flex items-center gap-2"
|
startIcon={<ChevronLeftIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ChevronLeftIcon className="w-4 h-4" />
|
|
||||||
Back to Clusters
|
Back to Clusters
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
@@ -241,8 +240,8 @@ export default function ClusterDetail() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<FileIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<FileIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Articles
|
Articles
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -253,8 +252,8 @@ export default function ClusterDetail() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<PageIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PageIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Pages
|
Pages
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -265,8 +264,8 @@ export default function ClusterDetail() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<GridIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<GridIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Products
|
Products
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -277,8 +276,8 @@ export default function ClusterDetail() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<TagIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<TagIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Taxonomy
|
Taxonomy
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ import {
|
|||||||
PageIcon,
|
PageIcon,
|
||||||
TableIcon,
|
TableIcon,
|
||||||
ChevronDownIcon,
|
ChevronDownIcon,
|
||||||
ChevronUpIcon
|
ChevronUpIcon,
|
||||||
|
FilterIcon,
|
||||||
|
BoxCubeIcon as SettingsIcon
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
import {
|
import {
|
||||||
fetchSites,
|
fetchSites,
|
||||||
@@ -67,6 +69,7 @@ export default function SiteList() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [viewType, setViewType] = useState<ViewType>('grid');
|
const [viewType, setViewType] = useState<ViewType>('grid');
|
||||||
const [showWelcomeGuide, setShowWelcomeGuide] = useState(false);
|
const [showWelcomeGuide, setShowWelcomeGuide] = useState(false);
|
||||||
|
const [showFilters, setShowFilters] = useState(false);
|
||||||
|
|
||||||
// Site Management Modals
|
// Site Management Modals
|
||||||
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
|
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
|
||||||
@@ -410,19 +413,16 @@ export default function SiteList() {
|
|||||||
{site.domain}
|
{site.domain}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<div className="flex items-center gap-2 mb-2 flex-wrap">
|
{/* Centered badges with uniform size */}
|
||||||
<SiteTypeBadge hostingType={site.hosting_type} />
|
<div className="flex items-center justify-center gap-2 mb-2 flex-wrap">
|
||||||
|
<SiteTypeBadge hostingType={site.hosting_type} size="sm" />
|
||||||
{site.industry_name && (
|
{site.industry_name && (
|
||||||
<Badge variant="light" color="info" className="text-xs">
|
<Badge variant="soft" color="warning" size="sm">
|
||||||
{site.industry_name}
|
{site.industry_name}
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
{site.integration_count && site.integration_count > 0 && (
|
|
||||||
<Badge variant="soft" color="success" className="text-[10px] px-1.5 py-0.5">
|
|
||||||
{site.integration_count} integration{site.integration_count > 1 ? 's' : ''}
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
{/* Status badge and toggle in top right */}
|
||||||
<div className="absolute top-4 right-4 flex items-center gap-3">
|
<div className="absolute top-4 right-4 flex items-center gap-3">
|
||||||
<Switch
|
<Switch
|
||||||
checked={site.is_active}
|
checked={site.is_active}
|
||||||
@@ -430,19 +430,21 @@ export default function SiteList() {
|
|||||||
disabled={togglingSiteId === site.id}
|
disabled={togglingSiteId === site.id}
|
||||||
/>
|
/>
|
||||||
<Badge
|
<Badge
|
||||||
variant={site.is_active ? "soft" : "light"}
|
variant="solid"
|
||||||
color={site.is_active ? "success" : "gray"}
|
color={site.is_active ? "success" : "error"}
|
||||||
size="sm"
|
size="xs"
|
||||||
>
|
>
|
||||||
{site.is_active ? 'Active' : 'Inactive'}
|
{site.is_active ? 'Active' : 'Inactive'}
|
||||||
</Badge>
|
</Badge>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{/* Centered button row */}
|
||||||
<div className="border-t border-gray-200 p-3 dark:border-gray-800">
|
<div className="border-t border-gray-200 p-3 dark:border-gray-800">
|
||||||
<div className="grid grid-cols-2 gap-2">
|
<div className="flex justify-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => navigate(`/sites/${site.id}`)}
|
onClick={() => navigate(`/sites/${site.id}`)}
|
||||||
variant="primary"
|
variant="primary"
|
||||||
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
startIcon={<EyeIcon className="w-4 h-4" />}
|
startIcon={<EyeIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
@@ -451,6 +453,7 @@ export default function SiteList() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => navigate(`/sites/${site.id}/content`)}
|
onClick={() => navigate(`/sites/${site.id}/content`)}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
|
tone="neutral"
|
||||||
size="sm"
|
size="sm"
|
||||||
startIcon={<FileIcon className="w-4 h-4" />}
|
startIcon={<FileIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
@@ -459,9 +462,9 @@ export default function SiteList() {
|
|||||||
<Button
|
<Button
|
||||||
onClick={() => navigate(`/sites/${site.id}/settings`)}
|
onClick={() => navigate(`/sites/${site.id}/settings`)}
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
tone="neutral"
|
||||||
size="sm"
|
size="sm"
|
||||||
startIcon={<PlugInIcon className="w-4 h-4" />}
|
startIcon={<SettingsIcon className="w-4 h-4" />}
|
||||||
className="col-span-2"
|
|
||||||
>
|
>
|
||||||
Settings
|
Settings
|
||||||
</Button>
|
</Button>
|
||||||
@@ -497,33 +500,46 @@ export default function SiteList() {
|
|||||||
|
|
||||||
{/* Custom Header Actions - Add Site button and view toggle */}
|
{/* Custom Header Actions - Add Site button and view toggle */}
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<div className="flex-1">
|
<div className="flex items-center gap-3">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setShowWelcomeGuide(!showWelcomeGuide)}
|
onClick={() => setShowWelcomeGuide(!showWelcomeGuide)}
|
||||||
variant="success"
|
variant="primary"
|
||||||
|
tone="brand"
|
||||||
size="md"
|
size="md"
|
||||||
startIcon={<PlusIcon className="w-5 h-5" />}
|
startIcon={<PlusIcon className="w-5 h-5" />}
|
||||||
>
|
>
|
||||||
Add New Website
|
Add New Website
|
||||||
</Button>
|
</Button>
|
||||||
|
{viewType === 'grid' && (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="md"
|
||||||
|
onClick={() => setShowFilters(!showFilters)}
|
||||||
|
startIcon={<FilterIcon className="w-4 h-4" />}
|
||||||
|
>
|
||||||
|
{showFilters ? 'Hide Filters' : 'Show Filters'}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-1 bg-gray-100 dark:bg-gray-800 rounded-lg p-1">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setViewType('table')}
|
onClick={() => setViewType('table')}
|
||||||
variant={viewType === 'table' ? 'secondary' : 'ghost'}
|
variant={viewType === 'table' ? 'primary' : 'ghost'}
|
||||||
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
startIcon={<TableIcon className="w-4 h-4" />}
|
startIcon={<TableIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<span className="hidden sm:inline">Table</span>
|
Table
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setViewType('grid')}
|
onClick={() => setViewType('grid')}
|
||||||
variant={viewType === 'grid' ? 'secondary' : 'ghost'}
|
variant={viewType === 'grid' ? 'primary' : 'ghost'}
|
||||||
|
tone="brand"
|
||||||
size="sm"
|
size="sm"
|
||||||
startIcon={<GridIcon className="w-4 h-4" />}
|
startIcon={<GridIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<span className="hidden sm:inline">Grid</span>
|
Grid
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -600,59 +616,61 @@ export default function SiteList() {
|
|||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{/* Standard Filters Bar for Grid View - Matches Table View */}
|
{/* Standard Filters Bar for Grid View - Collapsible like table view */}
|
||||||
<div className="flex justify-center mb-4">
|
{showFilters && (
|
||||||
<div
|
<div className="flex justify-center mb-4">
|
||||||
className="w-[75%] igny8-filter-bar p-3 rounded-lg bg-transparent shadow-theme-md"
|
<div
|
||||||
>
|
className="w-full igny8-filter-bar p-3 rounded-lg bg-white dark:bg-gray-900 border border-gray-200 dark:border-gray-800 shadow-sm"
|
||||||
<div className="flex flex-nowrap gap-3 items-center justify-between w-full">
|
>
|
||||||
<div className="flex flex-nowrap gap-3 items-center flex-1 min-w-0 w-full">
|
<div className="flex flex-nowrap gap-3 items-center justify-between w-full">
|
||||||
<div className="flex-1 min-w-[200px]">
|
<div className="flex flex-nowrap gap-3 items-center flex-1 min-w-0 w-full">
|
||||||
<InputField
|
<div className="flex-1 min-w-[200px]">
|
||||||
type="text"
|
<InputField
|
||||||
placeholder="Search sites..."
|
type="text"
|
||||||
value={searchTerm}
|
placeholder="Search sites..."
|
||||||
onChange={(e) => setSearchTerm(e.target.value)}
|
value={searchTerm}
|
||||||
/>
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
</div>
|
/>
|
||||||
<div className="flex-1 min-w-[140px]">
|
</div>
|
||||||
<Select
|
<div className="flex-1 min-w-[140px]">
|
||||||
options={SITE_TYPES}
|
<Select
|
||||||
placeholder="Show All Types"
|
options={SITE_TYPES}
|
||||||
defaultValue={siteTypeFilter}
|
placeholder="Show All Types"
|
||||||
onChange={(val) => setSiteTypeFilter(val)}
|
defaultValue={siteTypeFilter}
|
||||||
/>
|
onChange={(val) => setSiteTypeFilter(val)}
|
||||||
</div>
|
/>
|
||||||
<div className="flex-1 min-w-[140px]">
|
</div>
|
||||||
<Select
|
<div className="flex-1 min-w-[140px]">
|
||||||
options={HOSTING_TYPES}
|
<Select
|
||||||
placeholder="Show All Hosting"
|
options={HOSTING_TYPES}
|
||||||
defaultValue={hostingTypeFilter}
|
placeholder="Show All Hosting"
|
||||||
onChange={(val) => setHostingTypeFilter(val)}
|
defaultValue={hostingTypeFilter}
|
||||||
/>
|
onChange={(val) => setHostingTypeFilter(val)}
|
||||||
</div>
|
/>
|
||||||
<div className="flex-1 min-w-[140px]">
|
</div>
|
||||||
<Select
|
<div className="flex-1 min-w-[140px]">
|
||||||
options={STATUS_OPTIONS}
|
<Select
|
||||||
placeholder="Show All Status"
|
options={STATUS_OPTIONS}
|
||||||
defaultValue={statusFilter}
|
placeholder="Show All Status"
|
||||||
onChange={(val) => setStatusFilter(val)}
|
defaultValue={statusFilter}
|
||||||
/>
|
onChange={(val) => setStatusFilter(val)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{hasActiveFilters && (
|
||||||
|
<Button
|
||||||
|
variant="secondary"
|
||||||
|
size="sm"
|
||||||
|
onClick={clearFilters}
|
||||||
|
className="flex-shrink-0"
|
||||||
|
>
|
||||||
|
Clear Filters
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{hasActiveFilters && (
|
|
||||||
<Button
|
|
||||||
variant="secondary"
|
|
||||||
size="sm"
|
|
||||||
onClick={clearFilters}
|
|
||||||
className="flex-shrink-0"
|
|
||||||
>
|
|
||||||
Clear Filters
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
{/* Grid View */}
|
{/* Grid View */}
|
||||||
{filteredSites.length === 0 ? (
|
{filteredSites.length === 0 ? (
|
||||||
@@ -665,7 +683,7 @@ export default function SiteList() {
|
|||||||
Clear Filters
|
Clear Filters
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button onClick={() => setShowWelcomeGuide(true)} variant="success" startIcon={<PlusIcon className="w-5 h-5" />}>
|
<Button onClick={() => setShowWelcomeGuide(true)} variant="primary" tone="success" startIcon={<PlusIcon className="w-5 h-5" />}>
|
||||||
Add Your First Site
|
Add Your First Site
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import PageMeta from '../../components/common/PageMeta';
|
|||||||
import PageHeader from '../../components/common/PageHeader';
|
import PageHeader from '../../components/common/PageHeader';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Button from '../../components/ui/button/Button';
|
import Button from '../../components/ui/button/Button';
|
||||||
|
import IconButton from '../../components/ui/button/IconButton';
|
||||||
import { useToast } from '../../components/ui/toast/ToastContainer';
|
import { useToast } from '../../components/ui/toast/ToastContainer';
|
||||||
import { fetchAPI } from '../../services/api';
|
import { fetchAPI } from '../../services/api';
|
||||||
import {
|
import {
|
||||||
@@ -94,13 +95,10 @@ const DraggablePageItem: React.FC<{
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<Button variant="outline" size="sm" onClick={() => onEdit(page.id)}>
|
<Button variant="outline" size="sm" onClick={() => onEdit(page.id)} startIcon={<PencilIcon className="w-4 h-4" />}>
|
||||||
<PencilIcon className="w-4 h-4 mr-1" />
|
|
||||||
Edit
|
Edit
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" onClick={() => onDelete(page.id)}>
|
<IconButton variant="ghost" size="sm" onClick={() => onDelete(page.id)} icon={<TrashBinIcon className="w-4 h-4" />} />
|
||||||
<TrashBinIcon className="w-4 h-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -330,8 +328,8 @@ export default function PageManager() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleBulkDelete}
|
onClick={handleBulkDelete}
|
||||||
className="text-error-600 hover:text-error-700"
|
className="text-error-600 hover:text-error-700"
|
||||||
|
startIcon={<TrashBinIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<TrashBinIcon className="w-4 h-4 mr-1" />
|
|
||||||
Delete Selected
|
Delete Selected
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="ghost" size="sm" onClick={() => setSelectedPages(new Set())}>
|
<Button variant="ghost" size="sm" onClick={() => setSelectedPages(new Set())}>
|
||||||
|
|||||||
@@ -271,16 +271,16 @@ export default function PostEditor() {
|
|||||||
variant={activeTab === 'content' ? 'primary' : 'ghost'}
|
variant={activeTab === 'content' ? 'primary' : 'ghost'}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('content')}
|
onClick={() => setActiveTab('content')}
|
||||||
|
startIcon={<FileTextIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<FileTextIcon className="w-4 h-4" />
|
|
||||||
Content
|
Content
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant={activeTab === 'taxonomy' ? 'primary' : 'ghost'}
|
variant={activeTab === 'taxonomy' ? 'primary' : 'ghost'}
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setActiveTab('taxonomy')}
|
onClick={() => setActiveTab('taxonomy')}
|
||||||
|
startIcon={<TagIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<TagIcon className="w-4 h-4" />
|
|
||||||
Taxonomy & Cluster
|
Taxonomy & Cluster
|
||||||
</Button>
|
</Button>
|
||||||
{content.id && (
|
{content.id && (
|
||||||
@@ -291,8 +291,8 @@ export default function PostEditor() {
|
|||||||
setActiveTab('validation');
|
setActiveTab('validation');
|
||||||
loadValidation();
|
loadValidation();
|
||||||
}}
|
}}
|
||||||
|
startIcon={<CheckCircleIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<CheckCircleIcon className="w-4 h-4" />
|
|
||||||
Validation
|
Validation
|
||||||
{validationResult && !validationResult.is_valid && (
|
{validationResult && !validationResult.is_valid && (
|
||||||
<span className="ml-2 px-2 py-0.5 text-xs bg-error-100 dark:bg-error-900 text-error-600 dark:text-error-400 rounded-full">
|
<span className="ml-2 px-2 py-0.5 text-xs bg-error-100 dark:bg-error-900 text-error-600 dark:text-error-400 rounded-full">
|
||||||
|
|||||||
@@ -283,15 +283,15 @@ export default function PublishingQueue() {
|
|||||||
<ButtonGroupItem
|
<ButtonGroupItem
|
||||||
isActive={viewMode === 'list'}
|
isActive={viewMode === 'list'}
|
||||||
onClick={() => setViewMode('list')}
|
onClick={() => setViewMode('list')}
|
||||||
|
startIcon={<ListIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ListIcon className="w-4 h-4 mr-1.5" />
|
|
||||||
List
|
List
|
||||||
</ButtonGroupItem>
|
</ButtonGroupItem>
|
||||||
<ButtonGroupItem
|
<ButtonGroupItem
|
||||||
isActive={viewMode === 'calendar'}
|
isActive={viewMode === 'calendar'}
|
||||||
onClick={() => setViewMode('calendar')}
|
onClick={() => setViewMode('calendar')}
|
||||||
|
startIcon={<CalendarIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<CalendarIcon className="w-4 h-4 mr-1.5" />
|
|
||||||
Calendar
|
Calendar
|
||||||
</ButtonGroupItem>
|
</ButtonGroupItem>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import {
|
|||||||
} from '../../services/api';
|
} from '../../services/api';
|
||||||
import WordPressIntegrationForm from '../../components/sites/WordPressIntegrationForm';
|
import WordPressIntegrationForm from '../../components/sites/WordPressIntegrationForm';
|
||||||
import { integrationApi, SiteIntegration } from '../../services/integration.api';
|
import { integrationApi, SiteIntegration } from '../../services/integration.api';
|
||||||
import { GridIcon, PlugInIcon, PaperPlaneIcon, DocsIcon, BoltIcon, FileIcon, ChevronDownIcon, CloseIcon, PlusIcon } from '../../icons';
|
import { GridIcon, PlugInIcon, PaperPlaneIcon, DocsIcon, BoltIcon, FileIcon, ChevronDownIcon, CloseIcon, PlusIcon, RefreshCwIcon } from '../../icons';
|
||||||
import Badge from '../../components/ui/badge/Badge';
|
import Badge from '../../components/ui/badge/Badge';
|
||||||
import { Dropdown } from '../../components/ui/dropdown/Dropdown';
|
import { Dropdown } from '../../components/ui/dropdown/Dropdown';
|
||||||
import { DropdownItem } from '../../components/ui/dropdown/DropdownItem';
|
import { DropdownItem } from '../../components/ui/dropdown/DropdownItem';
|
||||||
@@ -621,8 +621,8 @@ export default function SiteSettings() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<GridIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<GridIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
General
|
General
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -636,8 +636,8 @@ export default function SiteSettings() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<PlugInIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PlugInIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Integrations
|
Integrations
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -651,8 +651,8 @@ export default function SiteSettings() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<PaperPlaneIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PaperPlaneIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Publishing
|
Publishing
|
||||||
</Button>
|
</Button>
|
||||||
{(wordPressIntegration || site?.wp_url || site?.wp_api_key || site?.hosting_type === 'wordpress') && (
|
{(wordPressIntegration || site?.wp_url || site?.wp_api_key || site?.hosting_type === 'wordpress') && (
|
||||||
@@ -667,8 +667,8 @@ export default function SiteSettings() {
|
|||||||
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
? 'border-brand-500 text-brand-600 dark:text-brand-400'
|
||||||
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
: 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300'
|
||||||
}`}
|
}`}
|
||||||
|
startIcon={<FileIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<FileIcon className="w-4 h-4 inline mr-2" />
|
|
||||||
Content Types
|
Content Types
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -951,21 +951,9 @@ export default function SiteSettings() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={syncLoading || !(wordPressIntegration || site?.wp_url || site?.wp_api_key || site?.hosting_type === 'wordpress')}
|
disabled={syncLoading || !(wordPressIntegration || site?.wp_url || site?.wp_api_key || site?.hosting_type === 'wordpress')}
|
||||||
onClick={handleManualSync}
|
onClick={handleManualSync}
|
||||||
className="flex items-center gap-2"
|
startIcon={syncLoading ? <RefreshCwIcon className="w-4 h-4 animate-spin" /> : <RefreshCwIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
{syncLoading ? (
|
{syncLoading ? 'Syncing...' : 'Sync Structure'}
|
||||||
<>
|
|
||||||
<div className="inline-block animate-spin rounded-full h-4 w-4 border-b-2 border-current"></div>
|
|
||||||
<span>Syncing...</span>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
|
||||||
</svg>
|
|
||||||
<span>Sync Structure</span>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -252,8 +252,8 @@ export default function SyncDashboard() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleSync('to_external')}
|
onClick={() => handleSync('to_external')}
|
||||||
disabled={syncing || !integration.sync_enabled}
|
disabled={syncing || !integration.sync_enabled}
|
||||||
|
startIcon={<ArrowRightIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ArrowRightIcon className="w-4 h-4 mr-1" />
|
|
||||||
Sync to WordPress
|
Sync to WordPress
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
@@ -261,8 +261,8 @@ export default function SyncDashboard() {
|
|||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => handleSync('from_external')}
|
onClick={() => handleSync('from_external')}
|
||||||
disabled={syncing || !integration.sync_enabled}
|
disabled={syncing || !integration.sync_enabled}
|
||||||
|
startIcon={<ArrowRightIcon className="w-4 h-4 rotate-180" />}
|
||||||
>
|
>
|
||||||
<ArrowRightIcon className="w-4 h-4 mr-1 rotate-180" />
|
|
||||||
Sync from WordPress
|
Sync from WordPress
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -107,8 +107,7 @@ export default function AuthorProfiles() {
|
|||||||
breadcrumb="Thinker / Author Profiles"
|
breadcrumb="Thinker / Author Profiles"
|
||||||
/>
|
/>
|
||||||
<div className="mb-6 flex justify-between items-center">
|
<div className="mb-6 flex justify-between items-center">
|
||||||
<Button onClick={handleCreate} variant="primary">
|
<Button onClick={handleCreate} variant="primary" startIcon={<PlusIcon className="w-4 h-4" />}>
|
||||||
<PlusIcon className="w-4 h-4 mr-2" />
|
|
||||||
Create Profile
|
Create Profile
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
SaveIcon, Loader2Icon, SettingsIcon, UserIcon, UsersIcon, UserIcon as UserPlusIcon, LockIcon, LockIcon as ShieldIcon, XIcon
|
SaveIcon, Loader2Icon, SettingsIcon, UserIcon, UsersIcon, UserPlusIcon, LockIcon, LockIcon as ShieldIcon, XIcon
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Button from '../../components/ui/button/Button';
|
import Button from '../../components/ui/button/Button';
|
||||||
@@ -605,7 +605,7 @@ export default function AccountSettingsPage() {
|
|||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
tone="brand"
|
tone="brand"
|
||||||
startIcon={<UserPlus className="w-4 h-4" />}
|
startIcon={<UserPlusIcon className="w-4 h-4" />}
|
||||||
onClick={() => setShowInviteModal(true)}
|
onClick={() => setShowInviteModal(true)}
|
||||||
>
|
>
|
||||||
Invite Someone
|
Invite Someone
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback } from 'react';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
SaveIcon, Loader2Icon, ImageIcon, FileTextIcon, PaperPlaneIcon as SendIcon, SettingsIcon
|
SaveIcon, Loader2Icon, ImageIcon, FileTextIcon, PaperPlaneIcon, SettingsIcon
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
import Button from '../../components/ui/button/Button';
|
import Button from '../../components/ui/button/Button';
|
||||||
|
|||||||
@@ -209,9 +209,8 @@ export default function NotificationsPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={() => setShowFilters(!showFilters)}
|
onClick={() => setShowFilters(!showFilters)}
|
||||||
className="flex items-center gap-2"
|
startIcon={<FilterIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<FilterIcon className="w-4 h-4" />
|
|
||||||
Filters
|
Filters
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
@@ -220,9 +219,8 @@ export default function NotificationsPage() {
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
size="sm"
|
size="sm"
|
||||||
onClick={handleMarkAllRead}
|
onClick={handleMarkAllRead}
|
||||||
className="flex items-center gap-2"
|
startIcon={<CheckCheckIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<CheckCheckIcon className="w-4 h-4" />
|
|
||||||
Mark All Read
|
Mark All Read
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
import { useState, useEffect, useRef } from 'react';
|
import { useState, useEffect, useRef } from 'react';
|
||||||
import { Link, useLocation } from 'react-router-dom';
|
import { Link, useLocation } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
CreditCardIcon, BoxIcon as PackageIcon, TrendingUpIcon, FileTextIcon, WalletIcon, ArrowUpIcon as ArrowUpCircleIcon,
|
CreditCardIcon, BoxIcon as PackageIcon, TrendingUpIcon, FileTextIcon, WalletIcon, ArrowUpIcon,
|
||||||
Loader2Icon, AlertCircleIcon, CheckCircleIcon, DownloadIcon, ZapIcon, GlobeIcon, UsersIcon, XIcon
|
Loader2Icon, AlertCircleIcon, CheckCircleIcon, DownloadIcon, ZapIcon, GlobeIcon, UsersIcon, XIcon
|
||||||
} from '../../icons';
|
} from '../../icons';
|
||||||
import { Card } from '../../components/ui/card';
|
import { Card } from '../../components/ui/card';
|
||||||
|
|||||||
@@ -763,8 +763,8 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
|
|||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={onBack}
|
onClick={onBack}
|
||||||
|
startIcon={<ArrowLeftIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon className="w-4 h-4" />
|
|
||||||
Back to Content List
|
Back to Content List
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -829,8 +829,8 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={onBack}
|
onClick={onBack}
|
||||||
className="mb-6"
|
className="mb-6"
|
||||||
|
startIcon={<ArrowLeftIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ArrowLeftIcon className="w-4 h-4" />
|
|
||||||
Back to Content List
|
Back to Content List
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
@@ -1030,16 +1030,16 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
|
|||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||||
|
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PencilIcon className="w-4 h-4" />
|
|
||||||
Edit Content
|
Edit Content
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
tone="brand"
|
tone="brand"
|
||||||
onClick={() => navigate(`/writer/images?contentId=${content.id}`)}
|
onClick={() => navigate(`/writer/images?contentId=${content.id}`)}
|
||||||
|
startIcon={<ImageIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<ImageIcon className="w-4 h-4" />
|
|
||||||
Generate Images
|
Generate Images
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
@@ -1051,16 +1051,16 @@ export default function ContentViewTemplate({ content, loading, onBack }: Conten
|
|||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
onClick={() => navigate(`/sites/${content.site_id}/posts/${content.id}/edit`)}
|
||||||
|
startIcon={<PencilIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<PencilIcon className="w-4 h-4" />
|
|
||||||
Edit Content
|
Edit Content
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="primary"
|
variant="primary"
|
||||||
tone="brand"
|
tone="brand"
|
||||||
onClick={() => navigate(`/writer/published?contentId=${content.id}&action=publish`)}
|
onClick={() => navigate(`/writer/published?contentId=${content.id}&action=publish`)}
|
||||||
|
startIcon={<BoltIcon className="w-4 h-4" />}
|
||||||
>
|
>
|
||||||
<BoltIcon className="w-4 h-4" />
|
|
||||||
Publish
|
Publish
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
|
|||||||