Skip to content

Commit 8b2f89a

Browse files
committed
Improving the note section!
1 parent a22fb99 commit 8b2f89a

6 files changed

Lines changed: 212 additions & 2 deletions

File tree

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
---
2+
import type { Note } from '../../../types/NoteType';
3+
4+
interface Props {
5+
notes: Note[];
6+
}
7+
8+
const { notes } = Astro.props;
9+
10+
// Extract unique types and tags from notes
11+
const types = Array.from(new Set(notes.map(n => n.data.type).filter(Boolean))) as string[];
12+
const tags = Array.from(new Set(notes.flatMap(n => n.data.tags || [])));
13+
---
14+
15+
<section class="py-12 bg-background">
16+
<div class="container mx-auto px-4 max-w-4xl">
17+
<h1 class="text-center text-3xl mb-10">Notes</h1>
18+
19+
<div class="mb-8 space-y-4">
20+
<div>
21+
<label class="block text-sm font-medium mb-2">Filter by Type</label>
22+
<div class="flex flex-wrap gap-2">
23+
<button
24+
class="filter-btn px-4 py-2 rounded border border-primary text-primary hover:bg-primary hover:text-white transition-colors active"
25+
data-filter="all"
26+
>
27+
All
28+
</button>
29+
{types.map(type => (
30+
<button
31+
class="filter-btn px-4 py-2 rounded border border-gray-300 hover:border-primary hover:text-primary transition-colors"
32+
data-filter={`type-${type}`}
33+
data-type={type}
34+
>
35+
{type.charAt(0).toUpperCase() + type.slice(1)}
36+
</button>
37+
))}
38+
</div>
39+
</div>
40+
41+
<div>
42+
<label class="block text-sm font-medium mb-2">Filter by Tag</label>
43+
<div class="flex flex-wrap gap-2">
44+
{tags.map(tag => (
45+
<button
46+
class="tag-filter px-3 py-1 rounded-full text-sm border border-gray-300 hover:border-primary hover:text-primary transition-colors"
47+
data-tag={tag}
48+
>
49+
#{tag}
50+
</button>
51+
))}
52+
</div>
53+
</div>
54+
</div>
55+
56+
<div id="notes-container" class="space-y-6">
57+
{notes.map((note: Note, index: number) => (
58+
<div
59+
class="note-item transition-all duration-300"
60+
data-type={note.data.type || 'all'}
61+
data-tags={JSON.stringify(note.data.tags || [])}
62+
>
63+
<a href={`/notes/${note.slug}`} class="block hover:opacity-80">
64+
<h3 class="text-xl font-semibold mb-2">{note.data.title}</h3>
65+
<p class="text-gray-600 dark:text-gray-400 mb-3">{note.data.description}</p>
66+
{(note.data.tags && note.data.tags.length > 0) && (
67+
<div class="flex flex-wrap gap-2 mb-2">
68+
{note.data.tags.map(tag => (
69+
<span class="inline-block px-2 py-1 text-xs rounded-full bg-primary/10 text-primary">
70+
#{tag}
71+
</span>
72+
))}
73+
</div>
74+
)}
75+
<span class="text-sm text-gray-500">
76+
{new Date(note.data.datetime).toLocaleDateString()}
77+
{note.data.type && ` • ${note.data.type}`}
78+
</span>
79+
</a>
80+
</div>
81+
))}
82+
</div>
83+
</div>
84+
</section>
85+
86+
<script>
87+
document.addEventListener('DOMContentLoaded', () => {
88+
const filterBtns = document.querySelectorAll('.filter-btn');
89+
const tagBtns = document.querySelectorAll('.tag-filter');
90+
const noteItems = document.querySelectorAll('.note-item');
91+
92+
let activeType = 'all';
93+
let activeTags = new Set<string>();
94+
95+
const updateDisplay = () => {
96+
noteItems.forEach(item => {
97+
const itemType = item.getAttribute('data-type');
98+
const itemTags = JSON.parse(item.getAttribute('data-tags') || '[]');
99+
100+
const typeMatch = activeType === 'all' || itemType === activeType;
101+
const tagMatch = activeTags.size === 0 || itemTags.some(tag => activeTags.has(tag));
102+
103+
if (typeMatch && tagMatch) {
104+
item.classList.remove('hidden');
105+
item.classList.add('opacity-100');
106+
} else {
107+
item.classList.add('hidden');
108+
item.classList.remove('opacity-100');
109+
}
110+
});
111+
};
112+
113+
filterBtns.forEach(btn => {
114+
btn.addEventListener('click', () => {
115+
filterBtns.forEach(b => b.classList.remove('active', 'bg-primary', 'text-white'));
116+
btn.classList.add('active', 'bg-primary', 'text-white');
117+
activeType = btn.getAttribute('data-filter')?.replace('type-', '') || 'all';
118+
updateDisplay();
119+
});
120+
});
121+
122+
tagBtns.forEach(btn => {
123+
btn.addEventListener('click', () => {
124+
const tag = btn.getAttribute('data-tag');
125+
if (tag) {
126+
if (activeTags.has(tag)) {
127+
activeTags.delete(tag);
128+
btn.classList.remove('active', 'border-primary', 'text-primary', 'bg-primary/10');
129+
} else {
130+
activeTags.add(tag);
131+
btn.classList.add('active', 'border-primary', 'text-primary', 'bg-primary/10');
132+
}
133+
updateDisplay();
134+
}
135+
});
136+
});
137+
});
138+
</script>
139+
140+
<style>
141+
.active {
142+
@apply bg-primary text-white border-primary;
143+
}
144+
</style>

src/content/config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ const notesCollection = defineCollection({
1515
title: z.string(),
1616
description: z.string(),
1717
datetime: z.string().or(z.date()),
18-
// Optional: add type field if you want to differentiate note types
19-
type: z.enum(['technical', 'personal', 'learning']).optional(),
18+
type: z.enum(['technical', 'personal', 'learning', 'linkedin']).optional(),
19+
tags: z.array(z.string()).optional(),
2020
}),
2121
});
2222

src/content/notes/technical/regex.mdx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
title: Regular Expressions (Regex) Cheatsheet
33
description: A concise, one-page reference for the most common regex tokens and patterns
44
datetime: 2025-12-17
5+
type: technical
6+
tags:
7+
- regex
8+
- pattern-matching
9+
- javascript
510
---
611

712
# 🧩 Regular Expressions (Regex) Cheatsheet

src/pages/notes/[...slug].astro

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
import { getCollection } from 'astro:content';
3+
import type { CollectionEntry } from 'astro:content';
4+
5+
import Layout from '../../layouts/Layout.astro';
6+
7+
export const getStaticPaths = async () => {
8+
const notes = await getCollection("notes");
9+
const paths = notes.map(note => {
10+
return {
11+
params: {
12+
slug: note.slug
13+
},
14+
props: {
15+
note
16+
}
17+
}
18+
});
19+
20+
return paths;
21+
}
22+
23+
type Props = {
24+
note: CollectionEntry<"notes">
25+
}
26+
27+
const {note} = Astro.props;
28+
const {Content} = await note.render();
29+
---
30+
31+
<Layout title={note.data.title}>
32+
<section class="flex justify-center mt-12">
33+
<div id="markdown" class="prose prose-lg overflow-visible relative mb-5">
34+
<Content />
35+
</div>
36+
</section>
37+
38+
<script>
39+
import { toggleMarkdownTheme } from "../../scripts/theme";
40+
41+
const theme = document.documentElement.dataset.theme;
42+
43+
toggleMarkdownTheme(theme)
44+
</script>
45+
</Layout>

src/pages/notes/index.astro

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
import { getCollection } from 'astro:content';
3+
import Layout from '../../layouts/Layout.astro';
4+
import NotesSearch from '../../components/sections/notes/NotesSearch.astro';
5+
6+
const notes = await getCollection("notes");
7+
const sortedNotes = notes.sort((a, b) =>
8+
new Date(b.data.datetime).getTime() - new Date(a.data.datetime).getTime()
9+
);
10+
---
11+
12+
<Layout title="Notes">
13+
<NotesSearch notes={sortedNotes} />
14+
</Layout>

src/types/NoteType.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ export interface Note {
77
description: string;
88
datetime: string;
99
image?: string;
10+
type?: string;
11+
tags?: string[];
1012
}
1113
}

0 commit comments

Comments
 (0)