/**
* TenantDetail Component
*
* Read-only detailed view of a single tenant and its integration keys.
* Keys are immutable — display and copy only, no regeneration.
*
* @component
* @layer Presentation
*/
import { useState, useEffect } from 'react';
import { Copy, CheckCircle, Shield, FlaskConical, Megaphone } from 'lucide-react';
import { Link } from '@tanstack/react-router';
import { Modal } from '@/components/shared';
import { Tenant, TenantStatus } from '@/domain/entities/Tenant';
import { IntegrationKey, KeyEnvironment } from '@/domain/entities/IntegrationKey';
import { useIntegrationKeys } from '@/features/integration-keys/hooks';
import { useTranslation } from '@/i18n/TranslationProvider';
import { isWordpressEmbed, wpGetSettings, wpSaveDockPosition } from '@/lib/wp-integration';
interface TenantDetailProps {
tenant: Tenant;
onClose: () => void;
onUpdate?: () => void;
}
/** WP plugin exposes 3 positions (same set as Shopify app). */
const WP_DOCK_OPTIONS = [
{ value: 'top-right', labelKey: 'topRight' as const },
{ value: 'middle-right', labelKey: 'middleRight' as const },
{ value: 'bottom-right', labelKey: 'bottomRight' as const },
];
/**
* Single Key Block Component (read-only)
*/
function KeyBlock({
label,
description,
icon,
integrationKey,
colorClass,
}: {
label: string;
description: string;
icon: React.ReactNode;
integrationKey: IntegrationKey | null;
colorClass: { bg: string; border: string; text: string; badge: string };
}) {
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const displayValue = integrationKey?.key || t('tenants.detail.notGenerated');
const canCopy = !!integrationKey;
const handleCopy = async () => {
if (!canCopy) return;
await navigator.clipboard.writeText(integrationKey!.key);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
{icon}
{label}
{integrationKey?.isActive() && (
{t('common.active')}
)}
{displayValue}
{canCopy && (
)}
{description}
{integrationKey?.environment === KeyEnvironment.TESTING && (
{t('tenants.detail.usage', { used: integrationKey.usageCount })}
)}
);
}
/**
* WP Position Picker — embedded dashboard only.
* Replaces both the integration-key cards and the snippet constructor when
* running inside the WP plugin admin. The WordPress plugin handles key
* distribution and snippet injection automatically; the only thing the
* merchant still needs to choose is where the widget sits on the page.
* Layout + options mirror the Shopify app Appearance page.
*/
function WpPositionPicker() {
const { t } = useTranslation();
const [position, setPosition] = useState('bottom-right');
const [saving, setSaving] = useState(false);
const [loaded, setLoaded] = useState(false);
useEffect(() => {
let cancelled = false;
wpGetSettings().then((settings) => {
if (cancelled) return;
const current = settings?.twwim_dock_position;
if (current && WP_DOCK_OPTIONS.some((o) => o.value === current)) {
setPosition(current);
}
setLoaded(true);
});
return () => { cancelled = true; };
}, []);
const handlePick = async (next: string) => {
if (saving || next === position) return;
const previous = position;
setPosition(next);
setSaving(true);
const ok = await wpSaveDockPosition(next);
setSaving(false);
if (!ok) setPosition(previous);
};
return (
{t('tenants.detail.widgetPosition')}
{t('tenants.detail.positionDesc')}
{WP_DOCK_OPTIONS.map((opt) => {
const active = position === opt.value;
return (
);
})}
);
}
/**
* TenantDetail Component
*/
export function TenantDetail({ tenant, onClose }: TenantDetailProps) {
const { t } = useTranslation();
const { keys: integrationKeys } = useIntegrationKeys({ tenantId: tenant.id });
const wpEmbed = isWordpressEmbed();
// Find keys by environment
const productionKey = integrationKeys.find(
(k) => k.environment === KeyEnvironment.PRODUCTION && k.isActive()
) || null;
const testingKey = integrationKeys.find(
(k) => k.environment === KeyEnvironment.TESTING && k.isActive()
) || null;
return (
{t('common.close')}
}
>
{/* Tenant Info */}
{tenant.status}
{tenant.domain}
{wpEmbed ? (
) : (
<>
{/* Integration Keys Section */}
{t('tenants.detail.integrationKeys')}
{t('tenants.detail.keysDescription')}
}
integrationKey={productionKey}
colorClass={{
bg: 'bg-blue-50/50',
border: 'border-blue-200',
text: 'text-blue-800',
badge: 'bg-blue-100 text-blue-800',
}}
/>
}
integrationKey={testingKey}
colorClass={{
bg: 'bg-purple-50/50',
border: 'border-purple-200',
text: 'text-purple-800',
badge: 'bg-purple-100 text-purple-800',
}}
/>
{/* Plugin settings — per-tenant widget configuration link.
The snippet builder (positioning + generated