1 Commits

Author SHA1 Message Date
cw afe7dcce3a tampilan card 2026-06-04 19:32:40 +07:00
11 changed files with 238 additions and 210 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3 -3
View File
@@ -1,4 +1,4 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="id" class="scroll-smooth"> <html lang="id" class="scroll-smooth">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
@@ -1148,9 +1148,9 @@
</div> </div>
<div id="assignment-body" class="prose prose-slate max-w-none"> <div id="assignment-body" class="prose prose-slate max-w-none">
<!-- ================= START: BAGIAN YANG DIEDIT KELOMPOK ================= --> <!-- ================= START: BAGIAN YANG DIEDIT KELOMPOK ================= -->
<link rel="stylesheet" href="./assets/acaab.css"> <link rel="stylesheet" href="./assets/acaab.css?v=8">
<div id="root" class="not-prose"></div> <div id="root" class="not-prose"></div>
<script type="module" src="./assets/acaab.js"></script> <script type="module" src="./assets/acaab.js?v=8"></script>
<!-- ================= END: BAGIAN YANG DIEDIT KELOMPOK ================= --> <!-- ================= END: BAGIAN YANG DIEDIT KELOMPOK ================= -->
</div> </div>
</div> </div>
+1 -1
View File
@@ -11,7 +11,7 @@ export default function App() {
<LanguageProvider> <LanguageProvider>
<DataProvider> <DataProvider>
{/* Breadcrumb */} {/* Breadcrumb */}
<nav className="py-4" aria-label="Breadcrumb"> <nav className="max-w-7xl mx-auto px-4 lg:px-8 py-4" aria-label="Breadcrumb">
<ol className="flex items-center text-[10px] md:text-xs text-slate-500 font-medium tracking-wide uppercase"> <ol className="flex items-center text-[10px] md:text-xs text-slate-500 font-medium tracking-wide uppercase">
<li> <li>
<a href="https://informatika.untan.ac.id" target="_blank" rel="noopener noreferrer" className="hover:text-untan-blue transition-colors"> <a href="https://informatika.untan.ac.id" target="_blank" rel="noopener noreferrer" className="hover:text-untan-blue transition-colors">
+18 -18
View File
@@ -14,7 +14,7 @@ export default function AchievementCard({ achievement, onReadMore }: Achievement
<article className="bg-white rounded-lg overflow-hidden group hover:-translate-y-1 transition-all duration-300 shadow-md hover:shadow-xl border border-slate-200 flex flex-col h-full relative"> <article className="bg-white rounded-lg overflow-hidden group hover:-translate-y-1 transition-all duration-300 shadow-md hover:shadow-xl border border-slate-200 flex flex-col h-full relative">
<div className="absolute top-0 left-0 w-full h-1 bg-primary-navy z-10 group-hover:bg-primary-gold transition-colors"></div> <div className="absolute top-0 left-0 w-full h-1 bg-primary-navy z-10 group-hover:bg-primary-gold transition-colors"></div>
<div className="relative h-56 overflow-hidden bg-slate-100"> <div className="relative h-44 overflow-hidden bg-slate-100">
<img <img
src={achievement.image} src={achievement.image}
alt={achievement.title} alt={achievement.title}
@@ -24,50 +24,50 @@ export default function AchievementCard({ achievement, onReadMore }: Achievement
(e.target as HTMLImageElement).src = 'https://placehold.co/800x600/003150/FDB813?text=Informatika+UNTAN'; (e.target as HTMLImageElement).src = 'https://placehold.co/800x600/003150/FDB813?text=Informatika+UNTAN';
}} }}
/> />
<div className="absolute top-3 left-3 bg-white/95 backdrop-blur text-primary-navy font-bold px-3 py-1 rounded shadow-sm flex items-center gap-1.5 text-xs"> <div className="absolute top-2.5 left-2.5 bg-white/95 backdrop-blur text-primary-navy font-bold px-2 py-0.5 rounded shadow-sm flex items-center gap-1 text-[10px]">
<Trophy size={14} className="text-primary-gold" /> <Trophy size={12} className="text-primary-gold" />
{achievement.rank} {achievement.rank}
</div> </div>
</div> </div>
<div className="p-6 flex flex-col flex-grow"> <div className="p-4 sm:p-5 flex flex-col flex-grow">
<div className="flex items-center gap-3 mb-3"> <div className="flex items-center gap-2 mb-2">
<span className="text-untan-blue text-xs font-bold uppercase tracking-wider"> <span className="text-untan-blue text-[10px] font-bold uppercase tracking-wider">
{achievement.category} {achievement.category}
</span> </span>
<span className="text-slate-400 text-xs font-medium flex items-center gap-1 border-l border-slate-300 pl-3"> <span className="text-slate-400 text-[10px] font-medium flex items-center gap-1 border-l border-slate-300 pl-2">
<Calendar size={12} /> <Calendar size={10} />
{achievement.year} {achievement.year}
</span> </span>
</div> </div>
<h3 className="text-xl font-bold font-display text-primary-navy mb-3 leading-snug group-hover:text-untan-blue transition-colors"> <h3 className="text-lg font-bold font-display text-primary-navy mb-2 leading-snug group-hover:text-untan-blue transition-colors">
{achievement.title} {achievement.title}
</h3> </h3>
<p className="text-slate-600 text-sm leading-relaxed mb-6 line-clamp-3"> <p className="text-slate-600 text-xs leading-relaxed mb-4 line-clamp-3">
{achievement.description} {achievement.description}
</p> </p>
<div className="mt-auto border-t border-slate-100 pt-4"> <div className="mt-auto border-t border-slate-100 pt-3">
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-2">
<div className="w-8 h-8 rounded bg-primary-navy/5 flex items-center justify-center text-primary-navy font-bold text-xs ring-1 ring-primary-navy/10"> <div className="w-7 h-7 rounded bg-primary-navy/5 flex items-center justify-center text-primary-navy font-bold text-[10px] ring-1 ring-primary-navy/10">
{achievement.avatar} {achievement.avatar}
</div> </div>
<div> <div>
<span className="font-bold text-slate-800 block text-sm">{achievement.team}</span> <span className="font-bold text-slate-800 block text-xs leading-none mb-0.5">{achievement.team}</span>
<span className="text-slate-500 block text-xs">{achievement.generation}</span> <span className="text-slate-500 block text-[10px] leading-none">{achievement.generation}</span>
</div> </div>
</div> </div>
<span className="bg-slate-100 text-slate-600 text-[10px] px-2 py-1 rounded font-bold uppercase tracking-wide"> <span className="bg-slate-100 text-slate-600 text-[9px] px-1.5 py-0.5 rounded font-bold uppercase tracking-wide">
{achievement.level} {achievement.level}
</span> </span>
</div> </div>
<button <button
onClick={() => onReadMore(achievement)} onClick={() => onReadMore(achievement)}
className="w-full py-2.5 rounded bg-primary-navy/5 text-primary-navy font-bold hover:bg-primary-navy hover:text-white transition-all duration-300 text-sm min-h-[44px]" className="w-full py-2 rounded bg-primary-navy/5 text-primary-navy font-bold hover:bg-primary-navy/10 hover:text-untan-blue transition-all duration-300 text-xs min-h-[38px] flex items-center justify-center gap-1.5"
> >
{t.achievement.readMore} {t.achievement.readMore}
</button> </button>
@@ -1,4 +1,4 @@
import type { ReactNode } from 'react'; import { useEffect, type ReactNode } from 'react';
import { X, Trophy, Calendar, Award, Users, BookOpen, Tag } from 'lucide-react'; import { X, Trophy, Calendar, Award, Users, BookOpen, Tag } from 'lucide-react';
import type { Achievement } from '../data'; import type { Achievement } from '../data';
import { useLang } from '../context/LanguageContext'; import { useLang } from '../context/LanguageContext';
@@ -22,6 +22,18 @@ function InfoRow({ icon, label, value }: { icon: ReactNode; label: string; value
export default function AchievementModal({ achievement, onClose }: AchievementModalProps) { export default function AchievementModal({ achievement, onClose }: AchievementModalProps) {
const { t } = useLang(); const { t } = useLang();
useEffect(() => {
if (achievement) {
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
}
return () => {
document.documentElement.style.overflow = '';
document.body.style.overflow = '';
};
}, [achievement]);
if (!achievement) return null; if (!achievement) return null;
return ( return (
@@ -33,11 +45,11 @@ export default function AchievementModal({ achievement, onClose }: AchievementMo
aria-label={achievement.title} aria-label={achievement.title}
> >
<div <div
className="bg-white rounded-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto shadow-2xl animate-fade-in" className="bg-white rounded-xl max-w-lg w-full max-h-[90vh] overflow-y-auto shadow-2xl animate-fade-in"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{/* Image header */} {/* Image header */}
<div className="relative h-56 overflow-hidden rounded-t-xl bg-slate-200 shrink-0"> <div className="relative h-56 md:h-64 overflow-hidden rounded-t-xl bg-slate-200 shrink-0">
<img <img
src={achievement.image} src={achievement.image}
alt={achievement.title} alt={achievement.title}
@@ -46,81 +58,60 @@ export default function AchievementModal({ achievement, onClose }: AchievementMo
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" />
{/* Close button */} {/* Close button — dark background so icon always visible on any image */}
<button <button
onClick={onClose} onClick={onClose}
aria-label={t.modal.close} aria-label={t.modal.close}
className="absolute top-3 right-3 w-9 h-9 bg-white/90 backdrop-blur rounded-full flex items-center justify-center hover:bg-white transition-colors shadow-md" className="absolute top-3 right-3 z-50 w-8 h-8 bg-slate-900/50 hover:bg-slate-900/80 backdrop-blur rounded-full flex items-center justify-center transition-colors shadow-md"
> >
<X size={18} className="text-slate-700" /> <X size={16} className="text-white" />
</button> </button>
{/* Rank badge */} {/* Rank Badge only */}
<div className="absolute bottom-4 left-4 flex gap-2"> <div className="absolute bottom-3 left-4">
<span className="bg-primary-gold text-slate-900 text-xs font-bold px-3 py-1.5 rounded flex items-center gap-1.5"> <span className="bg-primary-gold text-black text-[10px] font-bold px-2.5 py-1 rounded shadow-sm flex items-center gap-1">
<Trophy size={12} />
{achievement.rank} {achievement.rank}
</span> </span>
<span className="bg-white/20 backdrop-blur text-white text-xs font-bold px-3 py-1.5 rounded uppercase tracking-wide">
{achievement.level}
</span>
</div> </div>
</div> </div>
{/* Content */} {/* Content */}
<div className="p-6"> <div className="p-6">
<div className="mb-1"> <h2 className="text-lg font-bold text-slate-900 leading-snug mb-1">{achievement.title}</h2>
<span className="text-primary-untan-blue text-xs font-bold uppercase tracking-wider">
{achievement.category} <div className="text-xs text-slate-400 mb-4 font-medium">
</span> {achievement.year} &bull; {achievement.category} &bull; {achievement.level}
</div> </div>
<h2 className="text-xl font-bold text-primary-navy leading-snug mb-5">{achievement.title}</h2>
<p className="text-slate-600 text-xs leading-relaxed mb-5">
{achievement.description.replace(/\*\*/g, '')}
</p>
{/* Info grid */} {/* Info grid */}
<div className="grid grid-cols-2 gap-4 p-4 bg-slate-50 rounded-lg mb-5"> <div className="grid grid-cols-2 gap-4 mb-4 text-xs">
<InfoRow icon={<Users size={14} />} label={t.modal.tim} value={achievement.team} /> <div>
{achievement.nim && ( <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-1">Tim / Peserta</p>
<InfoRow icon={<BookOpen size={14} />} label={t.modal.nim} value={achievement.nim} /> <p className="font-bold text-slate-900">{achievement.team}</p>
)} </div>
<InfoRow icon={<Calendar size={14} />} label={t.modal.angkatan} value={achievement.generation} /> <div>
<InfoRow icon={<Calendar size={14} />} label={t.modal.tahun} value={String(achievement.year)} /> <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-1">Angkatan</p>
<InfoRow icon={<Award size={14} />} label={t.modal.kompetisi} value={achievement.competition} /> <p className="font-bold text-slate-900">{achievement.generation}</p>
<InfoRow icon={<Tag size={14} />} label={t.modal.bidang} value={achievement.category} /> </div>
</div> </div>
{/* Members list */} {/* Members list */}
{achievement.members && achievement.members.length > 0 && ( {achievement.members && achievement.members.length > 0 && (
<div className="border-t border-slate-100 pt-4 mb-4"> <div className="border-t border-slate-100 pt-3">
<p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">Anggota Tim</p> <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">Anggota Tim</p>
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2"> <div className="flex flex-wrap gap-1.5">
{achievement.members.map((m, i) => ( {achievement.members.map((m, i) => (
<li key={i} className="flex items-center gap-2 bg-slate-50 px-3 py-2 rounded-md"> <span key={i} className="bg-slate-100 text-slate-600 text-[10px] px-2.5 py-1 rounded">
<span className="w-6 h-6 rounded-full bg-primary-gold/20 text-primary-gold flex items-center justify-center text-xs font-bold shrink-0"> {m.nama}{m.angkatan ? ` (${m.angkatan})` : ''}
{m.nama.charAt(0).toUpperCase()} </span>
</span>
<div className="flex flex-col">
<span className="text-sm font-semibold text-slate-700 leading-none">{m.nama}</span>
{m.angkatan && <span className="text-[10px] text-slate-500 mt-1">Angkatan {m.angkatan}</span>}
</div>
</li>
))} ))}
</ul> </div>
</div> </div>
)} )}
{/* Description */}
<div className="border-t border-slate-100 pt-4">
<p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">{t.modal.deskripsi}</p>
<p className="text-slate-600 text-sm leading-relaxed">{achievement.description.replace(/\*\*/g, '')}</p>
</div>
{/* Close button */}
<button
onClick={onClose}
className="mt-6 w-full py-3 bg-primary-navy text-white font-bold rounded-lg hover:bg-untan-blue transition-colors duration-200 text-sm"
>
{t.modal.close}
</button>
</div> </div>
</div> </div>
</div> </div>
+1 -1
View File
@@ -6,7 +6,7 @@ export default function Hero() {
return ( return (
<div <div
id="hero" id="hero"
className="acaab-full-bleed bg-untan-navy text-white pt-6 md:pt-8 lg:pt-10 mb-8 border-t border-white/10 relative" className="acaab-full-bleed bg-untan-navy text-white pt-6 md:pt-8 lg:pt-10 mb-8 border-t border-white/10 relative overflow-hidden"
> >
<div className="container mx-auto px-4 lg:px-8 relative z-10"> <div className="container mx-auto px-4 lg:px-8 relative z-10">
+38 -13
View File
@@ -267,20 +267,45 @@ export default function ProjectCarousel() {
(e.target as HTMLImageElement).src = 'https://placehold.co/800x600/003150/FDB813?text=Informatika+UNTAN'; (e.target as HTMLImageElement).src = 'https://placehold.co/800x600/003150/FDB813?text=Informatika+UNTAN';
}} }}
/> />
<div className="absolute inset-0 bg-gradient-to-t from-primary-navy/80 to-transparent p-5 flex flex-col justify-end">
<h4 className="text-lg font-bold font-display text-white leading-snug mb-1"> {/* Tag at top-left, raised like achievements, max 2 tags then (..) */}
{project.title} {project.category && project.category.length > 0 && (
<div className="absolute top-3 left-3 flex flex-wrap gap-1.5 z-20">
{project.category.slice(0, 2).map((cat, i) => (
<span
key={i}
className="bg-white/95 backdrop-blur text-primary-navy font-bold px-2.5 py-1 rounded shadow-sm flex items-center gap-1 text-[10px] uppercase tracking-wider"
>
<Tag size={10} className="text-primary-gold" />
{cat}
</span>
))}
{project.category.length > 2 && (
<span className="bg-white/95 backdrop-blur text-primary-navy font-bold px-2 py-1 rounded shadow-sm flex items-center gap-1 text-[10px]">
(..)
</span>
)}
</div>
)}
{/* Title block — inline background follows text width */}
<div className="absolute bottom-3 left-3 z-20 max-w-[85%]">
<h4
className="text-sm font-bold font-display text-slate-900 leading-snug"
style={{ display: 'inline' }}
>
<span
className="bg-white/55 backdrop-blur-sm px-2 py-0.5 rounded"
style={{
boxDecorationBreak: 'clone',
WebkitBoxDecorationBreak: 'clone',
lineHeight: '1.9',
textShadow: '0 0 8px rgba(255,255,255,0.8)',
}}
>
{project.title}
</span>
</h4> </h4>
{project.category && project.category.length > 0 && (
<div className="flex flex-wrap gap-2 mt-1">
{project.category.map((cat, i) => (
<span key={i} className="inline-flex items-center gap-1 text-[10px] font-bold text-primary-gold uppercase tracking-wider bg-black/30 px-2 py-1 rounded backdrop-blur-sm">
<Tag size={10} />
{cat}
</span>
))}
</div>
)}
</div> </div>
</div> </div>
+55 -59
View File
@@ -1,4 +1,4 @@
import type { ReactNode } from 'react'; import { useEffect, type ReactNode } from 'react';
import { X, ExternalLink, Users, BookOpen, Calendar, Tag, FileText } from 'lucide-react'; import { X, ExternalLink, Users, BookOpen, Calendar, Tag, FileText } from 'lucide-react';
import type { Project } from '../data'; import type { Project } from '../data';
import { useLang } from '../context/LanguageContext'; import { useLang } from '../context/LanguageContext';
@@ -22,6 +22,18 @@ function InfoRow({ icon, label, value }: { icon: ReactNode; label: string; value
export default function ProjectModal({ project, onClose }: ProjectModalProps) { export default function ProjectModal({ project, onClose }: ProjectModalProps) {
const { t } = useLang(); const { t } = useLang();
useEffect(() => {
if (project) {
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
}
return () => {
document.documentElement.style.overflow = '';
document.body.style.overflow = '';
};
}, [project]);
if (!project) return null; if (!project) return null;
return ( return (
@@ -33,11 +45,11 @@ export default function ProjectModal({ project, onClose }: ProjectModalProps) {
aria-label={project.title} aria-label={project.title}
> >
<div <div
className="bg-white rounded-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto shadow-2xl animate-fade-in" className="bg-white rounded-xl max-w-lg w-full max-h-[90vh] overflow-y-auto shadow-2xl animate-fade-in"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
> >
{/* Image header */} {/* Image header */}
<div className="relative h-56 overflow-hidden rounded-t-xl bg-slate-200 shrink-0"> <div className="relative h-56 md:h-64 overflow-hidden rounded-t-xl bg-slate-200 shrink-0">
<img <img
src={project.image} src={project.image}
alt={project.title} alt={project.title}
@@ -46,21 +58,20 @@ export default function ProjectModal({ project, onClose }: ProjectModalProps) {
/> />
<div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" /> <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-black/20 to-transparent" />
{/* Close button */} {/* Close button — dark bg so icon always visible on any image */}
<button <button
onClick={onClose} onClick={onClose}
aria-label={t.modal.close} aria-label={t.modal.close}
className="absolute top-3 right-3 w-9 h-9 bg-white/90 backdrop-blur rounded-full flex items-center justify-center hover:bg-white transition-colors shadow-md" className="absolute top-3 right-3 z-50 w-8 h-8 bg-slate-900/50 hover:bg-slate-900/80 backdrop-blur rounded-full flex items-center justify-center transition-colors shadow-md"
> >
<X size={18} className="text-slate-700" /> <X size={16} className="text-white" />
</button> </button>
{/* Category badge */} {/* Category badge */}
{project.category && project.category.length > 0 && ( {project.category && project.category.length > 0 && (
<div className="absolute bottom-4 left-4 flex flex-wrap gap-2"> <div className="absolute bottom-3 left-4 flex flex-wrap gap-1.5">
{project.category.map((cat, i) => ( {project.category.map((cat, i) => (
<span key={i} className="bg-primary-gold text-slate-900 text-xs font-bold px-3 py-1.5 rounded flex items-center gap-1.5 uppercase tracking-wide"> <span key={i} className="bg-primary-gold text-slate-900 text-[10px] font-bold px-2.5 py-1 rounded shadow-sm flex items-center gap-1 uppercase tracking-wide">
<Tag size={12} />
{cat} {cat}
</span> </span>
))} ))}
@@ -70,80 +81,65 @@ export default function ProjectModal({ project, onClose }: ProjectModalProps) {
{/* Content */} {/* Content */}
<div className="p-6"> <div className="p-6">
<h2 className="text-xl font-bold text-primary-navy leading-snug mb-5">{project.title}</h2> <h2 className="text-lg font-bold text-slate-900 leading-snug mb-1">{project.title}</h2>
{project.category && project.category.length > 0 && (
<div className="text-xs text-slate-400 mb-4 font-medium">
{project.year} &bull; {project.category.join(', ')}
</div>
)}
<p className="text-slate-600 text-xs leading-relaxed mb-5">
{project.description}
</p>
{/* Info grid */} {/* Info grid */}
<div className="grid grid-cols-2 gap-4 p-4 bg-slate-50 rounded-lg mb-5"> <div className="grid grid-cols-2 gap-4 mb-4 text-xs">
{project.studentName && ( <div>
<InfoRow icon={<Users size={14} />} label={t.modal.mahasiswa} value={project.studentName} /> <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-1">Mahasiswa</p>
)} <p className="font-bold text-slate-900">{project.studentName}</p>
</div>
{project.nim && ( {project.nim && (
<InfoRow icon={<BookOpen size={14} />} label={t.modal.nim} value={project.nim} /> <div>
)} <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-1">NIM</p>
{project.year && ( <p className="font-bold text-slate-900">{project.nim}</p>
<InfoRow icon={<Calendar size={14} />} label={t.modal.tahun} value={String(project.year)} /> </div>
)}
{project.category && project.category.length > 0 && (
<InfoRow icon={<Tag size={14} />} label={t.modal.bidang} value={project.category.join(', ')} />
)} )}
</div> </div>
{/* Members list */} {/* Members list */}
{project.members && project.members.length > 0 && ( {project.members && project.members.length > 0 && (
<div className="border-t border-slate-100 pt-4 mb-4"> <div className="border-t border-slate-100 pt-3 mb-4">
<p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">Anggota Tim</p> <p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">Anggota Tim</p>
<ul className="grid grid-cols-1 sm:grid-cols-2 gap-2"> <div className="flex flex-wrap gap-1.5">
{project.members.map((m, i) => ( {project.members.map((m, i) => (
<li key={i} className="flex items-center gap-2 bg-slate-50 px-3 py-2 rounded-md border border-slate-100"> <span key={i} className="bg-slate-100 text-slate-600 text-[10px] px-2.5 py-1 rounded">
<span className="w-6 h-6 rounded-full bg-primary-gold/20 text-primary-gold flex items-center justify-center text-xs font-bold shrink-0"> {m.nama}{m.nim ? ` (${m.nim})` : ''}
{m.nama.charAt(0).toUpperCase()} </span>
</span>
<div className="flex flex-col">
<span className="text-sm font-semibold text-slate-700 leading-none">{m.nama}</span>
{m.nim && <span className="text-[10px] text-slate-500 mt-1">{m.nim}</span>}
</div>
</li>
))} ))}
</ul> </div>
</div> </div>
)} )}
{/* Description */} {project.catatan && (
<div className="border-t border-slate-100 pt-4 mb-5"> <div className="mb-4 p-2.5 bg-blue-50/50 border border-blue-100 rounded-lg">
<p className="text-[10px] font-bold uppercase tracking-wider text-slate-400 mb-2">{t.modal.deskripsi}</p> <p className="text-[10px] font-bold uppercase tracking-wider text-blue-400 mb-1">Catatan Tambahan</p>
<p className="text-slate-600 text-sm leading-relaxed">{project.description}</p> <p className="text-xs text-slate-600 italic">{project.catatan}</p>
{project.catatan && ( </div>
<div className="mt-4 p-3 bg-blue-50/50 border border-blue-100 rounded-lg"> )}
<p className="text-[10px] font-bold uppercase tracking-wider text-blue-400 mb-1">Catatan Tambahan</p>
<p className="text-sm text-slate-600 italic">{project.catatan}</p>
</div>
)}
</div>
{/* Project link */} {/* Project link */}
{project.link ? ( {project.link && (
<a <a
href={project.link} href={project.link}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex items-center justify-center gap-2 w-full py-3 bg-primary-navy text-white font-bold rounded-lg hover:bg-untan-blue transition-colors duration-200 text-sm mb-3" className="flex items-center justify-center gap-2 w-full py-2.5 bg-[#003150] text-white font-bold rounded-lg hover:bg-[#1e40af] transition-colors duration-200 text-sm"
> >
<ExternalLink size={16} /> <ExternalLink size={14} />
{t.project.visitLink} {t.project.visitLink}
</a> </a>
) : (
<div className="flex items-center justify-center gap-2 w-full py-3 bg-slate-100 text-slate-400 font-bold rounded-lg text-sm mb-3 cursor-not-allowed">
<FileText size={16} />
{t.project.noLink}
</div>
)} )}
<button
onClick={onClose}
className="w-full py-2.5 border-2 border-slate-200 text-slate-600 font-bold rounded-lg hover:border-slate-300 hover:bg-slate-50 transition-colors duration-200 text-sm"
>
{t.modal.close}
</button>
</div> </div>
</div> </div>
</div> </div>
+5 -2
View File
@@ -81,10 +81,12 @@ function StatPanel({
title, title,
children, children,
flexClass = 'flex-1', flexClass = 'flex-1',
cols = 'grid-cols-2 sm:grid-cols-4',
}: { }: {
title: string; title: string;
children: React.ReactNode; children: React.ReactNode;
flexClass?: string; flexClass?: string;
cols?: string;
}) { }) {
return ( return (
<div className={`flex flex-col gap-5 ${flexClass}`}> <div className={`flex flex-col gap-5 ${flexClass}`}>
@@ -95,8 +97,8 @@ function StatPanel({
> >
{title} {title}
</div> </div>
{/* Numbers row — equal spacing */} {/* Numbers row — responsive grid */}
<div className="flex items-end justify-evenly pb-2"> <div className={`grid ${cols} items-end justify-items-center gap-y-6 gap-x-2 pb-2`}>
{children} {children}
</div> </div>
</div> </div>
@@ -145,6 +147,7 @@ export default function Stats() {
<StatPanel <StatPanel
title={lang === 'id' ? 'Total Portofolio' : 'Total Portfolio'} title={lang === 'id' ? 'Total Portofolio' : 'Total Portfolio'}
flexClass="flex-[1.5]" flexClass="flex-[1.5]"
cols="grid-cols-1"
> >
<StatItem value={totalProyek} label={lang === 'id' ? 'proyek mahasiswa' : 'student projects'} delay={200} /> <StatItem value={totalProyek} label={lang === 'id' ? 'proyek mahasiswa' : 'student projects'} delay={200} />
</StatPanel> </StatPanel>
+41 -8
View File
@@ -1,16 +1,49 @@
@import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@400;700&family=Roboto:wght@300;400;500;700&family=Merriweather:wght@700;900&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Roboto+Condensed:wght@400;700&family=Roboto:wght@300;400;500;700&family=Merriweather:wght@700;900&display=swap');
@import "tailwindcss"; @import "tailwindcss";
/* Break out of the kit's container to go full-width */ /* Override parent index.html container to allow full-width React content */
.acaab-full-bleed { #main-content > .container {
width: 100vw; max-width: 100% !important;
position: relative; width: 100% !important;
left: 50%; padding-left: 0 !important;
margin-left: -50vw; padding-right: 0 !important;
margin-right: -50vw; padding-top: 0 !important;
padding-bottom: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
} }
body { overflow-x: hidden; } /* Re-apply standard container constraints to the static title block in index.html */
#main-content > .container > div.border-b {
max-width: 80rem; /* max-w-7xl */
margin-left: auto;
margin-right: auto;
padding-left: 1rem;
padding-right: 1rem;
padding-top: 3rem; /* py-12 = 3rem */
padding-bottom: 2rem;
}
@media (min-width: 1024px) {
#main-content > .container > div.border-b {
padding-left: 2rem;
padding-right: 2rem;
}
}
/* Clean, modern full-bleed styling since parent is now full-width */
.acaab-full-bleed {
width: 100%;
}
/* Prevent horizontal overflow on html, body, and the assignment wrapper */
html, body {
overflow-x: hidden !important;
width: 100% !important;
}
#assignment-body {
overflow-x: hidden !important;
width: 100% !important;
}
@theme { @theme {
--font-sans: "Roboto", ui-sans-serif, system-ui, sans-serif; --font-sans: "Roboto", ui-sans-serif, system-ui, sans-serif;