From a5f339be3a8a1729726bb5283e21683ebb187e2a Mon Sep 17 00:00:00 2001 From: CarelessInternet Date: Tue, 30 Dec 2025 18:45:55 +0000 Subject: [PATCH 1/2] refactor(bot): progress on less clutter editing --- .../src/commands/staff/bot-profile/command.ts | 61 ++---------------- .../src/commands/staff/bot-profile/helpers.ts | 63 +++++++++++++++++++ .../src/commands/staff/bot-profile/modal.ts | 29 +++++---- .../component.ts | 25 ++++++-- .../helpers.ts | 27 +++++++- .../configuration-automatic-threads/modal.ts | 16 +++-- .../subcommand.ts | 27 +------- .../configuration-user-forums/component.ts | 10 +-- .../configuration-user-forums/helpers.ts | 27 +++++++- .../staff/configuration-user-forums/modal.ts | 16 +++-- .../configuration-user-forums/subcommand.ts | 27 +------- .../component.ts | 38 ++++++----- .../configuration-welcome-farewell/modal.ts | 22 ++++--- apps/bot/src/i18n/en-GB/index.ts | 4 +- apps/bot/src/i18n/i18n-types.ts | 8 +-- apps/bot/src/i18n/sv-SE/index.ts | 4 +- packages/database/src/index.ts | 2 +- packages/database/src/schema.ts | 2 +- .../ThreadTicketActionsPermissionBitField.ts | 0 .../database/src/{utils => utility}/index.ts | 0 packages/djs-framework/src/index.ts | 2 +- .../{utils => utility}/InteractionTypes.ts | 0 .../src/{utils => utility}/customId.ts | 0 .../src/{utils => utility}/guards.ts | 0 .../src/{utils => utility}/index.ts | 0 .../src/{utils => utility}/permissions.ts | 0 .../src/{utils => utility}/responses.ts | 0 27 files changed, 233 insertions(+), 177 deletions(-) create mode 100644 apps/bot/src/commands/staff/bot-profile/helpers.ts rename packages/database/src/{utils => utility}/ThreadTicketActionsPermissionBitField.ts (100%) rename packages/database/src/{utils => utility}/index.ts (100%) rename packages/djs-framework/src/{utils => utility}/InteractionTypes.ts (100%) rename packages/djs-framework/src/{utils => utility}/customId.ts (100%) rename packages/djs-framework/src/{utils => utility}/guards.ts (100%) rename packages/djs-framework/src/{utils => utility}/index.ts (100%) rename packages/djs-framework/src/{utils => utility}/permissions.ts (100%) rename packages/djs-framework/src/{utils => utility}/responses.ts (100%) diff --git a/apps/bot/src/commands/staff/bot-profile/command.ts b/apps/bot/src/commands/staff/bot-profile/command.ts index 5ff82abb..da63bc4f 100644 --- a/apps/bot/src/commands/staff/bot-profile/command.ts +++ b/apps/bot/src/commands/staff/bot-profile/command.ts @@ -1,15 +1,7 @@ -import { Command, container, customId } from '@ticketer/djs-framework'; -import { - ActionRowBuilder, - HeadingLevel, - heading, - MessageFlags, - PermissionFlagsBits, - StringSelectMenuBuilder, - StringSelectMenuOptionBuilder, - TextDisplayBuilder, -} from 'discord.js'; +import { Command } from '@ticketer/djs-framework'; +import { PermissionFlagsBits } from 'discord.js'; import { getTranslations, translate } from '@/i18n'; +import { configurationMenu } from './helpers'; const dataTranslations = translate().commands['bot-profile'].data; @@ -21,51 +13,6 @@ export default class extends Command.Interaction { .setDefaultMemberPermissions(PermissionFlagsBits.ChangeNickname | PermissionFlagsBits.ManageGuild); public execute({ interaction }: Command.Context<'chat'>) { - const translations = translate(interaction.guildLocale).commands['bot-profile'].command.container; - - void interaction.reply({ - components: [ - container({ - builder: (cont) => - cont - .addTextDisplayComponents( - new TextDisplayBuilder().setContent(heading(translations.heading(), HeadingLevel.Three)), - ) - .addActionRowComponents( - new ActionRowBuilder().setComponents( - new StringSelectMenuBuilder() - .setCustomId(customId('bot_profile_menu')) - .setMinValues(1) - .setMaxValues(1) - .setPlaceholder(translations.menu.placeholder()) - .setOptions( - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿชช') - .setLabel(translations.menu.name.label()) - .setDescription(translations.menu.name.description()) - .setValue('name'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“”') - .setLabel(translations.menu.bio.label()) - .setDescription(translations.menu.bio.description()) - .setValue('bio'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ‘ค') - .setLabel(translations.menu.avatar.label()) - .setDescription(translations.menu.avatar.description()) - .setValue('avatar'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ–ผ๏ธ') - .setLabel(translations.menu.banner.label()) - .setDescription(translations.menu.banner.description()) - .setValue('banner'), - ), - ), - ), - client: interaction.client, - }), - ], - flags: [MessageFlags.IsComponentsV2], - }); + void interaction.reply(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } } diff --git a/apps/bot/src/commands/staff/bot-profile/helpers.ts b/apps/bot/src/commands/staff/bot-profile/helpers.ts new file mode 100644 index 00000000..d2fc8973 --- /dev/null +++ b/apps/bot/src/commands/staff/bot-profile/helpers.ts @@ -0,0 +1,63 @@ +import { container, customId } from '@ticketer/djs-framework'; +import { + ActionRowBuilder, + type Client, + HeadingLevel, + heading, + type InteractionReplyOptions, + type Locale, + MessageFlags, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, + TextDisplayBuilder, +} from 'discord.js'; +import { translate } from '@/i18n'; + +export function configurationMenu({ client, locale }: { client: Client; locale: Locale }) { + const translations = translate(locale).commands['bot-profile'].command.container; + + return { + components: [ + container({ + builder: (cont) => + cont + .addTextDisplayComponents( + new TextDisplayBuilder().setContent(heading(translations.heading(), HeadingLevel.Three)), + ) + .addActionRowComponents( + new ActionRowBuilder().setComponents( + new StringSelectMenuBuilder() + .setCustomId(customId('bot_profile_menu')) + .setMinValues(1) + .setMaxValues(1) + .setPlaceholder(translations.menu.placeholder()) + .setOptions( + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿชช') + .setLabel(translations.menu.name.label()) + .setDescription(translations.menu.name.description()) + .setValue('name'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“”') + .setLabel(translations.menu.bio.label()) + .setDescription(translations.menu.bio.description()) + .setValue('bio'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ‘ค') + .setLabel(translations.menu.avatar.label()) + .setDescription(translations.menu.avatar.description()) + .setValue('avatar'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ–ผ๏ธ') + .setLabel(translations.menu.banner.label()) + .setDescription(translations.menu.banner.description()) + .setValue('banner'), + ), + ), + ), + client, + }), + ], + flags: [MessageFlags.IsComponentsV2], + } satisfies InteractionReplyOptions; +} diff --git a/apps/bot/src/commands/staff/bot-profile/modal.ts b/apps/bot/src/commands/staff/bot-profile/modal.ts index b8056bf0..44826c80 100644 --- a/apps/bot/src/commands/staff/bot-profile/modal.ts +++ b/apps/bot/src/commands/staff/bot-profile/modal.ts @@ -1,4 +1,4 @@ -import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; +import { container, customId, DeferUpdate, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ContainerBuilder, codeBlock, @@ -13,6 +13,7 @@ import { } from 'discord.js'; import { prettifyError, z } from 'zod'; import { translate } from '@/i18n'; +import { configurationMenu } from './helpers'; export default class extends Modal.Interaction { public readonly customIds = [customId('bot_profile_menu_name')]; @@ -53,7 +54,7 @@ export default class extends Modal.Interaction { }); } - await interaction.deferReply(); + await interaction.deferUpdate(); let me = await interaction.guild.members.fetchMe(); const oldName = me.displayName; @@ -61,7 +62,7 @@ export default class extends Modal.Interaction { const guildTranslations = translate(interaction.guildLocale).commands['bot-profile'].command.modals.name.response .success; - return interaction.editReply({ + interaction.editReply({ components: [ container({ builder: (cont) => @@ -83,6 +84,7 @@ export default class extends Modal.Interaction { ], flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } } @@ -111,7 +113,7 @@ export class Bio extends Modal.Interaction { }); } - await interaction.deferReply(); + await interaction.deferUpdate(); await interaction.guild.members.editMe({ bio }); const guildTranslations = translate(interaction.guildLocale).commands['bot-profile'].command.modals.bio.response @@ -132,17 +134,18 @@ export class Bio extends Modal.Interaction { reply.addTextDisplayComponents(new TextDisplayBuilder().setContent(codeBlock(bio))); } - return interaction.editReply({ + interaction.editReply({ components: [container({ builder: reply, client: interaction.client })], flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } } export class Avatar extends Modal.Interaction { public readonly customIds = [customId('bot_profile_menu_avatar')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const avatar = interaction.fields.getUploadedFiles('avatar', false)?.at(0)?.url ?? ''; let me = await interaction.guild.members.fetchMe(); @@ -154,7 +157,7 @@ export class Avatar extends Modal.Interaction { const translatons = translate(interaction.locale).commands['bot-profile'].command.modals.avatar.response.errors .unknown; - return interaction.editReply({ + interaction.editReply({ embeds: [ userEmbedError({ client: interaction.client, @@ -164,12 +167,13 @@ export class Avatar extends Modal.Interaction { }), ], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } const translatons = translate(interaction.guildLocale).commands['bot-profile'].command.modals.avatar.response .success; - return interaction.editReply({ + interaction.editReply({ components: [ container({ builder: (cont) => @@ -191,13 +195,14 @@ export class Avatar extends Modal.Interaction { ], flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } } export class Banner extends Modal.Interaction { public readonly customIds = [customId('bot_profile_menu_banner')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const banner = interaction.fields.getUploadedFiles('banner', false)?.at(0)?.url ?? ''; // Force fetch due to caching issues. @@ -210,7 +215,7 @@ export class Banner extends Modal.Interaction { const translations = translate(interaction.locale).commands['bot-profile'].command.modals.banner.response.errors .unknown; - return interaction.editReply({ + interaction.editReply({ embeds: [ userEmbedError({ client: interaction.client, @@ -220,6 +225,7 @@ export class Banner extends Modal.Interaction { }), ], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } const translations = translate(interaction.locale).commands['bot-profile'].command.modals.banner.response.success; @@ -253,9 +259,10 @@ export class Banner extends Modal.Interaction { reply.addMediaGalleryComponents(builder); } - return interaction.editReply({ + interaction.editReply({ components: [container({ builder: reply, client: interaction.client })], flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp(configurationMenu({ client: interaction.client, locale: interaction.guildLocale })); } } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts index 30eac601..32f8e97a 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts @@ -17,7 +17,7 @@ import { import { ActionRowBuilder, channelMention, MessageFlags, RoleSelectMenuBuilder, roleMention } from 'discord.js'; import { prettifyError } from 'zod'; import { goToPage } from '@/utils'; -import { getConfigurations, openingMessageModal } from './helpers'; +import { configurationMenu, getConfigurations, openingMessageModal } from './helpers'; export default class extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_automatic_threads_configuration_menu')]; @@ -37,7 +37,7 @@ export default class extends Component.Interaction { const row = new ActionRowBuilder().setComponents(managersMenu); - return context.interaction.reply({ components: [row] }); + return context.interaction.update({ components: [row] }); } default: { return context.interaction.reply({ @@ -116,13 +116,27 @@ export class Managers extends Component.Interaction { public async execute({ interaction }: Component.Context<'role'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const managers = interaction.roles.map((role) => role.id); + const { + data: channelId, + error, + success, + } = automaticThreadsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); + + if (!success) { + return interaction.editReply({ + components: [], + embeds: [ + userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), + ], + }); + } await database .update(automaticThreadsConfigurations) .set({ managers }) .where( and( - eq(automaticThreadsConfigurations.channelId, dynamicValue), + eq(automaticThreadsConfigurations.channelId, channelId), eq(automaticThreadsConfigurations.guildId, interaction.guildId), ), ); @@ -131,12 +145,13 @@ export class Managers extends Component.Interaction { const embed = userEmbed(interaction) .setTitle('Updated the Automatic Threads Managers') .setDescription( - `${interaction.member} updated the managers of the automatic threads in ${channelMention(dynamicValue)} to: ${ + `${interaction.member} updated the managers of the automatic threads in ${channelMention(channelId)} to: ${ managers.length > 0 ? roles : 'none' }.`, ); - return interaction.editReply({ embeds: [embed], components: [] }); + interaction.editReply({ components: [], content: '', embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(channelId) }); } } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts index 5388e34d..9618df44 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/helpers.ts @@ -1,6 +1,7 @@ import { automaticThreadsConfigurations, database, desc, eq } from '@ticketer/database'; import { type Component, container, customId, type Subcommand, userEmbedError } from '@ticketer/djs-framework'; import { + ActionRowBuilder, bold, ChannelSelectMenuBuilder, ChannelType, @@ -13,6 +14,8 @@ import { roleMention, SeparatorBuilder, SeparatorSpacingSize, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, TextInputStyle, @@ -136,7 +139,29 @@ export async function openingMessageModal( const modal = new ModalBuilder() .setCustomId(customId('ticket_automatic_threads_configuration_opening_message')) .setTitle('Opening Message Title & Description') - .setLabelComponents([channelInput, titleInput, descriptionInput].filter((input) => !!input)); + .setLabelComponents(channelInput, titleInput, descriptionInput); return interaction.showModal(modal).catch(() => false); } + +export function configurationMenu(channelId: string) { + const selectMenu = new StringSelectMenuBuilder() + .setCustomId(customId('ticket_automatic_threads_configuration_menu', channelId)) + .setMinValues(1) + .setMaxValues(1) + .setPlaceholder('Edit one of the following automatic threads options:') + .setOptions( + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“”') + .setLabel('Message Title & Description') + .setDescription("Change the opening message's title and description.") + .setValue('message_title_description'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ›ก๏ธ') + .setLabel('Ticket Managers') + .setDescription('Choose the managers who are responsible for this text channel.') + .setValue('managers'), + ); + + return [new ActionRowBuilder().setComponents(selectMenu)]; +} diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts index 23d1849a..2b3275cb 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts @@ -3,15 +3,16 @@ import { automaticThreadsConfigurationsInsertSchema, database, } from '@ticketer/database'; -import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; +import { container, customId, DeferUpdate, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; import { automaticThreadsContainer } from '@/utils'; +import { configurationMenu } from './helpers'; -export class ModalInteraction extends Modal.Interaction { +export default class extends Modal.Interaction { public readonly customIds = [customId('ticket_automatic_threads_configuration_opening_message')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const channel = interaction.fields.getSelectedChannels('channel', true).at(0); @@ -35,11 +36,14 @@ export class ModalInteraction extends Modal.Interaction { }); if (!success) { - return interaction.editReply({ + interaction.editReply({ + components: [], + content: '', embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], }); + return interaction.followUp({ components: configurationMenu(channel.id) }); } await database @@ -57,7 +61,7 @@ export class ModalInteraction extends Modal.Interaction { }, }); - return interaction.editReply({ + interaction.editReply({ components: [ container({ builder: (cont) => @@ -83,7 +87,9 @@ export class ModalInteraction extends Modal.Interaction { client: interaction.client, }), ], + content: '', flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp({ components: configurationMenu(channel.id) }); } } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts index ee3b041e..b1e31abf 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/subcommand.ts @@ -1,7 +1,6 @@ import { automaticThreadsConfigurations, database, eq } from '@ticketer/database'; -import { customId, DeferReply, Subcommand, userEmbed, userEmbedError } from '@ticketer/djs-framework'; -import { ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from 'discord.js'; -import { getConfigurations, IsTextChannel, openingMessageModal } from './helpers'; +import { DeferReply, Subcommand, userEmbed, userEmbedError } from '@ticketer/djs-framework'; +import { configurationMenu, getConfigurations, IsTextChannel, openingMessageModal } from './helpers'; export default class extends Subcommand.Interaction { public readonly data = super.subcommand({ @@ -53,27 +52,7 @@ export class Edit extends Subcommand.Interaction { }); } - const selectMenu = new StringSelectMenuBuilder() - .setCustomId(customId('ticket_automatic_threads_configuration_menu', channel.id)) - .setMinValues(1) - .setMaxValues(1) - .setPlaceholder('Edit one of the following automatic threads options:') - .setOptions( - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“”') - .setLabel('Message Title & Description') - .setDescription("Change the opening message's title and description.") - .setValue('message_title_description'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ›ก๏ธ') - .setLabel('Ticket Managers') - .setDescription('Choose the managers who are responsible for this text channel.') - .setValue('managers'), - ); - - const row = new ActionRowBuilder().setComponents(selectMenu); - - return interaction.editReply({ components: [row], content: channel.toString() }); + return interaction.editReply({ components: configurationMenu(channel.id), content: channel.toString() }); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/component.ts b/apps/bot/src/commands/staff/configuration-user-forums/component.ts index b81a764b..3b388675 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/component.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/component.ts @@ -2,7 +2,6 @@ import { and, database, eq, userForumsConfigurations, userForumsConfigurationsSe import { Component, customId, - DeferReply, DeferUpdate, dynamicCustomId, extractCustomId, @@ -12,7 +11,7 @@ import { import { ActionRowBuilder, channelMention, MessageFlags, RoleSelectMenuBuilder, roleMention } from 'discord.js'; import { prettifyError } from 'zod'; import { goToPage } from '@/utils'; -import { getConfigurations, openingMessageModal } from './helpers'; +import { configurationMenu, getConfigurations, openingMessageModal } from './helpers'; export default class extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_user_forums_configuration_menu')]; @@ -32,7 +31,7 @@ export default class extends Component.Interaction { const row = new ActionRowBuilder().setComponents(managersMenu); - return context.interaction.reply({ components: [row] }); + return context.interaction.update({ components: [row] }); } default: { return context.interaction.reply({ @@ -107,7 +106,7 @@ export default class extends Component.Interaction { export class Managers extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_user_forums_configuration_managers')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Component.Context<'role'>) { const managers = interaction.roles.map((role) => role.id); const { dynamicValue } = extractCustomId(interaction.customId, true); @@ -145,7 +144,8 @@ export class Managers extends Component.Interaction { }.`, ); - return interaction.editReply({ components: [], embeds: [embed] }); + interaction.editReply({ components: [], content: '', embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(channelId) }); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts b/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts index ccb8e89a..230924c2 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/helpers.ts @@ -1,6 +1,7 @@ import { database, desc, eq, userForumsConfigurations } from '@ticketer/database'; import { type Command, type Component, container, customId, userEmbedError } from '@ticketer/djs-framework'; import { + ActionRowBuilder, bold, ChannelSelectMenuBuilder, ChannelType, @@ -13,6 +14,8 @@ import { roleMention, SeparatorBuilder, SeparatorSpacingSize, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, TextInputStyle, @@ -138,7 +141,29 @@ export async function openingMessageModal( const modal = new ModalBuilder() .setCustomId(customId('ticket_user_forums_configuration_opening_message')) .setTitle('Opening Message Title & Description') - .setLabelComponents([channelInput, titleInput, descriptionInput].filter((input) => !!input)); + .setLabelComponents(channelInput, titleInput, descriptionInput); return interaction.showModal(modal).catch(() => false); } + +export function configurationMenu(channelId: string) { + const selectMenu = new StringSelectMenuBuilder() + .setCustomId(customId('ticket_user_forums_configuration_menu', channelId)) + .setMinValues(1) + .setMaxValues(1) + .setPlaceholder('Edit one of the following user forum options:') + .setOptions( + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“”') + .setLabel('Message Title & Description') + .setDescription("Change the opening message's title and description.") + .setValue('message_title_description'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ›ก๏ธ') + .setLabel('Ticket Managers') + .setDescription('Choose the managers who are responsible for this forum.') + .setValue('managers'), + ); + + return [new ActionRowBuilder().setComponents(selectMenu)]; +} diff --git a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts index 6180a90d..18e31efe 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts @@ -1,13 +1,14 @@ import { database, userForumsConfigurations, userForumsConfigurationsInsertSchema } from '@ticketer/database'; -import { container, customId, DeferReply, Modal, userEmbedError } from '@ticketer/djs-framework'; +import { container, customId, DeferUpdate, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; import { userForumsContainer } from '@/utils'; +import { configurationMenu } from './helpers'; -export class ModalInteraction extends Modal.Interaction { +export default class extends Modal.Interaction { public readonly customIds = [customId('ticket_user_forums_configuration_opening_message')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const channel = interaction.fields.getSelectedChannels('channel', true).at(0); @@ -31,11 +32,14 @@ export class ModalInteraction extends Modal.Interaction { }); if (!success) { - return interaction.editReply({ + interaction.editReply({ + components: [], + content: '', embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], }); + return interaction.followUp({ components: configurationMenu(channel.id) }); } await database @@ -53,7 +57,7 @@ export class ModalInteraction extends Modal.Interaction { }, }); - return interaction.editReply({ + interaction.editReply({ components: [ container({ builder: (cont) => @@ -79,7 +83,9 @@ export class ModalInteraction extends Modal.Interaction { client: interaction.client, }), ], + content: '', flags: [MessageFlags.IsComponentsV2], }); + return interaction.followUp({ components: configurationMenu(channel.id) }); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts b/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts index 4b75ea25..847abdcc 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/subcommand.ts @@ -1,7 +1,6 @@ import { database, eq, userForumsConfigurations } from '@ticketer/database'; -import { customId, DeferReply, Subcommand, userEmbed, userEmbedError } from '@ticketer/djs-framework'; -import { ActionRowBuilder, StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from 'discord.js'; -import { getConfigurations, IsForumChannel, openingMessageModal } from './helpers'; +import { DeferReply, Subcommand, userEmbed, userEmbedError } from '@ticketer/djs-framework'; +import { configurationMenu, getConfigurations, IsForumChannel, openingMessageModal } from './helpers'; export default class extends Subcommand.Interaction { public readonly data = super.subcommand({ @@ -53,27 +52,7 @@ export class Edit extends Subcommand.Interaction { }); } - const selectMenu = new StringSelectMenuBuilder() - .setCustomId(customId('ticket_user_forums_configuration_menu', channel.id)) - .setMinValues(1) - .setMaxValues(1) - .setPlaceholder('Edit one of the following user forum options:') - .setOptions( - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“”') - .setLabel('Message Title & Description') - .setDescription("Change the opening message's title and description.") - .setValue('message_title_description'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ›ก๏ธ') - .setLabel('Ticket Managers') - .setDescription('Choose the managers who are responsible for this forum.') - .setValue('managers'), - ); - - const row = new ActionRowBuilder().setComponents(selectMenu); - - return interaction.editReply({ components: [row], content: channel.toString() }); + return interaction.editReply({ components: configurationMenu(channel.id), content: channel.toString() }); } } diff --git a/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts b/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts index e26f07d6..0030a1f6 100644 --- a/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts +++ b/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts @@ -1,13 +1,5 @@ import { database, eq, not, welcomeAndFarewell } from '@ticketer/database'; -import { - Component, - customId, - DeferReply, - DeferUpdate, - extractCustomId, - userEmbed, - userEmbedError, -} from '@ticketer/djs-framework'; +import { Component, customId, DeferUpdate, extractCustomId, userEmbed, userEmbedError } from '@ticketer/djs-framework'; import { ActionRowBuilder, ChannelSelectMenuBuilder, @@ -19,7 +11,7 @@ import { TextInputBuilder, TextInputStyle, } from 'discord.js'; -import type { InsertWithoutGuildId } from './helpers'; +import { configurationMenu, type InsertWithoutGuildId } from './helpers'; export default class extends Component.Interaction { public readonly customIds = [customId('welcome_configuration'), customId('farewell_configuration')]; @@ -40,7 +32,7 @@ export default class extends Component.Interaction { const channelRow = new ActionRowBuilder().setComponents(channelSelectMenu); - return interaction.reply({ components: [channelRow] }); + return interaction.update({ components: [channelRow] }); } case 'title': { const titleInput = new LabelBuilder() @@ -91,7 +83,7 @@ export default class extends Component.Interaction { const rolesRow = new ActionRowBuilder().setComponents(rolesSelectMenu); - return interaction.reply({ components: [rolesRow] }); + return interaction.update({ components: [rolesRow] }); } case 'enabled': { return this.welcomeAndFarewellConfigurationToggle({ interaction }); @@ -114,7 +106,7 @@ export default class extends Component.Interaction { text.replace(/./, (character) => character.toUpperCase()) as Capitalize; } - @DeferReply() + @DeferUpdate private async welcomeAndFarewellConfigurationToggle({ interaction }: Component.Context<'string'>) { const { farewellEnabled, welcomeEnabled } = welcomeAndFarewell; const { guildId } = interaction; @@ -139,10 +131,11 @@ export default class extends Component.Interaction { farewellEnabled: welcomeAndFarewell.farewellEnabled, }) .from(welcomeAndFarewell) - .where(eq(welcomeAndFarewell.guildId, interaction.guildId)); + .where(eq(welcomeAndFarewell.guildId, guildId)); if (!row) { - return interaction.editReply({ + interaction.editReply({ + components: [], embeds: [ userEmbedError({ client: interaction.client, @@ -151,6 +144,8 @@ export default class extends Component.Interaction { }), ], }); + + return interaction.followUp({ components: configurationMenu() }); } const valueAsBoolean = type === 'welcome' ? row.welcomeEnabled : row.farewellEnabled; @@ -160,7 +155,8 @@ export default class extends Component.Interaction { `${interaction.member} has toggled the ${type} option to ${valueAsBoolean ? 'enabled' : 'disabled'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu() }); } } @@ -187,9 +183,10 @@ export class Channel extends Component.Interaction { const embed = userEmbed(interaction) .setTitle('Updated the Welcome/Farewell Configuration') - .setDescription(`${interaction.member} updated the ${type} channel to ${channel}`); + .setDescription(`${interaction.member} updated the ${type} channel to ${channel}.`); - return interaction.editReply({ embeds: [embed], components: [] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu() }); } } @@ -212,9 +209,10 @@ export class Roles extends Component.Interaction { .setDescription( `${interaction.member} updated the roles given to new members: ${ welcomeNewMemberRoles.length > 0 ? roles : 'none' - }`, + }.`, ); - return interaction.editReply({ embeds: [embed], components: [] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu() }); } } diff --git a/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts b/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts index 5ce38033..ed3fb2ad 100644 --- a/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts +++ b/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts @@ -1,13 +1,13 @@ import { database, welcomeAndFarewell, welcomeAndFarewellInsertSchema } from '@ticketer/database'; -import { customId, DeferReply, Modal, userEmbed, userEmbedError } from '@ticketer/djs-framework'; +import { customId, DeferUpdate, Modal, userEmbed, userEmbedError } from '@ticketer/djs-framework'; import { inlineCode } from 'discord.js'; import { prettifyError } from 'zod'; -import type { InsertWithoutGuildId } from './helpers'; +import { configurationMenu, type InsertWithoutGuildId } from './helpers'; export default class extends Modal.Interaction { public readonly customIds = [customId('welcome_message_title'), customId('farewell_message_title')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const type = interaction.customId.includes('welcome') ? 'welcome' : 'farewell'; const rawTitle = interaction.fields.getTextInputValue('message_title'); @@ -20,7 +20,8 @@ export default class extends Modal.Interaction { : welcomeAndFarewellInsertSchema.shape.farewellMessageTitle.safeParse(rawTitle); if (!success) { - return interaction.editReply({ + interaction.editReply({ + components: [], embeds: [ userEmbedError({ client: interaction.client, @@ -29,6 +30,7 @@ export default class extends Modal.Interaction { }), ], }); + return interaction.followUp({ components: configurationMenu() }); } const titleDatabaseValue: InsertWithoutGuildId = @@ -46,14 +48,15 @@ export default class extends Modal.Interaction { (title ? `:\n\n${inlineCode(title)}` : ' the default title.'), ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu() }); } } export class Description extends Modal.Interaction { public readonly customIds = [customId('welcome_message_description'), customId('farewell_message_description')]; - @DeferReply() + @DeferUpdate public async execute({ interaction }: Modal.Context) { const type = interaction.customId.includes('welcome') ? 'welcome' : 'farewell'; const rawDescription = interaction.fields.getTextInputValue('message_description'); @@ -66,7 +69,8 @@ export class Description extends Modal.Interaction { : welcomeAndFarewellInsertSchema.shape.farewellMessageDescription.safeParse(rawDescription); if (!success) { - return interaction.editReply({ + interaction.editReply({ + components: [], embeds: [ userEmbedError({ client: interaction.client, @@ -75,6 +79,7 @@ export class Description extends Modal.Interaction { }), ], }); + return interaction.followUp({ components: configurationMenu() }); } const descriptionDatabaseValue: InsertWithoutGuildId = @@ -92,6 +97,7 @@ export class Description extends Modal.Interaction { (description ? `:\n\n${inlineCode(description)}` : ' the default description.'), ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu() }); } } diff --git a/apps/bot/src/i18n/en-GB/index.ts b/apps/bot/src/i18n/en-GB/index.ts index d5921ca8..0e80e34e 100644 --- a/apps/bot/src/i18n/en-GB/index.ts +++ b/apps/bot/src/i18n/en-GB/index.ts @@ -380,11 +380,11 @@ const en_GB = { fields: [ { name: 'Ping', - value: 'โŒ› {ms:number}ms', + value: 'โŒ› {ms:number} ms', }, { name: 'Latency', - value: '๐Ÿ“ Roughly {ms:number}ms', + value: '๐Ÿ“ Roughly {ms:number} ms', }, { name: 'WebSocket Status', diff --git a/apps/bot/src/i18n/i18n-types.ts b/apps/bot/src/i18n/i18n-types.ts index c5a3207d..e22b4011 100644 --- a/apps/bot/src/i18n/i18n-types.ts +++ b/apps/bot/src/i18n/i18n-types.ts @@ -833,7 +833,7 @@ type RootTranslation = { */ name: string /** - * โŒ›โ€‹ โ€‹{โ€‹mโ€‹sโ€‹}โ€‹mโ€‹s + * โŒ›โ€‹ โ€‹{โ€‹mโ€‹sโ€‹}โ€‹ โ€‹mโ€‹s * @param {number} ms */ value: RequiredParams<'ms'> @@ -844,7 +844,7 @@ type RootTranslation = { */ name: string /** - * ๏ฟฝโ€‹๏ฟฝโ€‹ โ€‹Rโ€‹oโ€‹uโ€‹gโ€‹hโ€‹lโ€‹yโ€‹ โ€‹{โ€‹mโ€‹sโ€‹}โ€‹mโ€‹s + * ๏ฟฝโ€‹๏ฟฝโ€‹ โ€‹Rโ€‹oโ€‹uโ€‹gโ€‹hโ€‹lโ€‹yโ€‹ โ€‹{โ€‹mโ€‹sโ€‹}โ€‹ โ€‹mโ€‹s * @param {number} ms */ value: RequiredParams<'ms'> @@ -2927,7 +2927,7 @@ export type TranslationFunctions = { */ name: () => LocalizedString /** - * โŒ› {ms}ms + * โŒ› {ms} ms */ value: (arg: { ms: number }) => LocalizedString } @@ -2937,7 +2937,7 @@ export type TranslationFunctions = { */ name: () => LocalizedString /** - * ๐Ÿ“ Roughly {ms}ms + * ๐Ÿ“ Roughly {ms} ms */ value: (arg: { ms: number }) => LocalizedString } diff --git a/apps/bot/src/i18n/sv-SE/index.ts b/apps/bot/src/i18n/sv-SE/index.ts index 5745429d..24a98712 100644 --- a/apps/bot/src/i18n/sv-SE/index.ts +++ b/apps/bot/src/i18n/sv-SE/index.ts @@ -380,11 +380,11 @@ const sv_SE = { fields: [ { name: 'Ping', - value: 'โŒ› {ms}ms', + value: 'โŒ› {ms} ms', }, { name: 'Latens', - value: '๐Ÿ“ Ungefรคr {ms}ms', + value: '๐Ÿ“ Ungefรคr {ms} ms', }, { name: 'WebSocket Status', diff --git a/packages/database/src/index.ts b/packages/database/src/index.ts index a3b7d894..92aa456a 100644 --- a/packages/database/src/index.ts +++ b/packages/database/src/index.ts @@ -22,4 +22,4 @@ export function migrate() { export * from 'drizzle-orm'; export { type MySqlSelect, unionAll } from 'drizzle-orm/mysql-core'; export * from './schema'; -export { ThreadTicketActionsPermissionBitField } from './utils/ThreadTicketActionsPermissionBitField'; +export { ThreadTicketActionsPermissionBitField } from './utility/ThreadTicketActionsPermissionBitField'; diff --git a/packages/database/src/schema.ts b/packages/database/src/schema.ts index 4a8fd89c..6e034d3b 100644 --- a/packages/database/src/schema.ts +++ b/packages/database/src/schema.ts @@ -17,7 +17,7 @@ import { jsonWithParsing, snowflake, snowflakeRequiredParser, -} from './utils'; +} from './utility'; export const guildBlacklists = mysqlTable( 'guildBlacklists', diff --git a/packages/database/src/utils/ThreadTicketActionsPermissionBitField.ts b/packages/database/src/utility/ThreadTicketActionsPermissionBitField.ts similarity index 100% rename from packages/database/src/utils/ThreadTicketActionsPermissionBitField.ts rename to packages/database/src/utility/ThreadTicketActionsPermissionBitField.ts diff --git a/packages/database/src/utils/index.ts b/packages/database/src/utility/index.ts similarity index 100% rename from packages/database/src/utils/index.ts rename to packages/database/src/utility/index.ts diff --git a/packages/djs-framework/src/index.ts b/packages/djs-framework/src/index.ts index e2859061..d311109f 100644 --- a/packages/djs-framework/src/index.ts +++ b/packages/djs-framework/src/index.ts @@ -1,3 +1,3 @@ export * from './classes'; export * from './decorators'; -export * from './utils'; +export * from './utility'; diff --git a/packages/djs-framework/src/utils/InteractionTypes.ts b/packages/djs-framework/src/utility/InteractionTypes.ts similarity index 100% rename from packages/djs-framework/src/utils/InteractionTypes.ts rename to packages/djs-framework/src/utility/InteractionTypes.ts diff --git a/packages/djs-framework/src/utils/customId.ts b/packages/djs-framework/src/utility/customId.ts similarity index 100% rename from packages/djs-framework/src/utils/customId.ts rename to packages/djs-framework/src/utility/customId.ts diff --git a/packages/djs-framework/src/utils/guards.ts b/packages/djs-framework/src/utility/guards.ts similarity index 100% rename from packages/djs-framework/src/utils/guards.ts rename to packages/djs-framework/src/utility/guards.ts diff --git a/packages/djs-framework/src/utils/index.ts b/packages/djs-framework/src/utility/index.ts similarity index 100% rename from packages/djs-framework/src/utils/index.ts rename to packages/djs-framework/src/utility/index.ts diff --git a/packages/djs-framework/src/utils/permissions.ts b/packages/djs-framework/src/utility/permissions.ts similarity index 100% rename from packages/djs-framework/src/utils/permissions.ts rename to packages/djs-framework/src/utility/permissions.ts diff --git a/packages/djs-framework/src/utils/responses.ts b/packages/djs-framework/src/utility/responses.ts similarity index 100% rename from packages/djs-framework/src/utils/responses.ts rename to packages/djs-framework/src/utility/responses.ts From d5f720b2386492466fa7442dffd9f559072787c3 Mon Sep 17 00:00:00 2001 From: CarelessInternet Date: Thu, 1 Jan 2026 23:21:24 +0000 Subject: [PATCH 2/2] refactor(bot): less clutter on configuration edits --- apps/bot/package.json | 2 +- .../component.ts | 62 +++---- .../configuration-automatic-threads/modal.ts | 14 +- .../configuration-ticket-threads/component.ts | 71 ++++---- .../configuration-ticket-threads/helpers.ts | 75 ++++++++ .../configuration-ticket-threads/modal.ts | 32 ++-- .../subcommand.ts | 96 +--------- .../configuration-user-forums/component.ts | 59 +++--- .../staff/configuration-user-forums/modal.ts | 14 +- .../component.ts | 5 +- .../configuration-welcome-farewell/modal.ts | 16 +- apps/website/package.json | 2 +- package.json | 2 +- packages/database/package.json | 2 +- packages/env/package.json | 2 +- pnpm-lock.yaml | 172 +++++++++--------- 16 files changed, 317 insertions(+), 309 deletions(-) diff --git a/apps/bot/package.json b/apps/bot/package.json index 4a44ed0b..9d00bdf2 100644 --- a/apps/bot/package.json +++ b/apps/bot/package.json @@ -20,7 +20,7 @@ "discord.js": "^14.25.1", "tsx": "^4.21.0", "typesafe-i18n": "^5.26.2", - "zod": "^4.2.1" + "zod": "^4.3.4" }, "devDependencies": { "@types/node": "^25.0.3" diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts index 32f8e97a..ffeb5e36 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/component.ts @@ -8,7 +8,6 @@ import { import { Component, customId, - DeferUpdate, dynamicCustomId, extractCustomId, userEmbed, @@ -63,18 +62,16 @@ export default class extends Component.Interaction { } = automaticThreadsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); if (!success) { - return context.interaction - .reply({ - embeds: [ - userEmbedError({ - client: context.interaction.client, - description: prettifyError(error), - member: context.interaction.member, - }), - ], - flags: [MessageFlags.Ephemeral], - }) - .catch(() => false); + return context.interaction.reply({ + embeds: [ + userEmbedError({ + client: context.interaction.client, + description: prettifyError(error), + member: context.interaction.member, + }), + ], + flags: [MessageFlags.Ephemeral], + }); } const [row] = await database @@ -91,18 +88,19 @@ export default class extends Component.Interaction { ); if (!row) { - return context.interaction - .reply({ - embeds: [ - userEmbedError({ - client: context.interaction.client, - description: 'No automatic threads configuration for the channel could be found.', - member: context.interaction.member, - }), - ], - flags: [MessageFlags.Ephemeral], - }) - .catch(() => false); + context.interaction.editReply({ + embeds: [ + userEmbedError({ + client: context.interaction.client, + description: 'No automatic threads configuration for the channel could be found.', + member: context.interaction.member, + }), + ], + }); + return context.interaction.followUp({ + components: configurationMenu(channelId), + content: context.interaction.message.content, + }); } void openingMessageModal(context, { channelId, ...row }); @@ -112,7 +110,6 @@ export default class extends Component.Interaction { export class Managers extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_automatic_threads_configuration_managers')]; - @DeferUpdate public async execute({ interaction }: Component.Context<'role'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const managers = interaction.roles.map((role) => role.id); @@ -123,14 +120,16 @@ export class Managers extends Component.Interaction { } = automaticThreadsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); if (!success) { - return interaction.editReply({ + return interaction.reply({ components: [], embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferUpdate(); await database .update(automaticThreadsConfigurations) .set({ managers }) @@ -151,7 +150,7 @@ export class Managers extends Component.Interaction { ); interaction.editReply({ components: [], content: '', embeds: [embed] }); - return interaction.followUp({ components: configurationMenu(channelId) }); + return interaction.followUp({ components: configurationMenu(channelId), content: interaction.message.content }); } } @@ -161,12 +160,11 @@ export class Overview extends Component.Interaction { dynamicCustomId('ticket_automatic_threads_view_next'), ]; - @DeferUpdate - public execute(context: Component.Context<'button'>) { + public async execute(context: Component.Context<'button'>) { const { success, error, page } = goToPage(context.interaction); if (!success) { - return context.interaction.editReply({ + return context.interaction.reply({ components: [], embeds: [ userEmbedError({ @@ -175,9 +173,11 @@ export class Overview extends Component.Interaction { member: context.interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } + await context.interaction.deferUpdate(); void getConfigurations(context, page); } } diff --git a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts index 2b3275cb..269b1600 100644 --- a/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts +++ b/apps/bot/src/commands/staff/configuration-automatic-threads/modal.ts @@ -3,7 +3,7 @@ import { automaticThreadsConfigurationsInsertSchema, database, } from '@ticketer/database'; -import { container, customId, DeferUpdate, Modal, userEmbedError } from '@ticketer/djs-framework'; +import { container, customId, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; import { automaticThreadsContainer } from '@/utils'; @@ -12,12 +12,11 @@ import { configurationMenu } from './helpers'; export default class extends Modal.Interaction { public readonly customIds = [customId('ticket_automatic_threads_configuration_opening_message')]; - @DeferUpdate public async execute({ interaction }: Modal.Context) { const channel = interaction.fields.getSelectedChannels('channel', true).at(0); if (channel?.type !== ChannelType.GuildText) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, @@ -25,6 +24,7 @@ export default class extends Modal.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } @@ -36,16 +36,18 @@ export default class extends Modal.Interaction { }); if (!success) { - interaction.editReply({ + interaction.reply({ components: [], content: '', embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); - return interaction.followUp({ components: configurationMenu(channel.id) }); + return interaction.followUp({ components: configurationMenu(channel.id), content: interaction.message?.content }); } + await interaction.deferUpdate(); await database .insert(automaticThreadsConfigurations) .values({ @@ -90,6 +92,6 @@ export default class extends Modal.Interaction { content: '', flags: [MessageFlags.IsComponentsV2], }); - return interaction.followUp({ components: configurationMenu(channel.id) }); + return interaction.followUp({ components: configurationMenu(channel.id), content: interaction.message?.content }); } } diff --git a/apps/bot/src/commands/staff/configuration-ticket-threads/component.ts b/apps/bot/src/commands/staff/configuration-ticket-threads/component.ts index 68793bd4..ed024f0e 100644 --- a/apps/bot/src/commands/staff/configuration-ticket-threads/component.ts +++ b/apps/bot/src/commands/staff/configuration-ticket-threads/component.ts @@ -11,7 +11,6 @@ import { import { Component, customId, - DeferReply, DeferUpdate, dynamicCustomId, extractCustomId, @@ -35,7 +34,7 @@ import { } from 'discord.js'; import { prettifyError } from 'zod'; import { goToPage, ThreadTicketing } from '@/utils'; -import { categoryFieldsModal, getCategories, HasGlobalConfiguration } from './helpers'; +import { categoryFieldsModal, configurationMenu, getCategories, HasGlobalConfiguration } from './helpers'; export default class extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_threads_category_configuration')]; @@ -57,7 +56,7 @@ export default class extends Component.Interaction { const row = new ActionRowBuilder().setComponents(managersMenu); - return interaction.reply({ components: [row] }); + return interaction.update({ components: [row] }); } case 'channel': case 'logs_channel': { @@ -74,7 +73,7 @@ export default class extends Component.Interaction { const row = new ActionRowBuilder().setComponents(channelMenu); - return interaction.reply({ components: [row] }); + return interaction.update({ components: [row] }); } case 'message_title_description': { return this.messageTitleDescriptionValues({ interaction }); @@ -117,7 +116,7 @@ export default class extends Component.Interaction { const row = new ActionRowBuilder().setComponents(selectMenu); - return interaction.reply({ components: [row] }); + return interaction.update({ components: [row] }); } case 'private': case 'notification': { @@ -257,7 +256,6 @@ export default class extends Component.Interaction { return interaction.showModal(modal).catch(() => false); } - @DeferReply() private async privateAndNotification({ interaction }: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const type = interaction.values.at(0)?.includes('private') ? 'private threads' : 'thread notification'; @@ -268,13 +266,15 @@ export default class extends Component.Interaction { } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(dynamicValue)); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferUpdate(); await database .update(ticketThreadsCategories) .set( @@ -311,10 +311,10 @@ export default class extends Component.Interaction { `${interaction.member} has toggled the ${type} option to ${valueAsBoolean ? 'enabled' : 'disabled'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } - @DeferReply() private async silentPings({ interaction }: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const { @@ -324,13 +324,15 @@ export default class extends Component.Interaction { } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(dynamicValue)); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferUpdate(); await database .update(ticketThreadsCategories) .set({ @@ -361,10 +363,10 @@ export default class extends Component.Interaction { `${interaction.member} has toggled the silent pings option to ${row.silentPings ? 'enabled' : 'disabled'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } - @DeferReply() private async skipModals({ interaction }: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const { @@ -374,13 +376,15 @@ export default class extends Component.Interaction { } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(dynamicValue)); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferUpdate(); await database .update(ticketThreadsCategories) .set({ @@ -411,7 +415,8 @@ export default class extends Component.Interaction { `${interaction.member} has toggled the skip modal option to ${row.skipModal ? 'enabled' : 'disabled'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } private async threadTitleValues({ interaction }: Component.Context<'string'>) { @@ -427,6 +432,7 @@ export default class extends Component.Interaction { embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } @@ -444,6 +450,7 @@ export default class extends Component.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } @@ -467,7 +474,6 @@ export default class extends Component.Interaction { return interaction.showModal(modal).catch(() => false); } - @DeferReply() private async ticketTitleDescription({ interaction }: Component.Context<'string'>) { const { dynamicValue } = extractCustomId(interaction.customId, true); const { @@ -477,13 +483,15 @@ export default class extends Component.Interaction { } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(dynamicValue)); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferUpdate(); await database .update(ticketThreadsCategories) .set({ @@ -514,7 +522,8 @@ export default class extends Component.Interaction { `${interaction.member} has toggled the ticket title and description option to ${row.titleAndDescriptionRequired ? 'required' : 'optional'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } } @@ -558,7 +567,8 @@ export class Channel extends Component.Interaction { .setTitle('Updated the Thread Ticket Category') .setDescription(`${interaction.member} updated the ${type} to ${channel}.`); - return interaction.editReply({ components: [], embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } } @@ -593,7 +603,7 @@ export class Managers extends Component.Interaction { const roles = managers.map((id) => roleMention(id)).join(', '); - return interaction.editReply({ + interaction.editReply({ components: [], embeds: [ userEmbed(interaction) @@ -603,6 +613,7 @@ export class Managers extends Component.Interaction { ), ], }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } } @@ -628,20 +639,6 @@ export class AuthorActions extends Component.Interaction { }); } - const value = interaction.values.at(0); - - if (!value) { - return interaction.editReply({ - embeds: [ - userEmbedError({ - client: interaction.client, - description: 'The selected value could not be found.', - member: interaction.member, - }), - ], - }); - } - const [row] = await database .select() .from(ticketThreadsCategories) @@ -660,6 +657,7 @@ export class AuthorActions extends Component.Interaction { } const authorPermissions = new ThreadTicketActionsPermissionBitField(row.allowedAuthorActions); + const value = interaction.values.at(0); let enabled = false; for (const [name, flag] of ThreadTicketing.actionsAsKeyAndFlagsMap) { @@ -671,7 +669,8 @@ export class AuthorActions extends Component.Interaction { await authorPermissions.updateAuthorPermissions(row.id, row.guildId); - return interaction.editReply({ + interaction.editReply({ + components: [], embeds: [ userEmbed(interaction) .setTitle('Updated the Thread Ticket Category') @@ -680,6 +679,7 @@ export class AuthorActions extends Component.Interaction { ), ], }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } } @@ -726,7 +726,7 @@ export class Delete extends Component.Interaction { ); } - return interaction.editReply({ + interaction.editReply({ components: [], embeds: [ userEmbed(interaction) @@ -738,6 +738,7 @@ export class Delete extends Component.Interaction { ), ], }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message.embeds }); } } diff --git a/apps/bot/src/commands/staff/configuration-ticket-threads/helpers.ts b/apps/bot/src/commands/staff/configuration-ticket-threads/helpers.ts index 58f0e645..9f5985ed 100644 --- a/apps/bot/src/commands/staff/configuration-ticket-threads/helpers.ts +++ b/apps/bot/src/commands/staff/configuration-ticket-threads/helpers.ts @@ -8,6 +8,7 @@ import { userEmbedError, } from '@ticketer/djs-framework'; import { + ActionRowBuilder, bold, channelMention, HeadingLevel, @@ -19,6 +20,8 @@ import { roleMention, SeparatorBuilder, SeparatorSpacingSize, + StringSelectMenuBuilder, + StringSelectMenuOptionBuilder, TextDisplayBuilder, TextInputBuilder, TextInputStyle, @@ -210,3 +213,75 @@ export async function categoryFieldsModal( return context.interaction.showModal(modal).catch(() => false); } + +export function configurationMenu(categoryId: number) { + const categoriesMenu = new StringSelectMenuBuilder() + .setCustomId(customId('ticket_threads_category_configuration', categoryId)) + .setMinValues(1) + .setMaxValues(1) + .setPlaceholder('Edit one of the following ticket category options:') + .setOptions( + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“ฐ') + .setLabel('Emoji, Title, & Description') + .setDescription('Change the emoji, title, and description used for this category.') + .setValue('emoji_title_description'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ›ก๏ธ') + .setLabel('Ticket Managers') + .setDescription('Choose the managers who are responsible for this category.') + .setValue('managers'), + new StringSelectMenuOptionBuilder() + .setEmoji('#๏ธโƒฃ') + .setLabel('Channel') + .setDescription('Change the channel where tickets of this category go.') + .setValue('channel'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“œ') + .setLabel('Logs Channel') + .setDescription('Change the channel where logs get sent during ticket activity for the category.') + .setValue('logs_channel'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“”') + .setLabel('Message Title & Description') + .setDescription("Change the opening message's title and description.") + .setValue('message_title_description'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿšฆ') + .setLabel('Allowed Author Actions') + .setDescription('Change what actions the ticket author can use.') + .setValue('allowed_author_actions'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ›ƒ') + .setLabel('Private Thread') + .setDescription('Toggle whether the tickets are private.') + .setValue('private'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ””') + .setLabel('Silent Pings') + .setDescription('Toggle whether managers get pinged (with noise) on ticket creation.') + .setValue('silent_pings'), + new StringSelectMenuOptionBuilder() + .setEmoji('โฉ') + .setLabel('Skip Modal') + .setDescription('Toggle whether modals are skipped.') + .setValue('skip_modals'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“ฃ') + .setLabel('Thread Notification') + .setDescription('Toggle whether the new thread system message stays on.') + .setValue('notification'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“‘') + .setLabel('Thread Title') + .setDescription("Edit the created thread's title.") + .setValue('thread_title'), + new StringSelectMenuOptionBuilder() + .setEmoji('๐Ÿ“') + .setLabel('Title & Description') + .setDescription('Toggle whether ticket authors must write a title and description.') + .setValue('ticket_title_description'), + ); + + return [new ActionRowBuilder().setComponents(categoriesMenu)]; +} diff --git a/apps/bot/src/commands/staff/configuration-ticket-threads/modal.ts b/apps/bot/src/commands/staff/configuration-ticket-threads/modal.ts index 52fd1c98..72869fe8 100644 --- a/apps/bot/src/commands/staff/configuration-ticket-threads/modal.ts +++ b/apps/bot/src/commands/staff/configuration-ticket-threads/modal.ts @@ -8,7 +8,7 @@ import { } from '@ticketer/database'; import { customId, - DeferReply, + DeferUpdate, dynamicCustomId, extractCustomId, Modal, @@ -18,7 +18,7 @@ import { import { inlineCode } from 'discord.js'; import { prettifyError } from 'zod'; import { extractEmoji } from '@/utils'; -import { HasGlobalConfiguration } from './helpers'; +import { configurationMenu, HasGlobalConfiguration } from './helpers'; const MAXIMUM_CATEGORY_AMOUNT = 10; @@ -28,12 +28,13 @@ export default class extends Modal.Interaction { dynamicCustomId('ticket_threads_category_fields_dynamic'), ]; - @DeferReply() @HasGlobalConfiguration public async execute({ interaction }: Modal.Context) { const { customId, fields, guildId } = interaction; const { dynamicValue } = extractCustomId(customId); + await (dynamicValue ? interaction.deferUpdate() : interaction.deferReply()); + const emoji = fields.getTextInputValue('emoji'); const categoryEmoji = extractEmoji(emoji); @@ -55,10 +56,11 @@ export default class extends Modal.Interaction { } const { categoryDescription, categoryTitle } = values; + let categoryId = 0; if (dynamicValue) { const { - data: categoryId, + data, error: idError, success: idSuccess, } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(dynamicValue)); @@ -75,6 +77,7 @@ export default class extends Modal.Interaction { }); } + categoryId = data; await database .update(ticketThreadsCategories) .set({ categoryDescription, categoryEmoji, categoryTitle }) @@ -125,16 +128,18 @@ export default class extends Modal.Interaction { }, ); - return interaction.editReply({ - embeds: [embed], - }); + interaction.editReply({ components: [], embeds: [embed] }); + + if (categoryId) { + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message?.embeds }); + } } } export class CategoryMessage extends Modal.Interaction { public readonly customIds = [dynamicCustomId('ticket_threads_category_message')]; - @DeferReply() + @DeferUpdate @HasGlobalConfiguration public async execute({ interaction }: Modal.Context) { const { dynamicValue } = extractCustomId(interaction.customId, true); @@ -168,11 +173,12 @@ export class CategoryMessage extends Modal.Interaction { }); if (!success) { - return interaction.editReply({ + interaction.editReply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message?.embeds }); } const { openingMessageDescription, openingMessageTitle } = values; @@ -204,14 +210,15 @@ export class CategoryMessage extends Modal.Interaction { }); } - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message?.embeds }); } } export class ThreadTitle extends Modal.Interaction { public readonly customIds = [dynamicCustomId('ticket_threads_category_thread_title')]; - @DeferReply() + @DeferUpdate @HasGlobalConfiguration public async execute({ interaction }: Modal.Context) { const { customId, fields } = interaction; @@ -263,6 +270,7 @@ export class ThreadTitle extends Modal.Interaction { `${interaction.member} updated the ticket thread title to the following if it has text: ${threadTitle ? inlineCode(threadTitle) : 'Empty'}.`, ); - return interaction.editReply({ embeds: [embed] }); + interaction.editReply({ components: [], embeds: [embed] }); + return interaction.followUp({ components: configurationMenu(categoryId), embeds: interaction.message?.embeds }); } } diff --git a/apps/bot/src/commands/staff/configuration-ticket-threads/subcommand.ts b/apps/bot/src/commands/staff/configuration-ticket-threads/subcommand.ts index 01410502..14eec7be 100644 --- a/apps/bot/src/commands/staff/configuration-ticket-threads/subcommand.ts +++ b/apps/bot/src/commands/staff/configuration-ticket-threads/subcommand.ts @@ -10,17 +10,10 @@ import { ticketThreadsConfigurationsInsertSchema, } from '@ticketer/database'; import { customId, DeferReply, embed, Subcommand, userEmbed, userEmbedError } from '@ticketer/djs-framework'; -import { - ActionRowBuilder, - ButtonBuilder, - ButtonStyle, - inlineCode, - StringSelectMenuBuilder, - StringSelectMenuOptionBuilder, -} from 'discord.js'; +import { ActionRowBuilder, ButtonBuilder, ButtonStyle, inlineCode, MessageFlags } from 'discord.js'; import { prettifyError } from 'zod'; import { ThreadTicketing } from '@/utils'; -import { categoryFieldsModal, getCategories, HasGlobalConfiguration } from './helpers'; +import { categoryFieldsModal, configurationMenu, getCategories, HasGlobalConfiguration } from './helpers'; export default class extends Subcommand.Interaction { public readonly data = super.subcommand({ @@ -29,7 +22,6 @@ export default class extends Subcommand.Interaction { subcommandNames: ['active-tickets'], }); - @DeferReply() public async execute({ interaction }: Subcommand.Context) { const { data: activeTickets, @@ -40,7 +32,7 @@ export default class extends Subcommand.Interaction { ); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, @@ -48,14 +40,14 @@ export default class extends Subcommand.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } - const { guildId } = interaction; - + await interaction.deferReply(); await database .insert(ticketThreadsConfigurations) - .values({ activeTickets, guildId }) + .values({ activeTickets, guildId: interaction.guildId }) .onDuplicateKeyUpdate({ set: { activeTickets } }); const embed = userEmbed(interaction) @@ -138,7 +130,6 @@ export class CategoriesEdit extends Subcommand.Interaction { subcommandNames: ['edit'], }); - @DeferReply() public async execute({ interaction }: Subcommand.Context) { const { data: categoryId, @@ -147,13 +138,15 @@ export class CategoriesEdit extends Subcommand.Interaction { } = ticketThreadsCategoriesSelectSchema.shape.id.safeParse(Number(interaction.options.getString('title', true))); if (!success) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); } + await interaction.deferReply(); const [result] = await database .select({ emoji: ticketThreadsCategories.categoryEmoji, title: ticketThreadsCategories.categoryTitle }) .from(ticketThreadsCategories) @@ -172,77 +165,8 @@ export class CategoriesEdit extends Subcommand.Interaction { } const reply = embed(interaction).setTitle(ThreadTicketing.titleAndEmoji(result.title, result.emoji)); - const categoriesMenu = new StringSelectMenuBuilder() - .setCustomId(customId('ticket_threads_category_configuration', categoryId)) - .setMinValues(1) - .setMaxValues(1) - .setPlaceholder('Edit one of the following ticket category options:') - .setOptions( - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“ฐ') - .setLabel('Emoji, Title, & Description') - .setDescription('Change the emoji, title, and description used for this category.') - .setValue('emoji_title_description'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ›ก๏ธ') - .setLabel('Ticket Managers') - .setDescription('Choose the managers who are responsible for this category.') - .setValue('managers'), - new StringSelectMenuOptionBuilder() - .setEmoji('#๏ธโƒฃ') - .setLabel('Channel') - .setDescription('Change the channel where tickets of this category go.') - .setValue('channel'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“œ') - .setLabel('Logs Channel') - .setDescription('Change the channel where logs get sent during ticket activity for the category.') - .setValue('logs_channel'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“”') - .setLabel('Message Title & Description') - .setDescription("Change the opening message's title and description.") - .setValue('message_title_description'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿšฆ') - .setLabel('Allowed Author Actions') - .setDescription('Change what actions the ticket author can use.') - .setValue('allowed_author_actions'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ›ƒ') - .setLabel('Private Thread') - .setDescription('Toggle whether the tickets are private.') - .setValue('private'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ””') - .setLabel('Silent Pings') - .setDescription('Toggle whether managers get pinged (with noise) on ticket creation.') - .setValue('silent_pings'), - new StringSelectMenuOptionBuilder() - .setEmoji('โฉ') - .setLabel('Skip Modal') - .setDescription('Toggle whether modals are skipped.') - .setValue('skip_modals'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“ฃ') - .setLabel('Thread Notification') - .setDescription('Toggle whether the new thread system message stays on.') - .setValue('notification'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“‘') - .setLabel('Thread Title') - .setDescription("Edit the created thread's title.") - .setValue('thread_title'), - new StringSelectMenuOptionBuilder() - .setEmoji('๐Ÿ“') - .setLabel('Title & Description') - .setDescription('Toggle whether ticket authors must write a title and description.') - .setValue('ticket_title_description'), - ); - - const row = new ActionRowBuilder().setComponents(categoriesMenu); - return interaction.editReply({ components: [row], embeds: [reply] }); + return interaction.editReply({ components: configurationMenu(categoryId), embeds: [reply] }); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/component.ts b/apps/bot/src/commands/staff/configuration-user-forums/component.ts index 3b388675..073fc185 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/component.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/component.ts @@ -2,7 +2,6 @@ import { and, database, eq, userForumsConfigurations, userForumsConfigurationsSe import { Component, customId, - DeferUpdate, dynamicCustomId, extractCustomId, userEmbed, @@ -57,18 +56,16 @@ export default class extends Component.Interaction { } = userForumsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); if (!success) { - return context.interaction - .reply({ - embeds: [ - userEmbedError({ - client: context.interaction.client, - description: prettifyError(error), - member: context.interaction.member, - }), - ], - flags: [MessageFlags.Ephemeral], - }) - .catch(() => false); + return context.interaction.reply({ + embeds: [ + userEmbedError({ + client: context.interaction.client, + description: prettifyError(error), + member: context.interaction.member, + }), + ], + flags: [MessageFlags.Ephemeral], + }); } const [row] = await database @@ -85,18 +82,19 @@ export default class extends Component.Interaction { ); if (!row) { - return context.interaction - .reply({ - embeds: [ - userEmbedError({ - client: context.interaction.client, - description: 'No user forum configuration for the channel could be found.', - member: context.interaction.member, - }), - ], - flags: [MessageFlags.Ephemeral], - }) - .catch(() => false); + context.interaction.editReply({ + embeds: [ + userEmbedError({ + client: context.interaction.client, + description: 'No user forum configuration for the channel could be found.', + member: context.interaction.member, + }), + ], + }); + return context.interaction.followUp({ + components: configurationMenu(channelId), + content: context.interaction.message.content, + }); } void openingMessageModal(context, { channelId, ...row }); @@ -106,7 +104,6 @@ export default class extends Component.Interaction { export class Managers extends Component.Interaction { public readonly customIds = [dynamicCustomId('ticket_user_forums_configuration_managers')]; - @DeferUpdate public async execute({ interaction }: Component.Context<'role'>) { const managers = interaction.roles.map((role) => role.id); const { dynamicValue } = extractCustomId(interaction.customId, true); @@ -117,7 +114,7 @@ export class Managers extends Component.Interaction { } = userForumsConfigurationsSelectSchema.shape.channelId.safeParse(dynamicValue); if (!success) { - return interaction.editReply({ + return interaction.reply({ components: [], embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), @@ -125,6 +122,7 @@ export class Managers extends Component.Interaction { }); } + await interaction.deferUpdate(); await database .update(userForumsConfigurations) .set({ managers }) @@ -145,7 +143,7 @@ export class Managers extends Component.Interaction { ); interaction.editReply({ components: [], content: '', embeds: [embed] }); - return interaction.followUp({ components: configurationMenu(channelId) }); + return interaction.followUp({ components: configurationMenu(channelId), content: interaction.message.content }); } } @@ -155,12 +153,11 @@ export class Overview extends Component.Interaction { dynamicCustomId('ticket_user_forums_view_next'), ]; - @DeferUpdate public async execute(context: Component.Context<'button'>) { const { success, error, page } = goToPage(context.interaction); if (!success) { - return context.interaction.editReply({ + return context.interaction.reply({ components: [], embeds: [ userEmbedError({ @@ -169,9 +166,11 @@ export class Overview extends Component.Interaction { member: context.interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } + await context.interaction.deferUpdate(); void getConfigurations(context, page); } } diff --git a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts index 18e31efe..4a5daa3f 100644 --- a/apps/bot/src/commands/staff/configuration-user-forums/modal.ts +++ b/apps/bot/src/commands/staff/configuration-user-forums/modal.ts @@ -1,5 +1,5 @@ import { database, userForumsConfigurations, userForumsConfigurationsInsertSchema } from '@ticketer/database'; -import { container, customId, DeferUpdate, Modal, userEmbedError } from '@ticketer/djs-framework'; +import { container, customId, Modal, userEmbedError } from '@ticketer/djs-framework'; import { ChannelType, HeadingLevel, heading, MessageFlags, TextDisplayBuilder } from 'discord.js'; import { prettifyError } from 'zod'; import { userForumsContainer } from '@/utils'; @@ -8,12 +8,11 @@ import { configurationMenu } from './helpers'; export default class extends Modal.Interaction { public readonly customIds = [customId('ticket_user_forums_configuration_opening_message')]; - @DeferUpdate public async execute({ interaction }: Modal.Context) { const channel = interaction.fields.getSelectedChannels('channel', true).at(0); if (channel?.type !== ChannelType.GuildForum) { - return interaction.editReply({ + return interaction.reply({ embeds: [ userEmbedError({ client: interaction.client, @@ -21,6 +20,7 @@ export default class extends Modal.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); } @@ -32,16 +32,18 @@ export default class extends Modal.Interaction { }); if (!success) { - interaction.editReply({ + interaction.reply({ components: [], content: '', embeds: [ userEmbedError({ client: interaction.client, description: prettifyError(error), member: interaction.member }), ], + flags: [MessageFlags.Ephemeral], }); - return interaction.followUp({ components: configurationMenu(channel.id) }); + return interaction.followUp({ components: configurationMenu(channel.id), content: interaction.message?.content }); } + await interaction.deferUpdate(); await database .insert(userForumsConfigurations) .values({ @@ -86,6 +88,6 @@ export default class extends Modal.Interaction { content: '', flags: [MessageFlags.IsComponentsV2], }); - return interaction.followUp({ components: configurationMenu(channel.id) }); + return interaction.followUp({ components: configurationMenu(channel.id), content: interaction.message?.content }); } } diff --git a/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts b/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts index 0030a1f6..aca4db10 100644 --- a/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts +++ b/apps/bot/src/commands/staff/configuration-welcome-farewell/component.ts @@ -126,10 +126,7 @@ export default class extends Component.Interaction { }); const [row] = await database - .select({ - welcomeEnabled: welcomeAndFarewell.welcomeEnabled, - farewellEnabled: welcomeAndFarewell.farewellEnabled, - }) + .select({ welcomeEnabled, farewellEnabled }) .from(welcomeAndFarewell) .where(eq(welcomeAndFarewell.guildId, guildId)); diff --git a/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts b/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts index ed3fb2ad..ad952f2c 100644 --- a/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts +++ b/apps/bot/src/commands/staff/configuration-welcome-farewell/modal.ts @@ -1,13 +1,12 @@ import { database, welcomeAndFarewell, welcomeAndFarewellInsertSchema } from '@ticketer/database'; -import { customId, DeferUpdate, Modal, userEmbed, userEmbedError } from '@ticketer/djs-framework'; -import { inlineCode } from 'discord.js'; +import { customId, Modal, userEmbed, userEmbedError } from '@ticketer/djs-framework'; +import { inlineCode, MessageFlags } from 'discord.js'; import { prettifyError } from 'zod'; import { configurationMenu, type InsertWithoutGuildId } from './helpers'; export default class extends Modal.Interaction { public readonly customIds = [customId('welcome_message_title'), customId('farewell_message_title')]; - @DeferUpdate public async execute({ interaction }: Modal.Context) { const type = interaction.customId.includes('welcome') ? 'welcome' : 'farewell'; const rawTitle = interaction.fields.getTextInputValue('message_title'); @@ -20,7 +19,7 @@ export default class extends Modal.Interaction { : welcomeAndFarewellInsertSchema.shape.farewellMessageTitle.safeParse(rawTitle); if (!success) { - interaction.editReply({ + return interaction.reply({ components: [], embeds: [ userEmbedError({ @@ -29,13 +28,14 @@ export default class extends Modal.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); - return interaction.followUp({ components: configurationMenu() }); } const titleDatabaseValue: InsertWithoutGuildId = type === 'welcome' ? { welcomeMessageTitle: title } : { farewellMessageTitle: title }; + await interaction.deferUpdate(); await database .insert(welcomeAndFarewell) .values({ guildId: interaction.guildId, ...titleDatabaseValue }) @@ -56,7 +56,6 @@ export default class extends Modal.Interaction { export class Description extends Modal.Interaction { public readonly customIds = [customId('welcome_message_description'), customId('farewell_message_description')]; - @DeferUpdate public async execute({ interaction }: Modal.Context) { const type = interaction.customId.includes('welcome') ? 'welcome' : 'farewell'; const rawDescription = interaction.fields.getTextInputValue('message_description'); @@ -69,7 +68,7 @@ export class Description extends Modal.Interaction { : welcomeAndFarewellInsertSchema.shape.farewellMessageDescription.safeParse(rawDescription); if (!success) { - interaction.editReply({ + return interaction.reply({ components: [], embeds: [ userEmbedError({ @@ -78,13 +77,14 @@ export class Description extends Modal.Interaction { member: interaction.member, }), ], + flags: [MessageFlags.Ephemeral], }); - return interaction.followUp({ components: configurationMenu() }); } const descriptionDatabaseValue: InsertWithoutGuildId = type === 'welcome' ? { welcomeMessageDescription: description } : { farewellMessageDescription: description }; + await interaction.deferUpdate(); await database .insert(welcomeAndFarewell) .values({ guildId: interaction.guildId, ...descriptionDatabaseValue }) diff --git a/apps/website/package.json b/apps/website/package.json index e5f6a73d..440a1b35 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -28,7 +28,7 @@ "deepmerge": "^4.3.1", "lucide-react": "^0.562.0", "next": "16.1.1", - "next-intl": "^4.6.1", + "next-intl": "^4.7.0", "next-themes": "^0.4.6", "react": "19.2.3", "react-dom": "19.2.3", diff --git a/package.json b/package.json index fa0e5c18..00f253c3 100644 --- a/package.json +++ b/package.json @@ -16,5 +16,5 @@ "@biomejs/biome": "^2.3.10", "turbo": "^2.7.2" }, - "packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6" + "packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a" } diff --git a/packages/database/package.json b/packages/database/package.json index 3adf4bbb..6dafa6b1 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -19,7 +19,7 @@ "drizzle-orm": "^0.45.1", "drizzle-zod": "^0.8.3", "mysql2": "^3.16.0", - "zod": "^4.2.1" + "zod": "^4.3.4" }, "devDependencies": { "drizzle-kit": "^0.31.8" diff --git a/packages/env/package.json b/packages/env/package.json index 23a3d984..32e66678 100644 --- a/packages/env/package.json +++ b/packages/env/package.json @@ -13,7 +13,7 @@ "dependencies": { "@t3-oss/env-core": "^0.13.10", "@t3-oss/env-nextjs": "^0.13.10", - "zod": "^4.2.1" + "zod": "^4.3.4" }, "devDependencies": { "@types/node": "^25.0.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99ddeb9f..b870421c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,8 +39,8 @@ importers: specifier: ^5.26.2 version: 5.26.2(typescript@5.9.3) zod: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.3.4 + version: 4.3.4 devDependencies: '@types/node': specifier: ^25.0.3 @@ -100,8 +100,8 @@ importers: specifier: 16.1.1 version: 16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) next-intl: - specifier: ^4.6.1 - version: 4.6.1(next@16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3) + specifier: ^4.7.0 + version: 4.7.0(next@16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -156,13 +156,13 @@ importers: version: 0.45.1(mysql2@3.16.0) drizzle-zod: specifier: ^0.8.3 - version: 0.8.3(drizzle-orm@0.45.1(mysql2@3.16.0))(zod@4.2.1) + version: 0.8.3(drizzle-orm@0.45.1(mysql2@3.16.0))(zod@4.3.4) mysql2: specifier: ^3.16.0 version: 3.16.0 zod: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.3.4 + version: 4.3.4 devDependencies: drizzle-kit: specifier: ^0.31.8 @@ -185,13 +185,13 @@ importers: dependencies: '@t3-oss/env-core': specifier: ^0.13.10 - version: 0.13.10(typescript@5.9.3)(zod@4.2.1) + version: 0.13.10(typescript@5.9.3)(zod@4.3.4) '@t3-oss/env-nextjs': specifier: ^0.13.10 - version: 0.13.10(typescript@5.9.3)(zod@4.2.1) + version: 0.13.10(typescript@5.9.3)(zod@4.3.4) zod: - specifier: ^4.2.1 - version: 4.2.1 + specifier: ^4.3.4 + version: 4.3.4 devDependencies: '@types/node': specifier: ^25.0.3 @@ -1471,68 +1471,68 @@ packages: '@schummar/icu-type-parser@1.21.5': resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} - '@swc/core-darwin-arm64@1.15.7': - resolution: {integrity: sha512-+hNVUfezUid7LeSHqnhoC6Gh3BROABxjlDNInuZ/fie1RUxaEX4qzDwdTgozJELgHhvYxyPIg1ro8ibnKtgO4g==} + '@swc/core-darwin-arm64@1.15.8': + resolution: {integrity: sha512-M9cK5GwyWWRkRGwwCbREuj6r8jKdES/haCZ3Xckgkl8MUQJZA3XB7IXXK1IXRNeLjg6m7cnoMICpXv1v1hlJOg==} engines: {node: '>=10'} cpu: [arm64] os: [darwin] - '@swc/core-darwin-x64@1.15.7': - resolution: {integrity: sha512-ZAFuvtSYZTuXPcrhanaD5eyp27H8LlDzx2NAeVyH0FchYcuXf0h5/k3GL9ZU6Jw9eQ63R1E8KBgpXEJlgRwZUQ==} + '@swc/core-darwin-x64@1.15.8': + resolution: {integrity: sha512-j47DasuOvXl80sKJHSi2X25l44CMc3VDhlJwA7oewC1nV1VsSzwX+KOwE5tLnfORvVJJyeiXgJORNYg4jeIjYQ==} engines: {node: '>=10'} cpu: [x64] os: [darwin] - '@swc/core-linux-arm-gnueabihf@1.15.7': - resolution: {integrity: sha512-K3HTYocpqnOw8KcD8SBFxiDHjIma7G/X+bLdfWqf+qzETNBrzOub/IEkq9UaeupaJiZJkPptr/2EhEXXWryS/A==} + '@swc/core-linux-arm-gnueabihf@1.15.8': + resolution: {integrity: sha512-siAzDENu2rUbwr9+fayWa26r5A9fol1iORG53HWxQL1J8ym4k7xt9eME0dMPXlYZDytK5r9sW8zEA10F2U3Xwg==} engines: {node: '>=10'} cpu: [arm] os: [linux] - '@swc/core-linux-arm64-gnu@1.15.7': - resolution: {integrity: sha512-HCnVIlsLnCtQ3uXcXgWrvQ6SAraskLA9QJo9ykTnqTH6TvUYqEta+TdTdGjzngD6TOE7XjlAiUs/RBtU8Z0t+Q==} + '@swc/core-linux-arm64-gnu@1.15.8': + resolution: {integrity: sha512-o+1y5u6k2FfPYbTRUPvurwzNt5qd0NTumCTFscCNuBksycloXY16J8L+SMW5QRX59n4Hp9EmFa3vpvNHRVv1+Q==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-arm64-musl@1.15.7': - resolution: {integrity: sha512-/OOp9UZBg4v2q9+x/U21Jtld0Wb8ghzBScwhscI7YvoSh4E8RALaJ1msV8V8AKkBkZH7FUAFB7Vbv0oVzZsezA==} + '@swc/core-linux-arm64-musl@1.15.8': + resolution: {integrity: sha512-koiCqL09EwOP1S2RShCI7NbsQuG6r2brTqUYE7pV7kZm9O17wZ0LSz22m6gVibpwEnw8jI3IE1yYsQTVpluALw==} engines: {node: '>=10'} cpu: [arm64] os: [linux] - '@swc/core-linux-x64-gnu@1.15.7': - resolution: {integrity: sha512-VBbs4gtD4XQxrHuQ2/2+TDZpPQQgrOHYRnS6SyJW+dw0Nj/OomRqH+n5Z4e/TgKRRbieufipeIGvADYC/90PYQ==} + '@swc/core-linux-x64-gnu@1.15.8': + resolution: {integrity: sha512-4p6lOMU3bC+Vd5ARtKJ/FxpIC5G8v3XLoPEZ5s7mLR8h7411HWC/LmTXDHcrSXRC55zvAVia1eldy6zDLz8iFQ==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-linux-x64-musl@1.15.7': - resolution: {integrity: sha512-kVuy2unodso6p0rMauS2zby8/bhzoGRYxBDyD6i2tls/fEYAE74oP0VPFzxIyHaIjK1SN6u5TgvV9MpyJ5xVug==} + '@swc/core-linux-x64-musl@1.15.8': + resolution: {integrity: sha512-z3XBnbrZAL+6xDGAhJoN4lOueIxC/8rGrJ9tg+fEaeqLEuAtHSW2QHDHxDwkxZMjuF/pZ6MUTjHjbp8wLbuRLA==} engines: {node: '>=10'} cpu: [x64] os: [linux] - '@swc/core-win32-arm64-msvc@1.15.7': - resolution: {integrity: sha512-uddYoo5Xmo1XKLhAnh4NBIyy5d0xk33x1sX3nIJboFySLNz878ksCFCZ3IBqrt1Za0gaoIWoOSSSk0eNhAc/sw==} + '@swc/core-win32-arm64-msvc@1.15.8': + resolution: {integrity: sha512-djQPJ9Rh9vP8GTS/Df3hcc6XP6xnG5c8qsngWId/BLA9oX6C7UzCPAn74BG/wGb9a6j4w3RINuoaieJB3t+7iQ==} engines: {node: '>=10'} cpu: [arm64] os: [win32] - '@swc/core-win32-ia32-msvc@1.15.7': - resolution: {integrity: sha512-rqq8JjNMLx3QNlh0aPTtN/4+BGLEHC94rj9mkH1stoNRf3ra6IksNHMHy+V1HUqElEgcZyx+0yeXx3eLOTcoFw==} + '@swc/core-win32-ia32-msvc@1.15.8': + resolution: {integrity: sha512-/wfAgxORg2VBaUoFdytcVBVCgf1isWZIEXB9MZEUty4wwK93M/PxAkjifOho9RN3WrM3inPLabICRCEgdHpKKQ==} engines: {node: '>=10'} cpu: [ia32] os: [win32] - '@swc/core-win32-x64-msvc@1.15.7': - resolution: {integrity: sha512-4BK06EGdPnuplgcNhmSbOIiLdRgHYX3v1nl4HXo5uo4GZMfllXaCyBUes+0ePRfwbn9OFgVhCWPcYYjMT6hycQ==} + '@swc/core-win32-x64-msvc@1.15.8': + resolution: {integrity: sha512-GpMePrh9Sl4d61o4KAHOOv5is5+zt6BEXCOCgs/H0FLGeii7j9bWDE8ExvKFy2GRRZVNR1ugsnzaGWHKM6kuzA==} engines: {node: '>=10'} cpu: [x64] os: [win32] - '@swc/core@1.15.7': - resolution: {integrity: sha512-kTGB8XI7P+pTKW83tnUEDVP4zduF951u3UAOn5eTi0vyW6MvL56A3+ggMdfuVFtDI0/DsbSzf5z34HVBbuScWw==} + '@swc/core@1.15.8': + resolution: {integrity: sha512-T8keoJjXaSUoVBCIjgL6wAnhADIb09GOELzKg10CjNg+vLX48P93SME6jTfte9MZIm5m+Il57H3rTSk/0kzDUw==} engines: {node: '>=10'} peerDependencies: '@swc/helpers': '>=0.5.17' @@ -1731,8 +1731,8 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - caniuse-lite@1.0.30001761: - resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} @@ -2098,11 +2098,11 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} - next-intl-swc-plugin-extractor@4.6.1: - resolution: {integrity: sha512-+HHNeVERfSvuPDF7LYVn3pxst5Rf7EYdUTw7C7WIrYhcLaKiZ1b9oSRkTQddAN3mifDMCfHqO4kAQ/pcKiBl3A==} + next-intl-swc-plugin-extractor@4.7.0: + resolution: {integrity: sha512-iAqflu2FWdQMWhwB0B2z52X7LmEpvnMNJXqVERZQ7bK5p9iqQLu70ur6Ka6NfiXLxfb+AeAkUX5qIciQOg+87A==} - next-intl@4.6.1: - resolution: {integrity: sha512-KlWgWtKLBPUsTPgxqwyjws1wCMD2QKxLlVjeeGj53DC1JWfKmBShKOrhIP0NznZrRQ0GleeoDUeHSETmyyIFeA==} + next-intl@4.7.0: + resolution: {integrity: sha512-gvROzcNr/HM0jTzQlKWQxUNk8jrZ0bREz+bht3wNbv+uzlZ5Kn3J+m+viosub18QJ72S08UJnVK50PXWcUvwpQ==} peerDependencies: next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 @@ -2151,8 +2151,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - po-parser@2.0.0: - resolution: {integrity: sha512-SZvoKi3PoI/hHa2V9je9CW7Xgxl4dvO74cvaa6tWShIHT51FkPxje6pt0gTJznJrU67ix91nDaQp2hUxkOYhKA==} + po-parser@2.1.1: + resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -2361,8 +2361,8 @@ packages: '@types/react': optional: true - use-intl@4.6.1: - resolution: {integrity: sha512-mUIj6QvJZ7Rk33mLDxRziz1YiBBAnIji8YW4TXXMdYHtaPEbVucrXD3iKQGAqJhbVn0VnjrEtIKYO1B18mfSJw==} + use-intl@4.7.0: + resolution: {integrity: sha512-jyd8nSErVRRsSlUa+SDobKHo9IiWs5fjcPl9VBUnzUyEQpVM5mwJCgw8eUiylhvBpLQzUGox1KN0XlRivSID9A==} peerDependencies: react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 @@ -2388,8 +2388,8 @@ packages: utf-8-validate: optional: true - zod@4.2.1: - resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} + zod@4.3.4: + resolution: {integrity: sha512-Zw/uYiiyF6pUT1qmKbZziChgNPRu+ZRneAsMUDU6IwmXdWt5JwcUfy2bvLOCUtz5UniaN/Zx5aFttZYbYc7O/A==} snapshots: @@ -3375,51 +3375,51 @@ snapshots: '@schummar/icu-type-parser@1.21.5': {} - '@swc/core-darwin-arm64@1.15.7': + '@swc/core-darwin-arm64@1.15.8': optional: true - '@swc/core-darwin-x64@1.15.7': + '@swc/core-darwin-x64@1.15.8': optional: true - '@swc/core-linux-arm-gnueabihf@1.15.7': + '@swc/core-linux-arm-gnueabihf@1.15.8': optional: true - '@swc/core-linux-arm64-gnu@1.15.7': + '@swc/core-linux-arm64-gnu@1.15.8': optional: true - '@swc/core-linux-arm64-musl@1.15.7': + '@swc/core-linux-arm64-musl@1.15.8': optional: true - '@swc/core-linux-x64-gnu@1.15.7': + '@swc/core-linux-x64-gnu@1.15.8': optional: true - '@swc/core-linux-x64-musl@1.15.7': + '@swc/core-linux-x64-musl@1.15.8': optional: true - '@swc/core-win32-arm64-msvc@1.15.7': + '@swc/core-win32-arm64-msvc@1.15.8': optional: true - '@swc/core-win32-ia32-msvc@1.15.7': + '@swc/core-win32-ia32-msvc@1.15.8': optional: true - '@swc/core-win32-x64-msvc@1.15.7': + '@swc/core-win32-x64-msvc@1.15.8': optional: true - '@swc/core@1.15.7': + '@swc/core@1.15.8': dependencies: '@swc/counter': 0.1.3 '@swc/types': 0.1.25 optionalDependencies: - '@swc/core-darwin-arm64': 1.15.7 - '@swc/core-darwin-x64': 1.15.7 - '@swc/core-linux-arm-gnueabihf': 1.15.7 - '@swc/core-linux-arm64-gnu': 1.15.7 - '@swc/core-linux-arm64-musl': 1.15.7 - '@swc/core-linux-x64-gnu': 1.15.7 - '@swc/core-linux-x64-musl': 1.15.7 - '@swc/core-win32-arm64-msvc': 1.15.7 - '@swc/core-win32-ia32-msvc': 1.15.7 - '@swc/core-win32-x64-msvc': 1.15.7 + '@swc/core-darwin-arm64': 1.15.8 + '@swc/core-darwin-x64': 1.15.8 + '@swc/core-linux-arm-gnueabihf': 1.15.8 + '@swc/core-linux-arm64-gnu': 1.15.8 + '@swc/core-linux-arm64-musl': 1.15.8 + '@swc/core-linux-x64-gnu': 1.15.8 + '@swc/core-linux-x64-musl': 1.15.8 + '@swc/core-win32-arm64-msvc': 1.15.8 + '@swc/core-win32-ia32-msvc': 1.15.8 + '@swc/core-win32-x64-msvc': 1.15.8 '@swc/counter@0.1.3': {} @@ -3431,17 +3431,17 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@t3-oss/env-core@0.13.10(typescript@5.9.3)(zod@4.2.1)': + '@t3-oss/env-core@0.13.10(typescript@5.9.3)(zod@4.3.4)': optionalDependencies: typescript: 5.9.3 - zod: 4.2.1 + zod: 4.3.4 - '@t3-oss/env-nextjs@0.13.10(typescript@5.9.3)(zod@4.2.1)': + '@t3-oss/env-nextjs@0.13.10(typescript@5.9.3)(zod@4.3.4)': dependencies: - '@t3-oss/env-core': 0.13.10(typescript@5.9.3)(zod@4.2.1) + '@t3-oss/env-core': 0.13.10(typescript@5.9.3)(zod@4.3.4) optionalDependencies: typescript: 5.9.3 - zod: 4.2.1 + zod: 4.3.4 '@tailwindcss/node@4.1.18': dependencies: @@ -3539,7 +3539,7 @@ snapshots: autoprefixer@10.4.23(postcss@8.5.6): dependencies: browserslist: 4.28.1 - caniuse-lite: 1.0.30001761 + caniuse-lite: 1.0.30001762 fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.6 @@ -3566,14 +3566,14 @@ snapshots: browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 + caniuse-lite: 1.0.30001762 electron-to-chromium: 1.5.267 node-releases: 2.0.27 update-browserslist-db: 1.2.3(browserslist@4.28.1) buffer-from@1.1.2: {} - caniuse-lite@1.0.30001761: {} + caniuse-lite@1.0.30001762: {} chalk@5.6.2: {} @@ -3639,10 +3639,10 @@ snapshots: optionalDependencies: mysql2: 3.16.0 - drizzle-zod@0.8.3(drizzle-orm@0.45.1(mysql2@3.16.0))(zod@4.2.1): + drizzle-zod@0.8.3(drizzle-orm@0.45.1(mysql2@3.16.0))(zod@4.3.4): dependencies: drizzle-orm: 0.45.1(mysql2@3.16.0) - zod: 4.2.1 + zod: 4.3.4 electron-to-chromium@1.5.267: {} @@ -3883,19 +3883,19 @@ snapshots: negotiator@1.0.0: {} - next-intl-swc-plugin-extractor@4.6.1: {} + next-intl-swc-plugin-extractor@4.7.0: {} - next-intl@4.6.1(next@16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3): + next-intl@4.7.0(next@16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react@19.2.3)(typescript@5.9.3): dependencies: '@formatjs/intl-localematcher': 0.5.10 '@parcel/watcher': 2.5.1 - '@swc/core': 1.15.7 + '@swc/core': 1.15.8 negotiator: 1.0.0 next: 16.1.1(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - next-intl-swc-plugin-extractor: 4.6.1 - po-parser: 2.0.0 + next-intl-swc-plugin-extractor: 4.7.0 + po-parser: 2.1.1 react: 19.2.3 - use-intl: 4.6.1(react@19.2.3) + use-intl: 4.7.0(react@19.2.3) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -3911,7 +3911,7 @@ snapshots: '@next/env': 16.1.1 '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.9.11 - caniuse-lite: 1.0.30001761 + caniuse-lite: 1.0.30001762 postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) @@ -3939,7 +3939,7 @@ snapshots: picomatch@2.3.1: {} - po-parser@2.0.0: {} + po-parser@2.1.1: {} postcss-value-parser@4.2.0: {} @@ -4130,7 +4130,7 @@ snapshots: optionalDependencies: '@types/react': 19.2.7 - use-intl@4.6.1(react@19.2.3): + use-intl@4.7.0(react@19.2.3): dependencies: '@formatjs/fast-memoize': 2.2.7 '@schummar/icu-type-parser': 1.21.5 @@ -4147,4 +4147,4 @@ snapshots: ws@8.18.3: {} - zod@4.2.1: {} + zod@4.3.4: {}