Skip to content

Commit 443d1ff

Browse files
committed
v10 - M365 training rate card + events chip cloud
1 parent ced5628 commit 443d1ff

42 files changed

Lines changed: 1126 additions & 402 deletions

Some content is hidden

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

Designer.png

-630 KB
Binary file not shown.

README.md

Lines changed: 22 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,40 @@
1-
# Krishna Andhe — Portfolio (v5)
1+
# Krishna Andhe — Portfolio (v10a)
22

33
Interactive, animated single-page portfolio / living resume with light/dark theming.
4+
Positioned for a **leadership-track IT professional & Microsoft 365 trainer**.
45
Live at **[krishnaandhe.github.io](https://krishnaandhe.github.io)**.
56

6-
## ✨ Features
7-
- 🖼️ **2-column hero** with profile photo + rotating gradient ring
8-
- 🪪 **Custom logo** (`site_logo.png`) in navbar + favicon
9-
- 🌗 **Light / Dark theme toggle**
10-
- 🖱️ **Custom animated cursor** (desktop) + scroll progress bar
11-
-**Glowing Tech Stack grid** (static, pulsing glow — no scrolling)
12-
- 🗂️ **Projects** — featured (manual) + **auto-pulled public GitHub repos**
13-
- 🧾 **Expandable Experience timeline** — click a role to reveal resume details
14-
- 🧊 **3D tilt contact cards** (multi-handle)
7+
## 🧭 Section Order
8+
1. **Hero** — identity, role, location, résumé + book-training buttons
9+
2. **About Me** — leadership + trainer highlights
10+
3. **Experience** — full 6-role career (hover to expand)
11+
4. **Tech Stack** — animated proficiency bars
12+
5. **Projects** — Featured + auto-pulled GitHub repos
13+
6. **Certifications & Education** — grouped + click-to-view proof modal
14+
7. **Training** — Microsoft 365 rate card (tiered, "Book" CTAs)
15+
8. **Testimonials** 🆕 — short & simple quotes under Training
16+
9. **Trainings & Events Attended** — playful hover chip cloud
17+
10. **Contact** — social grid + QR + copy-email + résumé
18+
19+
## 🆕 v10a Addition — Testimonials
20+
Short, simple quote cards placed directly under Training.
21+
Edit in `data/portfolio.js → testimonials` (each: `quote`, `name`, `role`).
22+
Add or remove freely — the grid adapts (1–3+ columns).
1523

1624
## 🚀 Stack
1725
Next.js 15 · React 19 · Tailwind CSS 3 · Framer Motion 11 · next-themes · react-icons
1826

19-
## 🖼️ Photo & Logo
20-
- Photo: put your image at `public/profile.jpg`, then set `photo: "/profile.jpg"` in `data/portfolio.js`.
21-
- Logo: replace `public/site_logo.png` with your own (navbar + favicon use it).
22-
23-
## 🔗 GitHub Auto-Pull (Projects)
24-
Configured in `data/portfolio.js``github`:
25-
```js
26-
export const github = {
27-
username: "krishnaandhe",
28-
enabled: true, // false = turn off auto-pull
29-
max: 6, // how many repos to show
30-
exclude: ["krishnaandhe.github.io", "krishnaandhe"],
31-
includeForks: false,
32-
onlyTopic: "", // e.g. "showcase" → only repos with this GitHub topic
33-
sort: "updated", // "updated" | "stars"
34-
};
35-
```
36-
Repos are fetched **in the browser** from the public GitHub API — no server needed.
37-
New public repos appear automatically. `featuredProjects` (e.g. private Route Arc)
38-
always show first.
39-
40-
> Tip: to curate, tag chosen repos with a GitHub **topic** (e.g. `showcase`) and set
41-
> `onlyTopic: "showcase"`. Only those will appear.
42-
43-
## 🧾 Experience (Resume Details)
44-
Edit `experience` in `data/portfolio.js`. Each role supports `summary`, a `details`
45-
array (bullets shown on expand) and `tags`.
46-
47-
## 🎨 Re-theme
48-
Colors live in `app/globals.css``.light` / `.dark` (RGB triplets).
27+
## 📁 Assets (in /public)
28+
`site_logo.png` · `logo.svg` · `profile.png` · `resume.pdf`
29+
(downloads as `Krishna_Andhe_CV.pdf`) · `qr.png` · `og.png` · `certs/*.jpg`
4930

5031
## 🛠️ Local Dev
5132
```bash
5233
npm install
5334
npm run dev # http://localhost:3000
5435
```
5536

56-
## 📦 Deploy
37+
## 🌐 Deploy
5738
```bash
5839
npm run build
5940
git add . && git commit -m "update" && git push

app/globals.css

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ body {
5050
border: 1px solid rgb(var(--line) / 0.8);
5151
}
5252

53-
/* Custom cursor (desktop only) */
5453
@media (pointer: fine) {
5554
* {
5655
cursor: none;

app/layout.jsx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,32 @@ import "./globals.css";
22
import { profile } from "@/data/portfolio";
33
import Providers from "@/components/Providers";
44

5+
const SITE_URL = "https://krishnaandhe.github.io";
6+
57
export const metadata = {
8+
metadataBase: new URL(SITE_URL),
69
title: `${profile.name}${profile.role}`,
710
description: profile.tagline,
811
icons: { icon: "/site_logo.png" },
912
openGraph: {
1013
title: `${profile.name} — Portfolio`,
1114
description: profile.tagline,
15+
url: SITE_URL,
16+
siteName: profile.name,
1217
type: "website",
18+
images: [{ url: "/og.png", width: 1200, height: 630, alt: `${profile.name}` }],
19+
},
20+
twitter: {
21+
card: "summary_large_image",
22+
title: `${profile.name} — Portfolio`,
23+
description: profile.tagline,
24+
images: ["/og.png"],
1325
},
1426
};
1527

1628
export default function RootLayout({ children }) {
1729
return (
1830
<html lang="en" suppressHydrationWarning>
19-
{/* suppressHydrationWarning on body too: some browser extensions inject
20-
attributes onto <body> before React hydrates (harmless). */}
2131
<body suppressHydrationWarning>
2232
<Providers>{children}</Providers>
2333
</body>

app/page.jsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ import CustomCursor from "@/components/CustomCursor";
22
import ScrollProgress from "@/components/ScrollProgress";
33
import Navbar from "@/components/Navbar";
44
import Hero from "@/components/Hero";
5-
import Stack from "@/components/Stack";
6-
import Projects from "@/components/Projects";
75
import About from "@/components/About";
86
import Experience from "@/components/Experience";
7+
import Stack from "@/components/Stack";
8+
import Projects from "@/components/Projects";
9+
import Credentials from "@/components/Credentials";
10+
import Training from "@/components/Training";
11+
import Testimonials from "@/components/Testimonials";
12+
import Events from "@/components/Events";
913
import Contact from "@/components/Contact";
1014

1115
export default function Home() {
@@ -16,10 +20,14 @@ export default function Home() {
1620
<Navbar />
1721
<main>
1822
<Hero />
19-
<Stack />
20-
<Projects />
2123
<About />
2224
<Experience />
25+
<Stack />
26+
<Projects />
27+
<Credentials />
28+
<Training />
29+
<Testimonials />
30+
<Events />
2331
<Contact />
2432
</main>
2533
</>

components/About.jsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22
import { motion } from "framer-motion";
3+
import { FiCheck } from "react-icons/fi";
34
import { about, services } from "@/data/portfolio";
45
import { getIcon } from "./icons";
56

@@ -13,14 +14,23 @@ export default function About() {
1314
viewport={{ once: true }}
1415
transition={{ duration: 0.6 }}
1516
>
16-
<h2 className="text-3xl sm:text-4xl font-bold mb-6">
17-
{about.heading}
18-
</h2>
17+
<h2 className="text-3xl sm:text-4xl font-bold mb-6">{about.heading}</h2>
1918
{about.paragraphs.map((p, i) => (
20-
<p key={i} className="text-muted leading-relaxed mb-4">
21-
{p}
22-
</p>
19+
<p key={i} className="text-muted leading-relaxed mb-4">{p}</p>
2320
))}
21+
22+
{about.highlights?.length > 0 && (
23+
<ul className="mt-6 grid sm:grid-cols-2 gap-2.5">
24+
{about.highlights.map((h) => (
25+
<li key={h} className="flex items-start gap-2 text-sm">
26+
<span className="mt-0.5 w-5 h-5 shrink-0 rounded-md bg-accent/15 text-accent flex items-center justify-center">
27+
<FiCheck size={12} />
28+
</span>
29+
{h}
30+
</li>
31+
))}
32+
</ul>
33+
)}
2434
</motion.div>
2535

2636
<div className="grid gap-4">
@@ -42,9 +52,7 @@ export default function About() {
4252
</span>
4353
<div>
4454
<h3 className="font-semibold">{s.title}</h3>
45-
<p className="text-muted text-sm mt-1 leading-relaxed">
46-
{s.desc}
47-
</p>
55+
<p className="text-muted text-sm mt-1 leading-relaxed">{s.desc}</p>
4856
</div>
4957
</div>
5058
</motion.div>

components/Contact.jsx

Lines changed: 103 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
11
"use client";
2-
import { motion } from "framer-motion";
2+
import { useState } from "react";
3+
import { motion, AnimatePresence } from "framer-motion";
4+
import { FiCopy, FiCheck, FiDownload } from "react-icons/fi";
35
import { profile, socials } from "@/data/portfolio";
46
import { getIcon } from "./icons";
57
import Tilt from "./Tilt";
68

79
export default function Contact() {
10+
const [copied, setCopied] = useState(false);
11+
12+
const copyEmail = async () => {
13+
try {
14+
await navigator.clipboard.writeText(profile.email);
15+
} catch {
16+
const t = document.createElement("textarea");
17+
t.value = profile.email;
18+
document.body.appendChild(t);
19+
t.select();
20+
document.execCommand("copy");
21+
document.body.removeChild(t);
22+
}
23+
setCopied(true);
24+
setTimeout(() => setCopied(false), 1800);
25+
};
26+
827
return (
9-
<section
10-
id="contact"
11-
className="py-24 border-t border-line relative overflow-hidden"
12-
>
28+
<section id="contact" className="py-24 border-t border-line relative overflow-hidden">
1329
<div className="pointer-events-none absolute inset-0 -z-10">
1430
<div className="absolute bottom-0 left-1/2 -translate-x-1/2 w-96 h-96 rounded-full bg-accent2/15 blur-3xl" />
1531
</div>
@@ -26,56 +42,96 @@ export default function Contact() {
2642
Let&apos;s build something.
2743
</motion.h2>
2844
<p className="mt-4 text-muted max-w-md mx-auto">
29-
Have a project or role in mind? Reach me on any of these.
45+
Have a project, role or training in mind? Reach me on any of these.
3046
</p>
3147
</div>
3248

33-
<div
34-
className="mt-12 grid grid-cols-2 sm:grid-cols-3 gap-4 max-w-3xl mx-auto"
35-
style={{ perspective: "1000px" }}
36-
>
37-
{socials.map((s, i) => {
38-
const Icon = getIcon(s.icon);
39-
return (
40-
<motion.a
41-
key={s.name}
42-
href={s.url}
43-
target="_blank"
44-
rel="noopener noreferrer"
45-
data-hover
46-
initial={{ opacity: 0, y: 20 }}
47-
whileInView={{ opacity: 1, y: 0 }}
48-
viewport={{ once: true }}
49-
transition={{ duration: 0.5, delay: i * 0.06 }}
50-
>
51-
<Tilt className="glass rounded-2xl p-5 h-full hover:border-accent/60 transition-colors">
52-
<div style={{ transform: "translateZ(35px)" }}>
53-
<span className="w-11 h-11 rounded-xl bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg mb-3">
54-
<Icon size={20} />
49+
<div className="mt-14 max-w-4xl mx-auto grid md:grid-cols-[1fr_260px] gap-6 items-stretch">
50+
<div className="grid grid-cols-3 grid-rows-2 gap-4" style={{ perspective: "1000px" }}>
51+
{socials.map((s, i) => {
52+
const Icon = getIcon(s.icon);
53+
return (
54+
<motion.a
55+
key={s.name}
56+
href={s.url}
57+
target="_blank"
58+
rel="noopener noreferrer"
59+
data-hover
60+
initial={{ opacity: 0, y: 16 }}
61+
whileInView={{ opacity: 1, y: 0 }}
62+
viewport={{ once: true }}
63+
transition={{ duration: 0.4, delay: i * 0.05 }}
64+
title={s.handle}
65+
className="block"
66+
>
67+
<Tilt max={12} className="glass rounded-2xl h-full flex flex-col items-center justify-center text-center gap-2 p-4 hover:border-accent/60 transition-colors">
68+
<span style={{ transform: "translateZ(28px)" }}
69+
className="w-11 h-11 rounded-xl bg-gradient-to-br from-accent to-accent2 flex items-center justify-center text-bg">
70+
<Icon size={18} />
5571
</span>
56-
<div className="font-semibold text-sm">{s.name}</div>
57-
<div className="text-muted text-xs mt-0.5 truncate">
58-
{s.handle}
59-
</div>
60-
</div>
61-
</Tilt>
62-
</motion.a>
63-
);
64-
})}
65-
</div>
72+
<span style={{ transform: "translateZ(18px)" }} className="text-sm font-medium">
73+
{s.name}
74+
</span>
75+
</Tilt>
76+
</motion.a>
77+
);
78+
})}
79+
</div>
6680

67-
<div className="text-center mt-12">
68-
<a
69-
href={`mailto:${profile.email}`}
70-
className="text-lg text-gradient font-semibold hover:opacity-80 transition"
81+
<motion.div
82+
initial={{ opacity: 0, scale: 0.95 }}
83+
whileInView={{ opacity: 1, scale: 1 }}
84+
viewport={{ once: true }}
85+
transition={{ duration: 0.5 }}
86+
className="glass rounded-2xl p-5 flex flex-col items-center justify-center gap-3"
7187
>
72-
{profile.email}
73-
</a>
88+
{/* eslint-disable-next-line @next/next/no-img-element */}
89+
<img src="/qr.png" alt="Scan to save my contact"
90+
className="w-40 h-40 rounded-lg bg-white p-2 object-contain" />
91+
<p className="text-xs text-muted text-center">Scan to save my contact 📇</p>
92+
</motion.div>
93+
</div>
94+
95+
<div className="mt-8 flex flex-wrap items-center justify-center gap-4">
96+
<div className="relative">
97+
<button
98+
data-hover
99+
onClick={copyEmail}
100+
className="flex items-center gap-2 px-5 py-3 rounded-full border border-line hover:border-accent hover:text-accent transition-colors text-sm font-medium"
101+
>
102+
{copied ? <FiCheck size={16} /> : <FiCopy size={16} />}
103+
{profile.email}
104+
</button>
105+
<AnimatePresence>
106+
{copied && (
107+
<motion.span
108+
initial={{ opacity: 0, y: 6, scale: 0.9 }}
109+
animate={{ opacity: 1, y: 0, scale: 1 }}
110+
exit={{ opacity: 0, y: -6, scale: 0.9 }}
111+
className="absolute -top-9 left-1/2 -translate-x-1/2 text-xs font-medium px-3 py-1.5 rounded-full bg-accent text-bg whitespace-nowrap"
112+
>
113+
Copied! ✓
114+
</motion.span>
115+
)}
116+
</AnimatePresence>
117+
</div>
118+
119+
{profile.resumeUrl && (
120+
<a
121+
href={profile.resumeUrl}
122+
target="_blank"
123+
rel="noopener noreferrer"
124+
download="Krishna_Andhe_CV.pdf"
125+
data-hover
126+
className="flex items-center gap-2 px-5 py-3 rounded-full bg-accent text-bg font-semibold hover:opacity-90 transition"
127+
>
128+
<FiDownload size={16} /> Download Résumé
129+
</a>
130+
)}
74131
</div>
75132

76-
<footer className="mt-16 pt-8 border-t border-line text-sm text-muted text-center">
77-
© {new Date().getFullYear()} {profile.name} · Built with Next.js &
78-
Tailwind CSS
133+
<footer className="mt-20 pt-8 border-t border-line text-sm text-muted text-center">
134+
© {new Date().getFullYear()} {profile.name} · Built with Next.js &amp; Tailwind CSS
79135
</footer>
80136
</div>
81137
</section>

0 commit comments

Comments
 (0)