Job search tracker: CRUD, AI parse/tailor/cover via OmniRoute, legacy import
This commit is contained in:
71
src/app/jobs/[id]/page.tsx
Normal file
71
src/app/jobs/[id]/page.tsx
Normal file
@@ -0,0 +1,71 @@
|
||||
import { prisma } from "@/lib/db";
|
||||
import { notFound } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import JobActions from "./job-actions";
|
||||
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
export default async function JobDetail({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = await params;
|
||||
const job = await prisma.job.findUnique({
|
||||
where: { id },
|
||||
include: { resumes: true, coverLetters: true, applications: true },
|
||||
});
|
||||
if (!job) notFound();
|
||||
|
||||
return (
|
||||
<div className="max-w-3xl space-y-6">
|
||||
<div>
|
||||
<Link href="/jobs" className="text-sm text-zinc-500 hover:text-blue-600">← Jobs</Link>
|
||||
<h1 className="text-2xl font-bold mt-1">{job.title}</h1>
|
||||
<div className="text-zinc-500">{job.company} · {job.location ?? "Remote"} · PH: {job.phEligibility}</div>
|
||||
<div className="mt-2 flex gap-3 text-sm">
|
||||
{job.url && <a href={job.url} target="_blank" className="text-blue-600 hover:underline">Listing ↗</a>}
|
||||
{job.applyUrl && <a href={job.applyUrl} target="_blank" className="text-blue-600 hover:underline">Apply page ↗</a>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<JobActions job={JSON.parse(JSON.stringify(job))} />
|
||||
|
||||
{job.description && (
|
||||
<section>
|
||||
<h2 className="font-semibold mb-1">Description</h2>
|
||||
<p className="text-sm whitespace-pre-wrap">{job.description}</p>
|
||||
</section>
|
||||
)}
|
||||
{job.requirements && (
|
||||
<section>
|
||||
<h2 className="font-semibold mb-1">Requirements</h2>
|
||||
<p className="text-sm whitespace-pre-wrap">{job.requirements}</p>
|
||||
</section>
|
||||
)}
|
||||
{job.notes && (
|
||||
<section>
|
||||
<h2 className="font-semibold mb-1">Notes</h2>
|
||||
<p className="text-sm whitespace-pre-wrap">{job.notes}</p>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{job.resumes.length > 0 && (
|
||||
<section>
|
||||
<h2 className="font-semibold mb-1">Tailored resumes</h2>
|
||||
<ul className="text-sm space-y-1">
|
||||
{job.resumes.map((r) => (
|
||||
<li key={r.id}><Link href={`/resumes/${r.id}`} className="text-blue-600 hover:underline">{r.name}</Link></li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
{job.coverLetters.length > 0 && (
|
||||
<section>
|
||||
<h2 className="font-semibold mb-1">Cover letters</h2>
|
||||
<ul className="text-sm space-y-2">
|
||||
{job.coverLetters.map((c) => (
|
||||
<li key={c.id} className="rounded border border-zinc-200 dark:border-zinc-800 p-3 whitespace-pre-wrap bg-white dark:bg-zinc-900">{c.markdown}</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user