Skip to content

Commit a7f31a9

Browse files
Updated photo
2 parents b73b8cf + 09fed3a commit a7f31a9

File tree

7 files changed

+187
-131
lines changed

7 files changed

+187
-131
lines changed

README.md

Lines changed: 0 additions & 1 deletion
This file was deleted.

app/layout.tsx

Lines changed: 86 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,82 @@
1-
"use client";
1+
'use client';
22
import { Hubballi } from "next/font/google";
3-
import React, { ReactNode } from "react";
3+
import React from "react";
44
import "./globals.css";
55
import Link from "next/link";
66
import { GoogleAnalytics } from "@next/third-parties/google";
7-
87
import SocialMediaButton from "../components/SocialMediaButton";
9-
108
import { links } from "../data/links";
11-
12-
interface RootLayoutProps {
13-
children: ReactNode;
14-
}
9+
import { useTab, TabProvider } from "../context/TabContext";
1510

1611
const hubballi = Hubballi({
17-
weight: ["400"],
18-
subsets: ["latin"],
12+
weight: ["400"],
13+
subsets: ["latin"],
1914
});
2015

21-
export default function RootLayout({ children }: RootLayoutProps) {
22-
return (
23-
<html lang="en">
24-
<body className={`${hubballi.className}`}>
25-
<GoogleAnalytics gaId="G-B1TRQFQPF3" />
26-
<div className="max-w-screen-xl w-full flex lg:flex-row md:flex-row flex-col items-center mx-auto gap-y-10 gap-x-5 mt-12 px-5">
27-
<img
28-
src="/img/github_photo.png"
29-
width={400}
30-
height={419}
31-
alt="Haries' Profile Picture"
32-
style={{ borderRadius: "50%" }}
33-
/>
34-
<div className="flex flex-col">
35-
<p style={{ fontSize: 50 }} className="leading-10 mb-4">
36-
Hi! I am{" "}
37-
<span className="text-gray-300">Muhamad</span>{" "}
38-
Haries{" "}
39-
<span className="text-gray-300">Ramdhani</span>
40-
</p>
41-
<div className="flex flex-row gap-x-2 mb-8">
42-
{links.map((link) => (
43-
<SocialMediaButton
44-
key={link.image_filename}
45-
url={link.url}
46-
image_filename={link.image_filename}
47-
/>
48-
))}
49-
</div>
50-
<p
16+
function NavItems() {
17+
const { setActiveTab, activeTab } = useTab();
18+
19+
const tabs = [
20+
{ id: 'projects', label: 'Side Projects' },
21+
{ id: 'blog', label: 'Blog', externalUrl: 'https://hariesramdhani.github.io/writing' },
22+
// { id: 'research', label: 'Research' },
23+
// { id: 'presentation', label: 'Presentation' },
24+
// { id: 'teaching', label: 'Teaching' },
25+
];
26+
27+
const handleTabClick = (tab: { id: string; externalUrl?: string }) => {
28+
if (tab.externalUrl) {
29+
window.location.href = tab.externalUrl;
30+
} else {
31+
setActiveTab(tab.id);
32+
}
33+
};
34+
35+
return (
36+
<div className="flex flex-wrap gap-x-8">
37+
{tabs.map((tab) => (
38+
<button
39+
key={tab.id}
40+
onClick={() => handleTabClick(tab)}
41+
className={`mb-5 px-2 rounded-lg ${activeTab === tab.id ? 'bg-gray-200' : 'text-gray-400'}`}
42+
style={{ fontSize: 24 }}
43+
>
44+
{tab.label}
45+
</button>
46+
))}
47+
</div>
48+
);
49+
}
50+
51+
export default function RootLayout({ children }: { children: React.ReactNode }) {
52+
return (
53+
<html lang="en">
54+
<body className={`${hubballi.className}`}>
55+
<GoogleAnalytics gaId="G-B1TRQFQPF3" />
56+
<TabProvider>
57+
<div className="max-w-screen-xl w-full flex lg:flex-row md:flex-row flex-col items-center mx-auto gap-y-10 gap-x-5 mt-12 px-5">
58+
<img
59+
src="/img/github_photo.png"
60+
width={400}
61+
height={419}
62+
alt="Haries' Profile Picture"
63+
style={{ borderRadius: "50%" }}
64+
/>
65+
<div className="flex flex-col">
66+
<p style={{ fontSize: 50 }} className="leading-10 mb-4">
67+
Hi! I am <span className="text-gray-300">M.</span> Haries{" "}
68+
<span className="text-gray-300">Ramdhani</span>
69+
</p>
70+
<div className="flex flex-row gap-x-2 mb-8">
71+
{links.map((link) => (
72+
<SocialMediaButton
73+
key={link.image_filename}
74+
url={link.url}
75+
image_filename={link.image_filename}
76+
/>
77+
))}
78+
</div>
79+
<p
5180
style={{ fontSize: 24 }}
5281
className="leading-7 text-justify"
5382
>
@@ -72,7 +101,7 @@ export default function RootLayout({ children }: RootLayoutProps) {
72101
href="https://www.abdn.ac.uk/ims/profiles/c.debari"
73102
className="text-indigo-400"
74103
>
75-
Prof. Casimo de Bari
104+
Prof. Cosimo de Bari
76105
</Link>
77106
. My research interests are in bioinformatics
78107
software and algorithm development, also the
@@ -119,52 +148,19 @@ export default function RootLayout({ children }: RootLayoutProps) {
119148
</Link>{" "}
120149
through the IMS studentship.
121150
</p>
122-
</div>
123-
</div>
124-
<div className="max-w-screen-xl w-full flex flex-col items-start gap-x-5 mt-12 mx-auto px-5">
125-
<div className="flex flex-wrap gap-x-8">
126-
<Link href="/">
127-
<h1
128-
style={{ fontSize: 24 }}
129-
className="mb-5 px-2 bg-gray-200 rounded-lg"
130-
>
131-
Side Projects
132-
</h1>
133-
</Link>
134-
<h1
135-
style={{ fontSize: 24 }}
136-
className="mb-5 text-gray-400"
137-
>
138-
Blog
139-
</h1>
140-
<h1
141-
style={{ fontSize: 24 }}
142-
className="mb-5 text-gray-400"
143-
>
144-
Research
145-
</h1>
146-
<h1
147-
style={{ fontSize: 24 }}
148-
className="mb-5 text-gray-400"
149-
>
150-
Presentation
151-
</h1>
152-
<h1
153-
style={{ fontSize: 24 }}
154-
className="mb-5 text-gray-400"
155-
>
156-
Teaching
157-
</h1>
158-
</div>
159-
160-
<div className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-4">
161-
{children}
162-
</div>
163-
</div>
164-
<div className="max-w-screen-xl w-full flex flex-col items-center gap-x-5 mt-12 mx-auto px-5">
165-
<p>Haries Ramdhani © 2024.</p>
166-
</div>
167-
</body>
168-
</html>
169-
);
170-
}
151+
</div>
152+
</div>
153+
<div className="max-w-screen-xl w-full flex flex-col items-start gap-x-5 mt-12 mx-auto px-5">
154+
<NavItems />
155+
<div className="w-full grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-x-10 gap-y-4">
156+
{children}
157+
</div>
158+
</div>
159+
<div className="max-w-screen-xl w-full flex flex-col items-center gap-x-5 mt-12 mx-auto px-5">
160+
<p>Haries Ramdhani © 2025.</p>
161+
</div>
162+
</TabProvider>
163+
</body>
164+
</html>
165+
);
166+
}

app/page.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import ProjectCard from "@/components/ProjectCard";
1+
import GenericCard from "@/components/GenericCard";
22

33
export const metadata = {
44
title: "Haries Ramdhani",
@@ -13,7 +13,7 @@ export const metadata = {
1313
export default function Home() {
1414
return (
1515
<>
16-
<ProjectCard />
16+
<GenericCard />
1717
</>
1818
);
1919
}

components/GenericCard.tsx

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'use client';
2+
3+
import React, { useEffect, useState } from "react";
4+
import Card from "./Card";
5+
import { useTab } from "../context/TabContext";
6+
7+
interface CardData {
8+
url: string | null;
9+
image_filename: string;
10+
title: string;
11+
description: string;
12+
}
13+
14+
export default function GenericCard() {
15+
const { activeTab } = useTab();
16+
const [data, setData] = useState<CardData[]>([]);
17+
const [loading, setLoading] = useState(true);
18+
const [error, setError] = useState<string | null>(null);
19+
20+
useEffect(() => {
21+
const fetchData = async () => {
22+
try {
23+
setLoading(true);
24+
setError(null);
25+
console.log('Fetching data for:', activeTab);
26+
27+
const response = await fetch(`/data/${activeTab}.json`);
28+
29+
if (!response.ok) {
30+
throw new Error(`Failed to load ${activeTab} data`);
31+
}
32+
33+
const jsonData = await response.json();
34+
setData(jsonData);
35+
} catch (err) {
36+
console.error('Error fetching data:', err);
37+
setError(`Failed to load ${activeTab} data`);
38+
setData([]);
39+
} finally {
40+
setLoading(false);
41+
}
42+
};
43+
44+
fetchData();
45+
}, [activeTab]);
46+
47+
if (loading) return <div>Loading...</div>;
48+
if (error) return <div>{error}</div>;
49+
50+
return (
51+
<>
52+
{data.map((item) => (
53+
<Card
54+
key={item.image_filename}
55+
url={item.url ?? '#'}
56+
image_filename={item.image_filename}
57+
title={item.title}
58+
description={item.description}
59+
/>
60+
))}
61+
</>
62+
);
63+
}

components/ProjectCard.tsx

Lines changed: 0 additions & 38 deletions
This file was deleted.

context/TabContext.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use client';
2+
3+
import React, { createContext, useContext, useState } from "react";
4+
5+
type TabContextType = {
6+
activeTab: string;
7+
setActiveTab: (tab: string) => void;
8+
};
9+
10+
const TabContext = createContext<TabContextType | undefined>(undefined);
11+
12+
export function TabProvider({ children }: { children: React.ReactNode }) {
13+
const [activeTab, setActiveTab] = useState('projects');
14+
15+
return (
16+
<TabContext.Provider value={{ activeTab, setActiveTab }}>
17+
{children}
18+
</TabContext.Provider>
19+
);
20+
}
21+
22+
export function useTab() {
23+
const context = useContext(TabContext);
24+
if (context === undefined) {
25+
throw new Error('useTab must be used within a TabProvider');
26+
}
27+
return context;
28+
}

public/data/blog.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"url": "https://github.com/SPARC-FAIR-Codeathon/SPARCTA",
4+
"image_filename": "sparcta",
5+
"title": "SPARCTA",
6+
"description": "SPARC Tiff Annotator (3rd Prize Winner at the SPARC FAIR CODEATHON 2024)"
7+
}
8+
]

0 commit comments

Comments
 (0)