Skip to content

Commit 0dad266

Browse files
committed
fix downlaod resume
1 parent 0ebe5f2 commit 0dad266

2 files changed

Lines changed: 158 additions & 51 deletions

File tree

‎apps/codebility/app/home/hire/_components/ApplicationDetailsModal.tsx‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,19 @@ export default function ApplicationDetailsModal({
212212
<Button
213213
variant="outline"
214214
className="border-gray-700 bg-gray-800 text-white hover:bg-gray-700"
215+
onClick={async () => {
216+
if (application.resume_url) {
217+
// Construct the Supabase storage URL
218+
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
219+
const resumeUrl = `${supabaseUrl}/storage/v1/object/public/codebility/${application.resume_url}`;
220+
window.open(resumeUrl, "_blank");
221+
} else {
222+
toast({
223+
title: "No Resume",
224+
description: "This applicant hasn't uploaded a resume",
225+
});
226+
}
227+
}}
215228
>
216229
<Download className="mr-2 h-4 w-4" />
217230
Download Resume

‎apps/codebility/app/home/hire/applications/[jobId]/client-page.tsx‎

Lines changed: 145 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,41 @@
22

33
import { useState } from "react";
44
import { useRouter } from "next/navigation";
5-
import { Mail, Phone, Linkedin, Globe, FileText, Calendar, User, Github, Trash2, Download, MoreVertical } from "lucide-react";
5+
import { H1 } from "@/components/shared/dashboard";
6+
import CustomBreadcrumb from "@/components/shared/dashboard/CustomBreadcrumb";
67
import { Button } from "@/components/ui/button";
8+
import { useToast } from "@/components/ui/use-toast";
9+
import {
10+
Calendar,
11+
Download,
12+
FileText,
13+
Github,
14+
Globe,
15+
Linkedin,
16+
Mail,
17+
MoreVertical,
18+
Phone,
19+
Trash2,
20+
User,
21+
} from "lucide-react";
22+
723
import { Badge } from "@codevs/ui/badge";
24+
import {
25+
DropdownMenu,
26+
DropdownMenuContent,
27+
DropdownMenuItem,
28+
DropdownMenuTrigger,
29+
} from "@codevs/ui/dropdown-menu";
830
import {
931
Select,
1032
SelectContent,
1133
SelectItem,
1234
SelectTrigger,
1335
SelectValue,
1436
} from "@codevs/ui/select";
15-
import { useToast } from "@/components/ui/use-toast";
37+
1638
import ApplicationDetailsModal from "../../_components/ApplicationDetailsModal";
17-
import CustomBreadcrumb from "@/components/shared/dashboard/CustomBreadcrumb";
18-
import { H1 } from "@/components/shared/dashboard";
1939
import { deleteJobApplication, updateApplicationStatus } from "../../actions";
20-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@codevs/ui/dropdown-menu";
2140

2241
interface JobApplication {
2342
id: string;
@@ -34,7 +53,7 @@ interface JobApplication {
3453
experience?: string;
3554
resume_url?: string;
3655
applied_at: string;
37-
status: 'pending' | 'reviewing' | 'shortlisted' | 'rejected' | 'hired';
56+
status: "pending" | "reviewing" | "shortlisted" | "rejected" | "hired";
3857
notes?: string;
3958
}
4059

@@ -44,18 +63,29 @@ interface JobApplicationsClientProps {
4463
applications: JobApplication[];
4564
}
4665

47-
export default function JobApplicationsClient({ jobId, jobTitle, applications }: JobApplicationsClientProps) {
66+
export default function JobApplicationsClient({
67+
jobId,
68+
jobTitle,
69+
applications,
70+
}: JobApplicationsClientProps) {
4871
const router = useRouter();
4972
const { toast } = useToast();
50-
const [selectedApplication, setSelectedApplication] = useState<JobApplication | null>(null);
73+
const [selectedApplication, setSelectedApplication] =
74+
useState<JobApplication | null>(null);
5175
const [isDetailsModalOpen, setIsDetailsModalOpen] = useState(false);
5276

53-
54-
55-
const handleStatusChange = async (applicationId: string, newStatus: string) => {
77+
const handleStatusChange = async (
78+
applicationId: string,
79+
newStatus: string,
80+
) => {
5681
const result = await updateApplicationStatus(
5782
applicationId,
58-
newStatus as 'pending' | 'reviewing' | 'shortlisted' | 'rejected' | 'hired'
83+
newStatus as
84+
| "pending"
85+
| "reviewing"
86+
| "shortlisted"
87+
| "rejected"
88+
| "hired",
5989
);
6090

6191
if (result.success) {
@@ -75,15 +105,35 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
75105

76106
const getStatusBadge = (status: string) => {
77107
const statusConfig = {
78-
pending: { color: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20", label: "Pending" },
79-
reviewing: { color: "bg-blue-500/10 text-blue-400 border-blue-500/20", label: "Reviewing" },
80-
shortlisted: { color: "bg-green-500/10 text-green-400 border-green-500/20", label: "Shortlisted" },
81-
rejected: { color: "bg-red-500/10 text-red-400 border-red-500/20", label: "Rejected" },
82-
hired: { color: "bg-purple-500/10 text-purple-400 border-purple-500/20", label: "Hired" },
108+
pending: {
109+
color: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20",
110+
label: "Pending",
111+
},
112+
reviewing: {
113+
color: "bg-blue-500/10 text-blue-400 border-blue-500/20",
114+
label: "Reviewing",
115+
},
116+
shortlisted: {
117+
color: "bg-green-500/10 text-green-400 border-green-500/20",
118+
label: "Shortlisted",
119+
},
120+
rejected: {
121+
color: "bg-red-500/10 text-red-400 border-red-500/20",
122+
label: "Rejected",
123+
},
124+
hired: {
125+
color: "bg-purple-500/10 text-purple-400 border-purple-500/20",
126+
label: "Hired",
127+
},
83128
};
84129

85-
const config = statusConfig[status as keyof typeof statusConfig] || statusConfig.pending;
86-
return <Badge variant="outline" className={config.color}>{config.label}</Badge>;
130+
const config =
131+
statusConfig[status as keyof typeof statusConfig] || statusConfig.pending;
132+
return (
133+
<Badge variant="outline" className={config.color}>
134+
{config.label}
135+
</Badge>
136+
);
87137
};
88138

89139
const breadcrumbItems = [
@@ -110,7 +160,6 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
110160
}
111161
};
112162

113-
114163
return (
115164
<div className="container mx-auto max-w-7xl p-6">
116165
<div className="mb-6">
@@ -120,7 +169,8 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
120169
<div className="mb-8">
121170
<H1>Applications for {jobTitle}</H1>
122171
<p className="mt-2 text-gray-400">
123-
{applications.length} applicant{applications.length !== 1 ? 's' : ''} found
172+
{applications.length} applicant{applications.length !== 1 ? "s" : ""}{" "}
173+
found
124174
</p>
125175
</div>
126176

@@ -129,11 +179,21 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
129179
<table className="w-full">
130180
<thead className="border-b border-gray-800">
131181
<tr className="text-left">
132-
<th className="px-4 py-3 text-sm font-medium text-gray-300">Applicant</th>
133-
<th className="px-4 py-3 text-sm font-medium text-gray-300">Contact</th>
134-
<th className="px-4 py-3 text-sm font-medium text-gray-300">Applied</th>
135-
<th className="px-4 py-3 text-sm font-medium text-gray-300">Status</th>
136-
<th className="px-4 py-3 text-sm font-medium text-gray-300 text-right">Actions</th>
182+
<th className="px-4 py-3 text-sm font-medium text-gray-300">
183+
Applicant
184+
</th>
185+
<th className="px-4 py-3 text-sm font-medium text-gray-300">
186+
Contact
187+
</th>
188+
<th className="px-4 py-3 text-sm font-medium text-gray-300">
189+
Applied
190+
</th>
191+
<th className="px-4 py-3 text-sm font-medium text-gray-300">
192+
Status
193+
</th>
194+
<th className="px-4 py-3 text-right text-sm font-medium text-gray-300">
195+
Actions
196+
</th>
137197
</tr>
138198
</thead>
139199
<tbody className="divide-y divide-gray-800">
@@ -155,7 +215,7 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
155215
href={application.linkedin}
156216
target="_blank"
157217
rel="noopener noreferrer"
158-
className="text-xs text-gray-400 hover:text-white flex items-center gap-1"
218+
className="flex items-center gap-1 text-xs text-gray-400 hover:text-white"
159219
>
160220
<Linkedin className="h-3 w-3" />
161221
</a>
@@ -165,7 +225,7 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
165225
href={application.github}
166226
target="_blank"
167227
rel="noopener noreferrer"
168-
className="text-xs text-gray-400 hover:text-white flex items-center gap-1"
228+
className="flex items-center gap-1 text-xs text-gray-400 hover:text-white"
169229
>
170230
<Github className="h-3 w-3" />
171231
</a>
@@ -175,7 +235,7 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
175235
href={application.portfolio}
176236
target="_blank"
177237
rel="noopener noreferrer"
178-
className="text-xs text-gray-400 hover:text-white flex items-center gap-1"
238+
className="flex items-center gap-1 text-xs text-gray-400 hover:text-white"
179239
>
180240
<Globe className="h-3 w-3" />
181241
</a>
@@ -211,17 +271,44 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
211271
<td className="px-4 py-3">
212272
<Select
213273
defaultValue={application.status}
214-
onValueChange={(value) => handleStatusChange(application.id, value)}
274+
onValueChange={(value) =>
275+
handleStatusChange(application.id, value)
276+
}
215277
>
216-
<SelectTrigger className="h-8 w-[120px] bg-gray-800 border-gray-700 text-xs text-white">
278+
<SelectTrigger className="h-8 w-[120px] border-gray-700 bg-gray-800 text-xs text-white">
217279
<SelectValue className="text-white" />
218280
</SelectTrigger>
219-
<SelectContent className="bg-gray-800 border-gray-700 text-white">
220-
<SelectItem value="pending" className="text-gray-300 hover:text-white">Pending</SelectItem>
221-
<SelectItem value="reviewing" className="text-gray-300 hover:text-white">Reviewing</SelectItem>
222-
<SelectItem value="shortlisted" className="text-gray-300 hover:text-white">Shortlisted</SelectItem>
223-
<SelectItem value="rejected" className="text-gray-300 hover:text-white">Rejected</SelectItem>
224-
<SelectItem value="hired" className="text-gray-300 hover:text-white">Hired</SelectItem>
281+
<SelectContent className="border-gray-700 bg-gray-800 text-white">
282+
<SelectItem
283+
value="pending"
284+
className="text-gray-300 hover:text-white"
285+
>
286+
Pending
287+
</SelectItem>
288+
<SelectItem
289+
value="reviewing"
290+
className="text-gray-300 hover:text-white"
291+
>
292+
Reviewing
293+
</SelectItem>
294+
<SelectItem
295+
value="shortlisted"
296+
className="text-gray-300 hover:text-white"
297+
>
298+
Shortlisted
299+
</SelectItem>
300+
<SelectItem
301+
value="rejected"
302+
className="text-gray-300 hover:text-white"
303+
>
304+
Rejected
305+
</SelectItem>
306+
<SelectItem
307+
value="hired"
308+
className="text-gray-300 hover:text-white"
309+
>
310+
Hired
311+
</SelectItem>
225312
</SelectContent>
226313
</Select>
227314
</td>
@@ -250,34 +337,40 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
250337
<span className="sr-only">Open resume menu</span>
251338
</Button>
252339
</DropdownMenuTrigger>
253-
<DropdownMenuContent align="end" className="bg-gray-900 border-gray-800">
340+
<DropdownMenuContent
341+
align="end"
342+
className="border-gray-800 bg-gray-900"
343+
>
254344
<DropdownMenuItem
255345
onClick={async () => {
256346
if (application.resume_url) {
257347
// If it's a full URL, open directly
258-
if (application.resume_url.startsWith('http')) {
348+
/* if (application.resume_url.startsWith('http')) {
259349
window.open(application.resume_url, '_blank');
260-
} else {
261-
// Otherwise, construct the Supabase storage URL
262-
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
263-
const resumeUrl = `${supabaseUrl}/storage/v1/object/public/resumes/${application.resume_url}`;
264-
window.open(resumeUrl, '_blank');
265-
}
350+
} else { */
351+
// Otherwise, construct the Supabase storage URL
352+
const supabaseUrl =
353+
process.env.NEXT_PUBLIC_SUPABASE_URL;
354+
const resumeUrl = `${supabaseUrl}/storage/v1/object/public/codebility/${application.resume_url}`;
355+
window.open(resumeUrl, "_blank");
266356
} else {
267357
toast({
268358
title: "No Resume",
269-
description: "This applicant hasn't uploaded a resume",
359+
description:
360+
"This applicant hasn't uploaded a resume",
270361
});
271362
}
272363
}}
273-
className="text-gray-300 hover:text-white hover:bg-gray-800"
364+
className="text-gray-300 hover:bg-gray-800 hover:text-white"
274365
>
275366
<Download className="mr-2 h-4 w-4" />
276367
Download Resume
277368
</DropdownMenuItem>
278369
<DropdownMenuItem
279-
onClick={() => handleDeleteApplication(application.id)}
280-
className="text-red-400 hover:text-red-300 hover:bg-gray-800"
370+
onClick={() =>
371+
handleDeleteApplication(application.id)
372+
}
373+
className="text-red-400 hover:bg-gray-800 hover:text-red-300"
281374
>
282375
<Trash2 className="mr-2 h-4 w-4" />
283376
Delete Application
@@ -298,7 +391,8 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
298391
<User className="mb-4 h-12 w-12 text-gray-600" />
299392
<p className="text-lg text-gray-400">No applications yet</p>
300393
<p className="mt-2 text-sm text-gray-500">
301-
Applications will appear here when candidates apply for this position
394+
Applications will appear here when candidates apply for this
395+
position
302396
</p>
303397
</div>
304398
)}
@@ -315,4 +409,4 @@ export default function JobApplicationsClient({ jobId, jobTitle, applications }:
315409
/>
316410
</div>
317411
);
318-
}
412+
}

0 commit comments

Comments
 (0)