Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Devjourney #13

Merged
merged 11 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/src/app/devjourney/[...dj]/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

import DevJourney from '../../../components/devjourney/devjourney';

const DevJourneyPage = () => {
return (
<>
<DevJourney />
</>
);
};

export default DevJourneyPage;
13 changes: 13 additions & 0 deletions app/src/app/devjourney/page.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import React from 'react';

import DevJourney from '../../components/devjourney/devjourney';

const DevJourneyPage = () => {
return (
<>
<DevJourney />
</>
);
};

export default DevJourneyPage;
24 changes: 14 additions & 10 deletions app/src/components/activity/activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,16 +83,20 @@ const Activity = () => {
{guild.activities.map((activity, activityIndex) => (
<div key={activityIndex} className={activityStyles.activityItem}>
<div className={activityStyles.imageContainer}>
<img
src={activity.largeImageURL}
alt={activity.largeText}
className={activityStyles.activityLargeImage}
/>
<img
src={activity.smallImageURL}
alt={activity.smallText}
className={activityStyles.activitySmallImage}
/>
{activity.largeImageURL && (
<img
src={activity.largeImageURL}
alt={activity.largeText}
className={activityStyles.activityLargeImage}
/>
)}
{activity.smallImageURL && (
<img
src={activity.smallImageURL}
alt={activity.smallText}
className={activityStyles.activitySmallImage}
/>
)}
</div>
<h3 className={activityStyles.activityName}>{activity.name}</h3>
<p className={activityStyles.activityDetails}>{activity.details}</p>
Expand Down
31 changes: 16 additions & 15 deletions app/src/components/activity/activity.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@
justify-content: center;
align-items: center;
flex-direction: column;
gap: 1rem;
padding: 1rem;
gap: 1em;
padding: 1em;
}

.activityCardWrapper {
display: flex;
gap: 1rem;
gap: 1em;
flex-wrap: wrap;
justify-content: center;
}

.activityCard {
background-color: #1f1f2b;
border-radius: 12px;
padding: 1rem;
padding: 1em;
width: fit-content;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
transform: scale(0.98);
Expand All @@ -37,12 +37,12 @@
}

.activityItem {
margin-bottom: 0.5rem;
margin-bottom: 0.5em;
}

.imageContainer {
position: relative;
margin-bottom: 0.5rem;
margin-bottom: 0.5em;
}

.activityLargeImage {
Expand Down Expand Up @@ -72,17 +72,17 @@
}

.activityName {
font-size: 1.4rem;
font-size: 1.4em;
color: #3498db;
margin: 0.5rem 0;
margin: 0.5em 0;
font-weight: 600;
}

.activityDetails,
.activityState,
.activityTime {
color: #95a5a6;
margin: 0.2rem 0;
margin: 0.2em 0;
opacity: 0.85;
}

Expand All @@ -94,9 +94,10 @@
.status {
display: flex;
align-items: center;
gap: 0.5rem;
margin-top: 0.5rem;
gap: 0.5em;
margin-top: 0.5em;
color: #d3d3d3;
text-transform: uppercase;
}

.status span {
Expand All @@ -105,11 +106,11 @@

@media (max-width: 768px) {
.activityContainer {
padding: 0.5rem;
padding: 0.5em;
}

.activityCardWrapper {
gap: 0.5rem;
gap: 0.5em;
}

.activityCard {
Expand All @@ -131,13 +132,13 @@

@media (max-width: 480px) {
.activityName {
font-size: 1.2rem;
font-size: 1.2em;
}

.activityDetails,
.activityState,
.activityTime {
font-size: 0.9rem;
font-size: 0.9em;
}

.activityLargeImage {
Expand Down
192 changes: 192 additions & 0 deletions app/src/components/devjourney/devjourney.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
"use client";

import { useEffect, useState } from "react";
import { usePathname, useRouter } from "next/navigation";
import styles from "./devjourney.module.css";

const Repository = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [fileContent, setFileContent] = useState(null);
const [readmeContent, setReadmeContent] = useState(null);
const pathname = usePathname();
const router = useRouter();
const repoOwner = "upayanmazumder";
const repoName = "DevJourney";

const fetchRepoContents = async (path = "") => {
const token = process.env.GITHUB_TOKEN || process.env.GITHUB_TOKEN;
const headers = token ? { Authorization: `token ${token}` } : {};

try {
const response = await fetch(
`https://api.github.com/repos/${repoOwner}/${repoName}/contents/${path}`,
{ headers }
);

if (!response.ok) throw new Error("Failed to fetch repository contents");

const result = await response.json();
return result;
} catch (err) {
setError(`Error fetching repository contents: ${err.message}`);
return null;
}
};

const fetchFileContent = async (path) => {
const token = process.env.GITHUB_TOKEN;
const headers = token ? { Authorization: `token ${token}` } : {};

try {
const response = await fetch(
`https://api.github.com/repos/${repoOwner}/${repoName}/contents/${path}`,
{ headers }
);

if (!response.ok) throw new Error("Failed to fetch file content");

const result = await response.json();

if (result.type === "file") {
const decodedContent = atob(result.content);
setFileContent({ name: result.name, content: decodedContent });
} else {
setFileContent(null);
throw new Error("Path is not a file");
}
} catch (err) {
setError(`Error fetching file content: ${err.message}`);
}
};

const fetchReadme = async (path) => {
const contents = await fetchRepoContents(path);
const readmeFile = contents?.find((item) => item.name.toLowerCase() === "readme.md");

if (readmeFile) {
await fetchFileContent(readmeFile.path);
setReadmeContent(fileContent?.content);
} else {
setReadmeContent(null);
}
};

useEffect(() => {
const fetchData = async () => {
setLoading(true);
setError(null);

const repoPath = pathname.replace("/devjourney", "").replace(/^\//, "");

try {
const contents = await fetchRepoContents(repoPath);
if (Array.isArray(contents)) {
setData(contents);
setFileContent(null);
fetchReadme(repoPath);
} else {
await fetchFileContent(repoPath);
}
} catch (err) {
setError(`Error during data fetching: ${err.message}`);
} finally {
setLoading(false);
}
};

if (pathname) fetchData();
}, [pathname]);

const handleItemClick = (item) => {
const newPath = `/devjourney/${item.path}`;
router.push(newPath);
};

const renderContents = () => {
if (loading) return <p className={styles.loading}>Loading...</p>;
if (error) return <p className={styles.error}>Error: {error}</p>;

const items = [];
for (let i = 0; i < data.length; i++) {
const item = data[i];
items.push(
<li key={item.sha} className={styles.fileItem}>
<button
className={styles.fileLink}
onClick={() => handleItemClick(item)}
>
{item.name}
</button>
</li>
);
}
return <ul className={styles.fileList}>{items}</ul>;
};

const renderFileContent = () => {
if (!fileContent) return null;

return (
<div className={styles.fileViewer}>
<h2 className={styles.fileName}>{fileContent.name}</h2>
<pre className={styles.fileContent}>{fileContent.content}</pre>
</div>
);
};

const renderReadme = () => {
if (readmeContent) {
return (
<div className={styles.fileViewer}>
<h2 className={styles.fileName}>README</h2>
<div className={styles.fileContent} dangerouslySetInnerHTML={{ __html: readmeContent }} />
</div>
);
}
return null;
};

const renderBreadcrumb = () => {
const pathSegments = pathname
.replace("/devjourney", "")
.split("/")
.filter(Boolean);

const breadcrumbItems = pathSegments.map((segment, index) => {
const path = `/devjourney/${pathSegments.slice(0, index + 1).join("/")}`;
const decodedSegment = decodeURIComponent(segment);

return (
<span key={path}>
<button className={styles.breadcrumbLink} onClick={() => router.push(path)}>
{decodedSegment}
</button>
{index < pathSegments.length - 1 && " / "}
</span>
);
});

return (
<div className={styles.breadcrumb}>
<button className={styles.breadcrumbLink} onClick={() => router.push("/devjourney")}>
Home
</button>
{breadcrumbItems.length > 0 && " / "}
{breadcrumbItems}
</div>
);
};

return (
<div className={styles.container}>
{renderBreadcrumb()}
{renderFileContent()}
{renderContents()}
{renderReadme()}
</div>
);
};

export default Repository;
Loading
Loading