Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/docs/public/assets/pages/edit-page-slug.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion apps/docs/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand Down
26 changes: 26 additions & 0 deletions apps/docs/src/pages/en/website/create-page.md
Original file line number Diff line number Diff line change
@@ -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 <a href="https://discord.com/invite/GR4bQsN" target="_blank">Discord</a> channel or send a tweet at <a href="https://twitter.com/courselit" target="_blank">@CourseLit</a>.
4 changes: 2 additions & 2 deletions apps/docs/src/pages/en/website/edit.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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?

Expand Down
20 changes: 12 additions & 8 deletions apps/docs/src/pages/en/website/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/<slug>`.

## 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.
Expand All @@ -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?

Expand Down
57 changes: 57 additions & 0 deletions apps/docs/src/pages/en/website/sales-pages.md
Original file line number Diff line number Diff line change
@@ -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 <a href="https://discord.com/invite/GR4bQsN" target="_blank">Discord</a> channel or send a tweet at <a href="https://twitter.com/courselit" target="_blank">@CourseLit</a>.
Original file line number Diff line number Diff line change
@@ -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=<mongodb-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();
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
],
[],
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand All @@ -194,6 +196,7 @@ export default function Page(props: {
mutation UpdateCommunity(
$id: String!
$name: String
$slug: String
$description: String
$enabled: Boolean
$autoAcceptMembers: Boolean
Expand All @@ -202,13 +205,15 @@ export default function Page(props: {
community: updateCommunity(
id: $id
name: $name
slug: $slug
description: $description
enabled: $enabled
autoAcceptMembers: $autoAcceptMembers
joiningReasonText: $joiningReasonText
) {
communityId
name
slug
description
enabled
banner
Expand Down Expand Up @@ -248,6 +253,7 @@ export default function Page(props: {
variables: {
id,
name,
slug: slug || undefined,
description: JSON.stringify(description),
enabled,
autoAcceptMembers,
Expand Down Expand Up @@ -287,6 +293,7 @@ export default function Page(props: {
) {
communityId
name
slug
description
enabled
banner
Expand Down Expand Up @@ -557,6 +564,23 @@ export default function Page(props: {
}
placeholder="Community name"
/>
<div className="space-y-2">
<Label htmlFor="slug" className="font-semibold">
Slug
</Label>
<Input
id="slug"
name="slug"
value={slug}
onChange={(e: ChangeEvent<HTMLInputElement>) =>
setSlug(e.target.value)
}
placeholder="my-community"
/>
<p className="text-sm text-muted-foreground">
The URL-friendly identifier for this community page.
</p>
</div>
<div>
<h2 className="font-semibold">Description</h2>
<Editor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import { Save, Loader2 } from "lucide-react";
import { Editor, emptyDoc as TextEditorEmptyDoc } from "@courselit/text-editor";

const MUTATION_UPDATE_BASIC_DETAILS = `
mutation UpdateBasicDetails($courseId: String!, $title: String!, $description: String!) {
updateCourse(courseData: { id: $courseId, title: $title, description: $description }) {
mutation UpdateBasicDetails($courseId: String!, $title: String!, $description: String!, $slug: String) {
updateCourse(courseData: { id: $courseId, title: $title, description: $description, slug: $slug }) {
courseId
}
}
Expand All @@ -39,9 +39,11 @@ export default function ProductDetails({ product }: ProductDetailsProps) {
const [refresh, setRefresh] = useState(0);
const [formData, setFormData] = useState<{
name: string;
slug: string;
description: any;
}>({
name: product?.title || "",
slug: product?.slug || "",
description: product?.description
? JSON.parse(product.description)
: TextEditorEmptyDoc,
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -120,6 +123,23 @@ export default function ProductDetails({ product }: ProductDetailsProps) {
)}
</div>

<div className="space-y-4">
<Label htmlFor="slug" className="text-base font-semibold">
Slug
</Label>
<Input
id="slug"
name="slug"
value={formData.slug}
onChange={handleInputChange}
disabled={loading}
placeholder="my-awesome-course"
/>
<p className="text-sm text-muted-foreground">
The URL-friendly identifier for this product page.
</p>
</div>

<div className="space-y-4">
<Label
htmlFor="description"
Expand Down
2 changes: 2 additions & 0 deletions apps/web/config/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ export const responses = {
"CourseID is required for demo certificate",
provider_not_configured: "Configure the provider before enabling",
provider_invalid_configuration: "Invalid provider configuration",
page_id_already_exists:
"This URL slug is already in use. Please choose a different one.",
};

export const internal = {
Expand Down
Loading
Loading