Skip to content

feat(category): associate roles to categories - #401

Merged
Akinator31 merged 8 commits into
mainfrom
396-associate-role-to-categories
Apr 24, 2026
Merged

feat(category): associate roles to categories#401
Akinator31 merged 8 commits into
mainfrom
396-associate-role-to-categories

Conversation

@Akinator31

Copy link
Copy Markdown
Member

This pull request introduces a comprehensive system for managing roles linked to ticket categories, including new API endpoints, slash and text command support, and database integration. It also ensures that when a category is deleted, its associated roles are cleaned up. The most important changes are summarized below.

API and Backend Enhancements:

  • Added new API endpoints in categories.rs for listing, adding, removing, setting, and clearing roles associated with categories. These endpoints validate role IDs and ensure the category exists before making changes.
  • Updated the database operation in ticket_categories.rs so that deleting a category also deletes its associated roles, preventing orphaned records.

Slash Command Improvements:

  • Expanded the category slash command to support a roles subcommand group with actions to add, remove, list, and clear roles linked to a category. Implemented the corresponding logic for each action.

Text Command Improvements:

  • Added support for roles actions (add, remove, list, clear) to the text-based category command, including role ID parsing and user feedback.

Routing and Configuration:

  • Registered the new role management API endpoints in the categories router, making them available for use.

These changes provide a robust and consistent interface for managing category-role associations across both API and command interfaces.

@Akinator31 Akinator31 self-assigned this Apr 24, 2026
@Akinator31 Akinator31 linked an issue Apr 24, 2026 that may be closed by this pull request
@Akinator31
Akinator31 requested a review from Copilot April 24, 2026 20:52

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for associating Discord role IDs with ticket categories, exposing this via DB schema + backend APIs, and wiring it into both bot commands and the panel UI (including mentioning linked roles when a new ticket thread is created).

Changes:

  • Introduces ticket_category_roles storage + new DB operations for listing/adding/removing/clearing/setting role links.
  • Adds category-role management via API routes/handlers and via slash + text category roles ... commands.
  • Updates panel UI/i18n to view/add/remove roles per category and optionally set roles on category creation.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
migrations/20260423120000_category_roles.sql Adds ticket_category_roles table with FK cascade + index
crates/rustmail_panel/src/i18n/en/en.json Panel strings for linked roles UI + create modal field
crates/rustmail_panel/src/i18n/fr/fr.json French translations for linked roles UI + create modal field
crates/rustmail_panel/src/components/categories.rs Panel UI: fetch/display/add/remove roles; create modal role parsing + post-create set
crates/rustmail/src/modules/categories.rs Mentions linked roles when a new ticket thread is created
crates/rustmail/src/i18n/language/en.rs Help/usage + messages for category roles text/slash commands
crates/rustmail/src/i18n/language/fr.rs French equivalents for category roles messages
crates/rustmail/src/db/operations/ticket_categories.rs Adds role-link CRUD ops; attempts role cleanup on category delete
crates/rustmail/src/commands/category/text_command/category.rs Adds category roles add/remove/list/clear text subcommands
crates/rustmail/src/commands/category/slash_command/category.rs Adds roles subcommand group for slash command
crates/rustmail/src/api/routes/categories.rs Registers new /categories/{id}/roles... endpoints
crates/rustmail/src/api/handler/categories/categories.rs Implements role-link list/add/remove/set handlers + validation
.run/Run.run.xml Changes IDE run config profile to release

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

roles_loaded.set(true);
role_error.set(None);
}
Err(e) => role_error.set(Some(e)),

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the error path for loading roles, roles_loaded is never set to true, so the UI can remain in the “Loading roles…” state indefinitely (and may show loading + error at the same time). Consider setting roles_loaded to true (and optionally clearing roles) when fetch_category_roles returns an error.

Suggested change
Err(e) => role_error.set(Some(e)),
Err(e) => {
roles.set(Vec::new());
roles_loaded.set(true);
role_error.set(Some(e));
}

Copilot uses AI. Check for mistakes.
Comment on lines +439 to +445
{
let load_roles = load_roles.clone();
use_effect_with(c.id.clone(), move |_| {
load_roles.emit(());
|| ()
});
}

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each CategoryCard triggers its own /api/categories/{id}/roles fetch on mount. When listing many categories, this becomes an N+1 request pattern and can slow down the page. Consider deferring role loading until the roles section is expanded, caching results, or adding a batched endpoint to fetch roles for all categories in one request.

Copilot uses AI. Check for mistakes.
Comment on lines 278 to +283
pub async fn delete_category(id: &str, pool: &SqlitePool) -> ModmailResult<bool> {
let _ = sqlx::query("DELETE FROM ticket_category_roles WHERE category_id = ?")
.bind(id)
.execute(pool)
.await;

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

delete_category runs a manual DELETE FROM ticket_category_roles ... but ignores any error/result (let _ = ...). This can silently leave orphaned role links if the deletion fails for reasons other than “no such table”. Either rely solely on the FK ON DELETE CASCADE behavior, or propagate/log the error and return a failure if the cleanup query fails.

Copilot uses AI. Check for mistakes.
Comment on lines +391 to +403
let now = Utc::now().timestamp();
for role_id in role_ids {
sqlx::query(
r#"
INSERT INTO ticket_category_roles (category_id, role_id, created_at)
VALUES (?, ?, ?)
ON CONFLICT(category_id, role_id) DO NOTHING
"#,
)
.bind(category_id)
.bind(role_id)
.bind(now)
.execute(&mut *tx)

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list_category_role_ids orders by created_at, but set_category_roles assigns the same created_at timestamp (now) to every inserted role in the loop. After a set/replace, ordering can become non-deterministic. Consider either ordering by a stable column (e.g., role_id) or storing an explicit position / using incrementing timestamps within the loop to preserve deterministic ordering.

Copilot uses AI. Check for mistakes.
Comment on lines +17 to +23
.route("/{id}/roles", get(list_category_roles_handler))
.route("/{id}/roles", post(add_category_role_handler))
.route("/{id}/roles", put(set_category_roles_handler))
.route(
"/{id}/roles/{role_id}",
delete(remove_category_role_handler),
)

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions an API endpoint for “clearing” category roles, but the router only exposes GET/POST/PUT on /{id}/roles and DELETE on /{id}/roles/{role_id} (no clear route like DELETE /{id}/roles). Either add the clear endpoint (e.g., map to clear_category_roles) or update the description to clarify that PUT { role_ids: [] } is the supported way to clear roles.

Copilot uses AI. Check for mistakes.
Comment thread .run/Run.run.xml Outdated
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Run" type="CargoCommandRunConfiguration" factoryName="Cargo Command" nameIsGenerated="true">
<option name="buildProfileId" value="dev" />
<option name="buildProfileId" value="release" />

Copilot AI Apr 24, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching the IDE run configuration default build profile to release makes local iteration/debugging slower and changes the default developer experience for the repo. If this wasn’t intentional/project-wide, consider keeping dev here (or adding a separate Release run config instead).

Suggested change
<option name="buildProfileId" value="release" />
<option name="buildProfileId" value="dev" />

Copilot uses AI. Check for mistakes.
@Akinator31

Copy link
Copy Markdown
Member Author

@copilot apply changes based on the comments in this thread

Copilot AI commented Apr 24, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied all changes from the review in commit cf96865:

  • categories.rs (panel): Error path now sets roles.set(Vec::new()) and roles_loaded.set(true) before setting the error, so the UI exits the loading state correctly.
  • ticket_categories.rs: delete_category now propagates the error from deleting associated roles instead of silently ignoring it. list_category_role_ids now orders by role_id ASC for deterministic ordering after set_category_roles.
  • API routes: Added DELETE /{id}/roles endpoint mapped to a new clear_category_roles_handler.
  • .run/Run.run.xml: Reverted build profile from release back to dev.

@Akinator31
Akinator31 merged commit 933486b into main Apr 24, 2026
2 checks passed
@Akinator31
Akinator31 deleted the 396-associate-role-to-categories branch April 24, 2026 22:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Associate role to categories

3 participants