All files / src/components/company-profile CertificationsSection.tsx

100% Statements 12/12
100% Branches 22/22
100% Functions 7/7
100% Lines 11/11

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 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133                        8x                                     267x 267x 77x   267x 77x     267x       267x                                                         77x                                                                           2x             3x                              
import { Award, Plus, Edit2, Trash2 } from "lucide-react";
import {
	Card,
	CardContent,
	CardHeader,
	CardTitle,
	CardDescription,
} from "../ui/card";
import { Button } from "../ui/button";
import type { ProCertification } from "../../lib/api";
import { getImageUrl } from "../../lib/api/base";
 
const PER_TYPE_LIMIT = 10;
 
interface CertificationsSectionProps {
	certifications: ProCertification[];
	onAdd: () => void;
	onEdit: (certification: ProCertification) => void;
	onDelete: (id: number) => void;
	/** #424: gate Add/Edit/Delete on caller's edit permission. Default
	 * `true` preserves admin-page behavior. */
	canEdit?: boolean;
}
 
export function CertificationsSection({
	certifications,
	onAdd,
	onEdit,
	onDelete,
	canEdit = true,
}: CertificationsSectionProps) {
	const awardCount = certifications.filter((c) => c.type === "award").length;
	const certCount = certifications.filter(
		(c) => c.type === "certification",
	).length;
	const membershipCount = certifications.filter(
		(c) => c.type === "membership",
	).length;
	const allTypesFull =
		awardCount >= PER_TYPE_LIMIT &&
		certCount >= PER_TYPE_LIMIT &&
		membershipCount >= PER_TYPE_LIMIT;
 
	return (
		<Card>
			<CardHeader className="flex flex-row items-center justify-between">
				<div>
					<CardTitle className="flex items-center gap-2">
						<Award className="h-5 w-5" />
						Certifications & Awards
					</CardTitle>
					<CardDescription>
						Awards {awardCount}/{PER_TYPE_LIMIT} • Certifications {certCount}/
						{PER_TYPE_LIMIT} • Memberships {membershipCount}/{PER_TYPE_LIMIT}
					</CardDescription>
				</div>
				{canEdit && !allTypesFull && (
					<Button variant="outline" size="sm" onClick={onAdd}>
						<Plus className="h-4 w-4 mr-1" />
						Add
					</Button>
				)}
			</CardHeader>
			<CardContent>
				{certifications.length === 0 ? (
					<div className="text-center py-8 text-foreground-muted">
						<Award className="h-12 w-12 mx-auto mb-2 text-foreground-subtle" />
						<p>No certifications or awards added yet</p>
					</div>
				) : (
					<div className="space-y-4">
						{certifications.map((cert) => (
							<div
								key={cert.id}
								className="flex items-start justify-between p-4 border rounded-lg"
							>
								<div className="flex items-start gap-3 min-w-0">
									{cert.imageUrl ? (
										<img
											src={getImageUrl(cert.imageUrl)}
											alt={cert.title}
											className="h-14 w-14 rounded-md object-cover border border-default shrink-0"
										/>
									) : (
										<div className="h-14 w-14 rounded-md bg-background-muted flex items-center justify-center text-xl shrink-0">
											{cert.type === "award"
												? "🏆"
												: cert.type === "membership"
													? "🤝"
													: "📜"}
										</div>
									)}
									<div className="min-w-0">
										<div className="flex items-center gap-2 flex-wrap">
											<h4 className="font-medium truncate">{cert.title}</h4>
											<span className="px-2 py-0.5 text-xs rounded bg-background-muted text-foreground-muted capitalize">
												{cert.type}
											</span>
										</div>
										<p className="text-sm text-foreground-muted">
											{cert.issuer && `By ${cert.issuer}`}
											{cert.issuer && cert.year && " • "}
											{cert.year}
										</p>
									</div>
								</div>
								{canEdit && (
									<div className="flex gap-2 shrink-0">
										<button
											type="button"
											onClick={() => onEdit(cert)}
											className="p-1 text-foreground-subtle hover:text-info"
										>
											<Edit2 className="h-4 w-4" />
										</button>
										<button
											type="button"
											onClick={() => onDelete(cert.id)}
											className="p-1 text-foreground-subtle hover:text-error"
										>
											<Trash2 className="h-4 w-4" />
										</button>
									</div>
								)}
							</div>
						))}
					</div>
				)}
			</CardContent>
		</Card>
	);
}