Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4797d0d
Created a gym plan manager
officialabd Dec 13, 2025
3f4cad6
Enabled multiple plans and multi users
officialabd Dec 13, 2025
afa0755
Some updates and fixes. Add reorder for exers. Added session info.
officialabd Dec 13, 2025
bf0eaff
Fixed the search button. Added a prompt when things change.
officialabd Dec 13, 2025
21817a0
Fixed the time to be based on the local machine time
officialabd Dec 13, 2025
8035350
Lots of updates and new features
officialabd Dec 14, 2025
e095884
feat(gym): add types, utils, and validation schemas
officialabd Dec 19, 2025
e9f531a
feat(gym): add Zustand state management stores
officialabd Dec 19, 2025
e5705d3
feat(gym): add Firebase service layer
officialabd Dec 19, 2025
aeba500
feat(gym): add custom hooks wrapping stores and services
officialabd Dec 19, 2025
0a65620
feat(gym): add base UI components
officialabd Dec 19, 2025
6655d41
feat(gym): add Plans and Logs view components
officialabd Dec 19, 2025
0734636
feat(gym): add unified SessionLogger component
officialabd Dec 19, 2025
e37e188
feat(gym): refactor page.tsx to use modular architecture
officialabd Dec 19, 2025
c2386cc
chore: add .env.example for Firebase configuration
officialabd Dec 19, 2025
01cf90c
chore: add zod and zustand dependencies
officialabd Dec 19, 2025
eddbe2c
refactor: use environment variables for Firebase config
officialabd Dec 19, 2025
e298778
refactor(gym): migrate to Firestore subcollections and cleanup
officialabd Dec 19, 2025
0a5ced5
Refactor Edit Plans view with compact list layout
officialabd Dec 19, 2025
e64fddd
Refactor Plans View with compact list layout
officialabd Dec 19, 2025
ba4ffe7
Merge Plans and Edit Plans tabs into unified Plans view
officialabd Dec 19, 2025
7fa6926
Style exercises list in Plans view to match SessionLogger
officialabd Dec 19, 2025
0f2b9d2
Refactor SessionLogger: unify add/edit exercise forms, add CheckIcon …
officialabd Dec 19, 2025
66acf47
removed note per exercise
officialabd Dec 22, 2025
461e501
Updated a few issues and added count for completed exercises
officialabd Dec 23, 2025
4b8c4ec
Migrated the data. Updates and fixes. New changes. Read the code to k…
officialabd Dec 27, 2025
5e32547
Added per attribute in plans view
officialabd Dec 27, 2025
ec1b95d
Added per field
officialabd Dec 28, 2025
29c7b7a
show per on plan exercises
officialabd Dec 28, 2025
447b0a9
Added filter
officialabd Dec 29, 2025
b9cb447
Small changes
officialabd Dec 29, 2025
0a07c4f
Fixed the coloring issue
officialabd Dec 30, 2025
890cff3
Fixed duration time
officialabd Dec 30, 2025
46ce909
Updated the appearance of the portfolio
officialabd Jan 16, 2026
f73d6d9
added the time line
officialabd Jan 16, 2026
bc97eed
Merge branch 'portfolio-updates' of https://github.com/officialabd/of…
officialabd Jan 16, 2026
86bcec5
Added into description
officialabd Jan 16, 2026
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
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Firebase Configuration
# Copy this file to .env.local and fill in your values

NEXT_PUBLIC_FIREBASE_API_KEY=your_api_key_here
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
NEXT_PUBLIC_FIREBASE_PROJECT_ID=your_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=your_app_id
26 changes: 12 additions & 14 deletions firebase.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";
import { getFirestore } from "firebase/firestore";

// Firebase configuration using environment variables
// For local development, create a .env.local file with these values
// See .env.example for the template
const firebaseConfig = {
// apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
// authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
// projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
// storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
// messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
// appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,

apiKey: "AIzaSyDovjYqTPUjV7iC_ic5TLgdzYAWyUfbVp4",
authDomain: "my-portfolio-ed76f.firebaseapp.com",
projectId: "my-portfolio-ed76f",
storageBucket: "my-portfolio-ed76f.appspot.com",
messagingSenderId: "499321285862",
appId: "1:499321285862:web:93f5992056d25119841f27",
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || "AIzaSyDovjYqTPUjV7iC_ic5TLgdzYAWyUfbVp4",
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || "my-portfolio-ed76f.firebaseapp.com",
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || "my-portfolio-ed76f",
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || "my-portfolio-ed76f.appspot.com",
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || "499321285862",
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID || "1:499321285862:web:93f5992056d25119841f27",
};

const app = initializeApp(firebaseConfig);
const db = getFirestore(app);
const auth = getAuth(app);

export { app, db };
export { app, db, auth };

42 changes: 41 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
"react-dom": "^19.2.1",
"react-icons": "^4.10.1",
"tailwindcss": "3.3.3",
"typescript": "5.1.6"
"typescript": "5.1.6",
"zod": "^4.2.1",
"zustand": "^5.0.9"
},
"devDependencies": {
"encoding": "^0.1.13"
Expand Down
2 changes: 1 addition & 1 deletion src/app/_layouts/card/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export default function Card(
<div className="py-5 mt-25 sm:py-10">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
{heading}
<div className="mx-auto mt-4 grid w-full max-w-2xl grid-cols-1 gap-x-8 gap-y-4 lg:max-w-none lg:grid-cols-1">
<div className="mx-auto mt-8 grid w-full max-w-2xl grid-cols-1 gap-x-8 gap-y-6 lg:max-w-none lg:grid-cols-1">
{children}
</div>
</div>
Expand Down
82 changes: 43 additions & 39 deletions src/app/_layouts/detailedList/detailedList.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,57 @@
import staticData from "@/app/staticData";
import { DetailedListItem } from "../../models/Item";
import Card from "../card/card";
import ImagerViewer from "../imagesViewer/ImagersViewer";
import LinePulse from "../pulse/line";
import MultiLinePulse from "../pulse/multiLine";
import { Tags } from "../tags/tags";
import Basic from "../texts/basic";
import TimelineItem from "../timeline/TimelineItem";

export default function DetailedList(
{ title, items, loading = false }: { title: string, items: Array<DetailedListItem>, loading?: boolean }) {
return <Card heading={
<Basic
text={title}
textColor="text-[#BFACDF]"
fontFamily="font-RobotoMono"
fontSize="text-3xl sm:text-4xl"
letterSpacing="tracking-tight"
fontWeight="font-bold"
margin="mx-auto lg:mx-0"
underline={true}
other=""
/>
}>
{ items, loading = false, showDateBadge = true }: { items: Array<DetailedListItem>, loading?: boolean, showDateBadge?: boolean }) {
return <>
{loading ?
<ListItemNode key={`temp-loading-${title}`} id={`temp-loading-${title}`} item={new DetailedListItem({})} loading={loading} />
<TimelineItem index={0}>
<ListItemNode key={`temp-loading`} id={`temp-loading`} item={new DetailedListItem({})} loading={loading} showDateBadge={showDateBadge} />
</TimelineItem>
:
items.map((item, i) => (
<div key={item.getId()}>
{i ?
<div className="flex justify-center w-full mb-4">
<div className="w-12 border-t border-gray-200" />
</div>
:
<></>
}
<ListItemNode key={item.getId()} id={item.getId()} item={item} loading={loading} />
</div>
<TimelineItem key={item.getId()} index={i}>
<ListItemNode id={item.getId()} item={item} loading={loading} showDateBadge={showDateBadge} />
</TimelineItem>
))}
</Card>
</>
}

const ListItemNode = (
{ id, item, loading = false }: { id: any, item: DetailedListItem, loading?: boolean }
{ id, item, loading = false, showDateBadge = true }: { id: any, item: DetailedListItem, loading?: boolean, showDateBadge?: boolean }
) => {
return <>
<div key={id} className="relative py-1 w-full ">
{/* <div className="relative w-full px-4 py-8 sm:rounded-3xl sm:p-10 bg-clip-padding bg-opacity-60 rounded-xl hover:bg-slate-800/50 hover:shadow-[inset_0_1px_0_0_rgba(148,163,184,0.1)] hover:drop-shadow-lg"> */}
<div className={`${loading ? "animate-pulse" : ""} relative w-full px-4 py-8 sm:rounded-3xl sm:p-10 bg-clip-padding bg-opacity-60 rounded-xl bg-slate-800/50 shadow-[inset_0_1px_0_0_rgba(148,163,184,0.1)] drop-shadow-lg`}>
<div className="flex w-full flex-col items-start justify-between">
const extractYear = (dateStr: string | undefined): string => {
if (!dateStr) return "";
if (dateStr === "Present") return "Present";
const yearMatch = dateStr.match(/\d{4}/);
return yearMatch ? yearMatch[0] : dateStr;
};

const startYear = extractYear(item.getStartDate());
const endYear = extractYear(item.getEndDate());
const dateText = startYear && endYear && startYear !== endYear
? `${startYear} - ${endYear}`
: startYear || endYear;

return (
<div key={id} className="relative w-full group">
{/* Date Badge on Timeline */}
{showDateBadge && dateText && !loading && (
<div className="absolute -left-20 md:-left-28 top-8 sm:block">
<div className="bg-slate-700/50 backdrop-blur-sm border border-slate-600/50 rounded-md px-2 py-1 text-xs font-mono text-slate-300 whitespace-normal md:whitespace-nowrap text-left max-w-[4.5rem] md:max-w-none">
{dateText}
</div>
</div>
)}

<div className={`${loading ? "animate-pulse" : ""} relative w-full px-6 py-6 sm:px-8 sm:py-8 rounded-xl bg-slate-800/40 backdrop-blur-sm transition-all duration-300 hover:bg-slate-800/60 border border-slate-700/40 hover:border-slate-600/60 hover:shadow-xl hover:shadow-slate-900/50`}>
<div className="relative z-10 flex w-full flex-col items-start justify-between">
<div className={`w-full items-start justify-between grid grid-cols-1 ${(!item.getImages() || item.getImages()?.length == 0) ? "sm:grid-cols-1" : "sm:grid-cols-2"}`}>
<div className={`grid grid-cols-1`}>
<div className={`grid grid-cols-1 items-start justify-between ${(!item.getImages() || item.getImages()?.length == 0) ? "sm:grid-cols-2" : ""}`}>
Expand All @@ -70,7 +74,7 @@ const ListItemNode = (
<Basic text={item.getTitle()!}
fontFamily="font-Nunito"
fontSize="text-lg"
textColor="text-teal-400"
textColor="text-blue-400"
fontWeight="font-semibold"
other="leading-6"
loading={loading}
Expand All @@ -81,7 +85,7 @@ const ListItemNode = (
<Basic text={item.getSite()!}
fontFamily="font-SourceCodePro"
fontSize="text-sm"
textColor="text-slate-500"
textColor="text-gray-500"
fontWeight="font-semibold"
other="leading-6"
loading={loading}
Expand All @@ -96,9 +100,9 @@ const ListItemNode = (
key={`${id}-links-${i}`}
target="_blank"
href={`${Object.entries(lk).at(0)?.[1]}`}
className="flex text-white relative justify-center items-center max-w-lg z-10 rounded-full px-3 py-1.5 font-medium hover:bg-[#141d32]"
className="flex text-white relative justify-center items-center max-w-lg z-10 rounded px-2 py-1 text-sm font-medium transition-colors duration-200 hover:bg-slate-700 border border-slate-700"
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-4 h-4 me-2 text-blue-400">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-3 h-3 me-1.5 text-gray-400">
<path strokeLinecap="round" strokeLinejoin="round" d={staticData.icons.link} />
</svg>
{Object.entries(lk).at(0)?.[0]}
Expand All @@ -113,7 +117,7 @@ const ListItemNode = (
<MultiLinePulse width="w-62" />
:
item.getDescription() && item.getDescription()!.map((dcp, i) =>
<p key={`${id}-description-${i}`} className="text-sm leading-6 text-white">{dcp}</p>
<p key={`${id}-description-${i}`} className="text-sm leading-6 text-gray-400">{dcp}</p>
)
}
</div>
Expand All @@ -136,5 +140,5 @@ const ListItemNode = (
</div>
</div>
</div>
</>
);
}
Loading