feat: Add dashboard for managing portfolio content (blog, experience, projects) and restructure public portfolio routes.
This commit is contained in:
176
src/app/dashboard/experience/page.tsx
Normal file
176
src/app/dashboard/experience/page.tsx
Normal file
@@ -0,0 +1,176 @@
|
||||
import { getExperiences, deleteExperience } from "@/lib/actions/experience";
|
||||
import { DeleteButton } from "@/components/dashboard/DeleteButton";
|
||||
import {
|
||||
Plus,
|
||||
Search,
|
||||
Briefcase,
|
||||
Edit2,
|
||||
Trash2,
|
||||
Building2,
|
||||
Calendar,
|
||||
Layers,
|
||||
AlertCircle,
|
||||
} from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { format } from "date-fns";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
export default async function DashboardExperiencePage() {
|
||||
const experiences = await getExperiences();
|
||||
|
||||
return (
|
||||
<div className="p-6 space-y-6 max-w-7xl mx-auto">
|
||||
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-3xl font-display font-bold text-foreground">
|
||||
Work Experience
|
||||
</h1>
|
||||
<p className="text-foreground-muted">
|
||||
Manage your career history and professional achievements.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/dashboard/experience/new"
|
||||
className="inline-flex items-center justify-center gap-2 px-4 py-2 bg-primary text-primary-foreground font-medium rounded-lg hover:bg-primary/90 transition-all shadow-sm shadow-primary/20"
|
||||
>
|
||||
<Plus size={18} />
|
||||
Add Experience
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{/* Search & Filter */}
|
||||
<div className="md:col-span-2 relative group mt-2">
|
||||
<Search
|
||||
className="absolute left-3 top-1/2 -translate-y-1/2 text-foreground-muted group-focus-within:text-primary transition-colors"
|
||||
size={18}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search experiences..."
|
||||
className="w-full pl-10 pr-4 py-2.5 bg-surface border border-border/50 rounded-xl focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all text-sm"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 justify-end">
|
||||
<div className="text-xs font-semibold text-foreground-muted uppercase tracking-wider">
|
||||
Total: {experiences.length} Experiences
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-surface border border-border/50 rounded-2xl overflow-hidden shadow-sm shadow-black/5">
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full text-left">
|
||||
<thead>
|
||||
<tr className="border-b border-border/50 bg-surface-elevated/50">
|
||||
<th className="px-6 py-4 text-xs font-semibold text-foreground-muted uppercase tracking-wider">
|
||||
Company & Role
|
||||
</th>
|
||||
<th className="px-6 py-4 text-xs font-semibold text-foreground-muted uppercase tracking-wider">
|
||||
Duration
|
||||
</th>
|
||||
<th className="px-6 py-4 text-xs font-semibold text-foreground-muted uppercase tracking-wider">
|
||||
Tech & Skills
|
||||
</th>
|
||||
<th className="px-6 py-4 text-xs font-semibold text-foreground-muted uppercase tracking-wider text-right">
|
||||
Actions
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y divide-border/50">
|
||||
{experiences.map((exp) => (
|
||||
<tr
|
||||
key={exp.id}
|
||||
className="hover:bg-surface-elevated/30 transition-colors group"
|
||||
>
|
||||
<td className="px-6 py-5">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="hidden sm:flex w-12 h-12 rounded-xl bg-primary/5 border border-primary/10 items-center justify-center shrink-0">
|
||||
<Building2 size={20} className="text-primary" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="text-sm font-semibold text-foreground">
|
||||
{exp.role}
|
||||
</div>
|
||||
<div className="text-xs text-primary font-medium mt-0.5">
|
||||
{exp.company}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-5">
|
||||
<div className="flex flex-col">
|
||||
<div className="text-sm text-foreground flex items-center gap-1.5 font-medium">
|
||||
<Calendar size={13} className="text-foreground-muted" />
|
||||
{format(new Date(exp.startDate), "MMM yyyy")} -{" "}
|
||||
{exp.current
|
||||
? "Present"
|
||||
: exp.endDate
|
||||
? format(new Date(exp.endDate), "MMM yyyy")
|
||||
: "-"}
|
||||
</div>
|
||||
{exp.current && (
|
||||
<span className="text-[10px] text-green-500 font-bold uppercase tracking-wider mt-1">
|
||||
Active Role
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-5">
|
||||
<div className="flex flex-wrap gap-1.5 max-w-[200px]">
|
||||
{exp.techStack.slice(0, 3).map((tech) => (
|
||||
<span
|
||||
key={tech}
|
||||
className="px-2 py-0.5 rounded-md bg-surface-elevated text-[10px] font-medium text-foreground-muted border border-border/50"
|
||||
>
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
{exp.techStack.length > 3 && (
|
||||
<span className="text-[10px] text-foreground-muted px-1.5">
|
||||
+{exp.techStack.length - 3} more
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-6 py-5 text-right whitespace-nowrap">
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Link
|
||||
href={`/dashboard/experience/edit/${exp.id}`}
|
||||
className="p-2 text-foreground-muted hover:text-primary hover:bg-primary/10 rounded-lg transition-all"
|
||||
title="Edit"
|
||||
>
|
||||
<Edit2 size={16} />
|
||||
</Link>
|
||||
<DeleteButton id={exp.id} onDelete={deleteExperience} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
{experiences.length === 0 && (
|
||||
<tr>
|
||||
<td colSpan={4} className="px-6 py-20 text-center">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<AlertCircle size={40} className="text-foreground-muted opacity-20" />
|
||||
<div>
|
||||
<p className="text-foreground font-medium">No experiences found</p>
|
||||
<p className="text-sm text-foreground-muted">Document your professional journey.</p>
|
||||
</div>
|
||||
<Link
|
||||
href="/dashboard/experience/new"
|
||||
className="mt-2 text-sm text-primary font-medium hover:underline"
|
||||
>
|
||||
Add your first experience
|
||||
</Link>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user