From d3e6f330d159793829f16a324bb1e39e8bc12af1 Mon Sep 17 00:00:00 2001 From: Rajat Date: Wed, 25 Feb 2026 22:52:59 +0530 Subject: [PATCH 1/3] Editable slug for products and communities --- ...6_00-00-copy-pageid-to-slug-communities.js | 32 +++++ .../(with-layout)/products/products-list.tsx | 5 +- .../(sidebar)/community/[id]/manage/page.tsx | 24 ++++ .../manage/components/product-details.tsx | 24 +++- apps/web/config/strings.ts | 2 + apps/web/graphql/communities/logic.ts | 103 ++++++++++----- apps/web/graphql/communities/mutation.ts | 4 + apps/web/graphql/communities/types.ts | 1 + apps/web/graphql/courses/helpers.ts | 78 +++++++----- apps/web/graphql/courses/logic.ts | 34 ++++- apps/web/graphql/courses/types/index.ts | 1 + apps/web/graphql/lessons/helpers.ts | 9 +- apps/web/graphql/pages/helpers.ts | 64 +++++++++- apps/web/graphql/pages/logic.ts | 65 +++++----- apps/web/hooks/use-community.ts | 1 + apps/web/models/Community.ts | 63 ++-------- apps/web/models/Course.ts | 11 +- apps/web/next-env.d.ts | 2 +- packages/common-logic/src/models/course.ts | 118 +----------------- packages/common-models/src/community.ts | 1 + packages/orm-models/src/models/community.ts | 2 + packages/orm-models/src/models/course.ts | 1 + 22 files changed, 369 insertions(+), 276 deletions(-) create mode 100644 apps/web/.migrations/24-02-26_00-00-copy-pageid-to-slug-communities.js diff --git a/apps/web/.migrations/24-02-26_00-00-copy-pageid-to-slug-communities.js b/apps/web/.migrations/24-02-26_00-00-copy-pageid-to-slug-communities.js new file mode 100644 index 000000000..d2af2e61c --- /dev/null +++ b/apps/web/.migrations/24-02-26_00-00-copy-pageid-to-slug-communities.js @@ -0,0 +1,32 @@ +/** + * Copies pageId to slug for all existing communities that don't have a slug. + * Must run BEFORE deploying the new code, since `slug` is `required: true` + * on the Community model. + * + * Usage: DB_CONNECTION_STRING= node 24-02-26_00-00-copy-pageid-to-slug-communities.js + */ +import mongoose from "mongoose"; + +const DB_CONNECTION_STRING = process.env.DB_CONNECTION_STRING; +if (!DB_CONNECTION_STRING) { + throw new Error("DB_CONNECTION_STRING is not set"); +} + +(async () => { + try { + await mongoose.connect(DB_CONNECTION_STRING); + const db = mongoose.connection.db; + if (!db) throw new Error("Could not connect to database"); + + const result = await db.collection("communities").updateMany( + { slug: { $exists: false } }, + [{ $set: { slug: "$pageId" } }], // aggregation pipeline: copy field value + ); + + console.log( + `✅ Updated ${result.modifiedCount} communities: copied pageId → slug`, + ); + } finally { + await mongoose.connection.close(); + } +})(); diff --git a/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx b/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx index 2bcddc925..7b6fb6bfc 100644 --- a/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx +++ b/apps/web/app/(with-contexts)/(with-layout)/products/products-list.tsx @@ -27,7 +27,10 @@ export function ProductsList({ }) { const siteinfo = useContext(SiteInfoContext); const filters = useMemo( - () => [Constants.CourseType.COURSE.toUpperCase()], + () => [ + Constants.CourseType.COURSE.toUpperCase(), + Constants.CourseType.DOWNLOAD.toUpperCase(), + ], [], ); diff --git a/apps/web/app/(with-contexts)/dashboard/(sidebar)/community/[id]/manage/page.tsx b/apps/web/app/(with-contexts)/dashboard/(sidebar)/community/[id]/manage/page.tsx index 35a1d1c02..a07620738 100644 --- a/apps/web/app/(with-contexts)/dashboard/(sidebar)/community/[id]/manage/page.tsx +++ b/apps/web/app/(with-contexts)/dashboard/(sidebar)/community/[id]/manage/page.tsx @@ -87,6 +87,7 @@ export default function Page(props: { const address = useContext(AddressContext); const [name, setName] = useState(""); + const [slug, setSlug] = useState(""); const [enabled, setEnabled] = useState(false); const [autoAcceptMembers, setAutoAcceptMembers] = useState(false); const [banner, setBanner] = useState(TextEditorEmptyDoc); @@ -171,6 +172,7 @@ export default function Page(props: { const setCommunity = (community: any) => { setName(community.name); + setSlug(community.slug || ""); if (community.description) { setDescription(community.description); } @@ -194,6 +196,7 @@ export default function Page(props: { mutation UpdateCommunity( $id: String! $name: String + $slug: String $description: String $enabled: Boolean $autoAcceptMembers: Boolean @@ -202,6 +205,7 @@ export default function Page(props: { community: updateCommunity( id: $id name: $name + slug: $slug description: $description enabled: $enabled autoAcceptMembers: $autoAcceptMembers @@ -209,6 +213,7 @@ export default function Page(props: { ) { communityId name + slug description enabled banner @@ -248,6 +253,7 @@ export default function Page(props: { variables: { id, name, + slug: slug || undefined, description: JSON.stringify(description), enabled, autoAcceptMembers, @@ -287,6 +293,7 @@ export default function Page(props: { ) { communityId name + slug description enabled banner @@ -557,6 +564,23 @@ export default function Page(props: { } placeholder="Community name" /> +
+ + ) => + setSlug(e.target.value) + } + placeholder="my-community" + /> +

+ The URL-friendly identifier for this community page. +

+

Description

({ name: product?.title || "", + slug: product?.slug || "", description: product?.description ? JSON.parse(product.description) : TextEditorEmptyDoc, @@ -78,6 +80,7 @@ export default function ProductDetails({ product }: ProductDetailsProps) { courseId: product.courseId, title: formData.name, description: JSON.stringify(formData.description), + slug: formData.slug || undefined, }, }) .build() @@ -120,6 +123,23 @@ export default function ProductDetails({ product }: ProductDetailsProps) { )}
+
+ + +

+ The URL-friendly identifier for this product page. +

+
+