diff --git a/NEWSLETTER_GUIDE.md b/NEWSLETTER_GUIDE.md new file mode 100644 index 00000000..6cddb01c --- /dev/null +++ b/NEWSLETTER_GUIDE.md @@ -0,0 +1,145 @@ +# newsletter feature documentation + +## overview +newsletters are premium content available exclusively to subscribed users on opensox.ai. they provide insights on ai, coding, open source, and developer tools. + +--- + +## 📝 adding a new newsletter + +### step 1: open the data file +navigate to: `apps/web/src/data/newsletters.ts` + +### step 2: add your newsletter entry +add a new object to the `newsletters` array: + +```typescript +{ + id: "4", // increment from last id + date: "DD-MM-YY", // e.g., "20-11-25" for nov 20, 2025 + title: "your newsletter title", + slug: "url-friendly-slug", // lowercase, hyphens only + excerpt: "a compelling 1-2 sentence description", + content: ```html +

main section heading

+

your paragraph content here. use bold text for emphasis.

+ + descriptive alt text + +

add external links for references.

+ +

subsection heading

+ + ```, + tags: ["relevant", "topic", "tags"], + readTime: 6 // optional: estimated minutes to read +} +``` + +### step 3: save and test +1. save the file +2. the dev server will auto-reload (or run `npm run dev`) +3. visit `/newsletters` to see your new entry +4. click through to verify content displays correctly + +--- + +## 🎨 content formatting guide + +### headings +```html +

main section heading

+

subsection heading

+``` +- use h2 for main sections +- use h3 for subsections +- keep headings descriptive and concise + +### paragraphs +```html +

regular paragraph text goes here.

+

use bold text to emphasize key points.

+``` +- keep paragraphs 2-4 sentences for readability +- use bold sparingly for impact + +### links +```html +descriptive link text +``` +- always use https:// +- make link text descriptive (avoid "click here") + +### images +```html +descriptive alt text +``` +- use high-quality images (minimum 1200px wide) +- always include descriptive alt text +- use unsplash for free stock photos +- images should be relevant to surrounding content + +### lists +```html + +``` +- use bold for list item titles +- follow with a dash and description +- keep list items parallel in structure + +--- + +## 🔒 access control + +newsletters are **automatically protected** for premium users: +- non-premium users see a subscription prompt +- authentication handled by `useSubscription` hook +- no additional configuration needed + +--- + +## 🐛 troubleshooting + +### newsletter not appearing? +- verify date format is `DD-MM-YY` +- check for syntax errors in the typescript +- ensure the newsletter is in the `newsletters` array +- restart dev server if needed + +### images not loading? +- use absolute urls starting with `https://` +- verify the url works in a browser +- check that alt text is provided +- try a different image source if loading fails + +### formatting looks wrong? +- ensure all html tags are properly closed +- check for typos in tag names +- use the examples above as templates +- view in dev environment before committing + +### content overflowing or breaking? +- avoid extremely long words +- break up long paragraphs +- ensure images have proper dimensions +- check responsive display on mobile + +--- + +## 📂 file structure + +``` +apps/web/src/ +├── app/(main)/newsletters/ +│ ├── page.tsx # newsletter list page +│ └── [slug]/page.tsx # individual newsletter page +└── data/ + └── newsletters.ts # ✏️ edit this file to add newsletters +``` diff --git a/apps/web/src/app/(main)/newsletters/[slug]/page.tsx b/apps/web/src/app/(main)/newsletters/[slug]/page.tsx new file mode 100644 index 00000000..5e7068b2 --- /dev/null +++ b/apps/web/src/app/(main)/newsletters/[slug]/page.tsx @@ -0,0 +1,240 @@ +"use client"; + +import { use } from "react"; +import { notFound } from "next/navigation"; +import Link from "next/link"; +import { ArrowLeft, Home } from "lucide-react"; +import { newsletters } from "@/data/newsletters"; +import { useSubscription } from "@/hooks/useSubscription"; + +export default function NewsletterPage({ + params +}: { + params: Promise<{ slug: string }> +}) { + const { slug } = use(params); + const { isPaidUser, isLoading } = useSubscription(); + const newsletter = newsletters.find(n => n.slug === slug); + + if (!newsletter) notFound(); + + // loading + if (isLoading) { + return ( +
+

loading...

+
+ ); + } + + // premium gate + if (!isPaidUser) { + return ( +
+
+

premium content

+

subscribe to access this newsletter

+ + view plans + +
+
+ ); + } + + return ( +
+ {/* navigation bar */} +
+
+
+ {/* back to newsletters */} + + + newsletters + + + {/* back to dashboard */} + + + dashboard + +
+
+
+ +
+ {/* header */} +
+ {/* metadata */} +
+ + {newsletter.date} + + + + {calculateReadTime(newsletter.content)} min read + +
+ + {/* title */} +

+ {newsletter.title} +

+ + {/* excerpt */} +

+ {newsletter.excerpt} +

+ + {/* tags */} +
+ {newsletter.tags.map(tag => ( + + {tag} + + ))} +
+
+ + {/* divider */} +
+ + {/* content */} +
+ + {/* divider */} +
+ + {/* footer navigation */} +
+ + + all newsletters + + + + + back to dashboard + +
+
+ + {/* styles */} + +
+ ); +} + +function calculateReadTime(content: string): number { + const text = content.replace(/<[^>]*>/g, ''); + const words = text.trim().split(/\s+/).length; + const minutes = words / 238; + return Math.max(1, Math.ceil(minutes)); +} \ No newline at end of file diff --git a/apps/web/src/app/(main)/newsletters/page.tsx b/apps/web/src/app/(main)/newsletters/page.tsx new file mode 100644 index 00000000..e284c00c --- /dev/null +++ b/apps/web/src/app/(main)/newsletters/page.tsx @@ -0,0 +1,254 @@ +"use client"; + +import { useState, useMemo } from "react"; +import Link from "next/link"; +import { ChevronDown, Search, Home } from "lucide-react"; +import { newsletters } from "@/data/newsletters"; +import { useSubscription } from "@/hooks/useSubscription"; + +export default function NewslettersPage() { + const { isPaidUser, isLoading } = useSubscription(); + const [searchQuery, setSearchQuery] = useState(""); + const [selectedMonth, setSelectedMonth] = useState("all"); + const [showMonthFilter, setShowMonthFilter] = useState(false); + + // group by month + const groupedByMonth = useMemo(() => { + const grouped: Record = {}; + newsletters.forEach(newsletter => { + const [day, month, year] = newsletter.date.split("-"); + const monthYear = `${month}-20${year}`; + if (!grouped[monthYear]) { + grouped[monthYear] = []; + } + grouped[monthYear].push(newsletter); + }); + return grouped; + }, []); + + // filter newsletters + const filteredNewsletters = useMemo(() => { + let filtered = newsletters; + + // filter by search + if (searchQuery) { + const query = searchQuery.toLowerCase(); + filtered = filtered.filter(n => + n.title.toLowerCase().includes(query) || + n.excerpt.toLowerCase().includes(query) || + n.tags.some(t => t.toLowerCase().includes(query)) + ); + } + + // filter by month + if (selectedMonth !== "all") { + filtered = filtered.filter(n => { + const [day, month, year] = n.date.split("-"); + const monthYear = `${month}-20${year}`; + return monthYear === selectedMonth; + }); + } + + // sort by date (latest first) + return filtered.sort((a, b) => { + const dateA = parseDate(a.date); + const dateB = parseDate(b.date); + return dateB.getTime() - dateA.getTime(); + }); + }, [searchQuery, selectedMonth]); + + // loading + if (isLoading) { + return ( +
+

loading...

+
+ ); + } + + // premium gate + if (!isPaidUser) { + return ( +
+
+

premium content

+

+ newsletters are exclusive to premium members +

+ + view plans + +
+
+ ); + } + + return ( +
+ {/* navigation breadcrumb */} +
+
+ + + dashboard + +
+
+ + {/* header */} +
+
+

newsletters

+

exclusive insights for premium members

+
+
+ +
+ {/* search */} +
+
+ + setSearchQuery(e.target.value)} + className="w-full bg-[#1a1a1a] border border-[#252525] rounded-lg pl-10 pr-4 py-3 text-white placeholder:text-gray-500 focus:outline-none focus:border-[#a472ea] transition" + /> +
+
+ + {/* month filter (collapsible) */} +
+ + + {showMonthFilter && ( +
+ + {Object.keys(groupedByMonth) + .sort((a, b) => { + const [monthA, yearA] = a.split("-").map(Number); + const [monthB, yearB] = b.split("-").map(Number); + return yearB - yearA || monthB - monthA; + }) + .map(monthYear => ( + + ))} +
+ )} +
+ + {/* results count */} +

+ {filteredNewsletters.length} {filteredNewsletters.length === 1 ? 'newsletter' : 'newsletters'} +

+ + {/* newsletter list */} + {filteredNewsletters.length === 0 ? ( +
+

no newsletters found

+
+ ) : ( +
+ {filteredNewsletters.map((newsletter) => ( + +
+ {/* date and tags */} +
+ + {newsletter.date} + + + {newsletter.tags.slice(0, 2).map(tag => ( + + {tag} + + ))} +
+ + {/* title */} +

+ {newsletter.title} +

+ + {/* excerpt */} +

+ {newsletter.excerpt} +

+ + {/* footer */} +
+ + {calculateReadTime(newsletter.content)} min read + + + read more → + +
+
+ + ))} +
+ )} +
+
+ ); +} + +// helper functions +function parseDate(dateStr: string): Date { + const [day, month, year] = dateStr.split("-").map(Number); + return new Date(2000 + year, month - 1, day); +} + +function formatMonthYear(monthYear: string): string { + const [month, year] = monthYear.split("-"); + const months = ["jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec"]; + return `${months[parseInt(month) - 1]} ${year}`; +} + +function calculateReadTime(content: string): number { + const text = content.replace(/<[^>]*>/g, ''); + const words = text.trim().split(/\s+/).length; + const minutes = words / 238; + return Math.max(1, Math.ceil(minutes)); +} \ No newline at end of file diff --git a/apps/web/src/components/dashboard/Sidebar.tsx b/apps/web/src/components/dashboard/Sidebar.tsx index eabb0461..5817ef90 100644 --- a/apps/web/src/components/dashboard/Sidebar.tsx +++ b/apps/web/src/components/dashboard/Sidebar.tsx @@ -17,12 +17,14 @@ import { StarIcon, HeartIcon, EnvelopeIcon, + EnvelopeOpenIcon } from "@heroicons/react/24/outline"; import { useShowSidebar } from "@/store/useShowSidebar"; import { signOut } from "next-auth/react"; import { Twitter } from "../icons/icons"; import { ProfilePic } from "./ProfilePic"; import { useFilterStore } from "@/store/useFilterStore"; +import { useSubscription } from "@/hooks/useSubscription"; const SIDEBAR_ROUTES = [ { @@ -35,6 +37,11 @@ const SIDEBAR_ROUTES = [ label: "Projects", icon: , }, + { + path: "/newsletters", + label: "Newsletters", + icon: , + }, ]; const getSidebarLinkClassName = (currentPath: string, routePath: string) => { @@ -47,6 +54,7 @@ export default function Sidebar() { useShowSidebar(); const pathname = usePathname(); const { setShowFilters } = useFilterStore(); + const { isPaidUser } = useSubscription(); const reqFeatureHandler = () => { window.open("https://discord.gg/37ke8rYnRM", "_blank"); @@ -115,6 +123,7 @@ export default function Sidebar() { {/* Find projects entry */} {SIDEBAR_ROUTES.map((route) => { + if(route.path === "/newsletters" && !isPaidUser) return null; const activeClass = getSidebarLinkClassName(pathname, route.path); return ( diff --git a/apps/web/src/data/newsletters.ts b/apps/web/src/data/newsletters.ts new file mode 100644 index 00000000..94a54e9d --- /dev/null +++ b/apps/web/src/data/newsletters.ts @@ -0,0 +1,245 @@ +// apps/web/src/data/newsletters.ts + +export interface Newsletter { + id: string; + date: string; // format: "DD-MM-YY" (e.g., "15-11-25") + title: string; + slug: string; + excerpt: string; + content: string; + tags: string[]; +} + +export const newsletters: Newsletter[] = [ + { + id: "1", + date: "13-11-25", + title: "the ai coding revolution: how developers are building faster", + slug: "ai-coding-revolution-developers-building-faster", + excerpt: "exploring how ai assistants like claude, cursor, and github copilot are transforming software development workflows in 2025", + content: ` +

the shift is happening now

+

we're witnessing a fundamental transformation in how software gets built. ai coding assistants have evolved from simple autocomplete tools into sophisticated pair programming partners that understand context, suggest architecture, and even debug complex issues.

+ + developer working with ai tools + +

the new developer toolkit

+

modern developers are embracing a new set of tools that fundamentally change the development experience:

+ +
    +
  • claude & chatgpt - for architecture decisions, code reviews, and complex problem solving
  • +
  • github copilot - inline code suggestions that understand your entire codebase
  • +
  • cursor & windsurf - ai-native code editors that feel like magic
  • +
  • v0 & bolt.new - generating full applications from natural language descriptions
  • +
+ +

real productivity gains

+

the numbers don't lie. developers using ai assistants report 30-50% productivity improvements across various tasks. but it's not just about speed - it's about quality and learning.

+ +

junior developers can now tackle senior-level problems with ai guidance. senior developers can focus on architecture and business logic while ai handles boilerplate. the entire industry is leveling up.

+ + productivity metrics dashboard + +

the skills that matter now

+

as ai handles more of the mechanical coding work, these skills become increasingly valuable:

+ +
    +
  • prompt engineering - knowing how to communicate with ai effectively
  • +
  • code review & validation - understanding what ai generates and why
  • +
  • system design - ai can't (yet) design complex distributed systems
  • +
  • product thinking - understanding what to build is more important than how
  • +
+ +

the future is collaborative

+

the future isn't about ai replacing developers - it's about augmented development. the best teams will be those that learn to work with ai as a force multiplier, not a replacement.

+ +

developers who embrace these tools today will have a significant advantage. those who resist will find themselves left behind. the choice is yours.

+ +

want to dive deeper? check out github trending to see what the community is building with ai assistance.

+ `, + tags: ["ai", "coding", "productivity", "tools"], + }, + { + id: "2", + date: "06-11-25", + title: "open source sustainability: solving the funding crisis", + slug: "open-source-sustainability-funding-crisis", + excerpt: "how the open source community is innovating new economic models to support maintainers and ensure project longevity", + content: ` +

the invisible infrastructure

+

open source software powers the world. 90% of modern applications rely on open source dependencies. yet the developers maintaining these critical projects often work for free, burning out while corporations profit from their labor.

+ + open source community collaboration + +

the current state of crisis

+

the situation is dire. maintainers of critical infrastructure projects often face:

+ +
    +
  • burnout from unpaid labor - maintaining projects in nights and weekends
  • +
  • security vulnerabilities - no time for proper security audits
  • +
  • abandoned projects - maintainers giving up due to lack of support
  • +
  • corporate exploitation - billion-dollar companies using free labor
  • +
+ +

we've seen high-profile cases where single maintainers support millions of developers. when they burn out, the entire ecosystem suffers.

+ +

emerging funding models

+

the community is fighting back with innovative approaches:

+ + funding and support concept + +

1. github sponsors & open collective

+

platforms enabling direct financial support from individuals and companies. recurring monthly sponsorships provide stable income for maintainers.

+ +

2. corporate sponsorship programs

+

companies like google, microsoft, and meta are funding critical infrastructure through dedicated programs. they're finally recognizing their dependency on open source.

+ +

3. bounty-based development

+

platforms like gitcoin and bountysource let users fund specific features and bug fixes. maintainers get paid for work they were going to do anyway.

+ +

4. dual licensing models

+

projects offering free licenses for individuals and paid licenses for commercial use. this creates sustainable revenue while staying true to open source values.

+ +

5. hosted services & support

+

maintainers offering paid hosting, support, and training around their open source projects. think wordpress.com around wordpress.

+ +

success stories

+

some projects are showing it's possible to sustain open source development:

+ +
    +
  • tailwind css - generates millions through tailwind ui and labs
  • +
  • sentry - open core model with hosted service
  • +
  • next.js - backed by vercel's platform revenue
  • +
  • vue.js - fully funded through sponsorships and donations
  • +
+ +

what you can do

+

everyone benefits from open source. here's how to contribute back:

+ +
    +
  • sponsor projects you depend on - even $5/month makes a difference
  • +
  • convince your company to sponsor - they're making money from open source
  • +
  • contribute code, docs, or support - reduce maintainer burden
  • +
  • spread awareness - help others understand the sustainability crisis
  • +
+ +

the future of open source depends on solving this crisis. let's build a sustainable ecosystem together.

+ +

learn more at open collective and github sponsors.

+ `, + tags: ["open source", "funding", "sustainability", "community"], + }, + { + id: "3", + date: "30-10-25", + title: "web performance in 2025: the speed imperative", + slug: "web-performance-2025-speed-imperative", + excerpt: "why performance is no longer optional and the modern techniques making websites lightning fast", + content: ` +

every millisecond counts

+

in 2025, speed isn't a feature - it's a requirement. users expect instant experiences. a 100ms delay can reduce conversions by 7%. a 2-second load time means losing half your visitors. performance is literally money.

+ + performance metrics dashboard + +

the performance landscape

+

modern web performance isn't just about file sizes anymore. it's a complex interplay of:

+ +
    +
  • core web vitals - google's metrics for user experience quality
  • +
  • time to interactive - how quickly users can actually use your site
  • +
  • largest contentful paint - when the main content becomes visible
  • +
  • cumulative layout shift - visual stability during loading
  • +
+ +

these metrics directly impact seo rankings, user satisfaction, and business outcomes. ignoring them is leaving money on the table.

+ +

modern optimization techniques

+ +

1. edge computing revolution

+

serving content from edge locations worldwide reduces latency dramatically. platforms like cloudflare, vercel edge, and netlify edge bring your content milliseconds away from users.

+ + global network visualization + +

2. smart bundling strategies

+

modern bundlers like vite, turbopack, and rspack are revolutionizing build times and bundle optimization. they understand your dependency graph and serve exactly what's needed.

+ +
    +
  • code splitting - load only what the current page needs
  • +
  • tree shaking - eliminate unused code automatically
  • +
  • dynamic imports - load heavy features on demand
  • +
  • preloading critical resources - fetch what you know you'll need
  • +
+ +

3. image optimization excellence

+

images are often the heaviest assets. modern solutions handle this automatically:

+ +
    +
  • avif & webp formats - 50% smaller than jpeg with better quality
  • +
  • responsive images - serve appropriate sizes for each device
  • +
  • lazy loading - load images as users scroll to them
  • +
  • blur-up placeholders - show something instantly while loading
  • +
+ +

4. the streaming ssr renaissance

+

frameworks like next.js 14+ and remix embrace streaming server-side rendering. instead of waiting for everything to render, they stream content as it becomes ready.

+ +

users see content faster. servers handle more load. it's a win-win enabled by react server components and edge runtimes.

+ + fast loading website + +

5. database optimization at the edge

+

new database solutions bring data closer to users:

+ +
    +
  • turso - sqlite at the edge with replication
  • +
  • planetscale - serverless mysql with global reads
  • +
  • neon - serverless postgres that scales to zero
  • +
  • cloudflare d1 - sqlite databases distributed worldwide
  • +
+ +

framework-level innovations

+

modern frameworks are making performance the default:

+ +
    +
  • next.js - automatic optimization, image optimization, and edge runtime
  • +
  • astro - zero javascript by default, partial hydration
  • +
  • remix - nested routing and aggressive prefetching
  • +
  • qwik - resumability instead of hydration
  • +
+ +

measuring success

+

you can't improve what you don't measure. essential tools for 2025:

+ +
    +
  • pagespeed insights - google's performance analysis
  • +
  • webpagetest - detailed waterfall analysis
  • +
  • lighthouse ci - automated performance testing in your pipeline
  • +
  • real user monitoring - track actual user experiences
  • +
+ +

the business case

+

performance optimization isn't just technical - it's business critical:

+ +
    +
  • pinterest reduced load times by 40% and saw 15% increase in signups
  • +
  • walmart found every 1 second improvement increased conversions by 2%
  • +
  • amazon calculated every 100ms delay costs 1% in sales
  • +
+ +

faster sites rank higher in search, convert better, and keep users engaged longer. performance is a competitive advantage.

+ +

getting started today

+

start your performance journey:

+ +
    +
  • run pagespeed insights on your site
  • +
  • identify your biggest bottlenecks
  • +
  • implement one optimization at a time
  • +
  • measure the impact and iterate
  • +
+ +

every improvement compounds. start today and watch your metrics soar.

+ `, + tags: ["performance", "web dev", "optimization", "user experience"], + } +]; \ No newline at end of file