Skip to content

Commit 06b53fb

Browse files
committed
feat: Add comprehensive portfolio update system 🚀
- Create devlog.md for personal development journal - Add updates.json for structured update data - Build interactive Matrix-themed CLI tool (portfolio-update.js) - Create UpdatesFeed React component with animations - Add npm scripts for quick updates (update, update:quick, update:stats) - Update CLAUDE.md with complete documentation The system enables quick, mood-based portfolio updates across multiple files with consistent Matrix styling and git integration.
1 parent c9b1d57 commit 06b53fb

8 files changed

Lines changed: 874 additions & 25 deletions

File tree

devlog.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# 💚 Tommy's Dev Journal
2+
3+
> *"From Liverpool with love - documenting the journey, one commit at a time"*
4+
5+
---
6+
7+
## 2025
8+
9+
### January 28, 2025 - Portfolio Update System 🚀
10+
11+
**What I Built:** A comprehensive portfolio update system to make documenting my journey quick and fun!
12+
13+
**The Cool Bit:** Created an interactive CLI tool that matches my mood and makes updates feel like celebrations rather than chores.
14+
15+
**Tech Used:** Node.js, React components, Matrix-themed CLI interface
16+
17+
**Impact:** Now I can update my portfolio in 30 seconds instead of 30 minutes!
18+
19+
*Building the future, one feature at a time.* 💚
20+
21+
---
22+
23+
### January 20, 2025 - React Migration Complete! 🎉
24+
25+
**Milestone Achieved:** Successfully migrated the entire portfolio to React 19 with TypeScript!
26+
27+
**The Journey:** Started with static HTML, fell in love with the Matrix theme, and now it's a modern React app that still keeps that iconic green glow.
28+
29+
**Tech Stack:** React 19, TypeScript, Vite 7, Anime.js v4
30+
31+
**Next Up:** Performance optimizations and more interactive features
32+
33+
*Every victory counts.* 👑
34+
35+
---
36+
37+
## Quick Stats 📊
38+
39+
- **Days of Code:** Continuous since March 2024
40+
- **Projects Shipped:** 12+
41+
- **Coffee Consumed:**
42+
- **Lives Impacted:** Growing daily
43+
- **Current Mood:** 🚀 Building mode
44+
45+
---
46+
47+
## Reflection Corner 💭
48+
49+
Sometimes I look back at where I started - a simple GitHub README - and I'm amazed at how far this journey has taken me. The Matrix theme wasn't just an aesthetic choice; it became a representation of seeing the code behind everything, understanding the patterns, and building something unique.
50+
51+
The best part? Every feature I add, every bug I fix, every late-night coding session - they're all steps toward making technology more human, more accessible, more fun.
52+
53+
*From "Hello World" to "Hello Universe" - the journey continues...*
54+
55+
---
56+
57+
## Template Section
58+
59+
<!--
60+
### ${DATE} - ${TITLE}
61+
62+
${CONTENT}
63+
64+
**Key Achievement:** ${ACHIEVEMENT}
65+
66+
**Tech/Learning:** ${TECH_OR_LEARNING}
67+
68+
**Next Steps:** ${WHATS_NEXT}
69+
70+
*${CLOSING_THOUGHT}* ${EMOJI}
71+
-->

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,10 @@
2525
"type-check": "tsc --noEmit",
2626
"test": "vitest",
2727
"test:ui": "vitest --ui",
28-
"test:coverage": "vitest --coverage"
28+
"test:coverage": "vitest --coverage",
29+
"update": "node scripts/portfolio-update.js",
30+
"update:quick": "echo '$(date): Quick update' >> devlog.md && git add . && git commit -m 'Portfolio: Quick update' && git push",
31+
"update:stats": "node scripts/generate-stats.js"
2932
},
3033
"keywords": [],
3134
"author": "",

plan.md

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -293,29 +293,5 @@ function createMatrixSpinner() {
293293

294294
---
295295

296-
## Phase 9: Portfolio Update System 📝
297-
298-
### 9.1 Update CHANGELOG.md
299-
- Add new entries in the [Unreleased] section or create new version sections
300-
- Follow the existing format with Added/Changed/Fixed/Enhanced categories
301-
- Document all technical changes, features, and fixes chronologically
302-
303-
### 9.2 Update Latest Updates Section
304-
- Modify both HomePage.tsx and index.html for consistency during migration
305-
- Add new project cards following the existing pattern
306-
- Include project image, description, tech tags, and action button
307-
- Ensure visual consistency with the Matrix theme
308-
309-
### 9.3 Consider Creating a Dedicated Updates File
310-
- Create `updates.md` or `devlog.md` for more detailed development notes
311-
- Link to it from the main documentation (README.md, CLAUDE.md)
312-
- Use this for more personal development journey documentation
313-
- Structure with date-based entries and categories
314-
315-
### Implementation Notes:
316-
- Maintain consistency between React and legacy versions during migration
317-
- Follow established formatting patterns in existing update locations
318-
- Keep the Matrix aesthetic in any new update components
319-
- Consider automation opportunities for future update processes
320296

321297
This comprehensive plan will transform your portfolio into a stunning, performant, and memorable experience that showcases both technical skills and creative vision while maintaining the iconic Matrix aesthetic.

scripts/portfolio-update.js

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
#!/usr/bin/env node
2+
3+
const fs = require('fs');
4+
const path = require('path');
5+
const readline = require('readline');
6+
const { execSync } = require('child_process');
7+
8+
// Matrix theme colors
9+
const colors = {
10+
green: '\x1b[32m',
11+
bright: '\x1b[92m',
12+
cyan: '\x1b[36m',
13+
yellow: '\x1b[33m',
14+
magenta: '\x1b[35m',
15+
reset: '\x1b[0m',
16+
dim: '\x1b[2m'
17+
};
18+
19+
// Update types configuration
20+
const updateTypes = {
21+
'1': { type: 'feature', emoji: '🎉', text: 'New Feature - "Look what I built!"' },
22+
'2': { type: 'achievement', emoji: '🏆', text: 'Achievement - "I did the thing!"' },
23+
'3': { type: 'learning', emoji: '📚', text: 'Learning - "My brain grew today"' },
24+
'4': { type: 'milestone', emoji: '🚀', text: 'Milestone - "Major moment alert!"' },
25+
'5': { type: 'thought', emoji: '💭', text: 'Thought - "Something worth sharing"' }
26+
};
27+
28+
// Mood configurations
29+
const moods = {
30+
celebrating: { text: '🎉 High Energy', prefix: "GUESS WHAT?!" },
31+
reflecting: { text: '😌 Reflective', prefix: "Been thinking..." },
32+
building: { text: '🔨 Building Mode', prefix: "Just shipped:" },
33+
conquering: { text: '👑 Conquering', prefix: "Achievement unlocked:" }
34+
};
35+
36+
const rl = readline.createInterface({
37+
input: process.stdin,
38+
output: process.stdout
39+
});
40+
41+
function question(prompt) {
42+
return new Promise(resolve => rl.question(prompt, resolve));
43+
}
44+
45+
function printHeader() {
46+
console.clear();
47+
console.log(colors.green + `
48+
${colors.bright}💚 PORTFOLIO UPDATE STATION 💚${colors.reset}${colors.green}
49+
50+
G'day Tommy! Ready to share your brilliance?
51+
Today's vibe check: [████████░░] 85% LEGENDARY
52+
${colors.reset}`);
53+
}
54+
55+
function printUpdateTypes() {
56+
console.log(`${colors.cyan}What are we celebrating today?${colors.reset}`);
57+
Object.entries(updateTypes).forEach(([key, value]) => {
58+
console.log(`${colors.green}[${key}]${colors.reset} ${value.emoji} ${value.text}`);
59+
});
60+
console.log();
61+
}
62+
63+
function formatDate() {
64+
const date = new Date();
65+
const months = ['January', 'February', 'March', 'April', 'May', 'June',
66+
'July', 'August', 'September', 'October', 'November', 'December'];
67+
return `${months[date.getMonth()]} ${date.getDate()}, ${date.getFullYear()}`;
68+
}
69+
70+
function generateUpdateId() {
71+
const date = new Date();
72+
const dateStr = date.toISOString().split('T')[0];
73+
const count = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'updates.json'), 'utf8'))
74+
.updates.filter(u => u.date === dateStr).length + 1;
75+
return `${dateStr}-${String(count).padStart(3, '0')}`;
76+
}
77+
78+
async function updateDevlog(update) {
79+
const devlogPath = path.join(__dirname, '..', 'devlog.md');
80+
const devlog = fs.readFileSync(devlogPath, 'utf8');
81+
82+
let newEntry = `\n### ${formatDate()} - ${update.title} ${update.emoji}\n\n`;
83+
84+
switch(update.type) {
85+
case 'feature':
86+
newEntry += `**What I Built:** ${update.description}\n\n`;
87+
newEntry += `**The Cool Bit:** ${update.coolBit}\n\n`;
88+
newEntry += `**Tech Used:** ${update.tech.join(', ')}\n\n`;
89+
newEntry += `**Impact:** ${update.impact}\n\n`;
90+
break;
91+
case 'achievement':
92+
newEntry += `${update.description}\n\n`;
93+
newEntry += `**Why it matters:** ${update.significance}\n\n`;
94+
newEntry += `**Next up:** ${update.nextSteps}\n\n`;
95+
break;
96+
case 'learning':
97+
newEntry += `**Today's brain expansion:** ${update.description}\n\n`;
98+
newEntry += `**The "aha!" moment:** ${update.insight}\n\n`;
99+
newEntry += `**How I'll use this:** ${update.application}\n\n`;
100+
break;
101+
case 'milestone':
102+
newEntry += `${update.description}\n\n`;
103+
newEntry += `**The journey:** ${update.journey}\n\n`;
104+
newEntry += `**The feeling:** ${update.feeling}\n\n`;
105+
newEntry += `**What's next:** ${update.future}\n\n`;
106+
break;
107+
case 'thought':
108+
newEntry += `${update.description}\n\n`;
109+
break;
110+
}
111+
112+
newEntry += `*${update.closingThought}* ${update.closingEmoji}\n\n---`;
113+
114+
// Insert after the year header
115+
const yearMatch = devlog.match(/## 2025\n/);
116+
if (yearMatch) {
117+
const insertPos = yearMatch.index + yearMatch[0].length;
118+
const updatedDevlog = devlog.slice(0, insertPos) + newEntry + devlog.slice(insertPos);
119+
fs.writeFileSync(devlogPath, updatedDevlog);
120+
}
121+
}
122+
123+
async function updateJSON(update) {
124+
const jsonPath = path.join(__dirname, '..', 'updates.json');
125+
const data = JSON.parse(fs.readFileSync(jsonPath, 'utf8'));
126+
127+
const newUpdate = {
128+
id: update.id,
129+
date: new Date().toISOString().split('T')[0],
130+
type: update.type,
131+
mood: update.mood,
132+
title: update.title,
133+
description: update.description,
134+
content: update.fullContent,
135+
tech: update.tech || [],
136+
impact: update.impact,
137+
links: update.links || {},
138+
featured: update.featured || false
139+
};
140+
141+
data.updates.unshift(newUpdate);
142+
data.stats.totalUpdates++;
143+
data.stats.lastUpdated = newUpdate.date;
144+
145+
fs.writeFileSync(jsonPath, JSON.stringify(data, null, 2));
146+
}
147+
148+
async function gitCommit(message) {
149+
try {
150+
execSync('git add .', { cwd: path.join(__dirname, '..') });
151+
execSync(`git commit -m "Portfolio: ${message}"`, { cwd: path.join(__dirname, '..') });
152+
console.log(`${colors.green}✓ Changes committed!${colors.reset}`);
153+
} catch (error) {
154+
console.log(`${colors.yellow}⚠ Git commit skipped (no changes or not a git repo)${colors.reset}`);
155+
}
156+
}
157+
158+
async function main() {
159+
printHeader();
160+
printUpdateTypes();
161+
162+
const typeChoice = await question(`${colors.green}> ${colors.reset}`);
163+
const updateType = updateTypes[typeChoice];
164+
165+
if (!updateType) {
166+
console.log(`${colors.yellow}Invalid choice. Exiting...${colors.reset}`);
167+
rl.close();
168+
return;
169+
}
170+
171+
console.log(`\n${colors.cyan}What's your mood today?${colors.reset}`);
172+
const moodOptions = Object.entries(moods);
173+
moodOptions.forEach(([key, value], index) => {
174+
console.log(`${colors.green}[${index + 1}]${colors.reset} ${value.text}`);
175+
});
176+
177+
const moodChoice = await question(`\n${colors.green}> ${colors.reset}`);
178+
const mood = moodOptions[parseInt(moodChoice) - 1]?.[0] || 'building';
179+
180+
console.log(`\n${colors.bright}${moods[mood].prefix}${colors.reset}`);
181+
182+
const update = {
183+
id: generateUpdateId(),
184+
type: updateType.type,
185+
emoji: updateType.emoji,
186+
mood: mood
187+
};
188+
189+
// Collect update details based on type
190+
update.title = await question(`\n${colors.cyan}Title your update:${colors.reset}\n> `);
191+
update.description = await question(`\n${colors.cyan}Describe it (one line):${colors.reset}\n> `);
192+
193+
if (updateType.type === 'feature') {
194+
update.coolBit = await question(`\n${colors.cyan}What makes it special?${colors.reset}\n> `);
195+
const techInput = await question(`\n${colors.cyan}Tech used (comma separated):${colors.reset}\n> `);
196+
update.tech = techInput.split(',').map(t => t.trim());
197+
update.impact = await question(`\n${colors.cyan}Who does it help?${colors.reset}\n> `);
198+
} else if (updateType.type === 'achievement') {
199+
update.significance = await question(`\n${colors.cyan}Why does it matter?${colors.reset}\n> `);
200+
update.nextSteps = await question(`\n${colors.cyan}What's next?${colors.reset}\n> `);
201+
} else if (updateType.type === 'learning') {
202+
update.insight = await question(`\n${colors.cyan}Key insight?${colors.reset}\n> `);
203+
update.application = await question(`\n${colors.cyan}How will you use it?${colors.reset}\n> `);
204+
} else if (updateType.type === 'milestone') {
205+
update.journey = await question(`\n${colors.cyan}How did you get here?${colors.reset}\n> `);
206+
update.feeling = await question(`\n${colors.cyan}How does it feel?${colors.reset}\n> `);
207+
update.future = await question(`\n${colors.cyan}What's next?${colors.reset}\n> `);
208+
}
209+
210+
// Common fields
211+
const closingThoughts = [
212+
'Building the future, one feature at a time.',
213+
'Every victory counts.',
214+
'Never stop learning, never stop growing.',
215+
'From Liverpool with love.',
216+
'Just Tommy being Tommy.',
217+
'The journey continues...',
218+
'Code is poetry in motion.'
219+
];
220+
221+
update.closingThought = closingThoughts[Math.floor(Math.random() * closingThoughts.length)];
222+
update.closingEmoji = '💚';
223+
224+
// Matrix celebration
225+
console.log(`\n${colors.bright}${colors.green}`);
226+
console.log('╔═══════════════════════════════════════╗');
227+
console.log('║ UPDATE SUCCESSFULLY CAPTURED! ║');
228+
console.log('║ THE MATRIX HAS YOU... ║');
229+
console.log('╚═══════════════════════════════════════╝');
230+
console.log(colors.reset);
231+
232+
// Update files
233+
console.log(`\n${colors.cyan}Updating your portfolio...${colors.reset}`);
234+
235+
await updateDevlog(update);
236+
console.log(`${colors.green}✓ devlog.md updated${colors.reset}`);
237+
238+
await updateJSON(update);
239+
console.log(`${colors.green}✓ updates.json updated${colors.reset}`);
240+
241+
// Ask about git commit
242+
const shouldCommit = await question(`\n${colors.cyan}Commit changes? (Y/n)${colors.reset} `);
243+
if (shouldCommit.toLowerCase() !== 'n') {
244+
await gitCommit(`${update.title} ${update.emoji}`);
245+
}
246+
247+
console.log(`\n${colors.bright}${colors.green}Mission accomplished! Your legacy grows stronger. 👑${colors.reset}\n`);
248+
249+
rl.close();
250+
}
251+
252+
main().catch(console.error);

0 commit comments

Comments
 (0)