Skip to content

Commit c2d41ff

Browse files
committed
Feature (JobFit Implementation)
1 parent c099a63 commit c2d41ff

File tree

7 files changed

+231
-16
lines changed

7 files changed

+231
-16
lines changed

src/App.jsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useState, Suspense, lazy } from 'react';
2-
import { BrowserRouter, Routes, Route } from 'react-router-dom';
2+
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
33
import { ThemeProvider } from './theme/ThemeProvider';
44
import { AnimationProvider } from './context/AnimationContext';
55
import { Box, Container } from '@mantine/core';
@@ -148,6 +148,16 @@ function App() {
148148
<Route path={resolvePath('/projects')} element={<Projects />} />
149149
<Route path={resolvePath('/about')} element={<About />} />
150150
<Route path={resolvePath('/contact')} element={<Contact />} />
151+
152+
{/* Redirect to JobFit application */}
153+
<Route
154+
path={resolvePath('/JobFit')}
155+
element={<Navigate to="https://hxndev.github.io/JobFit/" replace />}
156+
/>
157+
<Route
158+
path={resolvePath('/JobFit/*')}
159+
element={<Navigate to="https://hxndev.github.io/JobFit/" replace />}
160+
/>
151161
</Routes>
152162
</Suspense>
153163
</PageTransition>

src/components/projects/EnhancedProjectCard.jsx

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { useState, useEffect, useRef } from 'react';
22
import { Paper, Text, Title, Group, Badge, Box } from '@mantine/core';
3-
import { IconBrandGithub, IconExternalLink, IconInfoCircle } from '@tabler/icons-react';
3+
import { IconBrandGithub, IconExternalLink, IconInfoCircle, IconRocket } from '@tabler/icons-react';
44
import { useColorScheme } from '../../theme/ThemeProvider';
55

66
const EnhancedProjectCard = ({
@@ -22,6 +22,12 @@ const EnhancedProjectCard = ({
2222
const { colorScheme } = useColorScheme();
2323
const isDark = colorScheme === 'dark';
2424

25+
// Check if this is the JobFit project to highlight the live demo
26+
const isJobFit = projectId === 'jobfit' || (title && title.toLowerCase().includes('jobfit'));
27+
const isPortfolio =
28+
projectId === 'portfolio' || (title && title.toLowerCase().includes('portfolio'));
29+
const isLiveDemo = isJobFit || isPortfolio;
30+
2531
// Determine image source
2632
const imgSrc = imageError
2733
? fallbackImage || 'https://placehold.co/600x400/9B00FF/FFFFFF?text=Project'
@@ -67,6 +73,13 @@ const EnhancedProjectCard = ({
6773
}
6874
};
6975

76+
// Handle direct link to live demo
77+
const handleLiveDemo = e => {
78+
e.preventDefault();
79+
e.stopPropagation();
80+
window.open(liveUrl, '_blank');
81+
};
82+
7083
return (
7184
<Paper
7285
ref={cardRef}
@@ -174,6 +187,27 @@ const EnhancedProjectCard = ({
174187
</Badge>
175188
</Box>
176189
)}
190+
191+
{/* Live demo badge for JobFit */}
192+
{isLiveDemo && liveUrl && (
193+
<Box
194+
style={{
195+
position: 'absolute',
196+
top: '10px',
197+
right: '10px',
198+
zIndex: 2,
199+
}}
200+
>
201+
<Badge
202+
color="green"
203+
radius="xl"
204+
variant="filled"
205+
gradient={{ from: '#00F5FF', to: '#00B5AD' }}
206+
>
207+
Live Demo
208+
</Badge>
209+
</Box>
210+
)}
177211
</Box>
178212

179213
{/* Content */}
@@ -251,23 +285,27 @@ const EnhancedProjectCard = ({
251285
href={liveUrl}
252286
target="_blank"
253287
rel="noopener noreferrer"
254-
onClick={e => e.stopPropagation()}
288+
onClick={isLiveDemo ? handleLiveDemo : e => e.stopPropagation()}
255289
style={{
256290
padding: '8px 12px',
257291
borderRadius: '25px',
258292
color: 'white',
259-
background: 'linear-gradient(45deg, #9B00FF, #00F5FF)',
293+
background: isLiveDemo
294+
? 'linear-gradient(45deg, #00F5FF, #00B5AD)'
295+
: 'linear-gradient(45deg, #9B00FF, #00F5FF)',
260296
textDecoration: 'none',
261297
fontSize: '14px',
262298
display: 'flex',
263299
alignItems: 'center',
264300
gap: '6px',
265301
transition: 'all 0.3s ease',
266302
transform: isHovered ? 'translateY(-2px)' : 'translateY(0)',
303+
boxShadow: isLiveDemo && isHovered ? '0 4px 12px rgba(0, 245, 255, 0.3)' : 'none',
304+
fontWeight: isLiveDemo ? 600 : 400,
267305
}}
268306
>
269-
<IconExternalLink size={16} />
270-
Live Demo
307+
{isLiveDemo ? <IconRocket size={16} /> : <IconExternalLink size={16} />}
308+
{isLiveDemo ? 'Try Demo' : 'Live Demo'}
271309
</a>
272310
)}
273311

src/components/projects/ProjectDetail.jsx

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ import {
1818
IconBrandGithub,
1919
IconArrowLeft,
2020
IconCircleCheck,
21+
IconRocket,
22+
IconInfoCircle,
2123
} from '@tabler/icons-react';
2224
import { gsap } from 'gsap';
2325
import { useColorScheme } from '../../theme/ThemeProvider';
@@ -29,6 +31,10 @@ const ProjectDetail = ({ project, onBack }) => {
2931
const { reducedMotion } = useAnimationContext();
3032
const isDark = colorScheme === 'dark';
3133

34+
// Check if this is the JobFit project
35+
const isJobFit =
36+
project?.id === 'jobfit' || (project?.title && project?.title.toLowerCase().includes('jobfit'));
37+
3238
// Animate entrance
3339
useEffect(() => {
3440
if (!containerRef.current || reducedMotion) return;
@@ -135,6 +141,21 @@ const ProjectDetail = ({ project, onBack }) => {
135141
{project.date}
136142
</Badge>
137143
)}
144+
145+
{/* Live demo badge for JobFit */}
146+
{isJobFit && project.liveUrl && (
147+
<Badge
148+
color="teal"
149+
variant="filled"
150+
size="lg"
151+
sx={{
152+
background: 'linear-gradient(45deg, #00F5FF, #00B5AD)',
153+
boxShadow: '0 2px 8px rgba(0, 245, 255, 0.3)',
154+
}}
155+
>
156+
Live Demo Available
157+
</Badge>
158+
)}
138159
</Group>
139160
</Box>
140161
</Box>
@@ -184,9 +205,61 @@ const ProjectDetail = ({ project, onBack }) => {
184205
</>
185206
)}
186207

208+
{/* API Key note for JobFit */}
209+
{isJobFit && (
210+
<Box
211+
className="animate-in"
212+
sx={{
213+
padding: '15px',
214+
borderRadius: '8px',
215+
border: '1px solid #00F5FF',
216+
backgroundColor: 'rgba(0, 245, 255, 0.05)',
217+
marginBottom: '20px',
218+
}}
219+
>
220+
<Text size="sm" weight={500}>
221+
<IconInfoCircle size={16} style={{ marginRight: '8px', verticalAlign: 'middle' }} />
222+
The live demo requires a Google Gemini API key to function. You can obtain a free
223+
API key from{' '}
224+
<a
225+
href="https://aistudio.google.com/"
226+
target="_blank"
227+
rel="noopener noreferrer"
228+
style={{ color: '#00F5FF', textDecoration: 'underline' }}
229+
>
230+
Google AI Studio
231+
</a>
232+
.
233+
</Text>
234+
</Box>
235+
)}
236+
187237
{/* Action Buttons */}
188238
<Group className="animate-in" mt="xl">
189-
{project.liveUrl && (
239+
{/* Special button for JobFit */}
240+
{isJobFit && project.liveUrl && (
241+
<Button
242+
component="a"
243+
href={project.liveUrl}
244+
target="_blank"
245+
leftSection={<IconRocket size={16} />}
246+
size="lg"
247+
sx={{
248+
background: 'linear-gradient(45deg, #00F5FF, #00B5AD)',
249+
boxShadow: '0 4px 10px rgba(0, 245, 255, 0.3)',
250+
'&:hover': {
251+
boxShadow: '0 6px 15px rgba(0, 245, 255, 0.4)',
252+
transform: 'translateY(-2px)',
253+
},
254+
transition: 'all 0.3s ease',
255+
marginRight: '10px',
256+
}}
257+
>
258+
Try JobFit Live Demo
259+
</Button>
260+
)}
261+
262+
{!isJobFit && project.liveUrl && (
190263
<Button
191264
component="a"
192265
href={project.liveUrl}

src/components/projects/ProjectModal.jsx

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
IconCode,
2424
IconInfoCircle,
2525
IconCircleCheck,
26+
IconRocket,
2627
} from '@tabler/icons-react';
2728
import { gsap } from 'gsap';
2829
import { useColorScheme } from '../../theme/ThemeProvider';
@@ -37,6 +38,10 @@ const ProjectModal = ({ project, isOpen, onClose }) => {
3738
const { reducedMotion } = useAnimationContext();
3839
const isDark = colorScheme === 'dark';
3940

41+
// Check if this is the JobFit project
42+
const isJobFit =
43+
project?.id === 'jobfit' || (project?.title && project?.title.toLowerCase().includes('jobfit'));
44+
4045
// Prevent body scrolling when modal is open
4146
useEffect(() => {
4247
if (isOpen) {
@@ -254,6 +259,22 @@ const ProjectModal = ({ project, isOpen, onClose }) => {
254259
Featured Project
255260
</Badge>
256261
)}
262+
263+
{/* Live demo badge for JobFit */}
264+
{isJobFit && project.liveUrl && (
265+
<Badge
266+
className="animate-item"
267+
color="teal"
268+
variant="filled"
269+
ml="sm"
270+
sx={{
271+
background: 'linear-gradient(45deg, #00F5FF, #00B5AD)',
272+
boxShadow: '0 2px 8px rgba(0, 245, 255, 0.3)',
273+
}}
274+
>
275+
Live Demo Available
276+
</Badge>
277+
)}
257278
</Box>
258279
</Box>
259280

@@ -344,6 +365,38 @@ const ProjectModal = ({ project, isOpen, onClose }) => {
344365
</Badge>
345366
))}
346367
</Group>
368+
369+
{/* API Key note for JobFit */}
370+
{isJobFit && (
371+
<Box
372+
className="animate-item"
373+
sx={{
374+
padding: '15px',
375+
borderRadius: '8px',
376+
border: '1px solid #00F5FF',
377+
backgroundColor: 'rgba(0, 245, 255, 0.05)',
378+
marginBottom: '20px',
379+
}}
380+
>
381+
<Text size="sm" weight={500}>
382+
<IconInfoCircle
383+
size={16}
384+
style={{ marginRight: '8px', verticalAlign: 'middle' }}
385+
/>
386+
The live demo requires a Google Gemini API key to function. You can obtain
387+
a free API key from{' '}
388+
<a
389+
href="https://aistudio.google.com/"
390+
target="_blank"
391+
rel="noopener noreferrer"
392+
style={{ color: '#00F5FF', textDecoration: 'underline' }}
393+
>
394+
Google AI Studio
395+
</a>
396+
.
397+
</Text>
398+
</Box>
399+
)}
347400
</Box>
348401
)}
349402

@@ -443,7 +496,29 @@ const ProjectModal = ({ project, isOpen, onClose }) => {
443496

444497
{/* Action buttons */}
445498
<Group position="center" mt="xl" className="animate-item">
446-
{project.liveUrl && (
499+
{isJobFit && project.liveUrl && (
500+
<Button
501+
component="a"
502+
href={project.liveUrl}
503+
target="_blank"
504+
leftSection={<IconRocket size={16} />}
505+
size="lg"
506+
sx={{
507+
background: 'linear-gradient(45deg, #00F5FF, #00B5AD)',
508+
boxShadow: '0 4px 10px rgba(0, 245, 255, 0.3)',
509+
'&:hover': {
510+
boxShadow: '0 6px 15px rgba(0, 245, 255, 0.4)',
511+
transform: 'translateY(-2px)',
512+
},
513+
transition: 'all 0.3s ease',
514+
marginRight: '20px',
515+
}}
516+
>
517+
Try JobFit Live Demo
518+
</Button>
519+
)}
520+
521+
{!isJobFit && project.liveUrl && (
447522
<Button
448523
component="a"
449524
href={project.liveUrl}

src/data/projects.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"fallbackImage": "https://placehold.co/600x400/9B00FF/FFFFFF?text=JobFit",
1010
"technologies": ["Python", "Machine Learning", "NLP", "Flask", "React"],
1111
"githubUrl": "https://github.com/HxnDev/JobFit",
12-
"liveUrl": null,
12+
"liveUrl": "https://hxndev.github.io/JobFit/",
1313
"featured": true,
1414
"category": "ai",
1515
"date": "2023",
@@ -18,7 +18,9 @@
1818
"Resume parsing and analysis",
1919
"Personalized job recommendations",
2020
"Skill gap analysis",
21-
"User-friendly interface"
21+
"ATS compatibility checking",
22+
"Cover letter generation",
23+
"Interview preparation tools"
2224
]
2325
},
2426
{

src/pages/Home.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const Home = () => {
2424
}, [allProjects]);
2525

2626
const handleViewDetails = projectId => {
27-
window.location.href = `/hxndev.github.io/projects?project=${projectId}`;
27+
window.location.href = `/projects?project=${projectId}`;
2828
};
2929

3030
return (

0 commit comments

Comments
 (0)