diff --git a/public/sitemap.xml b/public/sitemap.xml index d3a6b57..3025d54 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -17,7 +17,7 @@ 0.8 - https://codeforgedev.vercel.app/blog + https://codeforgedev.vercel.app/blogs daily 0.9 @@ -37,15 +37,15 @@ 0.3 - https://codeforgedev.vercel.app/blog/building-codeforge-my-journey-creating-my-first-full-stack-portfolio2026-06-26weekly0.7 - https://codeforgedev.vercel.app/blog/linux-commands-every-computer-science-student-should-know2026-07-28monthly0.7 - https://codeforgedev.vercel.app/blog/why-every-computer-science-student-should-learn-git-and-github2026-07-28monthly0.7 - https://codeforgedev.vercel.app/blog/networking-basics-every-computer-science-student-should-understand2026-07-30monthly0.7 - https://codeforgedev.vercel.app/blog/what-happens-when-you-type-a-url2026-09-07monthly0.7 - https://codeforgedev.vercel.app/blog/understanding-apis2026-09-07monthly0.7 - https://codeforgedev.vercel.app/blog/sql-vs-nosql2026-09-07monthly0.7 - https://codeforgedev.vercel.app/blog/how-dns-works2026-09-07monthly0.7 - https://codeforgedev.vercel.app/blog/processes-threads-memory2026-09-07monthly0.7 - https://codeforgedev.vercel.app/blog/hashing-encryption-encoding2026-09-21monthly0.7 + https://codeforgedev.vercel.app/blogs/building-codeforge-my-journey-creating-my-first-full-stack-portfolio2026-06-26weekly0.7 + https://codeforgedev.vercel.app/blogs/linux-commands-every-computer-science-student-should-know2026-07-28monthly0.7 + https://codeforgedev.vercel.app/blogs/why-every-computer-science-student-should-learn-git-and-github2026-07-28monthly0.7 + https://codeforgedev.vercel.app/blogs/networking-basics-every-computer-science-student-should-understand2026-07-30monthly0.7 + https://codeforgedev.vercel.app/blogs/what-happens-when-you-type-a-url2026-09-07monthly0.7 + https://codeforgedev.vercel.app/blogs/understanding-apis2026-09-07monthly0.7 + https://codeforgedev.vercel.app/blogs/sql-vs-nosql2026-09-07monthly0.7 + https://codeforgedev.vercel.app/blogs/how-dns-works2026-09-07monthly0.7 + https://codeforgedev.vercel.app/blogs/processes-threads-memory2026-09-07monthly0.7 + https://codeforgedev.vercel.app/blogs/hashing-encryption-encoding2026-09-21monthly0.7 diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 01ff6a9..d31875d 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -70,7 +70,7 @@ export function Footer() {

Explore

diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 50c6510..f16a783 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -8,7 +8,7 @@ const NAV = [ { to: "/", label: "Home" }, { to: "/about", label: "About" }, { to: "/projects", label: "Projects" }, - { to: "/blog", label: "Blog" }, + { to: "/blogs", label: "Blogs" }, { to: "/contact", label: "Contact" }, ] as const; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 138dafe..44e6400 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -11,17 +11,19 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as ThankYouRouteImport } from './routes/thank-you' import { Route as TermsRouteImport } from './routes/terms' +import { Route as ResetPasswordRouteImport } from './routes/reset-password' import { Route as ProjectsRouteImport } from './routes/projects' import { Route as PrivacyPolicyRouteImport } from './routes/privacy-policy' import { Route as MfaVerifyRouteImport } from './routes/mfa-verify' +import { Route as MfaSetupRouteImport } from './routes/mfa-setup' +import { Route as ForgotPasswordRouteImport } from './routes/forgot-password' import { Route as ContactRouteImport } from './routes/contact' -import { Route as BlogRouteImport } from './routes/blog' -import { Route as Auth_backupRouteImport } from './routes/auth_backup' +import { Route as BlogsRouteImport } from './routes/blogs' import { Route as AuthRouteImport } from './routes/auth' import { Route as AboutRouteImport } from './routes/about' import { Route as AuthenticatedRouteRouteImport } from './routes/_authenticated/route' import { Route as IndexRouteImport } from './routes/index' -import { Route as BlogSlugRouteImport } from './routes/blog_.$slug' +import { Route as BlogsSlugRouteImport } from './routes/blogs_.$slug' import { Route as AuthenticatedDashboardRouteImport } from './routes/_authenticated/dashboard' import { Route as AuthenticatedAdminRouteImport } from './routes/_authenticated/admin' import { Route as AuthenticatedDashboardNewRouteImport } from './routes/_authenticated/dashboard.new' @@ -37,6 +39,11 @@ const TermsRoute = TermsRouteImport.update({ path: '/terms', getParentRoute: () => rootRouteImport, } as any) +const ResetPasswordRoute = ResetPasswordRouteImport.update({ + id: '/reset-password', + path: '/reset-password', + getParentRoute: () => rootRouteImport, +} as any) const ProjectsRoute = ProjectsRouteImport.update({ id: '/projects', path: '/projects', @@ -52,19 +59,24 @@ const MfaVerifyRoute = MfaVerifyRouteImport.update({ path: '/mfa-verify', getParentRoute: () => rootRouteImport, } as any) +const MfaSetupRoute = MfaSetupRouteImport.update({ + id: '/mfa-setup', + path: '/mfa-setup', + getParentRoute: () => rootRouteImport, +} as any) +const ForgotPasswordRoute = ForgotPasswordRouteImport.update({ + id: '/forgot-password', + path: '/forgot-password', + getParentRoute: () => rootRouteImport, +} as any) const ContactRoute = ContactRouteImport.update({ id: '/contact', path: '/contact', getParentRoute: () => rootRouteImport, } as any) -const BlogRoute = BlogRouteImport.update({ - id: '/blog', - path: '/blog', - getParentRoute: () => rootRouteImport, -} as any) -const Auth_backupRoute = Auth_backupRouteImport.update({ - id: '/auth_backup', - path: '/auth_backup', +const BlogsRoute = BlogsRouteImport.update({ + id: '/blogs', + path: '/blogs', getParentRoute: () => rootRouteImport, } as any) const AuthRoute = AuthRouteImport.update({ @@ -86,9 +98,9 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) -const BlogSlugRoute = BlogSlugRouteImport.update({ - id: '/blog_/$slug', - path: '/blog/$slug', +const BlogsSlugRoute = BlogsSlugRouteImport.update({ + id: '/blogs_/$slug', + path: '/blogs/$slug', getParentRoute: () => rootRouteImport, } as any) const AuthenticatedDashboardRoute = AuthenticatedDashboardRouteImport.update({ @@ -118,17 +130,19 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute '/auth': typeof AuthRoute - '/auth_backup': typeof Auth_backupRoute - '/blog': typeof BlogRoute + '/blogs': typeof BlogsRoute '/contact': typeof ContactRoute + '/forgot-password': typeof ForgotPasswordRoute + '/mfa-setup': typeof MfaSetupRoute '/mfa-verify': typeof MfaVerifyRoute '/privacy-policy': typeof PrivacyPolicyRoute '/projects': typeof ProjectsRoute + '/reset-password': typeof ResetPasswordRoute '/terms': typeof TermsRoute '/thank-you': typeof ThankYouRoute '/admin': typeof AuthenticatedAdminRoute '/dashboard': typeof AuthenticatedDashboardRouteWithChildren - '/blog/$slug': typeof BlogSlugRoute + '/blogs/$slug': typeof BlogsSlugRoute '/dashboard/new': typeof AuthenticatedDashboardNewRoute '/dashboard/edit/$id': typeof AuthenticatedDashboardEditIdRoute } @@ -136,17 +150,19 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute '/auth': typeof AuthRoute - '/auth_backup': typeof Auth_backupRoute - '/blog': typeof BlogRoute + '/blogs': typeof BlogsRoute '/contact': typeof ContactRoute + '/forgot-password': typeof ForgotPasswordRoute + '/mfa-setup': typeof MfaSetupRoute '/mfa-verify': typeof MfaVerifyRoute '/privacy-policy': typeof PrivacyPolicyRoute '/projects': typeof ProjectsRoute + '/reset-password': typeof ResetPasswordRoute '/terms': typeof TermsRoute '/thank-you': typeof ThankYouRoute '/admin': typeof AuthenticatedAdminRoute '/dashboard': typeof AuthenticatedDashboardRouteWithChildren - '/blog/$slug': typeof BlogSlugRoute + '/blogs/$slug': typeof BlogsSlugRoute '/dashboard/new': typeof AuthenticatedDashboardNewRoute '/dashboard/edit/$id': typeof AuthenticatedDashboardEditIdRoute } @@ -156,17 +172,19 @@ export interface FileRoutesById { '/_authenticated': typeof AuthenticatedRouteRouteWithChildren '/about': typeof AboutRoute '/auth': typeof AuthRoute - '/auth_backup': typeof Auth_backupRoute - '/blog': typeof BlogRoute + '/blogs': typeof BlogsRoute '/contact': typeof ContactRoute + '/forgot-password': typeof ForgotPasswordRoute + '/mfa-setup': typeof MfaSetupRoute '/mfa-verify': typeof MfaVerifyRoute '/privacy-policy': typeof PrivacyPolicyRoute '/projects': typeof ProjectsRoute + '/reset-password': typeof ResetPasswordRoute '/terms': typeof TermsRoute '/thank-you': typeof ThankYouRoute '/_authenticated/admin': typeof AuthenticatedAdminRoute '/_authenticated/dashboard': typeof AuthenticatedDashboardRouteWithChildren - '/blog_/$slug': typeof BlogSlugRoute + '/blogs_/$slug': typeof BlogsSlugRoute '/_authenticated/dashboard/new': typeof AuthenticatedDashboardNewRoute '/_authenticated/dashboard/edit/$id': typeof AuthenticatedDashboardEditIdRoute } @@ -176,17 +194,19 @@ export interface FileRouteTypes { | '/' | '/about' | '/auth' - | '/auth_backup' - | '/blog' + | '/blogs' | '/contact' + | '/forgot-password' + | '/mfa-setup' | '/mfa-verify' | '/privacy-policy' | '/projects' + | '/reset-password' | '/terms' | '/thank-you' | '/admin' | '/dashboard' - | '/blog/$slug' + | '/blogs/$slug' | '/dashboard/new' | '/dashboard/edit/$id' fileRoutesByTo: FileRoutesByTo @@ -194,17 +214,19 @@ export interface FileRouteTypes { | '/' | '/about' | '/auth' - | '/auth_backup' - | '/blog' + | '/blogs' | '/contact' + | '/forgot-password' + | '/mfa-setup' | '/mfa-verify' | '/privacy-policy' | '/projects' + | '/reset-password' | '/terms' | '/thank-you' | '/admin' | '/dashboard' - | '/blog/$slug' + | '/blogs/$slug' | '/dashboard/new' | '/dashboard/edit/$id' id: @@ -213,17 +235,19 @@ export interface FileRouteTypes { | '/_authenticated' | '/about' | '/auth' - | '/auth_backup' - | '/blog' + | '/blogs' | '/contact' + | '/forgot-password' + | '/mfa-setup' | '/mfa-verify' | '/privacy-policy' | '/projects' + | '/reset-password' | '/terms' | '/thank-you' | '/_authenticated/admin' | '/_authenticated/dashboard' - | '/blog_/$slug' + | '/blogs_/$slug' | '/_authenticated/dashboard/new' | '/_authenticated/dashboard/edit/$id' fileRoutesById: FileRoutesById @@ -233,15 +257,17 @@ export interface RootRouteChildren { AuthenticatedRouteRoute: typeof AuthenticatedRouteRouteWithChildren AboutRoute: typeof AboutRoute AuthRoute: typeof AuthRoute - Auth_backupRoute: typeof Auth_backupRoute - BlogRoute: typeof BlogRoute + BlogsRoute: typeof BlogsRoute ContactRoute: typeof ContactRoute + ForgotPasswordRoute: typeof ForgotPasswordRoute + MfaSetupRoute: typeof MfaSetupRoute MfaVerifyRoute: typeof MfaVerifyRoute PrivacyPolicyRoute: typeof PrivacyPolicyRoute ProjectsRoute: typeof ProjectsRoute + ResetPasswordRoute: typeof ResetPasswordRoute TermsRoute: typeof TermsRoute ThankYouRoute: typeof ThankYouRoute - BlogSlugRoute: typeof BlogSlugRoute + BlogsSlugRoute: typeof BlogsSlugRoute } declare module '@tanstack/react-router' { @@ -260,6 +286,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof TermsRouteImport parentRoute: typeof rootRouteImport } + '/reset-password': { + id: '/reset-password' + path: '/reset-password' + fullPath: '/reset-password' + preLoaderRoute: typeof ResetPasswordRouteImport + parentRoute: typeof rootRouteImport + } '/projects': { id: '/projects' path: '/projects' @@ -281,6 +314,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MfaVerifyRouteImport parentRoute: typeof rootRouteImport } + '/mfa-setup': { + id: '/mfa-setup' + path: '/mfa-setup' + fullPath: '/mfa-setup' + preLoaderRoute: typeof MfaSetupRouteImport + parentRoute: typeof rootRouteImport + } + '/forgot-password': { + id: '/forgot-password' + path: '/forgot-password' + fullPath: '/forgot-password' + preLoaderRoute: typeof ForgotPasswordRouteImport + parentRoute: typeof rootRouteImport + } '/contact': { id: '/contact' path: '/contact' @@ -288,18 +335,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ContactRouteImport parentRoute: typeof rootRouteImport } - '/blog': { - id: '/blog' - path: '/blog' - fullPath: '/blog' - preLoaderRoute: typeof BlogRouteImport - parentRoute: typeof rootRouteImport - } - '/auth_backup': { - id: '/auth_backup' - path: '/auth_backup' - fullPath: '/auth_backup' - preLoaderRoute: typeof Auth_backupRouteImport + '/blogs': { + id: '/blogs' + path: '/blogs' + fullPath: '/blogs' + preLoaderRoute: typeof BlogsRouteImport parentRoute: typeof rootRouteImport } '/auth': { @@ -330,11 +370,11 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } - '/blog_/$slug': { - id: '/blog_/$slug' - path: '/blog/$slug' - fullPath: '/blog/$slug' - preLoaderRoute: typeof BlogSlugRouteImport + '/blogs_/$slug': { + id: '/blogs_/$slug' + path: '/blogs/$slug' + fullPath: '/blogs/$slug' + preLoaderRoute: typeof BlogsSlugRouteImport parentRoute: typeof rootRouteImport } '/_authenticated/dashboard': { @@ -402,15 +442,17 @@ const rootRouteChildren: RootRouteChildren = { AuthenticatedRouteRoute: AuthenticatedRouteRouteWithChildren, AboutRoute: AboutRoute, AuthRoute: AuthRoute, - Auth_backupRoute: Auth_backupRoute, - BlogRoute: BlogRoute, + BlogsRoute: BlogsRoute, ContactRoute: ContactRoute, + ForgotPasswordRoute: ForgotPasswordRoute, + MfaSetupRoute: MfaSetupRoute, MfaVerifyRoute: MfaVerifyRoute, PrivacyPolicyRoute: PrivacyPolicyRoute, ProjectsRoute: ProjectsRoute, + ResetPasswordRoute: ResetPasswordRoute, TermsRoute: TermsRoute, ThankYouRoute: ThankYouRoute, - BlogSlugRoute: BlogSlugRoute, + BlogsSlugRoute: BlogsSlugRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 63a93cb..7dd1f5c 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -106,7 +106,7 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( url: "https://codeforgedev.vercel.app", potentialAction: { "@type": "SearchAction", - target: "https://codeforgedev.vercel.app/blog?q={search_term_string}", + target: "https://codeforgedev.vercel.app/blogs?q={search_term_string}", }, }), }, diff --git a/src/routes/_authenticated/admin.tsx b/src/routes/_authenticated/admin.tsx index 994d093..2b28c54 100644 --- a/src/routes/_authenticated/admin.tsx +++ b/src/routes/_authenticated/admin.tsx @@ -258,7 +258,7 @@ function Posts({ posts, refetch, onActivity }: { posts: Array(); const [creating, setCreating] = useState(false); const typedPosts = posts as Array; const remove = async (id: string) => { if (!window.confirm("Delete this post? This cannot be undone.")) return; const { error } = await supabase.from("posts").delete().eq("id", id); if (error) return toast.error(error.message); await onActivity("delete", "post", id); toast.success("Post deleted"); await refetch(); }; if (creating || editing) return { void onActivity(action, "post", id); setCreating(false); setEditing(undefined); void refetch(); }} />; - return
{typedPosts.map((p) =>
{p.title}
{p.published ? "Published" : "Draft"} · {p.views} views · {new Date(p.created_at).toLocaleDateString()}
{p.published && }
)}{typedPosts.length === 0 && }
; + return
{typedPosts.map((p) =>
{p.title}
{p.published ? "Published" : "Draft"} · {p.views} views · {new Date(p.created_at).toLocaleDateString()}
{p.published && }
)}{typedPosts.length === 0 && }
; } function Projects({ projects, refetch, onActivity }: { projects: Project[]; refetch: () => Promise; onActivity: (a: string, r: string, id?: string) => Promise; }) { @@ -325,8 +325,8 @@ function CommentsPanel({ comments, refetch, onActivity }: { comments: Array<{ id } function EngagementPanel({ posts }: { posts: Array<{ id: string; title: string; views: number }> }) { const [counts,setCounts]=useState>({}); useEffect(() => { let alive=true; void Promise.all(posts.map(async(p)=>{const {data}=await supabase.rpc("get_post_like_count",{_post_id:p.id}); return [p.id,data ?? 0] as const;})).then((pairs)=>{if(alive)setCounts(Object.fromEntries(pairs));}); return()=>{alive=false;}; },[posts]); return
{posts.map((p)=>)}
PostViewsLikes
{p.title}{p.views}{counts[p.id] ?? "…"}
{posts.length===0&&}
; } -function BookmarksPanel({ posts }: { posts: Array<{ id: string; title: string; slug: string }> }) { return

Use the normal Dashboard → Bookmarks view to manage the signed-in account's bookmarks.

{posts.slice(0,6).map((p)=>
{p.title}
Open article →
)}
; } -function GlobalSearch({ posts, projects, messages }: { posts: Array<{ id: string; title: string; slug: string; content: string }>; projects: Project[]; messages: ContactMessage[] }) { const [query,setQuery]=useState(""); const q=query.trim().toLowerCase(); const results=q?[...posts.filter((p)=>`${p.title} ${p.content}`.toLowerCase().includes(q)).map((p)=>({type:"Post",title:p.title,slug:p.slug})),...projects.filter((p)=>`${p.title} ${p.description} ${p.tags.join(" ")}`.toLowerCase().includes(q)).map((p)=>({type:"Project",title:p.title})),...messages.filter((m)=>`${m.name} ${m.email} ${m.subject??""} ${m.message}`.toLowerCase().includes(q)).map((m)=>({type:"Message",title:`${m.subject||"No subject"} — ${m.name}`}))]:[]; return
setQuery(e.target.value)} placeholder="Search CodeForge…" className="rounded-xl pl-9"/>
{results.map((r,i)=>
{r.type}
{r.type==="Post"?{r.title}:
{r.title}
}
)}{q&&results.length===0&&}{!q&&
Start typing to search.
}
; } +function BookmarksPanel({ posts }: { posts: Array<{ id: string; title: string; slug: string }> }) { return

Use the normal Dashboard → Bookmarks view to manage the signed-in account's bookmarks.

{posts.slice(0,6).map((p)=>
{p.title}
Open article →
)}
; } +function GlobalSearch({ posts, projects, messages }: { posts: Array<{ id: string; title: string; slug: string; content: string }>; projects: Project[]; messages: ContactMessage[] }) { const [query,setQuery]=useState(""); const q=query.trim().toLowerCase(); const results=q?[...posts.filter((p)=>`${p.title} ${p.content}`.toLowerCase().includes(q)).map((p)=>({type:"Post",title:p.title,slug:p.slug})),...projects.filter((p)=>`${p.title} ${p.description} ${p.tags.join(" ")}`.toLowerCase().includes(q)).map((p)=>({type:"Project",title:p.title})),...messages.filter((m)=>`${m.name} ${m.email} ${m.subject??""} ${m.message}`.toLowerCase().includes(q)).map((m)=>({type:"Message",title:`${m.subject||"No subject"} — ${m.name}`}))]:[]; return
setQuery(e.target.value)} placeholder="Search CodeForge…" className="rounded-xl pl-9"/>
{results.map((r,i)=>
{r.type}
{r.type==="Post"?{r.title}:
{r.title}
}
)}{q&&results.length===0&&}{!q&&
Start typing to search.
}
; } function HealthPanel() { const [status,setStatus]=useState>({}); const check=async()=>{setStatus({}); const started=Date.now(); const checks=await Promise.all([supabase.from("profiles").select("id",{count:"exact",head:true}),supabase.from("posts").select("id",{count:"exact",head:true}),supabase.storage.from("site-media").list("",{limit:1})]); setStatus({Database:checks[0].error?"Error":"Healthy",Posts:checks[1].error?"Error":"Healthy",Storage:checks[2].error?"Check migration":"Healthy",Latency:`${Date.now()-started} ms`});}; useEffect(()=>{void check();},[]); return
{Object.entries(status).map(([k,v])=>
{k}
{v}
)}
; } function ExportPanel({ posts, projects, messages }: { posts: Array>; projects: Project[]; messages: ContactMessage[] }) { const downloadCsv=(name:string,rows:Array>)=>{if(!rows.length)return toast.error("Nothing to export");const keys=Array.from(new Set(rows.flatMap((r)=>Object.keys(r))));const esc=(value:unknown)=>`"${String(value??"").replaceAll('"','""')}"`;const csv=[keys.join(","),...rows.map((r)=>keys.map((k)=>esc(r[k])).join(","))].join("\n");const blob=new Blob([csv],{type:"text/csv;charset=utf-8"});const url=URL.createObjectURL(blob);const a=document.createElement("a");a.href=url;a.download=name;a.click();URL.revokeObjectURL(url);}; return
; } function TrashPanel({ messages, refetch, onActivity }: { messages: ContactMessage[]; refetch: () => Promise; onActivity: (a: string, r: string, id?: string) => Promise }) { diff --git a/src/routes/_authenticated/dashboard.tsx b/src/routes/_authenticated/dashboard.tsx index b0c41bc..0c3b811 100644 --- a/src/routes/_authenticated/dashboard.tsx +++ b/src/routes/_authenticated/dashboard.tsx @@ -216,7 +216,7 @@ function Dashboard() {
- {p.published && View} + {p.published && View} @@ -231,9 +231,9 @@ function Dashboard() {
- {(bookmarks.data ?? []).length === 0 &&
No bookmarks yet. Explore articles.
} + {(bookmarks.data ?? []).length === 0 &&
No bookmarks yet. Explore articles.
} {(bookmarks.data ?? []).map((b) => b.posts && ( - +
{b.posts.category}

{b.posts.title}

{b.posts.excerpt}

diff --git a/src/routes/blog.tsx b/src/routes/blogs.tsx similarity index 88% rename from src/routes/blog.tsx rename to src/routes/blogs.tsx index 4183931..b859115 100644 --- a/src/routes/blog.tsx +++ b/src/routes/blogs.tsx @@ -6,15 +6,15 @@ import { SiteLayout } from "@/components/SiteLayout"; import { supabase } from "@/integrations/supabase/client"; import { Input } from "@/components/ui/input"; -const URL_BLOG = "https://codeforgedev.vercel.app/blog"; +const URL_BLOG = "https://codeforgedev.vercel.app/blogs"; const OG_IMAGE = "https://codeforgedev.vercel.app/og-image.png"; -export const Route = createFileRoute("/blog")({ +export const Route = createFileRoute("/blogs")({ head: () => ({ meta: [ - { title: "Blog — CodeForge" }, + { title: "Blogs — CodeForge" }, { name: "description", content: "Technical articles, tutorials and reflections on building modern web apps." }, - { property: "og:title", content: "Blog — CodeForge" }, + { property: "og:title", content: "Blogs — CodeForge" }, { property: "og:description", content: "Technical articles & tutorials on React, TypeScript and modern web development." }, { property: "og:url", content: URL_BLOG }, { property: "og:image", content: OG_IMAGE }, @@ -55,7 +55,7 @@ function Blog() {
Writing
-

The blog

+

The blogs

Long-form notes on building, shipping & learning in public.

@@ -74,7 +74,7 @@ function Blog() { ) : ( <> {featured && ( - +
Featured · {featured.category}

{featured.title}

{featured.excerpt}

@@ -85,7 +85,7 @@ function Blog() { )}
{rest.map((p) => ( - +
{p.category ?? "Article"}

{p.title}

{p.excerpt}

diff --git a/src/routes/blog_.$slug.tsx b/src/routes/blogs_.$slug.tsx similarity index 98% rename from src/routes/blog_.$slug.tsx rename to src/routes/blogs_.$slug.tsx index 8fa9e89..32cf4ba 100644 --- a/src/routes/blog_.$slug.tsx +++ b/src/routes/blogs_.$slug.tsx @@ -20,7 +20,7 @@ function toSafeJsonLd(value: unknown): string { .replace(/&/g, "\\u0026"); } -export const Route = createFileRoute("/blog_/$slug")({ +export const Route = createFileRoute("/blogs_/$slug")({ loader: async ({ params }) => { const { data } = await supabase .from("posts") @@ -31,10 +31,10 @@ export const Route = createFileRoute("/blog_/$slug")({ return { meta: data }; }, head: ({ params, loaderData }) => { - const url = `https://codeforgedev.vercel.app/blog/${params.slug}`; + const url = `https://codeforgedev.vercel.app/blogs/${params.slug}`; const m = loaderData?.meta; const title = m?.title ? `${m.title} — CodeForge` : "Article — CodeForge"; - const description = m?.excerpt ?? "Technical article on the CodeForge blog."; + const description = m?.excerpt ?? "Technical article on the CodeForge blogs."; const image = m?.cover_image ?? "https://codeforgedev.vercel.app/og-image.png"; return { meta: [ @@ -315,7 +315,7 @@ const post = useQuery({

Related articles

{related.data!.map((r) => ( - +

{r.title}

{r.reading_time} min
diff --git a/src/routes/index.tsx b/src/routes/index.tsx index e85006b..fb54aeb 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -159,12 +159,12 @@ function Home() {
- +
{posts.data && posts.data.length > 0 ? posts.data.map((p, i) => (
{p.category ?? "Article"}
-

{p.title}

+

{p.title}

{p.excerpt}

{p.reading_time} min read
diff --git a/src/routes/thank-you.tsx b/src/routes/thank-you.tsx index 5d5e128..7e78ec1 100644 --- a/src/routes/thank-you.tsx +++ b/src/routes/thank-you.tsx @@ -41,10 +41,10 @@ function ThankYouPage() { View projects - Read the blog + Read the blogs