diff --git a/src/app/(home)/_components/ArticleFeed.tsx b/src/app/(home)/_components/ArticleFeed.tsx
index a4b6e84..20c56ec 100644
--- a/src/app/(home)/_components/ArticleFeed.tsx
+++ b/src/app/(home)/_components/ArticleFeed.tsx
@@ -1,14 +1,11 @@
"use client";
import * as articleActions from "@/backend/services/article.actions";
-import * as seriesActions from "@/backend/services/series.action";
import ArticleCard from "@/components/ArticleCard";
-import SeriesCard from "@/components/SeriesCard";
import VisibilitySensor from "@/components/VisibilitySensor";
import { readingTime } from "@/lib/utils";
import getFileUrl from "@/utils/getFileUrl";
import { useInfiniteQuery } from "@tanstack/react-query";
-import { Loader } from "lucide-react";
import { useState } from "react";
const ArticleFeed = () => {
@@ -53,10 +50,22 @@ const ArticleFeed = () => {
{articleFeedQuery.isPending && (
<>
-
-
-
-
+
+
+
+
>
)}
diff --git a/src/app/(home)/_components/HomeLeftSidebar.tsx b/src/app/(home)/_components/HomeLeftSidebar.tsx
index 32aaea0..c40c724 100644
--- a/src/app/(home)/_components/HomeLeftSidebar.tsx
+++ b/src/app/(home)/_components/HomeLeftSidebar.tsx
@@ -96,7 +96,7 @@ const tags = [
{
icon: "https://res.cloudinary.com/techdiary-dev/image/upload/v1620782239/static-assets/tag-icons/erfbu54l2mquphszheck.svg",
label: "react",
- link: "/tags/186e052a-9c5b-4ffe-b753-ea172ac2e663",
+ link: "/tags/reactjs",
},
{
icon: "https://res.cloudinary.com/techdiary-dev/image/upload/v1620782240/static-assets/tag-icons/rh7xfiz28bxklfzymftd.svg",
diff --git a/src/app/tags/[tag_id]/_components/TagArticleFeed.tsx b/src/app/tags/[tag_name]/_components/TagArticleFeed.tsx
similarity index 76%
rename from src/app/tags/[tag_id]/_components/TagArticleFeed.tsx
rename to src/app/tags/[tag_name]/_components/TagArticleFeed.tsx
index 7dea926..9655e11 100644
--- a/src/app/tags/[tag_id]/_components/TagArticleFeed.tsx
+++ b/src/app/tags/[tag_name]/_components/TagArticleFeed.tsx
@@ -1,23 +1,26 @@
"use client";
+import { Tag } from "@/backend/models/domain-models";
import * as articleActions from "@/backend/services/article.actions";
import ArticleCard from "@/components/ArticleCard";
import VisibilitySensor from "@/components/VisibilitySensor";
+import { useTranslation } from "@/i18n/use-translation";
import { readingTime } from "@/lib/utils";
import getFileUrl from "@/utils/getFileUrl";
import { useInfiniteQuery } from "@tanstack/react-query";
-import { useMemo } from "react";
+import React, { useMemo } from "react";
interface TagArticleFeedProps {
- tagId: string;
+ tag: Tag;
}
-const TagArticleFeed: React.FC
= ({ tagId }) => {
+const TagArticleFeed: React.FC = ({ tag }) => {
+ const { _t } = useTranslation();
const tagFeedQuery = useInfiniteQuery({
- queryKey: ["tag-articles", tagId],
+ queryKey: ["tag-articles", tag.id],
queryFn: ({ pageParam }) =>
articleActions.articlesByTag({
- tag_id: tagId,
+ tag_id: tag.id,
limit: 5,
page: pageParam,
}),
@@ -37,18 +40,14 @@ const TagArticleFeed: React.FC = ({ tagId }) => {
return tagFeedQuery.data?.pages?.[0]?.meta?.total ?? 0;
}, [tagFeedQuery.data]);
- const tagName = useMemo(() => {
- return tagFeedQuery.data?.pages?.[0]?.tagName ?? "Unknown Tag";
- }, [tagFeedQuery.data]);
-
// Show loading skeletons
if (tagFeedQuery.isPending) {
return (
);
}
@@ -78,10 +77,10 @@ const TagArticleFeed: React.FC = ({ tagId }) => {
return (
- No articles found
+ {_t("No articles found")}
- No articles have been tagged with “{tagName}” yet.
+ {_t(`No articles have been tagged with "$" yet.`, [tag.name])}
);
@@ -91,10 +90,10 @@ const TagArticleFeed: React.FC = ({ tagId }) => {
<>
- Articles tagged with “{tagName}”
+ {_t(`Articles tagged with "$"`, [tag.name])}
- Found {totalArticles} articles
+ {_t(`Found $ articles`, [totalArticles])}
@@ -106,7 +105,9 @@ const TagArticleFeed: React.FC = ({ tagId }) => {
handle={article?.handle ?? ""}
title={article?.title ?? ""}
excerpt={article?.excerpt ?? ""}
- coverImage={article?.cover_image ? getFileUrl(article.cover_image) : ""}
+ coverImage={
+ article?.cover_image ? getFileUrl(article.cover_image) : ""
+ }
author={{
id: article?.user?.id ?? "",
name: article?.user?.name ?? "",
@@ -133,4 +134,4 @@ const TagArticleFeed: React.FC = ({ tagId }) => {
);
};
-export default TagArticleFeed;
\ No newline at end of file
+export default TagArticleFeed;
diff --git a/src/app/tags/[tag_id]/page.tsx b/src/app/tags/[tag_name]/page.tsx
similarity index 70%
rename from src/app/tags/[tag_id]/page.tsx
rename to src/app/tags/[tag_name]/page.tsx
index a4c20fb..9d8f323 100644
--- a/src/app/tags/[tag_id]/page.tsx
+++ b/src/app/tags/[tag_name]/page.tsx
@@ -3,15 +3,22 @@ import HomeRightSidebar from "@/app/(home)/_components/HomeRightSidebar";
import SidebarToggleButton from "@/app/(home)/_components/SidebarToggleButton";
import HomepageLayout from "@/components/layout/HomepageLayout";
import TagArticleFeed from "./_components/TagArticleFeed";
+import { getTag, getTags } from "@/backend/services/tag.action";
+import { notFound } from "next/navigation";
interface TagPageProps {
params: Promise<{
- tag_id: string;
+ tag_name: string;
}>;
}
export default async function TagPage({ params }: TagPageProps) {
- const { tag_id } = await params;
+ const { tag_name } = await params;
+ const tag = await getTag({ name: tag_name });
+
+ if (!tag?.success) {
+ throw notFound();
+ }
return (
}
>
-
+
);
}
export async function generateMetadata({ params }: TagPageProps) {
- const { tag_id } = await params;
+ const { tag_name } = await params;
// For now, use tag_id in the title. Later we can fetch the tag name if needed
return {
- title: `Tag ${tag_id} - Tech Diary`,
+ title: `Tag ${tag_name} - Tech Diary`,
description: `Browse all articles with this tag on Tech Diary`,
openGraph: {
- title: `Tag ${tag_id} - Tech Diary`,
+ title: `Tag ${tag_name} - Tech Diary`,
description: `Browse all articles with this tag on Tech Diary`,
},
};
diff --git a/src/backend/services/inputs/tag.input.ts b/src/backend/services/inputs/tag.input.ts
index 87d98e3..8e4a732 100644
--- a/src/backend/services/inputs/tag.input.ts
+++ b/src/backend/services/inputs/tag.input.ts
@@ -12,6 +12,9 @@ export const TagRepositoryInput = {
color: z.string().optional().nullable(),
description: z.string().optional().nullable(),
}),
+ getTag: z.object({
+ name: z.string(),
+ }),
updateInput: z.object({
tag_id: z.string(),
name: z.string().optional(),
diff --git a/src/backend/services/tag.action.ts b/src/backend/services/tag.action.ts
index bc068d7..ae020af 100644
--- a/src/backend/services/tag.action.ts
+++ b/src/backend/services/tag.action.ts
@@ -25,6 +25,24 @@ export const getTags = async (
}
};
+export const getTag = async (
+ _input: z.infer
+) => {
+ try {
+ const input = await TagRepositoryInput.createInput.parseAsync(_input);
+ const response = await persistenceRepository.tags.find({
+ where: eq("name", input.name),
+ });
+
+ return {
+ data: response[0],
+ success: true as const,
+ };
+ } catch (error) {
+ handleActionException(error);
+ }
+};
+
export const createTag = async (
_input: z.infer
) => {