From 191a4ff157b79c2177e681f58224324fe9b2e0c9 Mon Sep 17 00:00:00 2001 From: kingrayhan Date: Sun, 13 Apr 2025 00:40:51 +0600 Subject: [PATCH 1/6] wip: many to many --- .../dashboard/articles/[uuid]/page.tsx | 27 +++++++++++--- .../[articleHandle]/opengraph-image.tsx | 7 ++-- src/app/sitemaps/articles/sitemap.tsx | 4 +-- .../persistence/persistence-contracts.ts | 14 ++++++-- src/backend/persistence/persistence-utils.ts | 4 +-- .../persistence/persistence-where-operator.ts | 25 +++++++++++-- .../persistence/persistence.repository.ts | 1 + src/backend/services/article.actions.ts | 36 +++++++++++-------- src/backend/services/series.action.ts | 6 ++-- 9 files changed, 88 insertions(+), 36 deletions(-) diff --git a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx index 40865f9..ae3f12e 100644 --- a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx +++ b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx @@ -4,7 +4,14 @@ import React from "react"; import * as sessionActions from "@/backend/services/session.actions"; import { notFound } from "next/navigation"; import { persistenceRepository } from "@/backend/persistence-repositories"; -import { eq, and } from "@/backend/persistence/persistence-where-operator"; +import { + eq, + and, + manyToManyJoin, + leftJoin, +} from "@/backend/persistence/persistence-where-operator"; +import { Article, Tag, User } from "@/backend/models/domain-models"; +import { DatabaseTableName } from "@/backend/persistence/persistence-contracts"; interface Props { params: Promise<{ uuid: string }>; @@ -12,17 +19,29 @@ interface Props { const page: React.FC = async ({ params }) => { const sessionUserId = await sessionActions.getSessionUserId(); const _params = await params; - + // eq("author_id", sessionUserId) const [article] = await persistenceRepository.article.findRows({ limit: 1, - where: and(eq("id", _params.uuid), eq("author_id", sessionUserId)), + where: and(eq("id", _params.uuid)), + columns: ["id", "title", "handle"], + joins: [ + leftJoin({ + as: "author", + joinTo: DatabaseTableName.users, + localField: "author_id", + foreignField: "id", + columns: ["id", "name", "username"], + }), + ], }); if (!article) { throw notFound(); } - return ; + return
{JSON.stringify(article, null, 2)}
; + + // return ; }; export default page; diff --git a/src/app/[username]/[articleHandle]/opengraph-image.tsx b/src/app/[username]/[articleHandle]/opengraph-image.tsx index 7385f18..f169229 100644 --- a/src/app/[username]/[articleHandle]/opengraph-image.tsx +++ b/src/app/[username]/[articleHandle]/opengraph-image.tsx @@ -1,9 +1,6 @@ import { Article, User } from "@/backend/models/domain-models"; import { persistenceRepository } from "@/backend/persistence-repositories"; -import { - eq, - joinTable, -} from "@/backend/persistence/persistence-where-operator"; +import { eq, leftJoin } from "@/backend/persistence/persistence-where-operator"; import { ImageResponse } from "next/og"; import { readFile } from "node:fs/promises"; import { join } from "node:path"; @@ -33,7 +30,7 @@ export default async function Image(options: ArticlePageProps) { columns: ["title", "excerpt", "cover_image", "body"], limit: 1, joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", diff --git a/src/app/sitemaps/articles/sitemap.tsx b/src/app/sitemaps/articles/sitemap.tsx index 711a487..226175e 100644 --- a/src/app/sitemaps/articles/sitemap.tsx +++ b/src/app/sitemaps/articles/sitemap.tsx @@ -3,7 +3,7 @@ import { persistenceRepository } from "@/backend/persistence-repositories"; import { and, eq, - joinTable, + leftJoin, neq, } from "@/backend/persistence/persistence-where-operator"; import type { MetadataRoute } from "next"; @@ -14,7 +14,7 @@ export default async function sitemap(): Promise { columns: ["handle", "updated_at"], limit: -1, joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", diff --git a/src/backend/persistence/persistence-contracts.ts b/src/backend/persistence/persistence-contracts.ts index 7985958..1f19879 100644 --- a/src/backend/persistence/persistence-contracts.ts +++ b/src/backend/persistence/persistence-contracts.ts @@ -53,7 +53,7 @@ export type CompositeWhere = { }; export type WhereCondition = SimpleWhere | CompositeWhere; -export interface IPersistenceJoin { +export interface IPersistenceLeftJoin { as: string; joinTo: string; localField: string; @@ -61,9 +61,17 @@ export interface IPersistenceJoin { columns: string[]; } +export interface IPersistenceManyToManyJoin { + as: string; + pivotTable: DatabaseTableName; + localField: string; + foreignField: string; + columns: string[]; +} + export interface IPersistentPaginationPayload { where?: WhereCondition; - joins?: IPersistenceJoin[]; + joins?: IPersistenceLeftJoin[]; orderBy?: Array>; columns?: Array; limit?: number; @@ -88,7 +96,7 @@ export interface IPagination { where?: WhereCondition; // No longer allows arrays columns?: Array; orderBy?: Array>; - joins?: IPersistenceJoin[]; + joins?: IPersistenceLeftJoin[]; } //------------------------------------ diff --git a/src/backend/persistence/persistence-utils.ts b/src/backend/persistence/persistence-utils.ts index 69f1060..209c8a1 100644 --- a/src/backend/persistence/persistence-utils.ts +++ b/src/backend/persistence/persistence-utils.ts @@ -1,5 +1,5 @@ import { - IPersistenceJoin, + IPersistenceLeftJoin, IPersistentOrderBy, SimpleWhere, WhereCondition, @@ -144,7 +144,7 @@ export const buildOrderByClause = ( return `ORDER BY ${orderByConditions.join(", ")}`; }; -export const buildJoinClause = (joins?: Array) => { +export const buildJoinClause = (joins?: Array) => { if (!joins || joins.length === 0) { return { joinConditionClause: "", diff --git a/src/backend/persistence/persistence-where-operator.ts b/src/backend/persistence/persistence-where-operator.ts index 2394a2c..c69e7fc 100644 --- a/src/backend/persistence/persistence-where-operator.ts +++ b/src/backend/persistence/persistence-where-operator.ts @@ -2,7 +2,10 @@ * Drizzle-style operator functions with direct generic approach */ -import { IPersistenceJoin } from "./persistence-contracts"; +import { + DatabaseTableName, + IPersistenceLeftJoin, +} from "./persistence-contracts"; // Type for all possible operators type Operator = @@ -186,13 +189,13 @@ export function desc(key: keyof T): IPersistentOrderBy { }; } -export function joinTable(payload: { +export function leftJoin(payload: { as: string; joinTo: string; localField: keyof LOCAL_MODEL; foreignField: keyof FOREIGN_MODEL; columns: Array; -}): IPersistenceJoin { +}): IPersistenceLeftJoin { return { as: payload.as, joinTo: payload.joinTo, @@ -201,3 +204,19 @@ export function joinTable(payload: { columns: payload.columns.map((col) => col.toString()), }; } + +export function manyToManyJoin(payload: { + as: string; + pivotTable: DatabaseTableName; + localField: keyof LOCAL_MODEL; + foreignField: keyof FOREIGN_MODEL; + columns: Array; +}): IPersistenceLeftJoin { + return { + as: payload.as, + joinTo: payload.pivotTable, + localField: payload.localField.toString(), + foreignField: payload.foreignField.toString(), + columns: payload.columns.map((col) => col.toString()), + }; +} diff --git a/src/backend/persistence/persistence.repository.ts b/src/backend/persistence/persistence.repository.ts index 5f42d65..3638caf 100644 --- a/src/backend/persistence/persistence.repository.ts +++ b/src/backend/persistence/persistence.repository.ts @@ -13,6 +13,7 @@ import { buildOrderByClause, buildSetClause, buildWhereClause, + sql, toSnakeCase, } from "./persistence-utils"; import { removeNullOrUndefinedFromObject } from "@/lib/utils"; diff --git a/src/backend/services/article.actions.ts b/src/backend/services/article.actions.ts index 5b09b66..ac01418 100644 --- a/src/backend/services/article.actions.ts +++ b/src/backend/services/article.actions.ts @@ -9,8 +9,9 @@ import { and, desc, eq, - joinTable, + leftJoin, like, + manyToManyJoin, neq, notInArray, or, @@ -199,6 +200,7 @@ export async function updateArticle( published_at: input.is_published ? new Date() : null, }, }); + return article; } catch (error) { handleRepositoryException(error); @@ -229,6 +231,11 @@ export async function updateMyArticle( metadata: input.metadata, }, }); + // a 1, t 1 + // a 1, t 2 + // a 1, t 3 + + // a-> (2,3,4) if (input.tag_ids) { await persistenceRepository.articleTag.deleteRows({ @@ -238,8 +245,8 @@ export async function updateMyArticle( ), }); - await input.tag_ids.forEach((tag_id) => { - persistenceRepository.articleTag.createOne({ + input.tag_ids.forEach(async (tag_id) => { + await persistenceRepository.articleTag.createOne({ article_id: article.id, tag_id: tag_id, }); @@ -294,7 +301,7 @@ export async function findRecentArticles( orderBy: [desc("published_at")], columns: ["id", "title", "handle"], joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", @@ -337,7 +344,7 @@ export async function articleFeed( "excerpt", ], joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", @@ -386,7 +393,7 @@ export async function userArticleFeed( "excerpt", ], joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", @@ -429,19 +436,20 @@ export async function articleDetailByHandle(article_handle: string) { "updated_at", ], joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", foreignField: "id", columns: ["id", "name", "username", "profile_photo"], }), - // joinTable({ - // as: "tags", - // joinTo: "tags", - // localField: "author_id", - // foreignField: "id", - // }), + manyToManyJoin({ + as: "tags", + pivotTable: DatabaseTableName.article_tag, + localField: "id", + foreignField: "id", + columns: ["id", "name", "icon", "color", "description"], + }), ], limit: 1, }); @@ -476,7 +484,7 @@ export async function articleDetailByUUID(uuid: string) { "updated_at", ], joins: [ - joinTable({ + leftJoin({ as: "user", joinTo: "users", localField: "author_id", diff --git a/src/backend/services/series.action.ts b/src/backend/services/series.action.ts index 47867bc..5b10001 100644 --- a/src/backend/services/series.action.ts +++ b/src/backend/services/series.action.ts @@ -6,7 +6,7 @@ import { asc, desc, eq, - joinTable, + leftJoin, } from "../persistence/persistence-where-operator"; import { Article, Series, SeriesItem, User } from "../models/domain-models"; @@ -31,7 +31,7 @@ export const getSeriesDetailByHandle = async (handle: string) => { where: eq("handle", handle), limit: 1, joins: [ - joinTable({ + leftJoin({ as: "owner", joinTo: "users", localField: "owner_id", @@ -46,7 +46,7 @@ export const getSeriesDetailByHandle = async (handle: string) => { orderBy: [asc("index")], limit: -1, joins: [ - joinTable({ + leftJoin({ as: "article", joinTo: "articles", localField: "article_id", From e1b20f0231793097f9de5f59218e1db60a8d2f59 Mon Sep 17 00:00:00 2001 From: kingrayhan Date: Sun, 13 Apr 2025 00:57:07 +0600 Subject: [PATCH 2/6] Enhance article retrieval by adding author ID check in the query. Update persistence layer to include table name in WHERE clause construction for improved SQL query accuracy. Refactor related functions to support this change. --- .../dashboard/articles/[uuid]/page.tsx | 2 +- src/backend/persistence/persistence-utils.ts | 32 +++++++++++-------- .../persistence/persistence.repository.ts | 18 ++++++++--- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx index ae3f12e..8faa5ab 100644 --- a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx +++ b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx @@ -22,7 +22,7 @@ const page: React.FC = async ({ params }) => { // eq("author_id", sessionUserId) const [article] = await persistenceRepository.article.findRows({ limit: 1, - where: and(eq("id", _params.uuid)), + where: and(eq("id", _params.uuid), eq("author_id", sessionUserId)), columns: ["id", "title", "handle"], joins: [ leftJoin({ diff --git a/src/backend/persistence/persistence-utils.ts b/src/backend/persistence/persistence-utils.ts index 209c8a1..685c60c 100644 --- a/src/backend/persistence/persistence-utils.ts +++ b/src/backend/persistence/persistence-utils.ts @@ -1,4 +1,5 @@ import { + DatabaseTableName, IPersistenceLeftJoin, IPersistentOrderBy, SimpleWhere, @@ -13,7 +14,8 @@ export const sql = String.raw; * @returns Object containing the WHERE clause and values array */ export const buildWhereClause = ( - where: WhereCondition | undefined + where: WhereCondition | undefined, + tableName: DatabaseTableName ): { whereClause: string; values: any[] } => { // If no where clause is provided, return empty if (!where) { @@ -23,10 +25,12 @@ export const buildWhereClause = ( const values: any[] = []; // Process the where condition - const result = processWhereCondition(where, values); + const whereClause = processWhereCondition(where, values, tableName); + + console.log({ whereClause, tableName }); return { - whereClause: result, + whereClause, values, }; }; @@ -36,13 +40,14 @@ export const buildWhereClause = ( */ const processWhereCondition = ( where: WhereCondition, - values: any[] + values: any[], + tableName: DatabaseTableName ): string => { // Handle composite conditions (AND/OR) if (typeof where === "object") { if ("AND" in where && Array.isArray(where.AND) && where.AND.length > 0) { const conditions = where.AND.map((condition) => - processWhereCondition(condition, values) + processWhereCondition(condition, values, tableName) ).filter(Boolean); if (conditions.length === 0) return ""; @@ -53,7 +58,7 @@ const processWhereCondition = ( if ("OR" in where && Array.isArray(where.OR) && where.OR.length > 0) { const conditions = where.OR.map((condition) => - processWhereCondition(condition, values) + processWhereCondition(condition, values, tableName) ).filter(Boolean); if (conditions.length === 0) return ""; @@ -64,7 +69,7 @@ const processWhereCondition = ( // Handle simple conditions if ("key" in where && "operator" in where) { - return processSimpleCondition(where as SimpleWhere, values); + return processSimpleCondition(where as SimpleWhere, values, tableName); } } @@ -76,7 +81,8 @@ const processWhereCondition = ( */ const processSimpleCondition = ( condition: SimpleWhere, - values: any[] + values: any[], + tableName: DatabaseTableName ): string => { const { key, operator, value } = condition; @@ -96,21 +102,21 @@ const processSimpleCondition = ( }) .join(", "); - return `"${key.toString()}" ${operator} (${placeholders})`; + return `"${tableName}"."${key.toString()}" ${operator} (${placeholders})`; } // Handle NULL values if (value === null) { return operator === "=" - ? `"${key.toString()}" IS NULL` + ? `"${tableName}"."${key.toString()}" IS NULL` : operator === "<>" - ? `"${key.toString()}" IS NOT NULL` - : `"${key.toString()}" IS NULL`; + ? `"${tableName}"."${key.toString()}" IS NOT NULL` + : `"${tableName}"."${key.toString()}" IS NULL`; } // Standard case with non-null value values.push(value); - return `"${key.toString()}" ${operator} $${values.length}`; + return `"${tableName}"."${key.toString()}" ${operator} $${values.length}`; }; /** diff --git a/src/backend/persistence/persistence.repository.ts b/src/backend/persistence/persistence.repository.ts index 3638caf..1f66c1a 100644 --- a/src/backend/persistence/persistence.repository.ts +++ b/src/backend/persistence/persistence.repository.ts @@ -70,7 +70,10 @@ export class PersistentRepository { ?.map((col) => `${this.tableName}.${col.toString()}`) .join(",") ?? `${this.tableName}.*`; - const { whereClause, values } = buildWhereClause(payload.where); + const { whereClause, values } = buildWhereClause( + payload.where, + this.tableName + ); const orderByClause = buildOrderByClause(payload?.orderBy); const { joinConditionClause, joinSelectClause } = buildJoinClause( payload.joins @@ -102,7 +105,10 @@ export class PersistentRepository { async findRowCount( payload: IPersistentPaginationPayload ): Promise { - const { whereClause, values } = buildWhereClause(payload.where); + const { whereClause, values } = buildWhereClause( + payload.where, + this.tableName + ); // Construct the SQL query const query = ` @@ -176,7 +182,8 @@ export class PersistentRepository { ): Promise { // Build WHERE clause const { whereClause, values: whereValues } = buildWhereClause( - payload.where + payload.where, + this.tableName ); // Build SET clause using the where values as starting point @@ -213,7 +220,10 @@ export class PersistentRepository { async deleteRows( payload: IPersistentQueryPayload ): Promise { - const { whereClause, values } = buildWhereClause(payload.where); + const { whereClause, values } = buildWhereClause( + payload.where, + this.tableName + ); const columns = toSnakeCase(payload.columns as any) || "*"; const sql = ` From e86b79fb6ebb187469f723f45aa8dee32d8d8a23 Mon Sep 17 00:00:00 2001 From: kingrayhan Date: Sun, 13 Apr 2025 01:12:29 +0600 Subject: [PATCH 3/6] Enhance article page functionality by integrating ArticleTag model to fetch associated tags for articles. Update the Article interface to include optional tag relationships, improving the article retrieval process. Refactor the page component to display tags in the ArticleEditor. --- .../dashboard/articles/[uuid]/page.tsx | 26 ++++++++++++++++--- src/backend/models/domain-models.ts | 4 +++ 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx index 8faa5ab..1045423 100644 --- a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx +++ b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx @@ -9,8 +9,9 @@ import { and, manyToManyJoin, leftJoin, + inArray, } from "@/backend/persistence/persistence-where-operator"; -import { Article, Tag, User } from "@/backend/models/domain-models"; +import { Article, ArticleTag, Tag, User } from "@/backend/models/domain-models"; import { DatabaseTableName } from "@/backend/persistence/persistence-contracts"; interface Props { @@ -35,13 +36,30 @@ const page: React.FC = async ({ params }) => { ], }); + const aggregatedTags = await persistenceRepository.articleTag.findRows({ + where: inArray("article_id", [article.id]), + columns: ["tag_id", "article_id"], + joins: [ + leftJoin({ + as: "tag", + joinTo: "tags", + localField: "tag_id", + foreignField: "id", + columns: ["id", "name", "created_at"], + }), + ], + }); + + const tags = aggregatedTags?.map((item) => item?.tag); + if (tags.length) { + article.tags = tags as Tag[]; + } + if (!article) { throw notFound(); } - return
{JSON.stringify(article, null, 2)}
; - - // return ; + return ; }; export default page; diff --git a/src/backend/models/domain-models.ts b/src/backend/models/domain-models.ts index 6776aa8..bc67fb3 100644 --- a/src/backend/models/domain-models.ts +++ b/src/backend/models/domain-models.ts @@ -108,4 +108,8 @@ export interface ArticleTag { tag_id: string; created_at: Date; updated_at: Date; + + // Relationships + article?: Article; + tag?: Tag; } From af7f7067c913aefc070925e9f41f3973ffe0464d Mon Sep 17 00:00:00 2001 From: kingrayhan Date: Sun, 13 Apr 2025 01:27:59 +0600 Subject: [PATCH 4/6] Refactor article retrieval logic by removing unused columns in queries and updating the ArticleTag join to include additional fields. Enhance ArticleEditorDrawer to display article data for better debugging during editing. --- .../dashboard/articles/[uuid]/page.tsx | 4 +--- src/backend/services/article.actions.ts | 19 +++++++------------ src/components/Editor/ArticleEditorDrawer.tsx | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx index 1045423..718d1bc 100644 --- a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx +++ b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx @@ -24,7 +24,6 @@ const page: React.FC = async ({ params }) => { const [article] = await persistenceRepository.article.findRows({ limit: 1, where: and(eq("id", _params.uuid), eq("author_id", sessionUserId)), - columns: ["id", "title", "handle"], joins: [ leftJoin({ as: "author", @@ -38,14 +37,13 @@ const page: React.FC = async ({ params }) => { const aggregatedTags = await persistenceRepository.articleTag.findRows({ where: inArray("article_id", [article.id]), - columns: ["tag_id", "article_id"], joins: [ leftJoin({ as: "tag", joinTo: "tags", localField: "tag_id", foreignField: "id", - columns: ["id", "name", "created_at"], + columns: ["id", "name", "color", "icon", "description"], }), ], }); diff --git a/src/backend/services/article.actions.ts b/src/backend/services/article.actions.ts index ac01418..9552b07 100644 --- a/src/backend/services/article.actions.ts +++ b/src/backend/services/article.actions.ts @@ -231,11 +231,6 @@ export async function updateMyArticle( metadata: input.metadata, }, }); - // a 1, t 1 - // a 1, t 2 - // a 1, t 3 - - // a-> (2,3,4) if (input.tag_ids) { await persistenceRepository.articleTag.deleteRows({ @@ -443,13 +438,13 @@ export async function articleDetailByHandle(article_handle: string) { foreignField: "id", columns: ["id", "name", "username", "profile_photo"], }), - manyToManyJoin({ - as: "tags", - pivotTable: DatabaseTableName.article_tag, - localField: "id", - foreignField: "id", - columns: ["id", "name", "icon", "color", "description"], - }), + // manyToManyJoin({ + // as: "tags", + // pivotTable: DatabaseTableName.article_tag, + // localField: "id", + // foreignField: "id", + // columns: ["id", "name", "icon", "color", "description"], + // }), ], limit: 1, }); diff --git a/src/components/Editor/ArticleEditorDrawer.tsx b/src/components/Editor/ArticleEditorDrawer.tsx index 7144d9f..9b68c2c 100644 --- a/src/components/Editor/ArticleEditorDrawer.tsx +++ b/src/components/Editor/ArticleEditorDrawer.tsx @@ -109,7 +109,7 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => { className="flex flex-col gap-2" > {/* {JSON.stringify(form.formState.errors)} */} - +
{JSON.stringify(article, null, 2)}
Date: Sun, 13 Apr 2025 02:30:01 +0600 Subject: [PATCH 5/6] Refactor ArticleEditor and ArticleEditorDrawer components to improve tag management functionality. Integrate tag selection in the ArticleEditorDrawer, allowing users to categorize articles effectively. Update imports for better organization and enhance debugging capabilities by displaying article data during editing. --- .../dashboard/articles/[uuid]/page.tsx | 18 +-- src/components/Editor/ArticleEditorDrawer.tsx | 150 ++++++++++++------ src/components/ui/multi-select.tsx | 2 + 3 files changed, 114 insertions(+), 56 deletions(-) diff --git a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx index 718d1bc..5db623f 100644 --- a/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx +++ b/src/app/(dashboard-editor)/dashboard/articles/[uuid]/page.tsx @@ -1,18 +1,16 @@ -import ArticleEditor from "@/components/Editor/ArticleEditor"; -import * as articleActions from "@/backend/services/article.actions"; -import React from "react"; -import * as sessionActions from "@/backend/services/session.actions"; -import { notFound } from "next/navigation"; +import { Article, ArticleTag, Tag, User } from "@/backend/models/domain-models"; import { persistenceRepository } from "@/backend/persistence-repositories"; +import { DatabaseTableName } from "@/backend/persistence/persistence-contracts"; import { - eq, and, - manyToManyJoin, - leftJoin, + eq, inArray, + leftJoin, } from "@/backend/persistence/persistence-where-operator"; -import { Article, ArticleTag, Tag, User } from "@/backend/models/domain-models"; -import { DatabaseTableName } from "@/backend/persistence/persistence-contracts"; +import * as sessionActions from "@/backend/services/session.actions"; +import ArticleEditor from "@/components/Editor/ArticleEditor"; +import { notFound } from "next/navigation"; +import React from "react"; interface Props { params: Promise<{ uuid: string }>; diff --git a/src/components/Editor/ArticleEditorDrawer.tsx b/src/components/Editor/ArticleEditorDrawer.tsx index 9b68c2c..5b6d5f0 100644 --- a/src/components/Editor/ArticleEditorDrawer.tsx +++ b/src/components/Editor/ArticleEditorDrawer.tsx @@ -1,17 +1,18 @@ "use client"; -import { Article } from "@/backend/models/domain-models"; +import { Article, Tag } from "@/backend/models/domain-models"; import * as articleActions from "@/backend/services/article.actions"; +import * as tagActions from "@/backend/services/tag.action"; import { ArticleRepositoryInput } from "@/backend/services/inputs/article.input"; import MultipleSelector from "@/components/ui/multi-select"; import { useDebouncedCallback } from "@/hooks/use-debounced-callback"; import { useTranslation } from "@/i18n/use-translation"; import { useSession } from "@/store/session.atom"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { LinkIcon, Loader } from "lucide-react"; import { useRouter } from "next/navigation"; -import React from "react"; +import React, { useState } from "react"; import { SubmitHandler, useForm } from "react-hook-form"; import { z } from "zod"; import { Button } from "../ui/button"; @@ -52,6 +53,8 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => { }, }); + const [selectedTags, setSelectedTags] = useState(article.tags ?? []); + const setDebounceHandler = useDebouncedCallback(async (slug: string) => { const handle = await articleActions.getUniqueArticleHandle(slug); form.setValue("handle", handle); @@ -108,49 +111,104 @@ const ArticleEditorDrawer: React.FC = ({ article, open, onClose }) => { onSubmit={form.handleSubmit(handleOnSubmit)} className="flex flex-col gap-2" > - {/* {JSON.stringify(form.formState.errors)} */} -
{JSON.stringify(article, null, 2)}
- ( - - {_t("Handle")} - - - } - {...field} - onChange={(e) => { - setDebounceHandler(e.target.value); - form.setValue("handle", e.target.value); - }} - /> - - - https://www.techdiary.dev/@{session?.user?.username}/ - {form.watch("handle")} - - - - )} - /> - {/* */} - ( - - {_t("Excerpt")} - -