diff --git a/apps/docs/public/assets/pages/community-copy-sales-page.png b/apps/docs/public/assets/pages/community-copy-sales-page.png new file mode 100644 index 000000000..54d19f512 Binary files /dev/null and b/apps/docs/public/assets/pages/community-copy-sales-page.png differ diff --git a/apps/docs/public/assets/pages/edit-page-slug.png b/apps/docs/public/assets/pages/edit-page-slug.png new file mode 100644 index 000000000..7915b201f Binary files /dev/null and b/apps/docs/public/assets/pages/edit-page-slug.png differ diff --git a/apps/docs/public/assets/pages/new-page-screen.png b/apps/docs/public/assets/pages/new-page-screen.png new file mode 100644 index 000000000..2a93a73a1 Binary files /dev/null and b/apps/docs/public/assets/pages/new-page-screen.png differ diff --git a/apps/docs/public/assets/pages/product-copy-sales-page.png b/apps/docs/public/assets/pages/product-copy-sales-page.png new file mode 100644 index 000000000..a087b4043 Binary files /dev/null and b/apps/docs/public/assets/pages/product-copy-sales-page.png differ diff --git a/apps/docs/src/config.ts b/apps/docs/src/config.ts index dc75f676b..eb11b7704 100644 --- a/apps/docs/src/config.ts +++ b/apps/docs/src/config.ts @@ -103,8 +103,10 @@ export const SIDEBAR: Sidebar = { ], Website: [ { text: "Introduction", link: "en/website/introduction" }, - { text: "Page blocks", link: "en/website/blocks" }, + { text: "Create a page", link: "en/website/create-page" }, + { text: "Sales pages", link: "en/website/sales-pages" }, { text: "Edit page", link: "en/website/edit" }, + { text: "Page blocks", link: "en/website/blocks" }, { text: "Themes", link: "en/website/themes" }, ], Blog: [ diff --git a/apps/docs/src/pages/en/website/create-page.md b/apps/docs/src/pages/en/website/create-page.md new file mode 100644 index 000000000..3cf93f0f0 --- /dev/null +++ b/apps/docs/src/pages/en/website/create-page.md @@ -0,0 +1,26 @@ +--- +title: Create a page +description: Create a page +layout: ../../../layouts/MainLayout.astro +--- + +You can create custom pages in your CourseLit school. These pages can be used for various purposes, such as landing pages, email list builders, etc. + +## Creating a page + +1. Go to your school's dashboard and click on `Pages` in the left sidebar. +2. Click on the `New page` button in the top right corner. +3. On the `New page` screen, enter a name for your page. The slug will update automatically based on the name. +4. Click on `Continue`. + + ![New page screen](/assets/pages/new-page-screen.png) + + You will be taken to the [page builder](/en/website/edit) where you can start adding blocks to your page. + +## Next step + +[Learn about sales pages](/en/website/sales-pages). + +## Stuck somewhere? + +We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. diff --git a/apps/docs/src/pages/en/website/edit.md b/apps/docs/src/pages/en/website/edit.md index 47382171b..bbada4686 100644 --- a/apps/docs/src/pages/en/website/edit.md +++ b/apps/docs/src/pages/en/website/edit.md @@ -36,7 +36,7 @@ Before you can edit your page, you need to get to the page editor. Accessing the Let's see how. -> All the edits you make to a page will be saved in a draft. In order to see the changes on a live page, you need to publish it. See the [publishing guide](#publish-page). +> All the edits you make to a page will be saved in a draft. In order to see the changes on a live page, you need to publish it. ### To edit a website page @@ -95,7 +95,7 @@ Following are the step to delete a block. ## Next step -Explore all the blocks CourseLit has to offer. +[Explore all the blocks CourseLit has to offer](/en/website/blocks). ## Stuck somewhere? diff --git a/apps/docs/src/pages/en/website/introduction.md b/apps/docs/src/pages/en/website/introduction.md index 346b3c3e9..c2f513723 100644 --- a/apps/docs/src/pages/en/website/introduction.md +++ b/apps/docs/src/pages/en/website/introduction.md @@ -4,10 +4,18 @@ description: Introduction to CourseLit's Build-in Website Builder layout: ../../../layouts/MainLayout.astro --- -CourseLit comes with a powerful website builder. Using the website builder, you can build all sorts of web pages for various use cases like landing pages, sales pages, email list builder etc. +CourseLit comes with a powerful website builder. Using the website builder, you can build all sorts of web pages for various use cases like landing pages, [sales pages](/en/website/sales-pages), email list builder etc. ![CourseLit's Page builder](/assets/pages/page-builder-home.png) +## Understanding page URL + +The page URL is structured as `/p/`. + +## Understanding page slug + +A slug is a unique identifier for your page. No two pages can have the same slug in a school. Hence, a slug is unique among product sales pages, community sales pages, and other custom pages you create. + ## Default pages When you create a new school, the following pages are automatically created for you. @@ -20,21 +28,17 @@ This is your school's landing page hosted at `/`. Your blog's homepage hosted at `/blog`. You can add additional blocks to the blog's home page. -2. Terms +3. Terms This is a page for hosting your terms and conditions for your school. This is hosted at `/terms`. -3. Privacy policy +4. Privacy policy This is a page for hosting the privacy policy for your school. This is hosted at `/privacy`. -## Sales pages - -Each product that you create, gets its own sales page. This comes handy if you want to add additional information about your product for better conversion (and more sales). - ## Next step -[Learn about page blocks](/en/website/blocks). +[Learn how to create a page](/en/website/create-page). ## Stuck somewhere? diff --git a/apps/docs/src/pages/en/website/sales-pages.md b/apps/docs/src/pages/en/website/sales-pages.md new file mode 100644 index 000000000..62bea3774 --- /dev/null +++ b/apps/docs/src/pages/en/website/sales-pages.md @@ -0,0 +1,57 @@ +--- +title: Sales pages +description: Introduction to CourseLit's Sales Pages +layout: ../../../layouts/MainLayout.astro +--- + +Every product and community that you create gets its own sales page. This comes in handy if you want to add additional information about your product for better conversion (and more sales). + +## Sales page URL + +If your product's title is `Demo Product`, the sales page's [slug](/en/website/introduction#understanding-page-slug) will be `demo-product` and the sales page's URL will be `/p/demo-product`. + +## Obtaining a sales page's URL + +Click on the **Share** icon next to your product/community's name in its dashboard to copy the sales page URL. + +### Product's sales page URL + +![Copy product's sales page URL](/assets/pages/product-copy-sales-page.png) + +### Community's sales page URL + +![Copy community's sales page URL](/assets/pages/community-copy-sales-page.png) + +## Editing a product's sales page + +1. Go to your products dashboard. + +2. Select `Edit page` from the `Actions` dropdown. This will open up the [page builder](/en/website/edit). + +3. Edit the page. You can add, remove, or reorder blocks as you like. + +4. Hit `Publish` to make your changes visible on the public page. Before publishing, the changes are in draft mode and are only visible while editing. + +## Editing a community's sales page + +1. Go to your community's `Manage` section. + +2. Click on the `Edit page` button. This will open up the [page builder](/en/website/edit). + +3. Edit the page. You can add, remove, or reorder blocks as you like. + +4. Hit `Publish` to make your changes visible on the public page. Before publishing, the changes are in draft mode and are only visible while editing. + +### Changing the page slug + +You can change the page slug for a product or community. It is located in the respective `Manage` sections, under the title. + +![Edit page slug of community](/assets/pages/edit-page-slug.png) + +## Next step + +[Learn how to edit a page](/en/website/edit). + +## Stuck somewhere? + +We are always here for you. Come chat with us in our Discord channel or send a tweet at @CourseLit. 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. +

+
+