Skip to content

Commit 749651c

Browse files
committed
v10a Patched with training Module
1 parent 443d1ff commit 749651c

30 files changed

Lines changed: 593 additions & 111 deletions

app/training/page.jsx

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"use client";
2+
import { useState, useEffect } from "react";
3+
import { motion, AnimatePresence } from "framer-motion";
4+
import {
5+
FiArrowLeft, FiCheck, FiEye, FiDownload, FiX, FiFileText, FiBookOpen,
6+
} from "react-icons/fi";
7+
import { trainingDetails, profile } from "@/data/portfolio";
8+
import RateCard from "@/components/RateCard";
9+
10+
/* -------- material preview modal (cert-style) -------- */
11+
function MaterialModal({ item, onClose }) {
12+
const isPdf = item?.type === "pdf" || item?.file?.toLowerCase().endsWith(".pdf");
13+
useEffect(() => {
14+
const onKey = (e) => e.key === "Escape" && onClose();
15+
document.addEventListener("keydown", onKey);
16+
document.body.style.overflow = "hidden";
17+
return () => {
18+
document.removeEventListener("keydown", onKey);
19+
document.body.style.overflow = "";
20+
};
21+
}, [onClose]);
22+
23+
return (
24+
<motion.div
25+
initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}
26+
onClick={onClose}
27+
className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm"
28+
>
29+
<motion.div
30+
initial={{ scale: 0.94, y: 12, opacity: 0 }}
31+
animate={{ scale: 1, y: 0, opacity: 1 }}
32+
exit={{ scale: 0.94, y: 12, opacity: 0 }}
33+
transition={{ type: "spring", stiffness: 260, damping: 24 }}
34+
onClick={(e) => e.stopPropagation()}
35+
className="relative w-full max-w-3xl glass rounded-2xl border border-line overflow-hidden"
36+
>
37+
<div className="flex items-start justify-between gap-4 p-5 border-b border-line">
38+
<div className="min-w-0">
39+
<h3 className="font-bold truncate">{item.name}</h3>
40+
{item.detail && <p className="text-xs text-muted truncate">{item.detail}</p>}
41+
</div>
42+
<button data-hover onClick={onClose} aria-label="Close"
43+
className="w-9 h-9 shrink-0 rounded-full border border-line flex items-center justify-center hover:border-accent hover:text-accent transition-colors">
44+
<FiX size={18} />
45+
</button>
46+
</div>
47+
48+
<div className="p-5">
49+
{isPdf ? (
50+
<object data={item.file} type="application/pdf" className="w-full h-[65vh] rounded-lg bg-white">
51+
<p className="text-muted text-sm">
52+
Preview unavailable.{" "}
53+
<a href={item.file} target="_blank" rel="noopener noreferrer" className="text-accent hover:underline">Open PDF ↗</a>
54+
</p>
55+
</object>
56+
) : (
57+
/* eslint-disable-next-line @next/next/no-img-element */
58+
<img src={item.file} alt={item.name} className="w-full max-h-[65vh] object-contain rounded-lg bg-white" />
59+
)}
60+
</div>
61+
62+
<div className="flex flex-wrap items-center justify-end gap-3 p-5 border-t border-line">
63+
<a href={item.file} target="_blank" rel="noopener noreferrer" data-hover
64+
className="text-sm px-4 py-2 rounded-full border border-line hover:border-accent hover:text-accent transition-colors flex items-center gap-2">
65+
<FiEye size={15} /> Open full
66+
</a>
67+
{item.downloadable && (
68+
<a href={item.file} download data-hover
69+
className="text-sm px-4 py-2 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition flex items-center gap-2">
70+
<FiDownload size={15} /> Download
71+
</a>
72+
)}
73+
</div>
74+
</motion.div>
75+
</motion.div>
76+
);
77+
}
78+
79+
export default function TrainingDetailsPage() {
80+
const [active, setActive] = useState(null);
81+
const d = trainingDetails;
82+
83+
return (
84+
<main className="min-h-screen">
85+
{/* Hero + back link */}
86+
<section className="relative overflow-hidden pt-16 pb-14 border-b border-line">
87+
<div className="pointer-events-none absolute inset-0 -z-10">
88+
<div className="absolute top-0 right-1/4 w-80 h-80 rounded-full bg-accent/15 blur-3xl" />
89+
</div>
90+
<div className="container">
91+
<a href="/" data-hover
92+
className="inline-flex items-center gap-2 text-sm text-muted hover:text-accent transition-colors">
93+
<FiArrowLeft size={16} /> Back to home
94+
</a>
95+
<h1 className="text-3xl sm:text-5xl font-extrabold mt-6">{d.heading}</h1>
96+
<p className="text-muted mt-4 max-w-2xl">{d.intro}</p>
97+
</div>
98+
</section>
99+
100+
{/* Modules (simple list) + Outcomes */}
101+
<section className="py-16">
102+
<div className="container grid md:grid-cols-2 gap-12">
103+
<div>
104+
<h2 className="text-2xl font-bold mb-6 flex items-center gap-2">
105+
<FiBookOpen className="text-accent" /> Course Modules
106+
</h2>
107+
<ul className="space-y-4">
108+
{d.modules.map((m, i) => (
109+
<motion.li key={m.title}
110+
initial={{ opacity: 0, x: -12 }}
111+
whileInView={{ opacity: 1, x: 0 }}
112+
viewport={{ once: true }}
113+
transition={{ duration: 0.4, delay: i * 0.05 }}
114+
className="flex gap-3">
115+
<span className="mt-0.5 w-7 h-7 shrink-0 rounded-lg bg-gradient-to-br from-accent to-accent2 text-bg text-xs font-bold flex items-center justify-center">
116+
{i + 1}
117+
</span>
118+
<div>
119+
<h3 className="font-semibold">{m.title}</h3>
120+
<p className="text-muted text-sm">{m.desc}</p>
121+
</div>
122+
</motion.li>
123+
))}
124+
</ul>
125+
</div>
126+
127+
<div>
128+
<h2 className="text-2xl font-bold mb-6">What You&apos;ll Be Able To Do</h2>
129+
<ul className="space-y-3">
130+
{d.outcomes.map((o) => (
131+
<li key={o} className="flex items-start gap-3 glass rounded-xl p-4">
132+
<span className="mt-0.5 w-6 h-6 shrink-0 rounded-md bg-accent/15 text-accent flex items-center justify-center">
133+
<FiCheck size={14} />
134+
</span>
135+
<span className="text-sm">{o}</span>
136+
</li>
137+
))}
138+
</ul>
139+
</div>
140+
</div>
141+
</section>
142+
143+
{/* Materials — preview cards */}
144+
{d.materials?.length > 0 && (
145+
<section className="py-16 border-t border-line">
146+
<div className="container">
147+
<h2 className="text-2xl font-bold mb-3">Training Materials</h2>
148+
<p className="text-muted mb-8 text-sm">
149+
Click to preview. Samples only — full materials provided with booking.
150+
</p>
151+
<div className="grid sm:grid-cols-2 lg:grid-cols-3 gap-5">
152+
{d.materials.map((m, i) => (
153+
<motion.button
154+
key={m.name} type="button" data-hover
155+
onClick={() => setActive(m)}
156+
initial={{ opacity: 0, y: 20 }}
157+
whileInView={{ opacity: 1, y: 0 }}
158+
viewport={{ once: true }}
159+
transition={{ duration: 0.45, delay: i * 0.06 }}
160+
whileHover={{ y: -6 }}
161+
className="text-left glass rounded-2xl p-5 hover:border-accent/60 transition-colors"
162+
>
163+
<div className="flex items-center gap-3">
164+
<span className="w-11 h-11 shrink-0 rounded-xl bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg">
165+
<FiFileText size={20} />
166+
</span>
167+
<div className="min-w-0">
168+
<h3 className="font-semibold leading-tight">{m.name}</h3>
169+
{m.sample && (
170+
<span className="text-[10px] font-mono px-2 py-0.5 rounded-full bg-accent/10 text-accent mt-1 inline-block">
171+
SAMPLE
172+
</span>
173+
)}
174+
</div>
175+
</div>
176+
{m.detail && <p className="text-muted text-sm mt-3">{m.detail}</p>}
177+
<span className="mt-3 inline-flex items-center gap-1.5 text-xs font-medium text-accent">
178+
<FiEye size={13} /> Preview
179+
</span>
180+
</motion.button>
181+
))}
182+
</div>
183+
</div>
184+
</section>
185+
)}
186+
187+
{/* Rate card (reused) */}
188+
<section className="py-16 border-t border-line">
189+
<div className="container">
190+
<h2 className="text-2xl sm:text-3xl font-bold mb-2">Training Options &amp; Rates</h2>
191+
<p className="text-muted mb-10 text-sm">Pick the format that fits — book directly via WhatsApp or email.</p>
192+
<RateCard />
193+
</div>
194+
</section>
195+
196+
<footer className="py-10 border-t border-line text-center text-sm text-muted">
197+
<a href="/" className="text-accent hover:underline">← Back to portfolio</a>
198+
</footer>
199+
200+
<AnimatePresence>
201+
{active && <MaterialModal item={active} onClose={() => setActive(null)} />}
202+
</AnimatePresence>
203+
</main>
204+
);
205+
}

components/Events.jsx

Lines changed: 118 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,123 @@
11
"use client";
2-
import { motion } from "framer-motion";
2+
import { useState, useEffect } from "react";
3+
import { motion, AnimatePresence } from "framer-motion";
4+
import { FiX, FiExternalLink, FiEye, FiAward } from "react-icons/fi";
35
import { events } from "@/data/portfolio";
46

5-
// Small, informational chip cloud. Each chip wobbles / jumps playfully
6-
// on hover (random rotate + lift) — purely for fun, no claims.
7+
// Normalise: allow plain strings OR objects {name, image, link, detail}
8+
function norm(e) {
9+
return typeof e === "string" ? { name: e } : e;
10+
}
11+
12+
function Modal({ item, onClose }) {
13+
const isPdf = item?.image?.toLowerCase().endsWith(".pdf");
14+
useEffect(() => {
15+
const onKey = (e) => e.key === "Escape" && onClose();
16+
document.addEventListener("keydown", onKey);
17+
document.body.style.overflow = "hidden";
18+
return () => {
19+
document.removeEventListener("keydown", onKey);
20+
document.body.style.overflow = "";
21+
};
22+
}, [onClose]);
23+
24+
return (
25+
<motion.div
26+
initial={{ opacity: 0 }}
27+
animate={{ opacity: 1 }}
28+
exit={{ opacity: 0 }}
29+
onClick={onClose}
30+
className="fixed inset-0 z-[100] flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm"
31+
>
32+
<motion.div
33+
initial={{ scale: 0.94, y: 12, opacity: 0 }}
34+
animate={{ scale: 1, y: 0, opacity: 1 }}
35+
exit={{ scale: 0.94, y: 12, opacity: 0 }}
36+
transition={{ type: "spring", stiffness: 260, damping: 24 }}
37+
onClick={(e) => e.stopPropagation()}
38+
className="relative w-full max-w-2xl glass rounded-2xl border border-line overflow-hidden"
39+
>
40+
<div className="flex items-start justify-between gap-4 p-5 border-b border-line">
41+
<div className="min-w-0">
42+
<h3 className="font-bold truncate">{item.name}</h3>
43+
{item.detail && <p className="text-xs text-muted truncate">{item.detail}</p>}
44+
</div>
45+
<button
46+
data-hover
47+
onClick={onClose}
48+
aria-label="Close"
49+
className="w-9 h-9 shrink-0 rounded-full border border-line flex items-center justify-center hover:border-accent hover:text-accent transition-colors"
50+
>
51+
<FiX size={18} />
52+
</button>
53+
</div>
54+
55+
<div className="p-5">
56+
{item.image ? (
57+
isPdf ? (
58+
<object data={item.image} type="application/pdf" className="w-full h-[60vh] rounded-lg bg-white">
59+
<p className="text-muted text-sm">
60+
Preview unavailable.{" "}
61+
<a href={item.image} target="_blank" rel="noopener noreferrer" className="text-accent hover:underline">Open PDF ↗</a>
62+
</p>
63+
</object>
64+
) : (
65+
/* eslint-disable-next-line @next/next/no-img-element */
66+
<img src={item.image} alt={item.name} className="w-full max-h-[60vh] object-contain rounded-lg bg-white" />
67+
)
68+
) : (
69+
<div className="text-center py-10">
70+
<FiAward className="mx-auto text-accent mb-3" size={40} />
71+
<p className="text-muted text-sm">{item.detail || item.name}</p>
72+
</div>
73+
)}
74+
</div>
75+
76+
{item.link && (
77+
<div className="flex items-center justify-end gap-3 p-5 border-t border-line">
78+
<a
79+
href={item.link}
80+
target="_blank"
81+
rel="noopener noreferrer"
82+
data-hover
83+
className="text-sm px-4 py-2 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition flex items-center gap-2"
84+
>
85+
<FiExternalLink size={15} /> View credential
86+
</a>
87+
</div>
88+
)}
89+
</motion.div>
90+
</motion.div>
91+
);
92+
}
93+
794
export default function Events() {
95+
const [active, setActive] = useState(null);
96+
const list = (events || []).map(norm);
97+
898
return (
999
<section id="events" className="py-20 border-t border-line">
10100
<div className="container">
11101
<span className="text-sm font-mono text-accent">✦ ALWAYS LEARNING</span>
12-
<h2 className="text-2xl sm:text-3xl font-bold mt-2">Trainings &amp; Events Attended</h2>
102+
<h2 className="text-2xl sm:text-3xl font-bold mt-2">
103+
Trainings Given &amp; Events Attended
104+
</h2>
13105
<p className="text-muted mt-2 mb-8">
14-
A few of the events, bootcamps and sessions I&apos;ve taken part in. (Hover for fun!)
106+
Sessions I&apos;ve delivered and events I&apos;ve taken part in. Click any with a
107+
badge to preview the certificate.
15108
</p>
16109

17110
<div className="flex flex-wrap gap-3">
18-
{events.map((name, i) => {
19-
// deterministic-ish random values per chip
20-
const rot = ((i * 37) % 14) - 7; // -7..+7 deg
21-
const dx = ((i * 53) % 16) - 8; // -8..+8 px
111+
{list.map((ev, i) => {
112+
const rot = ((i * 37) % 14) - 7;
113+
const dx = ((i * 53) % 16) - 8;
114+
const clickable = Boolean(ev.image || ev.link);
22115
return (
23-
<motion.span
24-
key={name}
116+
<motion.button
117+
key={ev.name + i}
25118
data-hover
119+
type="button"
120+
onClick={() => clickable && setActive(ev)}
26121
initial={{ opacity: 0, scale: 0.9 }}
27122
whileInView={{ opacity: 1, scale: 1 }}
28123
viewport={{ once: true }}
@@ -34,14 +129,23 @@ export default function Events() {
34129
scale: 1.08,
35130
transition: { type: "spring", stiffness: 320, damping: 12 },
36131
}}
37-
className="cursor-default select-none px-4 py-2 rounded-full border border-line bg-surface text-sm text-muted hover:text-accent hover:border-accent"
132+
className={`select-none inline-flex items-center gap-1.5 px-4 py-2 rounded-full border text-sm transition-colors ${
133+
clickable
134+
? "cursor-pointer border-accent/50 text-text hover:text-accent hover:border-accent bg-surface"
135+
: "cursor-default border-line text-muted bg-surface hover:text-accent hover:border-accent"
136+
}`}
38137
>
39-
{name}
40-
</motion.span>
138+
{clickable && <FiAward size={13} className="text-accent" />}
139+
{ev.name}
140+
</motion.button>
41141
);
42142
})}
43143
</div>
44144
</div>
145+
146+
<AnimatePresence>
147+
{active && <Modal item={active} onClose={() => setActive(null)} />}
148+
</AnimatePresence>
45149
</section>
46150
);
47151
}

components/Hero.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default function Hero() {
6363
download="Krishna_Andhe_CV.pdf"
6464
className="px-6 py-3 rounded-full border border-accent text-accent font-semibold flex items-center gap-2 hover:bg-accent hover:text-bg transition"
6565
>
66-
<FiDownload size={18} /> Résumé
66+
<FiDownload size={18} /> Download Resume
6767
</a>
6868
)}
6969
</motion.div>

0 commit comments

Comments
 (0)