feat: implement portfolio dashboard with skill, experience, and message management features
This commit is contained in:
112
src/app/[locale]/admin/dashboard/skills/page.tsx
Normal file
112
src/app/[locale]/admin/dashboard/skills/page.tsx
Normal file
@@ -0,0 +1,112 @@
|
||||
import { prisma } from "@/core/db/prisma";
|
||||
import { verifySession } from "@/core/security/session";
|
||||
import { Link, redirect } from "@/i18n/routing";
|
||||
import { getLocale } from "next-intl/server";
|
||||
import { Plus, Pencil, ArrowLeft, Code2 } from "lucide-react";
|
||||
import { DeleteSkillButton } from "@/features/skills/delete-skill-button";
|
||||
import { SkillIcon } from "@/features/skills/skill-icon";
|
||||
|
||||
const CATEGORY_LABELS: Record<string, string> = {
|
||||
backend: "Enterprise Backend",
|
||||
infra: "Database & Infra",
|
||||
frontend: "Frontend",
|
||||
mobile: "Mobile",
|
||||
};
|
||||
|
||||
const CATEGORY_COLORS: Record<string, string> = {
|
||||
backend: "text-blue-500 bg-blue-500/10",
|
||||
infra: "text-emerald-500 bg-emerald-500/10",
|
||||
frontend: "text-violet-500 bg-violet-500/10",
|
||||
mobile: "text-orange-500 bg-orange-500/10",
|
||||
};
|
||||
|
||||
export default async function AdminSkillsPage() {
|
||||
const session = await verifySession();
|
||||
const locale = await getLocale();
|
||||
|
||||
if (!session) {
|
||||
redirect({ href: "/admin/login", locale });
|
||||
}
|
||||
|
||||
const skills = await prisma.skill.findMany({
|
||||
orderBy: [{ category: "asc" }, { name: "asc" }],
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-muted/10 p-6 lg:p-12">
|
||||
<div className="max-w-5xl mx-auto">
|
||||
<div className="flex items-center justify-between mb-8">
|
||||
<div className="flex items-center gap-4">
|
||||
<Link
|
||||
href="/admin/dashboard"
|
||||
className="p-2 rounded-full hover:bg-muted text-muted-foreground transition-colors"
|
||||
>
|
||||
<ArrowLeft size={20} />
|
||||
</Link>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight">Tech Stack</h1>
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
Manage your skills and technology arsenal.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href="/admin/dashboard/skills/create"
|
||||
className="flex items-center gap-2 px-5 py-2.5 rounded-xl bg-foreground text-background font-semibold text-sm shadow-md hover:scale-105 transition-all"
|
||||
>
|
||||
<Plus size={16} />
|
||||
Add Skill
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{skills.length === 0 ? (
|
||||
<div className="p-12 text-center flex flex-col items-center bg-card rounded-2xl border border-border/50">
|
||||
<Code2 size={48} className="text-muted-foreground/40 mb-4" />
|
||||
<h3 className="text-lg font-bold">No skills yet</h3>
|
||||
<p className="text-sm text-muted-foreground mb-6">
|
||||
Start adding your tech stack.
|
||||
</p>
|
||||
<Link
|
||||
href="/admin/dashboard/skills/create"
|
||||
className="px-6 py-2 rounded-xl border border-border hover:bg-muted/50 transition-colors font-medium text-sm"
|
||||
>
|
||||
Add First Skill
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{skills.map((skill) => (
|
||||
<div
|
||||
key={skill.id}
|
||||
className="group flex items-center justify-between p-4 rounded-2xl bg-card border border-border/50 hover:border-accent/30 hover:shadow-md transition-all"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SkillIcon iconName={skill.iconName} name={skill.name} />
|
||||
<div>
|
||||
<p className="font-semibold text-sm">{skill.name}</p>
|
||||
<span
|
||||
className={`text-[10px] font-mono font-bold px-2 py-0.5 rounded-full ${
|
||||
CATEGORY_COLORS[skill.category] ?? "text-muted-foreground bg-muted"
|
||||
}`}
|
||||
>
|
||||
{CATEGORY_LABELS[skill.category] ?? skill.category}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
|
||||
<Link
|
||||
href={`/admin/dashboard/skills/${skill.id}/edit`}
|
||||
className="p-2 text-muted-foreground hover:text-accent hover:bg-accent/10 rounded-lg transition-colors"
|
||||
>
|
||||
<Pencil size={15} />
|
||||
</Link>
|
||||
<DeleteSkillButton id={skill.id} />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user