Skip to content

Commit 5bdbe1a

Browse files
Merge pull request #13 from akirachix/feature/update
Feature/update
2 parents dac4782 + 6a36c42 commit 5bdbe1a

File tree

43 files changed

+1458
-788
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1458
-788
lines changed

recos/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
"@headlessui/react": "^2.2.8",
1515
"@heroicons/react": "^2.2.0",
1616
"class-variance-authority": "^0.7.1",
17-
"express": "^5.1.0",
1817
"js-cookie": "^3.0.5",
1918
"lucide-react": "^0.544.0",
2019
"next": "^15.5.3",
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
"use client"
3+
interface AboutSectionProps {
4+
about: string;
5+
}
6+
7+
export default function AboutSection({ about }: AboutSectionProps) {
8+
return (
9+
<div className="bg-white rounded-lg shadow p-6">
10+
<h3 className="text-lg font-semibold text-gray-800 mb-3">About</h3>
11+
<p className="text-gray-600 leading-relaxed">{about}</p>
12+
</div>
13+
);
14+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
"use client";
3+
4+
5+
import { Candidate } from "@/app/hooks/useCandidates";
6+
import { Job } from "@/app/hooks/useFetchJobs";
7+
8+
interface CandidateInfoProps {
9+
candidate: Candidate;
10+
job?: Job | null;
11+
}
12+
13+
export default function CandidateInfo({ candidate, job }: CandidateInfoProps) {
14+
return (
15+
<div className="bg-white rounded-lg shadow p-6">
16+
<div className="flex justify-between items-start">
17+
<div>
18+
<h2 className="text-xl font-bold text-gray-800">{candidate.name}</h2>
19+
<p className="text-gray-600 mt-1">
20+
{job ? job.job_title : candidate.job_title || "Position not specified"}
21+
</p>
22+
</div>
23+
<div className="text-right">
24+
<div className="flex items-center justify-end mb-2">
25+
<span className="text-sm text-gray-500 mr-2">Initial Match</span>
26+
<span className="text-lg font-semibold text-gray-800">85%</span>
27+
</div>
28+
<div className="flex items-center justify-end">
29+
<span className="text-sm text-gray-500 mr-2">Final Match</span>
30+
<span className="text-lg font-semibold text-green-600">90%</span>
31+
</div>
32+
</div>
33+
</div>
34+
</div>
35+
);
36+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import { useHiringDecision } from "@/app/hooks/useAiReport";
5+
6+
interface HiringDecisionProps {
7+
candidateId: string;
8+
}
9+
10+
export default function HiringDecision({ candidateId }: HiringDecisionProps) {
11+
const { decision, isSubmitting, makeDecision, resetDecision, error } = useHiringDecision(candidateId);
12+
const [showSuccess, setShowSuccess] = useState(false);
13+
14+
const handleDecision = async (value: "reject" | "advance") => {
15+
try {
16+
await makeDecision(value);
17+
setShowSuccess(true);
18+
setTimeout(() => {
19+
setShowSuccess(false);
20+
resetDecision();
21+
}, 3000);
22+
} catch (error) {
23+
console.error("Decision failed:", error);
24+
}
25+
};
26+
27+
return (
28+
<div className="bg-white rounded-lg shadow p-6 mt-6">
29+
<h3 className="text-lg font-semibold text-gray-800 mb-3">Hiring Decision</h3>
30+
{showSuccess && (
31+
<div className="mb-4 p-3 bg-green-100 border border-green-400 text-green-700 rounded">
32+
Decision submitted successfully!
33+
</div>
34+
)}
35+
{error && (
36+
<div className="mb-4 p-3 bg-red-100 border border-red-400 text-red-700 rounded">
37+
{error}
38+
</div>
39+
)}
40+
<div className="flex space-x-4">
41+
<button
42+
onClick={() => handleDecision("reject")}
43+
disabled={isSubmitting}
44+
className={`px-6 py-2 rounded-md font-medium transition-colors ${
45+
decision === "reject"
46+
? "bg-red-600 text-white"
47+
: "bg-white border border-gray-300 text-gray-700 hover:bg-gray-50"
48+
} ${isSubmitting ? "opacity-50 cursor-not-allowed" : ""}`}
49+
>
50+
{isSubmitting && decision === "reject" ? (
51+
<span className="flex items-center">
52+
<svg
53+
className="animate-spin -ml-1 mr-2 h-4 w-4"
54+
xmlns="http://www.w3.org/2000/svg"
55+
fill="none"
56+
viewBox="0 0 24 24"
57+
>
58+
<circle
59+
className="opacity-25"
60+
cx="12"
61+
cy="12"
62+
r="10"
63+
stroke="currentColor"
64+
strokeWidth="4"
65+
></circle>
66+
<path
67+
className="opacity-75"
68+
fill="currentColor"
69+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
70+
></path>
71+
</svg>
72+
Processing...
73+
</span>
74+
) : (
75+
"Reject"
76+
)}
77+
</button>
78+
<button
79+
onClick={() => handleDecision("advance")}
80+
disabled={isSubmitting}
81+
className={`px-6 py-2 rounded-md font-medium transition-colors ${
82+
decision === "advance"
83+
? "bg-green-600 text-white"
84+
: "bg-white border border-gray-300 text-gray-700 hover:bg-gray-50"
85+
} ${isSubmitting ? "opacity-50 cursor-not-allowed" : ""}`}
86+
>
87+
{isSubmitting && decision === "advance" ? (
88+
<span className="flex items-center">
89+
<svg
90+
className="animate-spin -ml-1 mr-2 h-4 w-4"
91+
xmlns="http://www.w3.org/2000/svg"
92+
fill="none"
93+
viewBox="0 0 24 24"
94+
>
95+
<circle
96+
className="opacity-25"
97+
cx="12"
98+
cy="12"
99+
r="10"
100+
stroke="currentColor"
101+
strokeWidth="4"
102+
></circle>
103+
<path
104+
className="opacity-75"
105+
fill="currentColor"
106+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
107+
></path>
108+
</svg>
109+
Processing...
110+
</span>
111+
) : (
112+
"Advance"
113+
)}
114+
</button>
115+
</div>
116+
</div>
117+
);
118+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
"use client";
3+
4+
interface PerformanceItem {
5+
name: string;
6+
value: 'High' | 'Medium' | 'Low';
7+
}
8+
9+
interface InterviewPerformanceProps {
10+
performance: PerformanceItem[];
11+
}
12+
13+
export default function InterviewPerformance({ performance }: InterviewPerformanceProps) {
14+
const getBarHeight = (value: 'High' | 'Medium' | 'Low'): string => {
15+
if (value === 'High') return '100%';
16+
if (value === 'Medium') return '66%';
17+
if (value === 'Low') return '33%';
18+
return '0%';
19+
};
20+
21+
const getBarColor = (value: 'High' | 'Medium' | 'Low'): string => {
22+
if (value === 'High') return 'bg-green-500';
23+
if (value === 'Medium') return 'bg-yellow-500';
24+
if (value === 'Low') return 'bg-red-500';
25+
return 'bg-gray-300';
26+
};
27+
28+
return (
29+
<div className="bg-white rounded-lg shadow p-6">
30+
<h3 className="text-lg font-semibold text-gray-800 mb-3">Interview Performance Analysis</h3>
31+
<div className="flex justify-around items-end h-40">
32+
{performance.map((item) => (
33+
<div key={item.name} className="flex flex-col items-center">
34+
<div className="w-12 bg-gray-200 rounded-t relative" style={{ height: '120px' }}>
35+
<div
36+
className={`absolute bottom-0 w-full ${getBarColor(item.value)} rounded-t transition-all duration-300`}
37+
style={{ height: getBarHeight(item.value) }}
38+
></div>
39+
</div>
40+
<p className="text-xs text-gray-600 mt-2 text-center w-16">{item.name}</p>
41+
</div>
42+
))}
43+
</div>
44+
</div>
45+
);
46+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
"use client";
3+
4+
interface Skill {
5+
name: string;
6+
match: number;
7+
}
8+
9+
interface SkillsSectionProps {
10+
skills: Skill[];
11+
}
12+
13+
export default function SkillsSection({ skills }: SkillsSectionProps) {
14+
return (
15+
<div className="bg-white rounded-lg shadow p-6">
16+
<h3 className="text-lg font-semibold text-gray-800 mb-3">Skills</h3>
17+
<div className="space-y-4">
18+
{skills.map((skill) => (
19+
<div key={skill.name}>
20+
<div className="flex justify-between mb-1">
21+
<span className="text-sm font-medium text-gray-700">{skill.name}</span>
22+
<span className="text-sm text-gray-500">{skill.match}%</span>
23+
</div>
24+
<div className="w-full bg-gray-200 rounded-full h-2">
25+
<div
26+
className="bg-blue-600 h-2 rounded-full transition-all duration-300"
27+
style={{ width: `${skill.match}%` }}
28+
></div>
29+
</div>
30+
</div>
31+
))}
32+
</div>
33+
</div>
34+
);
35+
}

0 commit comments

Comments
 (0)