diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..093af96
--- /dev/null
+++ b/.env.example
@@ -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
diff --git a/firebase.js b/firebase.js
index 20dd092..0690f8b 100644
--- a/firebase.js
+++ b/firebase.js
@@ -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 };
diff --git a/package-lock.json b/package-lock.json
index dd5c6b2..99a06a1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,7 +24,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"
@@ -5754,6 +5756,44 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zod": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-4.2.1.tgz",
+ "integrity": "sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zustand": {
+ "version": "5.0.9",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.9.tgz",
+ "integrity": "sha512-ALBtUj0AfjJt3uNRQoL1tL2tMvj6Gp/6e39dnfT6uzpelGru8v1tPOGBzayOWbPJvujM8JojDk3E1LxeFisBNg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18.0.0",
+ "immer": ">=9.0.6",
+ "react": ">=18.0.0",
+ "use-sync-external-store": ">=1.2.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "use-sync-external-store": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 3ef2aa5..aad683b 100644
--- a/package.json
+++ b/package.json
@@ -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"
diff --git a/src/app/_layouts/card/card.tsx b/src/app/_layouts/card/card.tsx
index 0878f46..a0708f4 100644
--- a/src/app/_layouts/card/card.tsx
+++ b/src/app/_layouts/card/card.tsx
@@ -6,7 +6,7 @@ export default function Card(
{heading}
-
diff --git a/src/app/_layouts/detailedList/detailedList.tsx b/src/app/_layouts/detailedList/detailedList.tsx
index 7612658..fd18968 100644
--- a/src/app/_layouts/detailedList/detailedList.tsx
+++ b/src/app/_layouts/detailedList/detailedList.tsx
@@ -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
, loading?: boolean }) {
- return
- }>
+ { items, loading = false, showDateBadge = true }: { items: Array, loading?: boolean, showDateBadge?: boolean }) {
+ return <>
{loading ?
-
+
+
+
:
items.map((item, i) => (
-
- {i ?
-
- :
- <>>
- }
-
-
+
+
+
))}
-
+ >
}
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 <>
-
- {/*
*/}
-
-
+ 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 (
+
+ {/* Date Badge on Timeline */}
+ {showDateBadge && dateText && !loading && (
+
+ )}
+
+
+
@@ -70,7 +74,7 @@ const ListItemNode = (
-
@@ -136,5 +140,5 @@ const ListItemNode = (
- >
+ );
}
\ No newline at end of file
diff --git a/src/app/_layouts/footer/footer.tsx b/src/app/_layouts/footer/footer.tsx
index f9fd052..af66699 100644
--- a/src/app/_layouts/footer/footer.tsx
+++ b/src/app/_layouts/footer/footer.tsx
@@ -16,29 +16,31 @@ export default function Footer(
}) {
return <>
-