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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | 3x 165x 165x 165x 165x 7x 6x | import { memo } from "react";
import { Link } from "@tanstack/react-router";
import { Star, StarOff, Eye } from "lucide-react";
import { Button } from "../../ui/button";
import type { Pro } from "../../../lib/api";
interface ProTableRowProps {
pro: Pro;
onToggleFeatured: (pro: Pro) => void;
onUpdateStatus: (
pro: Pro,
status: "draft" | "published" | "archived",
) => void;
}
export const ProTableRow = memo(function ProTableRow({
pro,
onToggleFeatured,
onUpdateStatus,
}: ProTableRowProps) {
// #360: admins reported "blank" rows at the top of the Pros table.
// These were pros whose onboarding flow never filled businessName (draft
// state), so the DB stores an empty string. Rather than render nothing —
// which makes the row unclickable-looking — fall back to the pro's slug
// or id in muted type so admins can still identify and open the record.
const trimmedName = pro.businessName?.trim();
const displayName = trimmedName || pro.slug || pro.id;
const isMissingName = !trimmedName;
return (
<tr className="border-b border-border-default last:border-0 hover:bg-background-muted">
<td className="py-4">
<Link
to={`/admin/pros/${pro.id}`}
className={`font-medium hover:text-primary-600 ${
isMissingName
? "text-foreground-muted italic"
: "text-foreground-default"
}`}
>
{displayName}
{isMissingName && (
<span className="ml-1 text-xs text-foreground-subtle not-italic">
(no business name)
</span>
)}
</Link>
</td>
<td className="py-4 text-foreground-muted">
{pro.primaryLocality
? `${pro.primaryLocality.name}, ${pro.primaryLocality.city}`
: "-"}
</td>
<td className="py-4">
<select
value={pro.status}
onChange={(e) =>
onUpdateStatus(
pro,
e.target.value as "draft" | "published" | "archived",
)
}
className={`text-xs font-medium rounded-full px-2 py-1 border-0 cursor-pointer focus:outline-none focus:ring-2 focus:ring-primary-500/40 focus:border-primary-500 ${
pro.status === "published"
? "bg-success-light text-success"
: pro.status === "draft"
? "bg-warning-light text-warning"
: "bg-background-muted text-foreground-muted"
}`}
>
<option value="draft">Draft</option>
<option value="published">Published</option>
<option value="archived">Archived</option>
</select>
</td>
<td className="py-4">
<button
type="button"
onClick={() => onToggleFeatured(pro)}
className="p-1 hover:bg-background-muted rounded"
title={pro.isFeatured ? "Remove from featured" : "Add to featured"}
>
{pro.isFeatured ? (
<Star className="h-5 w-5 text-yellow-500 fill-yellow-500" />
) : (
<StarOff className="h-5 w-5 text-foreground-subtle" />
)}
</button>
</td>
<td className="py-4">
<div className="flex items-center gap-2">
<Link to={`/admin/pros/${pro.id}`}>
<Button variant="ghost" size="sm">
<Eye className="h-4 w-4" />
</Button>
</Link>
</div>
</td>
</tr>
);
});
|