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
2 changes: 1 addition & 1 deletion .cursor/rules/basics.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ alwaysApply: true
- Command for running script in a workspace: `pnpm --filter <workspace> <command>`.
- Command for running tests: `pnpm test`.
- The project uses shadcn for building UI so stick to its conventions and design.
- In `apps/web` workspace, create a string first in `apps/web/config/strings.ts` and then import it in the `.tsx` files, instead of using inline strings.
- In `apps/web` workspace, create a string first in `apps/web/ui-config/strings.ts` and then import it in the `.tsx` files, instead of using inline strings.
- When working with forms, always use refs to keep the current state of the form's data and use it to enable/disable the form submit button.
1 change: 0 additions & 1 deletion .eslintignore

This file was deleted.

20 changes: 20 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
## Development Tips

- Use `pnpm` as package manager.
- The project is structured as a monorepo i.e. a pnpm workspace. The apps are in `apps` folder and re-usable packages are in `packages`.
- Command for running script in a workspace: `pnpm --filter <workspace> <command>`.
- Command for running tests: `pnpm test`.
- The project uses shadcn for building UI so stick to its conventions and design.
- In `apps/web` workspace, create a string first in `apps/web/config/strings.ts` and then import it in the `.tsx` files, instead of using inline strings.
- When working with forms, always use refs to keep the current state of the form's data and use it to enable/disable the form submit button.
- Check the name field inside each package's package.json to confirm the right name—skip the top-level one.

## Testing instructions

- Always add or update test when introducing changes to `apps/web/graphql` folder, even if nobody asked.
- Run `pnpm test` to run the tests.
- Fix any test or type errors until the whole suite is green.

## PR instructions

- Always run `pnpm lint` and `pnpm prettier` before committing.
10 changes: 5 additions & 5 deletions apps/docs/src/pages/en/website/blocks.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ You will also see the newly added link on the header itself.
3. Click on the pencil icon against the newly added link to edit it as shown above.
4. Change the label (displayed as text on the header block) and the URL (where the user should be taken upon clicking the label on the header) and click `Done` to save.
![Header edit link](/assets/pages/header-edit-link.png)
</details>
</details>

### [Rich Text](#rich-text)

Expand All @@ -61,7 +61,7 @@ You can also use the floating controls to do the same as shown below.
2. Click on the floating `link` button to reveal a popup text input.
3. In the popup text input, enter the URL as shown below.
![Create a hyperlink in rich text block](/assets/pages/rich-text-create-hyperlink.gif)
</details>
</details>

### [Hero](#hero)

Expand All @@ -87,7 +87,7 @@ Following is how it looks on a page.
4. In the button action, enter the URL the user should be taken to upon clicking.
a. If the URL is from your own school, use its relative form, i.e., `/courses`.
b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`.
</details>
</details>

### [Grid](#grid)

Expand Down Expand Up @@ -132,7 +132,7 @@ A grid block comes in handy when you want to show some sort of list, for example
4. In the button action, enter the URL the user should be taken to upon clicking.
a. If the URL is from your own school, use its relative form, i.e., `/courses`.
b. If the URL is from some external website, use the absolute (complete) URL, i.e., `https://website.com/courses`.
</details>
</details>

### [Featured](#featured)

Expand Down Expand Up @@ -268,7 +268,7 @@ In the `Design` panel, you can customize:
- Maximum width
- Vertical padding
- Social media links (Facebook, Twitter, Instagram, LinkedIn, YouTube, Discord, GitHub)
</details>
</details>

## [Shared blocks](#shared-blocks)

Expand Down
36 changes: 18 additions & 18 deletions apps/docs/src/pages/en/website/themes.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,30 +192,30 @@ The typography editor lets you customize text styles across your website. These
- Header 3: Smaller titles for subsections
- Header 4: Small titles for minor sections
- Preheader: Introductory text that appears above headers
</details>
</details>

<details>
<summary>Subheaders</summary>

- Subheader 1: Primary subheaders for section introductions
- Subheader 2: Secondary subheaders for supporting text
</details>
</details>

<details>
<summary>Body Text</summary>

- Text 1: Main body text for content
- Text 2: Secondary body text for supporting content
- Caption: Small text for image captions and footnotes
</details>
</details>

<details>
<summary>Interactive Elements</summary>

- Link: Text for clickable links
- Button: Text for buttons and calls-to-action
- Input: Text for form fields and search boxes
</details>
</details>

For each text style, you can customize:

Expand Down Expand Up @@ -243,7 +243,7 @@ CourseLit provides a carefully curated selection of professional fonts, organize
- **Mulish**: A geometric sans-serif with a modern feel
- **Nunito**: A well-balanced font with rounded terminals
- **Work Sans**: A clean, modern font with a geometric feel
</details>
</details>

<details>
<summary>Serif Fonts</summary>
Expand All @@ -253,7 +253,7 @@ CourseLit provides a carefully curated selection of professional fonts, organize
- **Playfair Display**: An elegant serif font for headings
- **Roboto Slab**: A serif variant of Roboto
- **Source Serif 4**: A serif font designed for digital reading
</details>
</details>

<details>
<summary>Display Fonts</summary>
Expand All @@ -264,15 +264,15 @@ CourseLit provides a carefully curated selection of professional fonts, organize
- **Rubik**: A sans-serif with a geometric feel
- **Oswald**: A reworking of the classic style
- **Bebas Neue**: A display font with a strong personality
</details>
</details>

<details>
<summary>Modern Fonts</summary>

- **Lato**: A sans-serif font with a warm feel
- **PT Sans**: A font designed for public use
- **Quicksand**: A display sans-serif with rounded terminals
</details>
</details>

Each font is optimized for web use and includes multiple weights for flexibility in design. All fonts support Latin characters and are carefully selected for their readability and professional appearance.

Expand All @@ -290,7 +290,7 @@ The interactives editor allows you to customize the appearance of interactive el
- Shadow effects: From None to 2X Large
- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes)
- Disabled state: How the button looks when it can't be clicked
</details>
</details>

<details>
<summary>Link</summary>
Expand All @@ -300,7 +300,7 @@ The interactives editor allows you to customize the appearance of interactive el
- Text shadow: Add depth to your links
- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes)
- Disabled state: How the link looks when it can't be clicked
</details>
</details>

<details>
<summary>Card</summary>
Expand All @@ -309,7 +309,7 @@ The interactives editor allows you to customize the appearance of interactive el
- Border style: Choose from various border styles
- Shadow effects: Add depth to your cards
- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes)
</details>
</details>

<details>
<summary>Input</summary>
Expand All @@ -320,7 +320,7 @@ The interactives editor allows you to customize the appearance of interactive el
- Shadow effects: Add depth to your input fields
- Custom styles: Add your own custom styles using [supported Tailwind classes](#supported-tailwind-classes)
- Disabled state: How the input looks when it can't be used
</details>
</details>

### 4. Structure

Expand All @@ -332,14 +332,14 @@ The structure editor lets you customize the layout of your pages, like section p
<summary>Page</summary>

- Maximum width options: - 2XL (42rem): Compact layout - 3XL (48rem): Standard layout - 4XL (56rem): Wide layout - 5XL (64rem): Extra wide layout - 6XL (72rem): Full width layout
</details>
</details>

<details>
<summary>Section</summary>

- Horizontal padding: Space on the left and right sides (None to 9X Large)
- Vertical padding: Space on the top and bottom (None to 9X Large)
</details>
</details>

## Publishing Changes

Expand Down Expand Up @@ -387,7 +387,7 @@ When adding custom styles to interactive elements, you can use the following Tai
- `text-6xl`: 6X large text
- `text-7xl`: 7X large text
- `text-8xl`: 8X large text
</details>
</details>

<details>
<summary>Padding</summary>
Expand All @@ -399,7 +399,7 @@ When adding custom styles to interactive elements, you can use the following Tai
#### Horizontal Padding

- `px-4` to `px-20`: Horizontal padding from 1rem to 5rem
</details>
</details>

<details>
<summary>Colors</summary>
Expand Down Expand Up @@ -454,7 +454,7 @@ Variants available: `hover`, `disabled`, `dark`
- `ease-out`: Ease out
- `ease-in-out`: Ease in and out
- `ease-linear`: Linear
</details>
</details>

<details>
<summary>Transforms</summary>
Expand All @@ -481,7 +481,7 @@ Variants available: `hover`, `disabled`, `dark`
- `scale-110`: 110% scale
- `scale-125`: 125% scale
- `scale-150`: 150% scale
</details>
</details>

<details>
<summary>Shadows</summary>
Expand Down
4 changes: 2 additions & 2 deletions apps/queue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"@types/nodemailer": "^6.4.8",
"tsconfig": "workspace:^",
"tsup": "^7.2.0",
"typescript": "^5.0.4",
"typescript-eslint": "^7.5.0"
"typescript": "^5.9.3",
"typescript-eslint": "^8.46.4"
}
}
37 changes: 24 additions & 13 deletions apps/queue/src/domain/model/domain.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,28 @@
import { Domain as PublicDomain } from "@courselit/common-models";
import mongoose from "mongoose";
import mongoose, { Document, Model } from "mongoose";
import SettingsSchema from "./site-info";

export interface Domain extends PublicDomain {
_id: mongoose.Types.ObjectId;
}
export type DomainDocument = Document &
PublicDomain & {
_id: mongoose.Types.ObjectId;
incrementEmailCount: () => Promise<DomainDocument>;
};

const DomainSchema = new mongoose.Schema<Domain>(
const DomainSchema = new mongoose.Schema<DomainDocument>(
{
name: { type: String, required: true, unique: true },
settings: SettingsSchema,
quota: new mongoose.Schema<Domain["quota"]>({
mail: new mongoose.Schema<Domain["quota"]["mail"]>({
quota: new mongoose.Schema<DomainDocument["quota"]>({
mail: new mongoose.Schema<DomainDocument["quota"]["mail"]>({
daily: { type: Number, default: 0 },
monthly: { type: Number, default: 0 },
dailyCount: { type: Number, default: 0 },
monthlyCount: { type: Number, default: 0 },
lastDailyCountUpdate: { type: Date, default: Date.now },
lastMonthlyCountUpdate: { type: Date, default: Date.now },
lastDailyCountUpdate: { type: Date, default: () => new Date() },
lastMonthlyCountUpdate: {
type: Date,
default: () => new Date(),
},
}),
}),
},
Expand All @@ -26,7 +31,9 @@ const DomainSchema = new mongoose.Schema<Domain>(
},
);

DomainSchema.methods.incrementEmailCount = async function () {
DomainSchema.methods.incrementEmailCount = async function (
this: DomainDocument,
) {
const today = new Date().toISOString().split("T")[0];
const thisMonth = new Date().toISOString().slice(0, 7);
const lastDailyUpdate = new Date(this.quota.mail.lastDailyCountUpdate)
Expand All @@ -40,17 +47,21 @@ DomainSchema.methods.incrementEmailCount = async function () {
this.quota.mail.dailyCount++;
} else {
this.quota.mail.dailyCount = 1;
this.quota.mail.lastDailyCountUpdate = Date.now();
this.quota.mail.lastDailyCountUpdate = new Date();
}

if (thisMonth === lastMonthlyUpdate) {
this.quota.mail.monthlyCount++;
} else {
this.quota.mail.monthlyCount = 1;
this.quota.mail.lastMonthlyCountUpdate = Date.now();
this.quota.mail.lastMonthlyCountUpdate = new Date();
}

return this.save();
};

export default mongoose.models.Domain || mongoose.model("Domain", DomainSchema);
const DomainModel =
(mongoose.models.Domain as Model<DomainDocument>) ||
mongoose.model<DomainDocument>("Domain", DomainSchema);

export default DomainModel;
9 changes: 6 additions & 3 deletions apps/queue/src/domain/model/email-template.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import mongoose from "mongoose";
import mongoose, { Model } from "mongoose";
import { EmailTemplate as PublicEmailTemplate } from "@courselit/common-models";
import { EmailContentSchema } from "@courselit/common-logic";

Expand All @@ -23,5 +23,8 @@ EmailTemplateSchema.index(
{ unique: true },
);

export default mongoose.models.EmailTemplate ||
mongoose.model("EmailTemplate", EmailTemplateSchema);
const EmailTemplateModel =
(mongoose.models.EmailTemplate as Model<EmailTemplate>) ||
mongoose.model<EmailTemplate>("EmailTemplate", EmailTemplateSchema);

export default EmailTemplateModel;
7 changes: 5 additions & 2 deletions apps/queue/src/domain/model/membership.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { MembershipSchema } from "@courselit/common-logic";
import mongoose from "mongoose";
import mongoose, { Model } from "mongoose";

export default mongoose.models.Membership ||
const MembershipModel =
(mongoose.models.Membership as Model<any>) ||
mongoose.model("Membership", MembershipSchema);

export default MembershipModel;
9 changes: 6 additions & 3 deletions apps/queue/src/domain/model/ongoing-sequence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import mongoose, { Schema, Document } from "mongoose";

export type OngoingSequence = OS &
Document & {
domain: mongoose.Schema.Types.ObjectId;
domain: mongoose.Types.ObjectId;
};

const OngoingSequenceSchema: Schema = new Schema<OngoingSequence>(
Expand All @@ -22,5 +22,8 @@ const OngoingSequenceSchema: Schema = new Schema<OngoingSequence>(

OngoingSequenceSchema.index({ sequenceId: 1, userId: 1 }, { unique: true });

export default mongoose.models.OngoingSequence ||
mongoose.model("OngoingSequence", OngoingSequenceSchema);
const OngoingSequenceModel =
(mongoose.models.OngoingSequence as mongoose.Model<OngoingSequence>) ||
mongoose.model<OngoingSequence>("OngoingSequence", OngoingSequenceSchema);

export default OngoingSequenceModel;
8 changes: 6 additions & 2 deletions apps/queue/src/domain/model/rule.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import mongoose from "mongoose";
import mongoose, { Model } from "mongoose";
import { RuleSchema } from "@courselit/common-logic";
export default mongoose.models.Rule || mongoose.model("Rule", RuleSchema);

const RuleModel =
(mongoose.models.Rule as Model<any>) || mongoose.model("Rule", RuleSchema);

export default RuleModel;
7 changes: 5 additions & 2 deletions apps/queue/src/domain/model/sequence.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import mongoose from "mongoose";
import mongoose, { Model } from "mongoose";
import { SequenceSchema } from "@courselit/common-logic";

export default mongoose.models.Sequence ||
const SequenceModel =
(mongoose.models.Sequence as Model<any>) ||
mongoose.model("Sequence", SequenceSchema);

export default SequenceModel;
7 changes: 5 additions & 2 deletions apps/queue/src/domain/model/user.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import mongoose from "mongoose";
import mongoose, { Model } from "mongoose";
import { UserSchema } from "@courselit/common-logic";

export default mongoose.models.User || mongoose.model("User", UserSchema);
const UserModel =
(mongoose.models.User as Model<any>) || mongoose.model("User", UserSchema);

export default UserModel;
Loading
Loading