Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | 18x 18x 18x 18x 2x 2x 1x 18x 4x 1x | import { useState } from "react";
import { Search } from "lucide-react";
import type { Pro } from "../../../lib/api";
import { useUpdateProProfile } from "../../../hooks/mutations/useProMutations";
import { SectionCard } from "../SectionCard";
import { ReadOnlyField } from "../ReadOnlyField";
import { SeoModal } from "../modals/SeoModal";
interface SeoTabProps {
pro: Pro;
proId: string;
/** #362: hide Edit affordances for roles the backend will 403 on save. */
canEdit?: boolean;
}
export function SeoTab({ pro, proId, canEdit = true }: SeoTabProps) {
const updateProfile = useUpdateProProfile(proId);
const [editingSection, setEditingSection] = useState(false);
const isSeoEmpty = !pro.metaTitle && !pro.metaDescription && !pro.ogImage;
const handleSave = async (data: Partial<Pro>) => {
try {
await updateProfile.mutateAsync(data);
setEditingSection(false);
} catch {
// Error toast shown by mutation onError callback
}
};
return (
<div className="space-y-6">
{/* SEO Settings */}
<SectionCard
title="SEO Settings"
icon={<Search className="h-5 w-5" />}
onEdit={canEdit ? () => setEditingSection(true) : undefined}
isEmpty={isSeoEmpty}
>
<dl className="grid grid-cols-1 gap-4">
<ReadOnlyField label="SEO Title" value={pro.metaTitle} />
<ReadOnlyField label="SEO Description" value={pro.metaDescription} />
<ReadOnlyField label="Social Share Image" value={pro.ogImage} type="image" />
</dl>
</SectionCard>
{/* Modal */}
{editingSection && (
<SeoModal
pro={pro}
onSave={handleSave}
onClose={() => setEditingSection(false)}
/>
)}
</div>
);
}
|