COmpoeentes standardization 2

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

View File

@@ -28,7 +28,9 @@ import {
PageIcon,
TableIcon,
ChevronDownIcon,
ChevronUpIcon
ChevronUpIcon,
FilterIcon,
BoxCubeIcon as SettingsIcon
} from '../../icons';
import {
fetchSites,
@@ -67,6 +69,7 @@ export default function SiteList() {
const [loading, setLoading] = useState(true);
const [viewType, setViewType] = useState<ViewType>('grid');
const [showWelcomeGuide, setShowWelcomeGuide] = useState(false);
const [showFilters, setShowFilters] = useState(false);
// Site Management Modals
const [selectedSite, setSelectedSite] = useState<Site | null>(null);
@@ -410,19 +413,16 @@ export default function SiteList() {
{site.domain}
</p>
)}
<div className="flex items-center gap-2 mb-2 flex-wrap">
<SiteTypeBadge hostingType={site.hosting_type} />
{/* Centered badges with uniform size */}
<div className="flex items-center justify-center gap-2 mb-2 flex-wrap">
<SiteTypeBadge hostingType={site.hosting_type} size="sm" />
{site.industry_name && (
<Badge variant="light" color="info" className="text-xs">
<Badge variant="soft" color="warning" size="sm">
{site.industry_name}
</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>
{/* Status badge and toggle in top right */}
<div className="absolute top-4 right-4 flex items-center gap-3">
<Switch
checked={site.is_active}
@@ -430,19 +430,21 @@ export default function SiteList() {
disabled={togglingSiteId === site.id}
/>
<Badge
variant={site.is_active ? "soft" : "light"}
color={site.is_active ? "success" : "gray"}
size="sm"
variant="solid"
color={site.is_active ? "success" : "error"}
size="xs"
>
{site.is_active ? 'Active' : 'Inactive'}
</Badge>
</div>
</div>
{/* Centered button row */}
<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
onClick={() => navigate(`/sites/${site.id}`)}
variant="primary"
tone="brand"
size="sm"
startIcon={<EyeIcon className="w-4 h-4" />}
>
@@ -451,6 +453,7 @@ export default function SiteList() {
<Button
onClick={() => navigate(`/sites/${site.id}/content`)}
variant="secondary"
tone="neutral"
size="sm"
startIcon={<FileIcon className="w-4 h-4" />}
>
@@ -459,9 +462,9 @@ export default function SiteList() {
<Button
onClick={() => navigate(`/sites/${site.id}/settings`)}
variant="outline"
tone="neutral"
size="sm"
startIcon={<PlugInIcon className="w-4 h-4" />}
className="col-span-2"
startIcon={<SettingsIcon className="w-4 h-4" />}
>
Settings
</Button>
@@ -497,33 +500,46 @@ export default function SiteList() {
{/* Custom Header Actions - Add Site button and view toggle */}
<div className="flex items-center justify-between mb-6">
<div className="flex-1">
<div className="flex items-center gap-3">
<Button
onClick={() => setShowWelcomeGuide(!showWelcomeGuide)}
variant="success"
variant="primary"
tone="brand"
size="md"
startIcon={<PlusIcon className="w-5 h-5" />}
>
Add New Website
</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 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
onClick={() => setViewType('table')}
variant={viewType === 'table' ? 'secondary' : 'ghost'}
variant={viewType === 'table' ? 'primary' : 'ghost'}
tone="brand"
size="sm"
startIcon={<TableIcon className="w-4 h-4" />}
>
<span className="hidden sm:inline">Table</span>
Table
</Button>
<Button
onClick={() => setViewType('grid')}
variant={viewType === 'grid' ? 'secondary' : 'ghost'}
variant={viewType === 'grid' ? 'primary' : 'ghost'}
tone="brand"
size="sm"
startIcon={<GridIcon className="w-4 h-4" />}
>
<span className="hidden sm:inline">Grid</span>
Grid
</Button>
</div>
</div>
@@ -600,59 +616,61 @@ export default function SiteList() {
/>
) : (
<>
{/* Standard Filters Bar for Grid View - Matches Table View */}
<div className="flex justify-center mb-4">
<div
className="w-[75%] igny8-filter-bar p-3 rounded-lg bg-transparent shadow-theme-md"
>
<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-1 min-w-[200px]">
<InputField
type="text"
placeholder="Search sites..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={SITE_TYPES}
placeholder="Show All Types"
defaultValue={siteTypeFilter}
onChange={(val) => setSiteTypeFilter(val)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={HOSTING_TYPES}
placeholder="Show All Hosting"
defaultValue={hostingTypeFilter}
onChange={(val) => setHostingTypeFilter(val)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={STATUS_OPTIONS}
placeholder="Show All Status"
defaultValue={statusFilter}
onChange={(val) => setStatusFilter(val)}
/>
{/* Standard Filters Bar for Grid View - Collapsible like table view */}
{showFilters && (
<div className="flex justify-center mb-4">
<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-1 min-w-[200px]">
<InputField
type="text"
placeholder="Search sites..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={SITE_TYPES}
placeholder="Show All Types"
defaultValue={siteTypeFilter}
onChange={(val) => setSiteTypeFilter(val)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={HOSTING_TYPES}
placeholder="Show All Hosting"
defaultValue={hostingTypeFilter}
onChange={(val) => setHostingTypeFilter(val)}
/>
</div>
<div className="flex-1 min-w-[140px]">
<Select
options={STATUS_OPTIONS}
placeholder="Show All Status"
defaultValue={statusFilter}
onChange={(val) => setStatusFilter(val)}
/>
</div>
</div>
{hasActiveFilters && (
<Button
variant="secondary"
size="sm"
onClick={clearFilters}
className="flex-shrink-0"
>
Clear Filters
</Button>
)}
</div>
{hasActiveFilters && (
<Button
variant="secondary"
size="sm"
onClick={clearFilters}
className="flex-shrink-0"
>
Clear Filters
</Button>
)}
</div>
</div>
</div>
)}
{/* Grid View */}
{filteredSites.length === 0 ? (
@@ -665,7 +683,7 @@ export default function SiteList() {
Clear Filters
</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
</Button>
)}

View File

@@ -11,6 +11,7 @@ import PageMeta from '../../components/common/PageMeta';
import PageHeader from '../../components/common/PageHeader';
import { Card } from '../../components/ui/card';
import Button from '../../components/ui/button/Button';
import IconButton from '../../components/ui/button/IconButton';
import { useToast } from '../../components/ui/toast/ToastContainer';
import { fetchAPI } from '../../services/api';
import {
@@ -94,13 +95,10 @@ const DraggablePageItem: React.FC<{
</div>
</div>
<div className="flex gap-2">
<Button variant="outline" size="sm" onClick={() => onEdit(page.id)}>
<PencilIcon className="w-4 h-4 mr-1" />
<Button variant="outline" size="sm" onClick={() => onEdit(page.id)} startIcon={<PencilIcon className="w-4 h-4" />}>
Edit
</Button>
<Button variant="ghost" size="sm" onClick={() => onDelete(page.id)}>
<TrashBinIcon className="w-4 h-4" />
</Button>
<IconButton variant="ghost" size="sm" onClick={() => onDelete(page.id)} icon={<TrashBinIcon className="w-4 h-4" />} />
</div>
</div>
);
@@ -330,8 +328,8 @@ export default function PageManager() {
size="sm"
onClick={handleBulkDelete}
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
</Button>
<Button variant="ghost" size="sm" onClick={() => setSelectedPages(new Set())}>

View File

@@ -271,16 +271,16 @@ export default function PostEditor() {
variant={activeTab === 'content' ? 'primary' : 'ghost'}
size="sm"
onClick={() => setActiveTab('content')}
startIcon={<FileTextIcon className="w-4 h-4" />}
>
<FileTextIcon className="w-4 h-4" />
Content
</Button>
<Button
variant={activeTab === 'taxonomy' ? 'primary' : 'ghost'}
size="sm"
onClick={() => setActiveTab('taxonomy')}
startIcon={<TagIcon className="w-4 h-4" />}
>
<TagIcon className="w-4 h-4" />
Taxonomy & Cluster
</Button>
{content.id && (
@@ -291,8 +291,8 @@ export default function PostEditor() {
setActiveTab('validation');
loadValidation();
}}
startIcon={<CheckCircleIcon className="w-4 h-4" />}
>
<CheckCircleIcon className="w-4 h-4" />
Validation
{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">

View File

@@ -283,15 +283,15 @@ export default function PublishingQueue() {
<ButtonGroupItem
isActive={viewMode === 'list'}
onClick={() => setViewMode('list')}
startIcon={<ListIcon className="w-4 h-4" />}
>
<ListIcon className="w-4 h-4 mr-1.5" />
List
</ButtonGroupItem>
<ButtonGroupItem
isActive={viewMode === 'calendar'}
onClick={() => setViewMode('calendar')}
startIcon={<CalendarIcon className="w-4 h-4" />}
>
<CalendarIcon className="w-4 h-4 mr-1.5" />
Calendar
</ButtonGroupItem>
</ButtonGroup>

View File

@@ -27,7 +27,7 @@ import {
} from '../../services/api';
import WordPressIntegrationForm from '../../components/sites/WordPressIntegrationForm';
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 { Dropdown } from '../../components/ui/dropdown/Dropdown';
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-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
</Button>
<Button
@@ -636,8 +636,8 @@ export default function SiteSettings() {
? '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'
}`}
startIcon={<PlugInIcon className="w-4 h-4" />}
>
<PlugInIcon className="w-4 h-4 inline mr-2" />
Integrations
</Button>
<Button
@@ -651,8 +651,8 @@ export default function SiteSettings() {
? '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'
}`}
startIcon={<PaperPlaneIcon className="w-4 h-4" />}
>
<PaperPlaneIcon className="w-4 h-4 inline mr-2" />
Publishing
</Button>
{(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-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
</Button>
)}
@@ -951,21 +951,9 @@ export default function SiteSettings() {
variant="outline"
disabled={syncLoading || !(wordPressIntegration || site?.wp_url || site?.wp_api_key || site?.hosting_type === 'wordpress')}
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 ? (
<>
<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>
</>
)}
{syncLoading ? 'Syncing...' : 'Sync Structure'}
</Button>
</div>

View File

@@ -252,8 +252,8 @@ export default function SyncDashboard() {
size="sm"
onClick={() => handleSync('to_external')}
disabled={syncing || !integration.sync_enabled}
startIcon={<ArrowRightIcon className="w-4 h-4" />}
>
<ArrowRightIcon className="w-4 h-4 mr-1" />
Sync to WordPress
</Button>
<Button
@@ -261,8 +261,8 @@ export default function SyncDashboard() {
size="sm"
onClick={() => handleSync('from_external')}
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
</Button>
</div>