Certification.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. import React, { useState } from "react";
  2. import CertificateModal from "@/components/CertificateModal";
  3. type Category = {
  4. id: string;
  5. label: string;
  6. };
  7. type Certificate = {
  8. id: number;
  9. category: string;
  10. title: string;
  11. imageUrl: string;
  12. };
  13. const categories: Category[] = [
  14. { id: "all", label: "전체" },
  15. { id: "eval", label: "시공능력 확인서, 평가액" },
  16. { id: "design", label: "상표 디자인" },
  17. { id: "patent", label: "특허" },
  18. { id: "award", label: "상장" },
  19. { id: "iso", label: "ISO 인증서" },
  20. ];
  21. // FIXME : public/image 하위에 cert 폴더 만들고, 인증서 쭈셔넣으면 됨
  22. const certificates: Certificate[] = [
  23. {
  24. id: 1,
  25. category: "eval",
  26. title: "시공능력 확인서 2024",
  27. imageUrl: "https://placehold.co/564x788?text=Eval+1",
  28. },
  29. {
  30. id: 2,
  31. category: "design",
  32. title: "상표등록증",
  33. imageUrl: "https://placehold.co/564x788?text=Design+1",
  34. },
  35. ];
  36. const Certification: React.FC = () => {
  37. const [activeTab, setActiveTab] = useState<string>("all");
  38. const [selectedCert, setSelectedCert] = useState<Certificate | null>(null);
  39. const filteredCerts =
  40. activeTab === "all"
  41. ? certificates
  42. : certificates.filter((cert) => cert.category === activeTab);
  43. return (
  44. <div className="max-w-7xl mx-auto px-4 sm:px-6 py-8 sm:py-10">
  45. {/* 탭 */}
  46. <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-6 gap-1 mb-6 sm:mb-8 border border-gray-200 rounded overflow-hidden">
  47. {categories.map((category, index) => (
  48. <div
  49. key={category.id}
  50. onClick={() => setActiveTab(category.id)}
  51. className={`h-10 sm:h-11 flex items-center justify-center cursor-pointer transition-colors px-2 ${
  52. activeTab === category.id
  53. ? "bg-blue-900 text-white font-semibold text-xs sm:text-sm"
  54. : "border-l border-zinc-300/75 text-black/70 text-xs font-medium hover:bg-gray-50"
  55. } ${index === 0 ? 'border-l-0' : ''}`}
  56. >
  57. <span className="text-center leading-tight">{category.label}</span>
  58. </div>
  59. ))}
  60. </div>
  61. {/* 인증서 그리드 */}
  62. <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3 sm:gap-4">
  63. {filteredCerts.length > 0 ? (
  64. filteredCerts.map((cert) => (
  65. <div
  66. key={cert.id}
  67. className="flex flex-col items-center cursor-pointer hover:transform hover:scale-105 transition-transform duration-200"
  68. onClick={() => setSelectedCert(cert)}
  69. >
  70. <div className="w-full max-w-[200px] aspect-[3/4] bg-gray-100 rounded-lg overflow-hidden shadow-md hover:shadow-lg transition-shadow duration-200">
  71. <img
  72. src={cert.imageUrl}
  73. alt={cert.title}
  74. className="w-full h-full object-cover"
  75. />
  76. </div>
  77. <div className="w-full text-center mt-2 px-2 text-black text-xs sm:text-sm font-medium font-['Noto_Sans_KR'] leading-tight">
  78. {cert.title}
  79. </div>
  80. </div>
  81. ))
  82. ) : (
  83. <div className="col-span-full text-center py-12">
  84. <div className="text-gray-500 text-lg font-medium font-['Noto_Sans_KR']">
  85. 해당 카테고리에 인증서가 없습니다.
  86. </div>
  87. </div>
  88. )}
  89. </div>
  90. {/* 모달 */}
  91. {selectedCert && (
  92. <CertificateModal
  93. title={selectedCert.title}
  94. imageUrl={selectedCert.imageUrl}
  95. onClose={() => setSelectedCert(null)}
  96. />
  97. )}
  98. </div>
  99. );
  100. };
  101. export default Certification;