Development (#97)

diff --git a/src/actions/roleMenu.ts b/src/actions/roleMenu.ts
index 8b623a4..5eb1d7e 100644
--- a/src/actions/roleMenu.ts
+++ b/src/actions/roleMenu.ts
@@ -32,7 +32,7 @@
 
 interface ObjectSchema {
     name: string;
-    description: string | null;
+    description?: string;
     min: number;
     max: number;
     options: {
@@ -46,7 +46,7 @@
     placeholder: string,
     currentPageData: ObjectSchema,
     selectedRoles?: string[],
-    disabled?: boolean
+    disabled: boolean = false
 ): ActionRowBuilder<StringSelectMenuBuilder> => {
     return new ActionRowBuilder<StringSelectMenuBuilder>().addComponents(
         new StringSelectMenuBuilder()
@@ -72,7 +72,7 @@
     if (!interaction.member) return;
     if (!interaction.guild) return;
     const config = await client.database.guilds.read(interaction.guild.id);
-    const options = config.roleMenu.options.filter((option) => option.options.length > 0);
+    const options = config.roleMenu.options.filter((option) => option.options.length > 0 && option.enabled);
     if (!config.roleMenu.enabled) {
         return await interaction.reply({
             embeds: [
@@ -195,6 +195,7 @@
 
     while (!(complete && done)) {
         const currentPageData = options[page]!;
+        currentPageData.max = currentPageData.max === 0 ? currentPageData.options.length : currentPageData.max;
         const embed = new EmojiEmbed()
             .setTitle("Roles")
             .setDescription(
diff --git a/src/commands/settings/rolemenu.ts b/src/commands/settings/rolemenu.ts
index 47914c6..1ca10a4 100644
--- a/src/commands/settings/rolemenu.ts
+++ b/src/commands/settings/rolemenu.ts
@@ -27,14 +27,17 @@
 import ellipsis from "../../utils/ellipsis.js";
 import _ from "lodash";
 
+const cross = getEmojiByName("CONTROL.CROSS");
+const tick = getEmojiByName("CONTROL.TICK");
 const isEqual = _.isEqual;
 
 const command = (builder: SlashCommandSubcommandBuilder) =>
     builder.setName("rolemenu").setDescription("Allows you to change settings for the servers rolemenu");
 
 interface ObjectSchema {
+    enabled?: boolean;
     name: string;
-    description: string | null;
+    description?: string;
     min: number;
     max: number;
     options: {
@@ -106,34 +109,60 @@
     i: ButtonInteraction,
     interaction: StringSelectMenuInteraction | ButtonInteraction,
     m: Message,
-    data: { name?: string; description?: string | null }
-) => {
-    let { name, description } = data;
-    const modal = new ModalBuilder()
-        .setTitle("Edit Name and Description")
-        .setCustomId("editNameDescription")
-        .addComponents(
+    data: { name?: string; description?: string; bounds?: { min: number; max: number } },
+    addBounds: boolean = false
+): Promise<[string | undefined, string | undefined, { min: number; max: number } | undefined]> => {
+    let { name, description, bounds } = data;
+    const components = [
+        new ActionRowBuilder<TextInputBuilder>().addComponents(
+            new TextInputBuilder()
+                .setLabel("Name")
+                .setCustomId("name")
+                .setPlaceholder("The name of the role (e.g. Programmer)")
+                .setStyle(TextInputStyle.Short)
+                .setValue(name ?? "")
+                .setRequired(true)
+                .setMaxLength(100)
+        ),
+        new ActionRowBuilder<TextInputBuilder>().addComponents(
+            new TextInputBuilder()
+                .setLabel("Description")
+                .setCustomId("description")
+                .setPlaceholder("A short description of the role (e.g. A role for people who code)")
+                .setStyle(TextInputStyle.Short)
+                .setValue(description ?? "")
+                .setRequired(false)
+                .setMaxLength(100)
+        )
+    ];
+    if (addBounds && bounds) {
+        components.push(
             new ActionRowBuilder<TextInputBuilder>().addComponents(
                 new TextInputBuilder()
-                    .setLabel("Name")
-                    .setCustomId("name")
-                    .setPlaceholder("The name of the role (e.g. Programmer)")
+                    .setLabel("Minimum")
+                    .setCustomId("min")
+                    .setPlaceholder("The minimum number of roles a user can select")
                     .setStyle(TextInputStyle.Short)
-                    .setValue(name ?? "")
+                    .setValue(bounds.min.toString())
                     .setRequired(true)
-                    .setMaxLength(100)
+                    .setMaxLength(2)
             ),
             new ActionRowBuilder<TextInputBuilder>().addComponents(
                 new TextInputBuilder()
-                    .setLabel("Description")
-                    .setCustomId("description")
-                    .setPlaceholder("A short description of the role (e.g. A role for people who code)")
+                    .setLabel("Maximum")
+                    .setCustomId("max")
+                    .setPlaceholder("The maximum number of roles a user can select (0 for no limit)")
                     .setStyle(TextInputStyle.Short)
-                    .setValue(description ?? "")
-                    .setRequired(false)
-                    .setMaxLength(100)
+                    .setValue(bounds.max.toString())
+                    .setRequired(true)
+                    .setMaxLength(2)
             )
         );
+    }
+    const modal = new ModalBuilder()
+        .setTitle("Edit Name and Description")
+        .setCustomId("editNameDescription")
+        .addComponents(...components);
     const button = new ActionRowBuilder<ButtonBuilder>().addComponents(
         new ButtonBuilder()
             .setCustomId("back")
@@ -160,14 +189,20 @@
         console.error(e);
         out = null;
     }
-    if (!out) return [name, description];
-    if (out.isButton()) return [name, description];
+    if (!out) return [name, description, bounds];
+    if (out.isButton()) return [name, description, bounds];
     name = out.fields.fields.find((f) => f.customId === "name")?.value ?? name;
-    description = out.fields.fields.find((f) => f.customId === "description")?.value ?? null;
-    return [name, description];
+    description = out.fields.fields.find((f) => f.customId === "description")?.value;
+    if (addBounds) {
+        const min = parseInt(out.fields.fields.find((f) => f.customId === "min")?.value!);
+        const max = parseInt(out.fields.fields.find((f) => f.customId === "max")?.value!);
+        bounds = { min, max };
+    }
+    return [name, description, bounds];
 };
 
 const defaultRoleMenuData = {
+    enabled: true,
     name: "New Page",
     description: "",
     min: 0,
@@ -204,13 +239,20 @@
         const noRoles = data.options.length === 0;
         const previewSelect = configToDropdown(
             "Edit Roles",
-            {
-                name: data.name,
-                description: data.description ?? null,
-                min: 1,
-                max: 1,
-                options: noRoles ? [{ name: "Role 1", description: null, role: "No role set" }] : data.options
-            },
+            data.description
+                ? {
+                      name: data.name,
+                      description: data.description,
+                      min: 1,
+                      max: 1,
+                      options: noRoles ? [{ name: "Role 1", description: null, role: "No role set" }] : data.options
+                  }
+                : {
+                      name: data.name,
+                      min: 1,
+                      max: 1,
+                      options: noRoles ? [{ name: "Role 1", description: null, role: "No role set" }] : data.options
+                  },
             undefined,
             noRoles
         );
@@ -257,9 +299,9 @@
                     break;
                 }
                 case "edit": {
-                    const [name, description] = await editNameDescription(i, interaction, m, data);
+                    const [name, description, _bounds] = await editNameDescription(i, interaction, m, data);
                     data.name = name ? name : data.name;
-                    data.description = description ? description : data.description;
+                    description ? (data.description = description) : null;
                     break;
                 }
                 case "addRole": {
@@ -474,15 +516,16 @@
             if (page > 0) (actionSelect as StringSelectMenuBuilder).setDisabled(true);
             pageSelect.addOptions(new StringSelectMenuOptionBuilder().setLabel("No role menu pages").setValue("none"));
         } else if (page === 0) {
-            const cross = getEmojiByName("CONTROL.CROSS");
-            const tick = getEmojiByName("CONTROL.TICK");
             embed.setDescription(
                 `**Enabled:** ${currentObject.enabled ? `${tick} Yes` : `${cross} No`}\n\n` +
                     `**Pages:** ${currentObject.options.length}\n` +
                     (currentObject.options.length > 0
                         ? currentObject.options
                               .map((key: ObjectSchema) => {
-                                  return `> **${key.name}:** ${key.description ?? "*No description set*"}`;
+                                  const crossOut = key.enabled ? "" : "~~";
+                                  return `> ${crossOut}**${key.name}:** ${
+                                      key.description ?? "*No description set*"
+                                  }${crossOut}`;
                               })
                               .join("\n")
                         : "")
@@ -502,7 +545,7 @@
                                     50
                                 )
                             )
-                            .setValue(index.toString());
+                            .setValue((index + 1).toString());
                     })
                 );
             } else {
@@ -514,10 +557,28 @@
         } else {
             page = Math.max(Math.min(page, currentObject.options.length), 0);
             current = currentObject.options[page - 1]!;
+            (actionSelect as StringSelectMenuBuilder).addOptions(
+                new StringSelectMenuOptionBuilder()
+                    .setLabel(current.enabled ? "Disable" : "Enable")
+                    .setDescription("Enable or disable this page")
+                    .setValue("switch")
+                    .setEmoji(
+                        getEmojiByName(
+                            current.enabled ? "CONTROL.CROSS" : "CONTROL.TICK",
+                            "id"
+                        ) as APIMessageComponentEmoji
+                    )
+            );
             embed.setDescription(
                 `**Currently Editing:** ${current.name}\n\n` +
+                    `**Visible:** ${current.enabled ? `${tick} Yes` : `${cross} No`}\n\n` +
                     `**Description:**\n> ${current.description ?? "*No description set*"}\n` +
-                    `\n\n${createPageIndicator(Object.keys(config.roleMenu.options).length, page)}`
+                    `\n\n${createPageIndicator(
+                        Object.keys(currentObject.options).length,
+                        page - 1,
+                        false,
+                        Object.values(currentObject.options).map((o) => !(o.enabled ?? true))
+                    )}`
             );
 
             pageSelect.addOptions(
@@ -534,7 +595,7 @@
                                 50
                             )
                         )
-                        .setValue(index.toString());
+                        .setValue((index + 1).toString());
                 })
             );
         }
@@ -595,8 +656,7 @@
                 }
                 case "save": {
                     await client.database.guilds.write(interaction.guild.id, {
-                        "roleMenu.options": currentObject.options,
-                        "rolemenu.enabled": currentObject.enabled
+                        roleMenu: currentObject
                     });
                     modified = false;
                     await client.memory.forceUpdate(interaction.guild.id);
@@ -615,7 +675,7 @@
                         case "edit": {
                             const edited = await editRoleMenuPage(i, m, current!);
                             if (!edited) break;
-                            currentObject.options[page] = edited;
+                            currentObject.options[page - 1] = edited;
                             modified = true;
                             break;
                         }
@@ -626,6 +686,11 @@
                             modified = true;
                             break;
                         }
+                        case "switch": {
+                            currentObject.options[page - 1]!.enabled = !currentObject.options[page - 1]!.enabled;
+                            modified = true;
+                            break;
+                        }
                     }
                     break;
                 }
diff --git a/src/config/emojis.ts b/src/config/emojis.ts
index b04c569..8bcab8d 100644
--- a/src/config/emojis.ts
+++ b/src/config/emojis.ts
@@ -344,15 +344,27 @@
         HORIZONTAL: {
             LEFT: {
                 ACTIVE: "963121920038035506",
-                INACTIVE: "963121944239153242"
+                INACTIVE: "963121944239153242",
+                GRAY: {
+                    ACTIVE: "1115723064500572240",
+                    INACTIVE: "1115722930970697788"
+                }
             },
             MIDDLE: {
                 ACTIVE: "963121925893263420",
-                INACTIVE: "963121949796597870"
+                INACTIVE: "963121949796597870",
+                GRAY: {
+                    ACTIVE: "1115723063263232112",
+                    INACTIVE: "1115722848405827614"
+                }
             },
             RIGHT: {
                 ACTIVE: "963121933384302602",
-                INACTIVE: "963121956125831168"
+                INACTIVE: "963121956125831168",
+                GRAY: {
+                    ACTIVE: "1115722933445349398",
+                    INACTIVE: "1115722847051071639"
+                }
             }
         },
         VERTICAL: {
diff --git a/src/utils/createPageIndicator.ts b/src/utils/createPageIndicator.ts
index e16422d..0831c83 100644
--- a/src/utils/createPageIndicator.ts
+++ b/src/utils/createPageIndicator.ts
@@ -1,17 +1,29 @@
 import getEmojiByName from "./getEmojiByName.js";
 
-function pageIndicator(amount: number, selected: number, showDetails?: boolean, disabled?: boolean | string) {
+function pageIndicator(
+    amount: number,
+    selected: number,
+    showDetails?: boolean,
+    disabled?: boolean | string | boolean[]
+) {
     let out = "";
-    disabled = disabled ? "GRAY." : "";
+    // disabled = disabled ? "GRAY." : "";
+    // If disabled is a string, make a list of booleans the same length as amount.
+    if (!Array.isArray(disabled)) disabled = new Array(amount).fill(disabled);
+    // If the length is wrong, fill extra with false
+    if (disabled.length !== amount) {
+        disabled = disabled.concat(new Array(amount).fill(false));
+    }
+    const grey = disabled.map((x) => (x ? "GRAY." : ""));
     if (amount === 1) {
-        out += getEmojiByName("TRACKS.SINGLE." + disabled + (selected === 0 ? "ACTIVE" : "INACTIVE"));
+        out += getEmojiByName("TRACKS.SINGLE." + grey[0] + (selected === 0 ? "ACTIVE" : "INACTIVE"));
     } else {
         for (let i = 0; i < amount; i++) {
             out += getEmojiByName(
                 "TRACKS.HORIZONTAL." +
                     (i === 0 ? "LEFT" : i === amount - 1 ? "RIGHT" : "MIDDLE") +
                     "." +
-                    disabled +
+                    grey[i] +
                     (i === selected ? "ACTIVE" : "INACTIVE")
             );
         }
diff --git a/src/utils/database.ts b/src/utils/database.ts
index a1d5f49..67049cb 100644
--- a/src/utils/database.ts
+++ b/src/utils/database.ts
@@ -1015,8 +1015,9 @@
         enabled: boolean;
         allowWebUI: boolean;
         options: {
+            enabled?: boolean;
             name: string;
-            description: string | null;
+            description?: string;
             min: number;
             max: number;
             options: {