A modern, high-performance personal portfolio and blog built with Astro.js. Designed to showcase your projects and technical writing with blazing-fast performance and exceptional SEO.
- π Blazing fast performance - Built with Astro.js for optimal loading speeds
- π Full-featured blog - Support for technical articles with code highlighting
- π¨ Beautiful UI - Modern, responsive design powered by Tailwind CSS
- π Dark mode support - Seamless light/dark theme switching
- π± Fully responsive - Looks great on all devices from mobile to desktop
- π SEO optimized - Structured data, meta tags, and optimized for search engines
- π·οΈ Tag system - Categorize blog posts and projects with tags
- π Content collection - Organized content management with Astro's content collections
- πΌοΈ Project showcase - Display your work with images, descriptions, and technology tags
- Astro.js - Static site generator with excellent performance
- React.js - For interactive components
- Tailwind CSS - Utility-first CSS framework
- TypeScript - Type safety and improved developer experience
- MDX - Markdown with JSX for rich content creation
- Preact - Lightweight alternative to React for UI components
Before you begin, ensure you have the following installed:
-
Clone the repository
git clone https://github.com/cojocaru-david/portfolio.git cd portfolio -
Install dependencies
npm install # or yarn install -
Start the development server
npm run dev # or yarn dev -
Open your browser Navigate to
http://localhost:4321to see the site running locally.
portfolio/
βββ public/ # Static assets
βββ src/
β βββ components/ # UI components
β βββ content/ # Content collections
β β βββ blog/ # Blog posts in MD/MDX format
β β βββ projects/ # Project data
β βββ layouts/ # Page layouts
β βββ lib/ # Utility functions
β βββ pages/ # Page routes
β βββ styles/ # Global styles
βββ astro.config.mjs # Astro configuration
βββ tailwind.config.cjs # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ package.json # Project dependencies
- Create a new
.mdxor.mdfile insrc/content/blog - Add frontmatter with title, description, date, tags, and authors
- Write your content using Markdown and MDX components
---
title: "Your Post Title"
description: "A brief description of your post"
date: 2025-04-20
tags: ["tag1", "tag2", "tag3"]
authors: ["Your Name", "Co-author (optional)"]
---
# Your Post Title
Write your content here using Markdown.
## Subheading
More content...
- Create a new
.mdfile in projects - Add project details including name, description, tags, and image path
The project includes dark mode support using Tailwind CSS and Preact. It detects user preferences and applies the appropriate theme, with an option to toggle between light and dark modes.
The project includes several utility functions in data-utils.ts:
getAllPosts()- Retrieve all blog postsgetRecentPosts(count)- Get the most recent postsgetAdjacentPosts(currentId)- Get next and previous postsgetAllTags()- Get all tags used in postsgetSortedTags()- Get tags sorted by usage countgetPostsByAuthor(authorId)- Get all posts by a specific author
The portfolio includes a post feedback system allowing visitors to like or dislike blog posts. This feature requires a PostgreSQL database to store vote data. This guide explains how to set it up using Neon.tech.
-
Create a Neon.tech Account
- Go to Neon.tech and sign up for an account
- Neon offers a generous free tier suitable for personal portfolio sites
-
Create a New Project
- From the Neon dashboard, click "New Project"
- Choose a name for your project (e.g., "portfolio-feedback")
- Select the closest region to your target audience
- Click "Create Project"
-
Get Connection Details
- In your project dashboard, find the connection string under "Connection Details"
- Save this connection string as you'll need it for your environment variables
-
Set Environment Variables
- Create a
.envfile in your project root (if not already present) - Add your database connection string:
DATABASE_URL=your_neon_connection_string_here
- Create a
Run the following SQL queries in the Neon SQL Editor to create the necessary tables for the feedback system:
CREATE TABLE post_feedback (
id SERIAL PRIMARY KEY,
post_id VARCHAR(255) NOT NULL,
likes INTEGER DEFAULT 0,
dislikes INTEGER DEFAULT 0,
CONSTRAINT unique_post_id UNIQUE (post_id)
);
CREATE TABLE post_likes (
id SERIAL PRIMARY KEY,
post_id VARCHAR(255) NOT NULL,
fingerprint_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_like UNIQUE (post_id, fingerprint_id)
);
CREATE TABLE post_dislikes (
id SERIAL PRIMARY KEY,
post_id VARCHAR(255) NOT NULL,
fingerprint_id VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT unique_dislike UNIQUE (post_id, fingerprint_id)
);
CREATE INDEX idx_post_feedback_post_id ON post_feedback (post_id);
CREATE INDEX idx_post_likes_post_id_fingerprint ON post_likes (post_id, fingerprint_id);
CREATE INDEX idx_post_dislikes_post_id_fingerprint ON post_dislikes (post_id, fingerprint_id);The feedback system consists of three tables:
post_feedback: Stores aggregate counts of likes and dislikes for each postpost_likes: Records individual like actions with fingerprint IDs to prevent duplicate votespost_dislikes: Records individual dislike actions with fingerprint IDs
The portfolio includes API endpoints for handling likes and dislikes:
-
Fetching Post Feedback
- GET request to
/api/like/{postId}returns current like/dislike counts
- GET request to
-
Submitting Likes
- POST request to
/api/like/{postId}with fingerprint ID in the request body - The system checks if the user has already liked/disliked the post
- If not, a like is recorded and the count is updated
- POST request to
-
Submitting Dislikes
- POST request to
/api/dislike/{postId}works similarly to the like endpoint - Prevents duplicate votes from the same visitor
- POST request to
When a new blog post is created:
- No manual database entry is needed
- The first like/dislike action will automatically create the entry in
post_feedback
To reset likes for a post:
DELETE FROM post_likes WHERE post_id = 'your-post-id';
DELETE FROM post_dislikes WHERE post_id = 'your-post-id';
UPDATE post_feedback SET likes = 0, dislikes = 0 WHERE post_id = 'your-post-id';To view post statistics:
SELECT * FROM post_feedback ORDER BY likes DESC;To find which posts a specific user has interacted with:
SELECT post_id FROM post_likes WHERE fingerprint_id = 'specific-fingerprint-id';If you encounter issues with the feedback system:
-
Check Environment Variables
- Ensure your
.envfile contains the correctDATABASE_URL
- Ensure your
-
Verify Database Connection
- Add logging to your database connection code to check for errors
- Make sure your Neon.tech project is active and not in suspended state
-
Check for Errors in Console
- The feedback component logs errors that can help diagnose issues
-
Reset User Vote State
- Users can clear their localStorage to reset their voting state:
// In browser console localStorage.clear()
- Users can clear their localStorage to reset their voting state:
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.