Initialize ZUJ OL Apps website with React + TypeScript + Vite

This commit is contained in:
2026-05-22 09:21:24 +08:00
commit 5e79c96364
55 changed files with 7409 additions and 0 deletions
+94
View File
@@ -0,0 +1,94 @@
import type { ComponentType } from 'react';
import { Link } from 'react-router-dom';
import { ExternalLink, Download, BookOpen, Star, NotebookPen, Terminal, MapPin, Smartphone, Monitor, Brain, KeyRound } from 'lucide-react';
import { useI18n } from '../hooks/useI18n';
import { siteData } from '../data/siteData';
import type { Project } from '../types';
const iconMap: Record<string, ComponentType<{ size?: number }>> = {
NotebookPen,
Terminal,
MapPin,
Smartphone,
BookOpen,
Monitor,
Brain,
KeyRound,
};
interface ProjectCardProps {
project: Project;
}
export default function ProjectCard({ project }: ProjectCardProps) {
const { t, bi } = useI18n();
const status = siteData.statuses[project.status];
const IconComponent = iconMap[project.icon];
return (
<div className="project-card">
<div className="project-card-header">
<div className="project-icon">
{IconComponent && <IconComponent size={22} />}
</div>
<div className="project-card-info">
<div className="project-name">
<Link to={`/projects/${project.id}`}>{project.name}</Link>
{status && (
<span
className="badge badge-status"
style={{ color: status.color }}
>
{bi(status.label)}
</span>
)}
</div>
<div className="project-slogan">{bi(project.slogan)}</div>
</div>
</div>
<div className="project-card-meta">
{project.techStack.slice(0, 4).map((tech) => (
<span key={tech} className="badge">{tech}</span>
))}
{project.platforms.slice(0, 3).map((platform) => (
<span key={platform} className="badge">
{siteData.platforms[platform] || platform}
</span>
))}
</div>
<div className="project-card-meta">
<span className="badge badge-accent">
<Star size={12} /> {project.stars}
</span>
</div>
<div className="project-card-actions">
<a
href={project.repoUrl}
target="_blank"
rel="noopener noreferrer"
className="btn btn-sm"
>
<ExternalLink size={14} /> GitHub
</a>
{project.downloads.length > 0 && (
<Link to={`/projects/${project.id}`} className="btn btn-sm">
<Download size={14} /> {t('common.download')}
</Link>
)}
{project.docsUrl && (
<a
href={project.docsUrl}
target="_blank"
rel="noopener noreferrer"
className="btn btn-sm"
>
<BookOpen size={14} /> {t('common.docs')}
</a>
)}
</div>
</div>
);
}