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
243 changes: 126 additions & 117 deletions src/commands/Community/app-admin.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SlashCommandBuilder, PermissionFlagsBits, PermissionsBitField, ChannelType, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ComponentType } from 'discord.js';
import { SlashCommandBuilder, PermissionFlagsBits, PermissionsBitField, ChannelType, ActionRowBuilder, ButtonBuilder, ButtonStyle, EmbedBuilder, ModalBuilder, TextInputBuilder, TextInputStyle, ComponentType, LabelBuilder, RoleSelectMenuBuilder } from 'discord.js';
import { createEmbed, errorEmbed, successEmbed } from '../../utils/embeds.js';
import { getColor } from '../../config/bot.js';
import { logger } from '../../utils/logger.js';
Expand Down Expand Up @@ -148,141 +148,150 @@ async function handleSetup(interaction) {
});
}

// Show modal for setting up a new application
// Build modal using LabelBuilder API with a native role select dropdown
const modal = new ModalBuilder()
.setCustomId('app_setup_modal')
.setTitle('Set Up New Application');

const rows = [
new ActionRowBuilder().addComponents(
new TextInputBuilder()
.setCustomId('app_name')
.setLabel('Application Name')
.setStyle(TextInputStyle.Short)
.setPlaceholder('e.g., Moderator, Helper, Developer')
.setMaxLength(50)
.setMinLength(1)
.setRequired(true),
),
new ActionRowBuilder().addComponents(
new TextInputBuilder()
.setCustomId('role_id')
.setLabel('Role ID')
.setStyle(TextInputStyle.Short)
.setPlaceholder('Right-click a role and copy its ID')
.setMaxLength(20)
.setMinLength(1)
.setRequired(true),
),
new ActionRowBuilder().addComponents(
new TextInputBuilder()
.setCustomId('app_question_1')
.setLabel('Question 1 (required)')
.setStyle(TextInputStyle.Short)
.setPlaceholder('Why do you want this role?')
.setMaxLength(100)
.setMinLength(1)
.setRequired(true),
),
new ActionRowBuilder().addComponents(
new TextInputBuilder()
.setCustomId('app_question_2')
.setLabel('Question 2 (optional)')
.setStyle(TextInputStyle.Short)
.setPlaceholder('What experience do you have?')
.setMaxLength(100)
.setRequired(false),
),
new ActionRowBuilder().addComponents(
new TextInputBuilder()
.setCustomId('app_question_3')
.setLabel('Question 3 (optional)')
.setStyle(TextInputStyle.Short)
.setMaxLength(100)
.setRequired(false),
),
];

modal.addComponents(...rows);

await interaction.showModal(modal);
const roleSelect = new RoleSelectMenuBuilder()
.setCustomId('role_id')
.setPlaceholder('Select the role users will apply for')
.setRequired(true);

const roleLabel = new LabelBuilder()
.setLabel('Application Role')
.setDescription('The role that users will be applying for')
.setRoleSelectMenuComponent(roleSelect);

const appNameInput = new TextInputBuilder()
.setCustomId('app_name')
.setStyle(TextInputStyle.Short)
.setPlaceholder('e.g., Moderator, Helper, Developer')
.setMaxLength(50)
.setMinLength(1)
.setRequired(true);

const appNameLabel = new LabelBuilder()
.setLabel('Application Name')
.setTextInputComponent(appNameInput);

const q1Input = new TextInputBuilder()
.setCustomId('app_question_1')
.setStyle(TextInputStyle.Short)
.setPlaceholder('Why do you want this role?')
.setMaxLength(100)
.setMinLength(1)
.setRequired(true);

const q1Label = new LabelBuilder()
.setLabel('Question 1 (required)')
.setTextInputComponent(q1Input);

const q2Input = new TextInputBuilder()
.setCustomId('app_question_2')
.setStyle(TextInputStyle.Short)
.setPlaceholder('What experience do you have?')
.setMaxLength(100)
.setRequired(false);

const q2Label = new LabelBuilder()
.setLabel('Question 2 (optional)')
.setTextInputComponent(q2Input);

const q3Input = new TextInputBuilder()
.setCustomId('app_question_3')
.setStyle(TextInputStyle.Short)
.setMaxLength(100)
.setRequired(false);

const q3Label = new LabelBuilder()
.setLabel('Question 3 (optional)')
.setTextInputComponent(q3Input);

modal.addLabelComponents(roleLabel, appNameLabel, q1Label, q2Label, q3Label);

try {
const submitted = await interaction.awaitModalSubmit({
time: 15 * 60 * 1000, // 15 minutes
filter: (i) =>
i.customId === 'app_setup_modal' &&
i.user.id === interaction.user.id,
});
await interaction.showModal(modal);

const appName = submitted.fields.getTextInputValue('app_name').trim();
const roleId = submitted.fields.getTextInputValue('role_id').trim();
const questions = [
submitted.fields.getTextInputValue('app_question_1').trim(),
submitted.fields.getTextInputValue('app_question_2').trim(),
submitted.fields.getTextInputValue('app_question_3').trim(),
].filter(q => q.length > 0);
const submitted = await interaction.awaitModalSubmit({
time: 15 * 60 * 1000, // 15 minutes
filter: (i) =>
i.customId === 'app_setup_modal' &&
i.user.id === interaction.user.id,
}).catch(() => null);

// Get the role to verify it exists
let role;
try {
role = await interaction.guild.roles.fetch(roleId);
} catch (error) {
await submitted.reply({
embeds: [errorEmbed('Invalid Role', 'The role ID you provided does not exist.')],
flags: ['Ephemeral'],
});
return;
}
if (!submitted) {
logger.info('App setup modal dismissed or timed out', { guildId: interaction.guild.id, userId: interaction.user.id });
return;
}

// Check if this role is already an application
const existingRoles = await getApplicationRoles(interaction.client, interaction.guild.id);
if (existingRoles.some(r => r.roleId === roleId)) {
await submitted.reply({
embeds: [errorEmbed('Already Configured', `The role ${role} is already configured as an application.`)],
flags: ['Ephemeral'],
});
return;
}
const appName = submitted.fields.getTextInputValue('app_name').trim();
const selectedRoles = submitted.fields.getSelectedRoles('role_id');
const roleId = selectedRoles.first()?.id;

// Add the role to applications with enabled status
existingRoles.push({
roleId: roleId,
name: appName,
enabled: true, // New applications start enabled
if (!roleId) {
await submitted.reply({
embeds: [errorEmbed('No Role Selected', 'You must select a role for the application.')],
flags: ['Ephemeral'],
});
return;
}

await saveApplicationRoles(interaction.client, interaction.guild.id, existingRoles);

// Enable the system
const settings = await getApplicationSettings(interaction.client, interaction.guild.id);
if (!settings.enabled) {
await ApplicationService.updateSettings(interaction.client, interaction.guild.id, { enabled: true });
}
const questions = [
submitted.fields.getTextInputValue('app_question_1').trim(),
submitted.fields.getTextInputValue('app_question_2').trim(),
submitted.fields.getTextInputValue('app_question_3').trim(),
].filter(q => q.length > 0);

// Save the questions for this specific role
await saveApplicationRoleSettings(interaction.client, interaction.guild.id, roleId, { questions });
// Get the role to verify it exists
const role = await interaction.guild.roles.fetch(roleId).catch(() => null);
if (!role) {
await submitted.reply({
embeds: [errorEmbed('Invalid Role', 'The selected role could not be found.')],
flags: ['Ephemeral'],
});
return;
}

// Check if this role is already an application
const existingRoles = await getApplicationRoles(interaction.client, interaction.guild.id);
if (existingRoles.some(r => r.roleId === roleId)) {
await submitted.reply({
embeds: [successEmbed(
'✅ Application Created',
`**${appName}** application has been created for ${role}.\n\nYou can customize the log channel, manager roles, questions, and retention period in the dashboard.`,
)],
embeds: [errorEmbed('Already Configured', `The role ${role} is already configured as an application.`)],
flags: ['Ephemeral'],
});
return;
}

// Auto-open dashboard with this app selected
setTimeout(() => {
appDashboard.execute(submitted, null, interaction.client, appName);
}, 500);
// Add the role to applications with enabled status
existingRoles.push({
roleId: roleId,
name: appName,
enabled: true, // New applications start enabled
});

} catch (error) {
if (error.message.includes('timeout')) {
logger.info('App setup modal timed out', { guildId: interaction.guild.id, userId: interaction.user.id });
return;
}
throw error;
await saveApplicationRoles(interaction.client, interaction.guild.id, existingRoles);

// Enable the system
const settings = await getApplicationSettings(interaction.client, interaction.guild.id);
if (!settings.enabled) {
await ApplicationService.updateSettings(interaction.client, interaction.guild.id, { enabled: true });
}

// Save the questions for this specific role
await saveApplicationRoleSettings(interaction.client, interaction.guild.id, roleId, { questions });

await submitted.reply({
embeds: [successEmbed(
'✅ Application Created',
`**${appName}** application has been created for ${role}.\n\nYou can customize the log channel, manager roles, questions, and retention period in the dashboard.`,
)],
flags: ['Ephemeral'],
});

// Auto-open dashboard with this app selected
setTimeout(() => {
appDashboard.execute(submitted, null, interaction.client, appName);
}, 500);
}


Expand Down
Loading
Loading