Skip to content

Conversation

@batmnkh2344
Copy link
Contributor

@batmnkh2344 batmnkh2344 commented Jun 19, 2025

Summary by Sourcery

Move ticket functionality into frontline_api: introduce a comprehensive ticket management system with data models, GraphQL schemas, resolvers, and utilities, and refactor sales_api deal handling to leverage generic item resolution and unified PubSub events.

New Features:

  • Add full ticket management in frontline_api including modules for tickets, pipelines, stages, boards, labels, checklists, and comments
  • Expose GraphQL types, queries, and mutations for ticket CRUD and workflow operations in frontline_api
  • Implement utility functions for ticket ordering, filtering, permission checks, and pipeline label copying

Enhancements:

  • Refactor sales_api deal resolvers to use a unified itemResolver, rename itemMover, and streamline PubSub notifications
  • Update connectionResolvers to load ticket models alongside existing modules
  • Clean up commented code in sales_api and ensure consistent naming for deals operations

Important

Introduces comprehensive ticket management in frontline_api and refactors sales_api for unified item resolution and streamlined notifications.

  • Behavior:
    • Introduces full ticket management in frontline_api with models for tickets, pipelines, stages, boards, labels, checklists, and comments.
    • Adds GraphQL types, queries, and mutations for ticket CRUD operations in frontline_api.
    • Implements utility functions for ticket ordering, filtering, and permission checks.
  • Enhancements:
    • Refactors sales_api deal resolvers to use a unified itemResolver and streamline PubSub notifications.
    • Updates connectionResolvers.ts to load ticket models.
  • Models:
    • Adds models for Board, Pipeline, Stage, Ticket, Checklist, Label, and Comment in frontline_api.
  • GraphQL Schemas:
    • Defines schemas for Ticket, Pipeline, Stage, Board, Checklist, and Label in frontline_api.
  • Utilities:
    • Adds utility functions in utils.ts for handling ticket operations, including getNewOrder, copyPipelineLabels, and checkMovePermission.

This description was created by Ellipsis for 215f0c1. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Expanded ticketing system with enhanced boards, pipelines, stages, tickets, checklists, labels, and comments management.
    • Added comprehensive GraphQL queries, mutations, and subscriptions supporting real-time updates and advanced filtering.
    • Introduced Gantt timeline item management, checklist progress tracking, ticket archiving, and detailed permission controls.
    • Integrated enriched data resolvers for users, departments, and external services to improve visibility and access.
    • Provided UI-ready data structures and operations for seamless ticket and pipeline management.
  • Bug Fixes

    • Enabled active real-time GraphQL subscription notifications for sales checklists, deals, and stages, ensuring immediate user updates.
  • Documentation

    • Delivered extensive GraphQL schema definitions covering ticketing entities, federation extensions, and operation inputs.
  • Chores

    • Refined codebase structure by consolidating imports and modularizing resolvers, mutations, and schema definitions.
    • Improved backend model loading and initialization for ticket-related entities to ensure consistent data handling.

@sourcery-ai
Copy link

sourcery-ai bot commented Jun 19, 2025

Reviewer's Guide

This PR extracts ticket management from sales_api into a dedicated frontline_api plugin and refactors sales_api to adopt a new itemResolver utility, replacing the legacy itemMover and re-enabling real-time GraphQL subscription events across deals, checklists, and stages.

Sequence diagram for real-time GraphQL subscription event publishing (deals, checklists, stages)

sequenceDiagram
  participant User as actor User
  participant API as sales_api/frontline_api
  participant PubSub as graphqlPubsub
  participant Client as actor Client

  User->>API: Perform mutation (e.g., dealsAdd, ticketsAdd, checklistsEdit)
  API->>PubSub: Publish event (e.g., salesPipelinesChanged, ticketsPipelinesChanged)
  PubSub-->>Client: Push subscription event
Loading

Class diagram for new ticket management models in frontline_api

classDiagram
  class Ticket {
    +createTicket(doc)
    +getTicket(_id)
    +updateTicket(_id, doc)
    +watchTicket(_id, isAdd, userId)
    +removeTickets(_ids)
    +createTicketComment(...)
  }
  class Stage {
    +getStage(_id)
    +removeStage(_id)
  }
  class Pipeline {
    +getPipeline(_id)
    +createPipeline(doc, stages)
    +updatePipeline(_id, doc, stages)
    +updateOrder(orders)
    +watchPipeline(_id, isAdd, userId)
    +removePipeline(_id, checked)
    +archivePipeline(_id)
  }
  class Board {
    +getBoard(_id)
  }
  class Checklist {
    +getChecklist(_id)
    +removeChecklists(contentTypeIds)
    +createChecklist(...)
    +updateChecklist(_id, doc)
    +removeChecklist(_id)
  }
  class ChecklistItem {
    +getChecklistItem(_id)
    +createChecklistItem(...)
    +updateChecklistItem(_id, doc)
    +removeChecklistItem(_id)
    +updateItemOrder(_id, destinationOrder)
  }
  class PipelineLabel {
    +labelsLabel(pipelineId, itemId, labelIds)
  }
  class Comment {
    +getComment(typeId)
    +createComment(doc)
    +deleteComment(_id)
  }
  Ticket --> Stage
  Ticket --> Pipeline
  Ticket --> Board
  Ticket --> Checklist
  Ticket --> PipelineLabel
  Ticket --> Comment
  Checklist --> ChecklistItem
  Pipeline --> Stage
  Stage --> Pipeline
  PipelineLabel --> Pipeline
Loading

Class diagram for itemResolver utility replacing itemMover

classDiagram
  class itemResolver {
    +async itemResolver(models, user, item)
  }
  class resolvers
  itemResolver ..> resolvers : uses sub-resolvers
Loading

File-Level Changes

Change Details Files
Introduce frontline_api tickets plugin with full ticket board, pipeline, stage, ticket, checklist, label, and comment support
  • Add modules/tickets directory under frontline_api with utils, constants, db models, GraphQL schemas, queries, mutations, and custom resolvers
  • Update connectionResolvers.ts to import and load ticket classes into the IModels context
  • Extend frontline_api root schema and resolvers (apollo/schema/schema.ts, apollo/resolvers/*) to merge ticket types, queries, mutations, and federated resolvers
backend/plugins/frontline_api/src/connectionResolvers.ts
backend/plugins/frontline_api/src/apollo/schema/schema.ts
backend/plugins/frontline_api/src/apollo/resolvers/mutations.ts
backend/plugins/frontline_api/src/apollo/resolvers/queries.ts
backend/plugins/frontline_api/src/apollo/resolvers/resolvers.ts
backend/plugins/frontline_api/src/modules/tickets/utils.ts
backend/plugins/frontline_api/src/modules/tickets/db/models/**/*
backend/plugins/frontline_api/src/modules/tickets/db/definitions/**/*
backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/**/*
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/**/*
backend/plugins/frontline_api/src/modules/tickets/@types/**/*
Refactor sales_api to use itemResolver and re-enable subscription publishing
  • Replace itemMover import and calls with the new itemResolver in sales_api utils and deal mutations
  • Implement itemResolver utility to aggregate sub-resolver outputs for subscription contexts
  • Uncomment and standardize graphqlPubsub.publish calls in deals.ts, checklists.ts, and stages.ts to restore real-time subscription events
backend/plugins/sales_api/src/modules/sales/utils.ts
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/checklists.ts
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link

coderabbitai bot commented Jun 19, 2025

Walkthrough

This update introduces a comprehensive ticketing system to the backend, including data models, GraphQL schemas, resolvers, and utility functions for boards, pipelines, stages, tickets, checklists, labels, and comments. It integrates these features into the main API, extends federation support, and enables real-time GraphQL subscription notifications for both ticket and sales modules.

Changes

File(s) Change Summary
.../apollo/resolvers/mutations.ts, .../apollo/resolvers/queries.ts, .../apollo/resolvers/resolvers.ts Integrated ticket mutations, queries, and resolvers into the main API exports.
.../apollo/schema/extensions.ts, .../apollo/schema/schema.ts Added GraphQL federation type extensions and included ticket schema parts in the main schema.
.../connectionResolvers.ts, .../modules/tickets/resolver.ts Added ticket models to model loader and extended the IModels interface.
.../modules/inbox/graphql/schemas/conversation.ts Removed type extensions for User, Customer, Brand, and Tag.
.../modules/tickets/@types/*.ts Introduced TypeScript interfaces for boards, pipelines, stages, tickets, checklists, labels, and comments.
.../modules/tickets/constants.ts Added ticket-related constants and enums.
.../modules/tickets/db/definitions/*.ts Added Mongoose schemas for boards, pipelines, stages, tickets, checklists, labels, and comments.
.../modules/tickets/db/models/*.ts Added model classes for boards, pipelines, stages, tickets, checklists, labels, and comments with CRUD and business logic methods.
.../modules/tickets/graphql/resolvers/customResolvers/*.ts Added custom field resolvers for ticket-related entities.
.../modules/tickets/graphql/resolvers/mutations/*.ts Added GraphQL mutation resolvers for boards, pipelines, stages, tickets, checklists, and labels.
.../modules/tickets/graphql/resolvers/queries/*.ts Added GraphQL query resolvers for boards, pipelines, stages, tickets, checklists, and labels.
.../modules/tickets/graphql/schemas/*.ts Added GraphQL schema definitions for all ticketing entities and operations.
.../modules/tickets/utils.ts Added utility functions for ticket operations, filtering, ordering, permissions, and data enrichment.
.../sales_api/src/modules/sales/graphql/resolvers/mutations/checklists.ts Enabled real-time checklist change notifications via GraphQL subscriptions.
.../sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts Activated GraphQL subscription events for deal mutations, enriching payloads with resolved data.
.../sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts Enabled real-time pipeline reordering notifications for stages.
.../sales_api/src/modules/sales/utils.ts Added and used an itemResolver function to enrich published deal events; enabled subscription publishing.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant GraphQL_API
    participant TicketModels
    participant PubSub
    participant ExternalServices

    Client->>GraphQL_API: Mutation/Query (e.g., ticketsAdd, ticketsBoards)
    GraphQL_API->>TicketModels: Perform DB operation (CRUD, aggregation)
    TicketModels-->>GraphQL_API: Result (ticket, board, etc.)
    GraphQL_API->>PubSub: Publish event (e.g., ticketsPipelinesChanged)
    GraphQL_API->>ExternalServices: (Optional) Fetch/Update via TRPC
    GraphQL_API-->>Client: Response (data, status)
Loading

Poem

A ticket hops in, a pipeline appears,
Boards and checklists—oh, what cheers!
Stages and labels, comments galore,
Real-time updates—rabbits adore!
With schemas and models, the backend's complete,
This burrow of features is truly elite! 🐇✨

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc19858 and 215f0c1.

📒 Files selected for processing (1)
  • scripts/create-backend-plugin.js (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
scripts/create-backend-plugin.js (1)

251-251: LGTM! Import path now matches the actual file structure.

This change correctly aligns the import path with the capitalized model filename that gets created on line 571. The import now properly references ${capitalizedModuleName} to match the actual file ${capitalizedModuleName}.ts being generated.

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @batmnkh2344 - I've reviewed your changes - here's some feedback:

  • Remove or refactor commented‐out code blocks to keep the resolvers clear and maintainable.
  • There’s significant duplication between the Deals and Tickets modules—consider extracting shared mutation and pubsub logic into common utilities.
  • Ensure the new itemResolver API is invoked consistently (parameter order and arguments) across all mutation and query resolvers.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- Remove or refactor commented‐out code blocks to keep the resolvers clear and maintainable.
- There’s significant duplication between the Deals and Tickets modules—consider extracting shared mutation and pubsub logic into common utilities.
- Ensure the new itemResolver API is invoked consistently (parameter order and arguments) across all mutation and query resolvers.

## Individual Comments

### Comment 1
<location> `backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts:28` </location>
<code_context>
+      return comment;
+    }
+
+    public static async createComment(doc: ICommentDocument) {
+      return models.Comments.create({
+        ...doc,
</code_context>

<issue_to_address>
The createComment method accepts ICommentDocument but should likely accept IComment.

Consider updating the method to accept IComment instead, as this better matches typical creation patterns and avoids potential type confusion.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    public static async createComment(doc: ICommentDocument) {
      return models.Comments.create({
        ...doc,
        createdAt: new Date(),
      });
    }
=======
    public static async createComment(comment: IComment) {
      return models.Comments.create({
        ...comment,
        createdAt: new Date(),
      });
    }
>>>>>>> REPLACE

</suggested_fix>

### Comment 2
<location> `backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts:18` </location>
<code_context>
+    /**
+     * Retreives comment
+     */
+    public static async getComment(typeId: string) {
+      const comment = await models.Comments.find({ typeId });
+
</code_context>

<issue_to_address>
getComment returns an array but the interface suggests a single document.

There's a type mismatch: getComment returns an array, but the interface expects a single document. This could lead to confusion or runtime errors.
</issue_to_address>

### Comment 3
<location> `backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts:94` </location>
<code_context>
+        checklistId: checklist._id,
+      });
+
+      return await models.CheckListItems.findOneAndDelete({ _id });
+    }
+  }
</code_context>

<issue_to_address>
removeChecklist deletes a checklist but then deletes a checklist item by the same _id, which may not exist.

The method tries to delete a checklist item using the checklist's _id, which may not correspond to any checklist item. Please verify if this is intended or correct the logic.
</issue_to_address>

### Comment 4
<location> `backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts:185` </location>
<code_context>
+        throw new Error(`ChecklistItems _id = ${_id} not found`);
+      }
+
+      await models.CheckListItems.updateOne(
+        { checklistId: currentItem.checklistId, order: destinationOrder },
+        { $set: { order: currentItem.order } },
</code_context>

<issue_to_address>
updateItemOrder may not handle order collisions robustly.

If multiple items have the same order value, swapping may not work as intended. Please ensure order values are unique or implement a more reliable reordering method.
</issue_to_address>

<suggested_fix>
<<<<<<< SEARCH
    public static async updateItemOrder(_id: string, destinationOrder: number) {
      const currentItem = await models.CheckListItems.findOne({ _id }).lean();

      if (!currentItem) {
        throw new Error(`ChecklistItems _id = ${_id} not found`);
      }

      await models.CheckListItems.updateOne(
        { checklistId: currentItem.checklistId, order: destinationOrder },
        { $set: { order: currentItem.order } },
      );

      await models.CheckListItems.updateOne(
        { _id },
        { $set: { order: destinationOrder } },
      );

      return models.CheckListItems.findOne({ _id }).lean();
    }
=======
    public static async updateItemOrder(_id: string, destinationOrder: number) {
      const currentItem = await models.CheckListItems.findOne({ _id }).lean();

      if (!currentItem) {
        throw new Error(`ChecklistItems _id = ${_id} not found`);
      }

      const currentOrder = currentItem.order;
      const checklistId = currentItem.checklistId;

      if (destinationOrder === currentOrder) {
        return models.CheckListItems.findOne({ _id }).lean();
      }

      if (destinationOrder < currentOrder) {
        // Moving up: increment order of items between destinationOrder and currentOrder - 1
        await models.CheckListItems.updateMany(
          {
            checklistId,
            order: { $gte: destinationOrder, $lt: currentOrder },
            _id: { $ne: _id }
          },
          { $inc: { order: 1 } }
        );
      } else {
        // Moving down: decrement order of items between currentOrder + 1 and destinationOrder
        await models.CheckListItems.updateMany(
          {
            checklistId,
            order: { $gt: currentOrder, $lte: destinationOrder },
            _id: { $ne: _id }
          },
          { $inc: { order: -1 } }
        );
      }

      // Set the current item's order to the destination
      await models.CheckListItems.updateOne(
        { _id },
        { $set: { order: destinationOrder } }
      );

      return models.CheckListItems.findOne({ _id }).lean();
    }
>>>>>>> REPLACE

</suggested_fix>

### Comment 5
<location> `backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/ticket.ts:168` </location>
<code_context>
+  },
+
+  async tags(ticket: ITicketDocument) {
+    return (ticket.tagIds || [])
+      .filter((_id) => !!_id)
+      .map((_id) => ({ __typename: 'Tag', _id }));
</code_context>

<issue_to_address>
tags resolver does not handle the case where tagIds is undefined.

Normalizing tagIds at the data layer would prevent storing falsy values and simplify this logic.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines 28 to 33
public static async createComment(doc: ICommentDocument) {
return models.Comments.create({
...doc,
createdAt: new Date(),
});
}
Copy link

Choose a reason for hiding this comment

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

suggestion: The createComment method accepts ICommentDocument but should likely accept IComment.

Consider updating the method to accept IComment instead, as this better matches typical creation patterns and avoids potential type confusion.

Suggested change
public static async createComment(doc: ICommentDocument) {
return models.Comments.create({
...doc,
createdAt: new Date(),
});
}
public static async createComment(comment: IComment) {
return models.Comments.create({
...comment,
createdAt: new Date(),
});
}

/**
* Retreives comment
*/
public static async getComment(typeId: string) {
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): getComment returns an array but the interface suggests a single document.

There's a type mismatch: getComment returns an array, but the interface expects a single document. This could lead to confusion or runtime errors.

checklistId: checklist._id,
});

return await models.CheckListItems.findOneAndDelete({ _id });
Copy link

Choose a reason for hiding this comment

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

issue (bug_risk): removeChecklist deletes a checklist but then deletes a checklist item by the same _id, which may not exist.

The method tries to delete a checklist item using the checklist's _id, which may not correspond to any checklist item. Please verify if this is intended or correct the logic.

Comment on lines +178 to +196
public static async updateItemOrder(_id: string, destinationOrder: number) {
const currentItem = await models.CheckListItems.findOne({ _id }).lean();

if (!currentItem) {
throw new Error(`ChecklistItems _id = ${_id} not found`);
}

await models.CheckListItems.updateOne(
{ checklistId: currentItem.checklistId, order: destinationOrder },
{ $set: { order: currentItem.order } },
);

await models.CheckListItems.updateOne(
{ _id },
{ $set: { order: destinationOrder } },
);

return models.CheckListItems.findOne({ _id }).lean();
}
Copy link

Choose a reason for hiding this comment

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

suggestion: updateItemOrder may not handle order collisions robustly.

If multiple items have the same order value, swapping may not work as intended. Please ensure order values are unique or implement a more reliable reordering method.

Suggested change
public static async updateItemOrder(_id: string, destinationOrder: number) {
const currentItem = await models.CheckListItems.findOne({ _id }).lean();
if (!currentItem) {
throw new Error(`ChecklistItems _id = ${_id} not found`);
}
await models.CheckListItems.updateOne(
{ checklistId: currentItem.checklistId, order: destinationOrder },
{ $set: { order: currentItem.order } },
);
await models.CheckListItems.updateOne(
{ _id },
{ $set: { order: destinationOrder } },
);
return models.CheckListItems.findOne({ _id }).lean();
}
public static async updateItemOrder(_id: string, destinationOrder: number) {
const currentItem = await models.CheckListItems.findOne({ _id }).lean();
if (!currentItem) {
throw new Error(`ChecklistItems _id = ${_id} not found`);
}
const currentOrder = currentItem.order;
const checklistId = currentItem.checklistId;
if (destinationOrder === currentOrder) {
return models.CheckListItems.findOne({ _id }).lean();
}
if (destinationOrder < currentOrder) {
// Moving up: increment order of items between destinationOrder and currentOrder - 1
await models.CheckListItems.updateMany(
{
checklistId,
order: { $gte: destinationOrder, $lt: currentOrder },
_id: { $ne: _id }
},
{ $inc: { order: 1 } }
);
} else {
// Moving down: decrement order of items between currentOrder + 1 and destinationOrder
await models.CheckListItems.updateMany(
{
checklistId,
order: { $gt: currentOrder, $lte: destinationOrder },
_id: { $ne: _id }
},
{ $inc: { order: -1 } }
);
}
// Set the current item's order to the destination
await models.CheckListItems.updateOne(
{ _id },
{ $set: { order: destinationOrder } }
);
return models.CheckListItems.findOne({ _id }).lean();
}

},

async tags(ticket: ITicketDocument) {
return (ticket.tagIds || [])
Copy link

Choose a reason for hiding this comment

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

nitpick: tags resolver does not handle the case where tagIds is undefined.

Normalizing tagIds at the data layer would prevent storing falsy values and simplify this logic.

Comment on lines +510 to +517
const { list, pageInfo, totalCount } =
await cursorPaginate<ITicketDocument>({
model: models.Tickets,
params: args,
query: filter,
});

return { list, pageInfo, totalCount };
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const { list, pageInfo, totalCount } =
await cursorPaginate<ITicketDocument>({
model: models.Tickets,
params: args,
query: filter,
});
return { list, pageInfo, totalCount };
return await cursorPaginate<ITicketDocument>({
model: models.Tickets,
params: args,
query: filter,
});


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.

});

if (pipeline?.lastNum) {
const lastNum = pipeline.lastNum;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const lastNum = pipeline.lastNum;
const {lastNum} = pipeline;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

Comment on lines 122 to 129
const generatedNum = await boardNumberGenerator(
models,
doc.numberConfig || '',
doc.numberSize || '',
true,
);

return generatedNum;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Inline variable that is immediately returned (inline-immediately-returned-variable)

Suggested change
const generatedNum = await boardNumberGenerator(
models,
doc.numberConfig || '',
doc.numberSize || '',
true,
);
return generatedNum;
return await boardNumberGenerator(
models,
doc.numberConfig || '',
doc.numberSize || '',
true,
);


ExplanationSomething that we often see in people's code is assigning to a result variable
and then immediately returning it.

Returning the result directly shortens the code and removes an unnecessary
variable, reducing the mental load of reading the function.

Where intermediate variables can be useful is if they then get used as a
parameter or a condition, and the name can act like a comment on what the
variable represents. In the case where you're returning it from a function, the
function name is there to tell you what the result is, so the variable name
is unnecessary.


const doc: any = { ...stage, order, pipelineId };

const _id = doc._id;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Prefer object destructuring when accessing and using properties. (use-object-destructuring)

Suggested change
const _id = doc._id;
const {_id} = doc;


ExplanationObject destructuring can often remove an unnecessary temporary reference, as well as making your code more succinct.

From the Airbnb Javascript Style Guide

},
});

ord = ord + 10;
Copy link

Choose a reason for hiding this comment

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

suggestion (code-quality): Replace assignment with assignment operator (assignment-operator)

Suggested change
ord = ord + 10;
ord += 10;


ExplanationThe augmented assignment operators, such as += are a more streamlined and easy to understand way
to add values. Using them removes a little bit of repetition and makes the code slightly easier to
understand.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Caution

Changes requested ❌

Reviewed everything up to dc5387f in 5 minutes and 5 seconds. Click for details.
  • Reviewed 7587 lines of code in 63 files
  • Skipped 0 files when reviewing.
  • Skipped posting 46 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:24
  • Draft comment:
    It appears the identifier “proccessId” is used throughout—consider renaming to “processId” for clarity and consistency.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
2. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts:64
  • Draft comment:
    The check 'if (!sort)' is redundant since an empty object is truthy; instead, validate that sortTypes[sortType] exists and throw an error if not.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
3. backend/plugins/sales_api/src/modules/sales/utils.ts:182
  • Draft comment:
    In the recursive call inside createBoardItem (lines 182–185), the function recursively calls itself on duplicate key errors. Consider adding a maximum retry count to avoid potential infinite recursion.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. backend/plugins/sales_api/src/modules/sales/utils.ts:1000
  • Draft comment:
    The getNewOrder function uses recursion if the computed order equals an existing value. Consider adding a maximum recursion depth or a fallback to prevent infinite loops.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:115
  • Draft comment:
    In dealsEdit, ensure that any changes to assigned user lists properly handle removal when users are still referenced in product/service data. The checkUserIds call is good, but consider additional logging or explicit error messaging.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
6. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts:64
  • Draft comment:
    In the sort items mutation, the code uses 'const sort = sortTypes[sortType] || {}' then immediately checks 'if (!sort)'. Consider validating sortType explicitly (e.g. 'if (!(sortType in sortTypes))') since {} is always truthy.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
7. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/queries/ticket.ts:22
  • Draft comment:
    The generateFilter function is quite long and sequential. Consider refactoring into smaller helpers to improve readability and possibly parallelize independent TRPC calls for performance improvements.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
8. backend/plugins/sales_api/src/modules/sales/utils.ts:129
  • Draft comment:
    There are several sections of commented-out code throughout the file. Cleaning these up or moving them to separate documentation could improve maintainability.
  • Reason this comment was not posted:
    Confidence changes required: 50% <= threshold 50% None
9. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/queries/stage.ts:68
  • Draft comment:
    A debug ‘console.log’ is left in the code (line 68). Remove or replace it with proper logging before production.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
10. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/queries/ticket.ts:123
  • Draft comment:
    The 'generateFilter' function contains many sequential await calls. Depending on the context, consider running independent TRPC queries concurrently to improve performance.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
11. backend/plugins/sales_api/src/modules/sales/graphql/schemas/checklist.ts:1
  • Draft comment:
    Ensure that the JSON return type used in mutations (e.g. for ticketsBoardRemove) is defined as a custom scalar in your GraphQL schema to avoid SDL validation issues.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
12. backend/plugins/frontline_api/src/connectionResolvers.ts:31
  • Draft comment:
    Typo: The module path imported from '@/integrations/facebook/db/models/Comment_conversations' seems to have an inconsistent capitalization. Most imports use lowercase (e.g., 'comment_conversations'). Please verify if this should be corrected to '@/integrations/facebook/db/models/comment_conversations' for consistency.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
13. backend/plugins/frontline_api/src/modules/tickets/@types/ticket.ts:11
  • Draft comment:
    Typographical note: In the comment "// TODO migrate after remove 2row", consider revising it for clarity. For example, consider something like "// TODO: Migrate after removing two rows".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While the suggested change would make the TODO comment more grammatically correct and clearer, this is an extremely minor formatting issue. TODO comments are typically informal development notes and their exact formatting is not critical to code functionality. This kind of nitpick about comment formatting violates the rule about not making purely informative comments that don't require clear code changes. The comment does point out a legitimate clarity issue that could help future developers better understand the TODO. Maybe clearer TODOs are important for maintainability? While clear documentation is valuable, this level of nitpicking about TODO comment formatting is excessive and doesn't meet the bar for requiring a code change. The meaning is still understandable. This comment should be deleted as it's an extremely minor formatting suggestion that doesn't warrant a required code change.
14. backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts:43
  • Draft comment:
    The label 'Default tick used' may contain a typographical error. If the intent was to refer to a ticket, consider updating it to 'Default ticket used'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% The field name is 'defaultTick', not 'defaultTicket', suggesting this might be intentional. Looking at the broader context, this is a schema for ticket stages and the field is a boolean, which makes 'tick' potentially valid - it could be referring to a checkbox or selection mark. Without more context about the business logic, we can't be certain this is a typo. The comment could be correct - 'tick' does seem unusual in a ticket management system. The presence of 'ticket' as the default type elsewhere in the schema lends some credence to this being a typo. However, we don't have strong evidence that this is definitely wrong. The field name itself uses 'Tick' and being a boolean field, it could legitimately refer to a checkbox or selection mark. Without clear evidence that this is definitely a typo, and given that the field name matches the current label, we should not keep this speculative comment.
15. backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts:16
  • Draft comment:
    Typo: "Retreives comment" should be corrected to "Retrieves comment".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While the comment is technically correct about the typo, it's an extremely minor documentation issue that doesn't affect functionality. JSDoc comments are for documentation purposes only. The rules state we should not make comments that are obvious or unimportant. A simple typo fix in documentation falls into this category. The typo could propagate to generated documentation. Also, maintaining high quality even in comments shows attention to detail. While documentation quality is important, this is such a minor issue that it creates more noise than value in the PR review process. The benefit doesn't justify the cognitive overhead for the PR author. Delete this comment as it points out an unimportant typo that doesn't affect code functionality.
16. backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts:35
  • Draft comment:
    Typo in comment: "uniquness" should be "uniqueness".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While the comment is technically correct, typos in comments are very minor issues that don't affect functionality. The rules state we should not make purely informative comments or comments about unimportant issues. A typo in a comment is extremely minor and doesn't require a code change. The typo could be seen as reducing code quality and professionalism. Poor documentation can lead to confusion. While documentation quality matters, this is an extremely minor issue that doesn't impact understanding - the meaning is still clear despite the typo. This comment should be deleted as it points out an extremely minor issue that doesn't impact functionality or understanding.
17. backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts:28
  • Draft comment:
    Typo in comment: "Retreives Ticket" should be "Retrieves Ticket".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While the typo correction is accurate, we need to consider if this is important enough to warrant a comment. Documentation typos don't affect functionality. The rules state we should only keep comments that require clear code changes and avoid obvious or unimportant issues. This seems like a very minor issue. The typo could theoretically make the code slightly less professional looking, and fixing it would improve code quality. Documentation is important for maintainability. However, this is an extremely minor issue that doesn't impact functionality or understanding. The meaning is still completely clear despite the typo. This comment should be deleted as it points out an extremely minor documentation issue that doesn't meaningfully impact code quality or understanding.
18. backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts:79
  • Draft comment:
    Typo in comment: "configed pipelines" should likely be "configured pipelines".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While technically correct, this is an extremely minor issue. The meaning is still clear even with the typo. Comments are meant to help developers understand the code, and this typo doesn't significantly impact comprehension. The rules state we should not make purely informative comments or comments about obvious/unimportant things. The typo could be seen as unprofessional, and fixing it would improve code quality marginally. Some might argue that maintaining high standards even in comments is important. While professionalism is important, this comment doesn't highlight a significant issue that impacts code functionality or maintainability. The rules explicitly state not to make obvious or unimportant comments. This comment should be deleted as it addresses an extremely minor typo that doesn't impact code understanding and falls under the category of "obvious or unimportant" comments that should be avoided.
19. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts:3
  • Draft comment:
    The imported constant 'VISIBLITIES' might be a typographical error. Should it be 'VISIBILITIES'? Please verify the correct spelling.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While this could be a legitimate typo, we don't have access to the actual constants file to verify. If this was a real typo, it would likely cause compilation errors that would be caught by the build process. Variable/constant naming issues are typically caught by the build system. The typo could be in the constants file itself, making this a valid issue that wouldn't be caught by the build. Even if the constant file has the typo, this isn't the right place to fix it - it should be fixed at the source. Also, per the rules, we should not comment on things that would be caught by the build system. Delete the comment as it relates to a potential naming issue that would either be caught by the build system or should be addressed in the constants file instead.
20. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts:3
  • Draft comment:
    Typo: Consider renaming 'VISIBLITIES' to 'VISIBILITIES' for correct spelling, unless it's an intentional naming in your constants file.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While this is technically a spelling error, it's an imported constant from another file. We can't verify if this is actually a typo or an intentional naming without seeing the constants file. The code works as is, and renaming a constant would require changes across multiple files. This seems more like a nitpick than a critical issue. The spelling error could cause confusion for future developers. Constants should generally use correct spelling for maintainability. However, this is an imported constant and we don't have context about whether this naming is intentional or used consistently throughout the codebase. Making this change could require widespread updates. Delete the comment. While it points out a real spelling issue, we don't have enough context to know if this is actually a problem, and changing it would require cross-file changes that are outside the scope of this review.
21. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts:23
  • Draft comment:
    Typo: The function name 'unUsedAmount' seems to have an inconsistent casing. Consider renaming it to 'unusedAmount'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While the casing is technically inconsistent with common JavaScript/TypeScript conventions, this could be intentionally matching a GraphQL schema definition. GraphQL fields often have their own conventions. Without seeing the schema, we can't be certain this is actually wrong. Even if it is inconsistent, it's a minor stylistic issue that doesn't affect functionality. The comment could be correct from a pure TypeScript style perspective. The inconsistent casing does stand out. However, GraphQL resolvers often need to match schema definitions exactly, and we don't have that context. The comment could lead to breaking changes if the casing needs to match a schema. This comment should be removed as it makes assumptions about naming without full context and represents a minor stylistic issue that could be intentional.
22. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts:57
  • Draft comment:
    Typo in comment: "Total count of ticket that are created on this stage initially" should probably be "Total count of tickets that are created on this stage initially".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% While technically correct, this is an extremely minor documentation issue that doesn't affect code functionality or readability. The meaning is perfectly clear even with the grammatical error. The rules say to only keep comments that require clear code changes and to not make purely informative comments. The grammatical error could be seen as reducing code quality and professionalism. Consistent documentation is important for maintainability. While documentation quality matters, this is too minor of an issue to warrant a PR comment. The benefit doesn't justify the overhead of the review comment. Delete the comment as it's too minor of an issue and doesn't require a meaningful code change.
23. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/stage.ts:96
  • Draft comment:
    Typo: 'proccessId' is used here. It should be 'processId' to maintain consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While this is technically a spelling error, the parameter name is used consistently throughout the file. Changing it would require changes in other files that aren't visible here. Without seeing the broader codebase context, we don't know if this spelling is actually intentional or used consistently across the system. The misspelling doesn't affect functionality. The spelling error could propagate through the codebase and cause confusion for future developers. It's a valid concern for code quality. However, we don't have enough context to know if this is actually an error or if it's an intentional spelling used throughout the codebase. Making this change could break compatibility. We should delete this comment since we don't have strong evidence that this is actually incorrect without seeing the rest of the codebase.
24. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts:16
  • Draft comment:
    Typographical: Consider renaming 'generateArhivedItemsFilter' to 'generateArchivedItemsFilter' for correct spelling.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While this is a valid spelling correction, the PR review rules state we should not make purely informative comments. The misspelling is consistent throughout the codebase (both in import and usage) so it's likely intentional or would require broader changes. The typo doesn't affect functionality. The spelling error could propagate to other parts of the codebase and make the code less maintainable. It's a legitimate issue that should be fixed at some point. However, this is a minor cosmetic issue that doesn't affect functionality. The PR rules explicitly state not to make purely informative comments. The consistent use suggests this would require changes beyond this PR's scope. Delete this comment as it's purely informative and doesn't require immediate action within this PR's scope.
25. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts:302
  • Draft comment:
    Typographical: The variable name 'uqinueCheckUserIds' appears to be misspelled. Consider renaming it to 'uniqueCheckUserIds'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While this is technically a spelling error, it's a very minor issue. The variable name is still understandable and the typo doesn't affect functionality. The variable is used locally in a small scope and isn't part of an API or public interface. Fixing it would require changes in multiple places (lines 302 and 349) for minimal benefit. The comment is technically correct, but is it worth the overhead of making the change? Variable naming is important for code readability, but this typo doesn't significantly impact understanding. While the typo is minor, consistently spelled variable names do contribute to code quality and professionalism. However, the benefit is very small compared to the effort of making the change. The comment should be deleted because it points out a very minor spelling issue that doesn't significantly impact code readability or maintainability. The effort to fix it outweighs the benefit.
26. backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/checklist.ts:30
  • Draft comment:
    There seems to be an extra trailing comma after the contentTypeId: String argument in the ticketsChecklistsEdit mutation. Please remove the trailing comma.
  • Reason this comment was not posted:
    Marked as duplicate.
27. backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/ticket.ts:177
  • Draft comment:
    Typo in parameter name: 'proccessId' should be corrected to 'processId' for consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While technically 'processId' would be the correct English spelling, the codebase consistently uses 'proccessId'. Changing it would require changes in multiple places and possibly in other files we can't see. Without seeing the broader codebase context, we can't be certain this isn't an intentional spelling used throughout the system. The spelling could genuinely be a mistake that should be fixed across the codebase. The consistency in this file doesn't necessarily mean it's correct. However, making this change would require a broader refactoring effort across multiple files, and we don't have enough context to know if this would break existing integrations or APIs. The comment should be removed as it would require changes beyond the scope of this file, and we don't have enough context to be certain it's actually an issue.
28. backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/ticket.ts:181
  • Draft comment:
    Typo in parameter name: 'proccessId' is misspelled. It should be 'processId'.
  • Reason this comment was not posted:
    Marked as duplicate.
29. backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/ticket.ts:182
  • Draft comment:
    Typo in parameter name: 'proccessId' appears here as well. It should be 'processId'.
  • Reason this comment was not posted:
    Marked as duplicate.
30. backend/plugins/frontline_api/src/modules/tickets/utils.ts:600
  • Draft comment:
    Comment typo: "hans't this carts assigned and created" should be "hasn't these cards assigned and created".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% The comment is pointing out a minor typo in a code comment that is already commented out. The code it's commenting on isn't even active. While the typo fix would be technically correct, fixing typos in inactive code comments provides no real value. Additionally, per the rules, we should not make purely informative comments that don't require code changes. The typo does make the comment slightly harder to read, and if the code gets uncommented in the future, having correct comments would be better. However, the code is currently inactive and commented out. If it gets uncommented in the future, that would be a better time to fix any comment issues as part of properly reviewing and updating the code. The comment should be deleted as it only suggests a cosmetic change to an inactive code comment and doesn't require any actual code changes.
31. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:93
  • Draft comment:
    Typo Alert: The property name 'proccessId' appears to be misspelled. Consider renaming it to 'processId' if that was the intention.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the spelling does appear incorrect, this parameter name is used consistently throughout the codebase. Changing it would require changes across multiple files and could break API compatibility. Without seeing the full codebase and understanding if this is an intentional spelling choice or part of an external API contract, we can't be certain this change should be made. The spelling could be intentional for backward compatibility reasons. Without seeing other files that might use this parameter name, we can't be 100% sure this is a mistake that should be fixed. Even if intentional, consistently wrong spelling is still wrong. However, the risk of breaking changes outweighs the benefit of fixing a spelling issue. Remove the comment. While likely a spelling mistake, changing it could break API compatibility and the benefit doesn't outweigh the risk without more context.
32. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:289
  • Draft comment:
    Typo: 'proccessId' seems to be misspelled; consider renaming it to 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While technically 'proccessId' appears to be misspelled, this seems to be a consistent naming choice throughout the codebase. Renaming it would require changes across multiple files and could break API compatibility. The variable name, while misspelled, is functioning correctly and changing it would be more risky than beneficial. This is more of a stylistic suggestion than a functional issue. The spelling error is real and fixing it would improve code readability. Also, I might be underestimating the importance of consistent variable naming in a GraphQL API. However, this is a working codebase and the misspelling is consistent. Making this change would require coordinated updates across multiple files and could introduce bugs for minimal benefit. Delete this comment. While technically correct, changing a consistently used identifier across the codebase is more risky than beneficial and doesn't clearly require a code change.
33. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:437
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled. It should be 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the spelling does appear incorrect, this seems to be an intentional choice used consistently throughout the codebase. Changing it would require changes in many places and potentially in other files we can't see. Without seeing the full codebase context, we can't be certain this isn't an intentional spelling used across the system. The spelling does look wrong, and generally fixing typos is good practice. Maybe this is a real issue that should be fixed across the codebase. However, without seeing the full codebase context and understanding if this spelling is used consistently elsewhere, making this change could cause more problems than it solves. The consistent use within this file suggests it's an intentional choice. The comment should be removed. While the spelling looks incorrect, we don't have enough context to be certain this isn't an intentional choice used consistently across the codebase.
34. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:457
  • Draft comment:
    Typo: The parameter name 'proccessId' appears to be misspelled. Consider renaming it to 'processId'.
  • Reason this comment was not posted:
    Comment was on unchanged code.
35. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:490
  • Draft comment:
    Typographical error: 'proccessId' appears to be a typo. It should likely be 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While 'proccessId' is technically misspelled, it appears to be a consistent spelling choice throughout the codebase. Changing it would require changes in many places and potentially in other files not shown here. The misspelling doesn't affect functionality. However, maintaining correct spelling in code is generally good practice for readability and professionalism. The comment identifies a real spelling issue, but I may be underestimating the scope of the change required - there could be many other files using this spelling that we can't see. Given that this appears to be a consistently used identifier across the codebase, changing it would be a significant refactoring effort that goes beyond the scope of this PR. The benefit doesn't justify the risk. While technically correct, this comment suggests a change that would require significant refactoring for minimal benefit. The current spelling, while incorrect, is at least consistent.
36. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:549
  • Draft comment:
    Typo: 'proccessId' might be intended to be spelled as 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While 'proccessId' does appear to be misspelled, it's used consistently throughout the file and likely throughout the codebase. Changing it would require changes across multiple files and could break functionality. This is more of a stylistic/naming issue rather than a functional bug. The comment doesn't suggest a critical code change that's needed. The spelling is technically incorrect, and fixing it early could prevent the incorrect spelling from propagating further in the codebase. Variable naming is important for code readability. However, this appears to be an established variable name used consistently throughout the codebase. Changing it now would require coordinated changes across multiple files and could introduce bugs. The benefit doesn't outweigh the risk. Delete the comment. While the spelling is technically incorrect, changing an established variable name is risky and not critical enough to warrant a PR comment.
37. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:567
  • Draft comment:
    Typo: 'proccessId' might be intended to be spelled as 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While 'proccessId' does appear to be misspelled (should be 'processId'), this is a consistent spelling throughout the codebase. Changing it would require changes across multiple files and could break API compatibility. The typo doesn't affect functionality. The comment is not about a specific change in this diff - this spelling existed before and is just being used in new places. The typo could cause confusion for new developers. Consistent misspellings are still misspellings and could be worth fixing. While fixing typos is good, this appears to be an established variable name in the codebase. Making this change would be a breaking change requiring coordination across multiple files and possibly affecting external API consumers. The comment should be deleted. While technically correct about the spelling, this is an established variable name and changing it would be a significant refactoring effort beyond the scope of this PR review.
38. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:620
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled. Consider renaming it to 'processId' for clarity and consistency.
  • Reason this comment was not posted:
    Comment was on unchanged code.
39. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:634
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled. Consider renaming it to 'processId' for clarity and consistency.
  • Reason this comment was not posted:
    Comment was on unchanged code.
40. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:686
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled. Consider renaming it to 'processId' for clarity.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While this appears to be a legitimate typo that occurs throughout the file, suggesting a rename would require changes across the entire codebase, not just this file. The parameter name is used consistently within this file and likely in other files too. Without seeing the full codebase, we can't be sure this change wouldn't break things. The typo is real, but fixing it could have far-reaching implications beyond this file. The consistent use suggests it might be an intentional spelling used throughout the codebase. Even though it's a real typo, suggesting a rename without understanding the full codebase context could lead to breaking changes. Delete the comment. While it identifies a real typo, suggesting a rename without full codebase context could cause more harm than good.
41. backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts:700
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled here as well. Consider renaming it to 'processId'.
  • Reason this comment was not posted:
    Comment was on unchanged code.
42. backend/plugins/sales_api/src/modules/sales/utils.ts:1198
  • Draft comment:
    Typo: "proccessId" appears to be misspelled. Consider correcting it to "processId" for clarity and consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While the spelling is technically incorrect, this is a variable name that is used consistently throughout the codebase. Changing it would require changes in multiple places and could break compatibility with other parts of the system. The typo doesn't affect functionality. Variable names are an implementation detail that doesn't affect the logic. The spelling is definitely wrong, and consistent naming is important for code readability. Maybe this is a good time to fix it across the codebase? While the spelling is wrong, changing variable names is risky without seeing the full codebase. The consistent use suggests this may be an intentional spelling used in other files or systems. The benefit doesn't outweigh the risk. Delete this comment. While technically correct about the spelling, changing variable names is risky without full context and the current spelling works fine functionally.
43. backend/plugins/sales_api/src/modules/sales/utils.ts:1209
  • Draft comment:
    Typo: "proccessId" appears to be misspelled. Consider correcting it to "processId" for clarity and consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 20% vs. threshold = 50% While the spelling is technically incorrect, this is a variable name that is used consistently throughout the codebase. Changing it would require changes in multiple places and could break compatibility with other parts of the system we can't see. Variable names, even if misspelled, are often left as-is to maintain consistency and avoid breaking changes. The comment is technically correct about the spelling, but may not consider the implications of changing a widely-used variable name. There could be external dependencies relying on this exact spelling. While fixing typos is generally good, in this case the consistent use throughout the codebase suggests this may be an intentional spelling or a legacy name that should be preserved for compatibility. The comment should be deleted. While technically correct about the spelling, changing variable names can be risky and the current spelling is used consistently.
44. backend/plugins/sales_api/src/modules/sales/utils.ts:1225
  • Draft comment:
    Typo: "proccessId" appears to be misspelled. Consider correcting it to "processId" for clarity and consistency.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the comment correctly identifies a typo, changing this variable name would require changes across multiple places in the codebase. The variable is used consistently within this file and likely in other files too. Renaming it could cause breaking changes. The typo doesn't affect functionality. The comment identifies a real issue, but fixing it could be risky without seeing the full codebase. The typo might be intentional or required for compatibility. Even though the typo is real, fixing it would require a larger coordinated change across the codebase. This kind of rename should be part of a dedicated refactoring effort, not a drive-by comment. The comment should be deleted because fixing this typo would require changes beyond this file's scope and could introduce breaking changes. Variable renames should be part of dedicated refactoring efforts.
45. backend/plugins/sales_api/src/modules/sales/utils.ts:1343
  • Draft comment:
    Typo detected: Did you mean 'processId' instead of 'proccessId'?
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
46. backend/plugins/sales_api/src/modules/sales/utils.ts:1382
  • Draft comment:
    Typo: 'proccessId' appears to be misspelled. It looks like it should be 'processId'.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the word is technically misspelled, this appears to be a consistent variable naming choice throughout the codebase. The variable is used in multiple places with the same spelling. Changing it would require changes in many places and could break compatibility with other parts of the system. Variable names are arbitrary identifiers and don't strictly need to match English spelling. The comment is technically correct about English spelling, but may not consider the implications of changing a consistently used variable name across a codebase. While spelling corrections can be good, changing variable names requires more consideration of the full codebase context and compatibility concerns. This comment should be deleted. The variable name, while technically misspelled, is used consistently throughout the code and changing it could have broader implications that aren't worth the cosmetic fix.

Workflow ID: wflow_8txcyxWtKaZnRxF9

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.


export const mutations = `
ticketsChecklistsAdd(contentTypeId: String, title: String): TicketsChecklist
ticketsChecklistsEdit(_id: String!, title: String, contentTypeId: String,): TicketsChecklist
Copy link

Choose a reason for hiding this comment

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

There is a trailing comma in the schema definition (in the ticketsChecklistsEdit mutation declaration) that might cause GraphQL SDL validation issues—verify the SDL syntax.

Suggested change
ticketsChecklistsEdit(_id: String!, title: String, contentTypeId: String,): TicketsChecklist
ticketsChecklistsEdit(_id: String!, title: String, contentTypeId: String): TicketsChecklist

ALL: ['active', 'archived'],
};

export const VISIBLITIES = {
Copy link

Choose a reason for hiding this comment

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

Typo: The constant name 'VISIBLITIES' appears to be misspelled. It should likely be 'VISIBILITIES'.

TEN: '10%',
TWENTY: '20%',
THIRTY: '30%',
FOURTY: '40%',
Copy link

Choose a reason for hiding this comment

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

Typo: The key 'FOURTY' appears to be misspelled. Consider changing it to 'FORTY'.

Suggested change
FOURTY: '40%',
FORTY: '40%',

value: 'noCloseDate',
},
{
name: 'Over due',
Copy link

Choose a reason for hiding this comment

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

Typo: The name 'Over due' contains an extra space. Consider using 'Overdue'.

Suggested change
name: 'Over due',
name: 'Overdue',

excludeCheckUserIds: {
type: [String],
optional: true,
label: 'Users elligible to see all cards',
Copy link

Choose a reason for hiding this comment

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

Typographical error: In the label 'Users elligible to see all cards', 'elligible' should be corrected to 'eligible'.

Suggested change
label: 'Users elligible to see all cards',
label: 'Users eligible to see all cards',


export const getCloseDateByType = (closeDateType: string) => {
if (closeDateType === CLOSE_DATE_TYPES.NEXT_DAY) {
const tommorrow = moment().add(1, 'days');
Copy link

Choose a reason for hiding this comment

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

Typo: "tommorrow" should be "tomorrow".

return Math.random() * (max - min) + min;
};

const orderHeler = (aboveOrder, belowOrder) => {
Copy link

Choose a reason for hiding this comment

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

Typo in function name: "orderHeler" should be "orderHelper".

};
};

export const generateArhivedItemsFilter = (
Copy link

Choose a reason for hiding this comment

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

Typo in function name: "generateArhivedItemsFilter" should likely be "generateArchivedItemsFilter".

Suggested change
export const generateArhivedItemsFilter = (
export const generateArchivedItemsFilter = (

graphqlPubsub.publish(`salesPipelinesChanged:${stage.pipelineId}`, {
salesPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
Copy link

Choose a reason for hiding this comment

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

Typographical error: 'proccessId' appears to be misspelled. Consider correcting it to 'processId'.

Suggested change
proccessId,
processId,

import { generateFilter } from './graphql/resolvers/queries/deals';

export const itemResolver = async (models: IModels, user: any, item: IDeal) => {
const additionInfo = {};
Copy link

Choose a reason for hiding this comment

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

Typo: Consider renaming 'additionInfo' to 'additionalInfo' for better clarity.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 31

🔭 Outside diff range comments (1)
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts (1)

55-103: Fix variable naming inconsistency.

The variable is named ticket but this is dealing with deals. This inconsistency suggests copy-paste from ticket code and could cause confusion.

Apply this diff to fix the naming:

-    const ticket = await models.Deals.createDeal(extendedDoc);
+    const deal = await models.Deals.createDeal(extendedDoc);

-    const stage = await models.Stages.getStage(ticket.stageId);
+    const stage = await models.Stages.getStage(deal.stageId);

     //   await createConformity(subdomain, {
     //     mainType: type,
-    //     mainTypeId: ticket._id,
+    //     mainTypeId: deal._id,
     //     companyIds: doc.companyIds,
     //     customerIds: doc.customerIds,
     //   });

         data: {
-          item: ticket,
+          item: deal,
           aboveItemId: doc.aboveItemId,
           destinationStageId: stage._id,
         },

-    return ticket;
+    return deal;
🧹 Nitpick comments (49)
backend/plugins/frontline_api/src/modules/tickets/db/definitions/boards.ts (1)

4-18: Consider adding field validation for better data integrity.

The schema definition is well-structured and follows good practices. However, consider adding validation rules for required fields:

export const boardSchema = new Schema(
  {
    _id: mongooseStringRandomId,
-   name: { type: String, label: 'Name' },
+   name: { type: String, label: 'Name', required: true },
    order: { type: Number, label: 'Order' },
    type: {
      type: String,
      label: 'Type',
      default: 'ticket',
    },
  },
  {
    timestamps: true,
  },
);
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/checklists.ts (1)

12-21: Consider differentiating event payloads for different operations.

The pub/sub implementation is well-structured, but consider adding operation context to distinguish between add/remove operations:

const checklistsChanged = (checklist: IChecklistDocument, operation: 'add' | 'remove' = 'update') => {
  graphqlPubsub.publish(
    `salesChecklistsChanged:${checklist.contentType}:${checklist.contentTypeId}`,
    {
      salesChecklistsChanged: {
        _id: checklist._id,
        contentType: checklist.contentType,
        contentTypeId: checklist.contentTypeId,
+       operation,
      },
    },
  );
};

This would help subscribers handle different types of changes appropriately.

backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts (3)

34-35: Remove redundant optional property.

The optional: true property is redundant in Mongoose schemas. Fields are optional by default unless marked as required: true.

code: {
  type: String,
  label: 'Code',
-  optional: true,
},

36-36: Remove redundant optional property.

Same issue with the age field.

-age: { type: Number, optional: true, label: 'Age' },
+age: { type: Number, label: 'Age' },

41-45: Remove redundant optional property.

Same issue with the defaultTick field.

defaultTick: {
  type: Boolean,
  label: 'Default tick used',
-  optional: true,
},
backend/plugins/frontline_api/src/modules/tickets/resolver.ts (1)

44-86: Excellent model loading pattern with minor suggestions.

The model loading function follows good practices:

  • Consistent use of generics for type safety
  • Proper separation of document and model interfaces
  • Clean, readable structure

However, consider these improvements:

  1. Inconsistent collection naming - Some collections use tickets_ prefix while others don't (e.g., tickets vs ticket_comments).

  2. Function naming - loadClasses might be confusing since it's loading models, not classes. Consider loadTicketModels.

-export const loadClasses = (models: IModels, db: mongoose.Connection) => {
+export const loadTicketModels = (models: IModels, db: mongoose.Connection) => {

For consistency, consider standardizing collection names:

  models.Comments = db.model<ICommentDocument, ICommentModel>(
-    'ticket_comments',
+    'tickets_comments',
    loadCommentClass(models),
  );
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/checklist.ts (1)

21-27: Unnecessary sorting on single document query.

The ticketsChecklistDetail resolver uses findOne() but still applies sorting. Since findOne() returns only one document, the sorting operation is unnecessary and adds overhead.

Remove the sorting from the single document query:

-    return models.CheckLists.findOne({ _id }).sort({ order: 1 });
+    return models.CheckLists.findOne({ _id });

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/label.ts (1)

26-32: Consider adding input validation for the _id parameter.

While the function logic is correct, adding basic input validation would improve robustness and security.

Consider this improvement:

  async ticketsPipelineLabelDetail(
    _root: undefined,
    { _id }: { _id: string },
    { models }: IContext,
  ) {
+    if (!_id) {
+      throw new Error('Pipeline label ID is required');
+    }
    return models.PipelineLabels.findOne({ _id });
  },
backend/plugins/frontline_api/src/modules/tickets/db/definitions/tickets.ts (1)

66-68: Track the migration status of sourceConversationId.

The TODO comment indicates this field should be removed after migration, but there's no timeline or tracking mechanism.

Consider creating a GitHub issue to track this migration or add a more specific TODO with a target date. Would you like me to help create a migration tracking issue?

backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/index.ts (1)

1-66: Well-structured schema consolidation.

The approach of consolidating schemas from different modules is clean and maintainable. The consistent naming pattern (boardTypes, boardQueries, boardMutations) makes it easy to understand the structure.

Consider adding JSDoc comments to document the purpose of each export for better maintainability:

+/**
+ * Consolidated GraphQL types for the tickets module
+ */
export const types = `
    ${commonDeps}
    ${checkListTypes}
    ${boardTypes}
    ${pipelineTypes}
    ${ticketTypes}
    ${pipelineLabelTypes}
    ${stageTypes}
`;

+/**
+ * Consolidated GraphQL queries for the tickets module
+ */
export const queries = `
    ${checkListQueries}
    ${boardQueries}
    ${pipelineQueries}
    ${ticketQueries}
    ${pipelineLabelQueries}
    ${stageQueries}
`;

+/**
+ * Consolidated GraphQL mutations for the tickets module
+ */
export const mutations = `
    ${checkListMutations}
    ${boardMutations}
    ${pipelineMutations}
    ${ticketMutations}
    ${pipelineLabelMutations}
    ${stageMutations}
`;
backend/plugins/frontline_api/src/modules/tickets/@types/checklist.ts (1)

3-13: Well-structured interface definitions.

The separation between base interfaces (IChecklist) and document interfaces (IChecklistDocument) is a good pattern that promotes reusability and type safety with Mongoose.

Consider adding JSDoc comments for better documentation:

+/**
+ * Base interface for checklist entity
+ */
export interface IChecklist {
  contentType: string;
  contentTypeId: string;
  title: string;
}

+/**
+ * Mongoose document interface for checklist with additional metadata
+ */
export interface IChecklistDocument extends IChecklist, Document {
  _id: string;
  createdUserId: string;
  createdDate: Date;
}
backend/plugins/frontline_api/src/modules/tickets/@types/ticket.ts (1)

11-11: Clarify the TODO comment for better maintainability.

The TODO comment "migrate after remove 2row" is unclear. Please provide more context about what "2row" refers to and what migration is needed.

backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (3)

16-16: Fix typo in comment.

"Retreives" should be "Retrieves".

-     * Retreives comment
+     * Retrieves comment

9-9: Implement the missing updateComment method.

The updateComment method is declared in the interface but not implemented in the class.

Would you like me to generate the implementation for the updateComment method?

+    public static async updateComment(_id: string, doc: IComment) {
+      const updated = await models.Comments.findByIdAndUpdate(
+        _id,
+        { ...doc, modifiedAt: new Date() },
+        { new: true }
+      );
+      
+      if (!updated) {
+        throw new Error('Comment not found');
+      }
+      
+      return updated;
+    }

13-43: Consider using simple functions instead of a class with only static members.

The static analysis tool suggests using simple functions instead of classes with only static members for better performance and simplicity.

-export const loadCommentClass = (models: IModels) => {
-  class Comment {
-    // static methods...
-  }
-  
-  commentSchema.loadClass(Comment);
-  return commentSchema;
-};
+export const loadCommentClass = (models: IModels) => {
+  const commentMethods = {
+    getComment: async (typeId: string) => {
+      // implementation
+    },
+    createComment: async (doc: IComment) => {
+      // implementation  
+    },
+    updateComment: async (_id: string, doc: IComment) => {
+      // implementation
+    },
+    deleteComment: async (_id: string) => {
+      // implementation
+    }
+  };
+  
+  commentSchema.statics = commentMethods;
+  return commentSchema;
+};
backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1)

70-74: Remove unnecessary multi: true option.

Since _id is unique, the multi: true option is unnecessary and potentially misleading.

      await models.Tickets.updateMany(
        { _id: targetId },
        { $set: { labelIds } },
-        { multi: true },
      );
backend/plugins/frontline_api/src/modules/tickets/constants.ts (2)

7-11: Fix typo in constant name.

"VISIBLITIES" should be "VISIBILITIES" for correct spelling.

-export const VISIBLITIES = {
+export const VISIBILITIES = {

24-24: Fix typo in constant name.

"FOURTY" should be "FORTY" for correct spelling.

-  FOURTY: '40%',
+  FORTY: '40%',
backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts (1)

53-90: To understand if removeStage also handles ticket and checklist deletion (making manual deletion redundant), let’s inspect its implementation:

#!/bin/bash
# Show full removeStage method in the tickets Stages model
rg -n -C50 "public static async removeStage" backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts
backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (2)

18-100: Consider refactoring to use plain functions instead of a static-only class.

The class contains only static methods, which can be confusing and goes against typical OOP patterns. Static analysis tools correctly flag this as a code smell.

Consider refactoring to use plain functions:

-export const loadStageClass = (models: IModels) => {
-  class Stage {
-    public static async getStage(_id: string) {
+export const loadStageClass = (models: IModels) => {
+  const stageHelpers = {
+    async getStage(_id: string) {
       const stage = await models.Stages.findOne({ _id });
       // ... rest of method
     }
     // ... other methods
-  }
-  stageSchema.loadClass(Stage);
+  };
+  
+  stageSchema.methods = stageHelpers;
   return stageSchema;
 };

47-47: Replace this with explicit class name in static methods.

Using this in static context is confusing as it refers to the class itself.

-        await this.checkCodeDuplication(doc.code);
+        await Stage.checkCodeDuplication(doc.code);
-        await this.checkCodeDuplication(doc.code);
+        await Stage.checkCodeDuplication(doc.code);

Also applies to: 57-57

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/board.ts (1)

35-47: Consider optimizing board count aggregation for better performance.

The current implementation uses a loop with individual database queries, which could be inefficient with many boards. Consider using MongoDB aggregation pipeline for better performance.

-    for (const board of boards) {
-      const count = await models.Pipelines.find({
-        boardId: board._id,
-      }).countDocuments();
-
-      counts.push({
-        _id: board._id,
-        name: board.name || '',
-        count,
-      });
-
-      allCount += count;
-    }
+    const pipelineCounts = await models.Pipelines.aggregate([
+      { $group: { _id: '$boardId', count: { $sum: 1 } } }
+    ]);
+    
+    const countMap = new Map(pipelineCounts.map(p => [p._id, p.count]));
+    
+    for (const board of boards) {
+      const count = countMap.get(board._id) || 0;
+      counts.push({
+        _id: board._id,
+        name: board.name || '',
+        count,
+      });
+      allCount += count;
+    }
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/ticket.ts (4)

10-47: Complete the companies resolver implementation or remove if not needed.

The entire companies resolver is commented out and returns an empty array. This appears to be unfinished work that should either be implemented or removed if the functionality isn't required.

Do you want me to help implement the companies resolver based on the commented code patterns, or should this be removed if companies relationships aren't needed for tickets?


49-89: Complete the customers resolver implementation or remove if not needed.

Similar to the companies resolver, this is completely commented out and returns an empty array. This inconsistency with other resolvers suggests incomplete implementation.


119-139: Optimize database calls in boardId resolver.

The boardId resolver makes three sequential database calls (getStage → getPipeline → getBoard) that could be optimized with aggregation or caching.

Consider using MongoDB aggregation to fetch the boardId in a single query:

async boardId(
  ticket: ITicketDocument,
  _args: undefined,
  { models }: IContext,
) {
-  const stage = await models.Stages.getStage(ticket.stageId);
-  const pipeline = await models.Pipelines.getPipeline(stage.pipelineId);
-  const board = await models.Boards.getBoard(pipeline.boardId);
-
-  return board._id;
+  const result = await models.Stages.aggregate([
+    { $match: { _id: ticket.stageId } },
+    { $lookup: { from: 'pipelines', localField: 'pipelineId', foreignField: '_id', as: 'pipeline' } },
+    { $unwind: '$pipeline' },
+    { $lookup: { from: 'boards', localField: 'pipeline.boardId', foreignField: '_id', as: 'board' } },
+    { $unwind: '$board' },
+    { $project: { boardId: '$board._id' } }
+  ]);
+  
+  return result[0]?.boardId;
}

91-117: Consider consistent behavior for assignedUsers resolver.

The resolver has different behavior for subscriptions vs regular queries, which could lead to confusion. For subscriptions, it fetches full user data, but for regular queries, it returns reference objects.

Consider documenting this behavior difference or making it consistent across both code paths if the use cases allow.

backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/ticket.ts (2)

78-124: Consider using input types to simplify large parameter lists.

The queryParams definition is very long (45+ parameters) which makes the schema harder to read and maintain. Consider grouping related parameters into input types.

Create specialized input types for better organization:

+input TicketsDateFilters {
+  startDate: String
+  endDate: String
+  createdStartDate: Date
+  createdEndDate: Date
+  stateChangedStartDate: Date
+  stateChangedEndDate: Date
+  startDateStartDate: Date
+  startDateEndDate: Date
+  closeDateStartDate: Date
+  closeDateEndDate: Date
+}
+
+input TicketsFilterInput {
+  _ids: [String]
+  search: String
+  assignedUserIds: [String]
+  priority: [String]
+  source: [String]
+  labelIds: [String]
+  tagIds: [String]
+  dateFilters: TicketsDateFilters
+  customFieldsDataFilters: JSON
+  dateRangeFilters: JSON
+}

-tickets(${queryParams}): TicketsListResponse
+tickets(filters: TicketsFilterInput, pagination: PaginationInput): TicketsListResponse

150-172: Simplify mutation parameters using input types.

Similar to queries, the mutation parameters are extensive and could benefit from being grouped into input types for better maintainability.

Consider creating a TicketInput type:

+input TicketInput {
+  source: String
+  type: String
+  parentId: String
+  stageId: String
+  assignedUserIds: [String]
+  attachments: [AttachmentInput]
+  startDate: Date
+  closeDate: Date
+  description: String
+  priority: String
+  status: String
+  customFieldsData: JSON
+  tagIds: [String]
+  branchIds: [String]
+  departmentIds: [String]
+}

-ticketsAdd(name: String!, companyIds: [String], customerIds: [String], labelIds: [String], ${mutationParams}): Ticket
+ticketsAdd(name: String!, companyIds: [String], customerIds: [String], labelIds: [String], input: TicketInput): Ticket
backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts (1)

233-238: Clean up commented code for consistency.

The commented itemMover call uses inconsistent variable naming that doesn't match the current context.

Clean up the commented code for future reference:

-    // const { content, action } = await itemMover(
-    //   models,
-    //   user._id,
-    //   deal,
-    //   destinationStageId,
-    // );
+    // const { content, action } = await itemMover(
+    //   models,
+    //   user._id,
+    //   deal,
+    //   destinationStageId,
+    // );
backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts (1)

25-167: Consider refactoring static-only class to individual functions.

The static analysis correctly identifies that this class contains only static members. Consider refactoring to individual functions for better simplicity and tree-shaking.

-export const loadTicketClass = (models: IModels) => {
-  class Ticket {
-    public static async getTicket(_id: string) {
+export const loadTicketClass = (models: IModels) => {
+  const getTicket = async (_id: string) => {
     const ticket = await models.Tickets.findOne({ _id });
     
     if (!ticket) {
       throw new Error('Ticket not found');
     }
     
     return ticket;
-    }
+  };

-    public static async createTicket(doc: ITicket) {
+  const createTicket = async (doc: ITicket) => {
     // ... existing implementation
-    }
+  };

   // ... other functions

-  ticketSchema.loadClass(Ticket);
+  ticketSchema.statics = {
+    getTicket,
+    createTicket,
+    updateTicket,
+    watchTicket,
+    removeTickets,
+    createTicketComment,
+  };

   return ticketSchema;
};
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/pipeline.ts (2)

19-71: Consider extracting permission logic for reusability.

The complex permission checking logic for pipelines could be extracted into a utility function for reuse across other resolvers.

+// In utils.ts
+export const buildPipelinePermissionQuery = async (user: any, isAll: boolean) => {
+  const query: any = user.isOwner || isAll
+    ? {}
+    : {
+        status: { $ne: 'archived' },
+        $or: [
+          { visibility: 'public' },
+          {
+            $and: [
+              { visibility: 'private' },
+              {
+                $or: [
+                  { memberIds: { $in: [user._id] } },
+                  { userId: user._id },
+                ],
+              },
+            ],
+          },
+        ],
+      };
+
+  if (!user.isOwner && !isAll) {
+    const userDetail = await sendTRPCMessage({
+      pluginName: 'core',
+      method: 'query',
+      module: 'users',
+      action: 'find',
+      input: { _id: user._id },
+      defaultValue: [],
+    });
+
+    const departmentIds = userDetail?.departmentIds || [];
+
+    if (Object.keys(query) && departmentIds.length > 0) {
+      query.$or.push({
+        $and: [
+          { visibility: 'private' },
+          { departmentIds: { $in: departmentIds } },
+        ],
+      });
+    }
+  }
+
+  return query;
+};

 async ticketsPipelines(
   _root: undefined,
   { boardId, isAll }: { boardId: string; isAll: boolean },
   { user, models }: IContext,
 ) {
-  const query: any = user.isOwner || isAll ? {} : { /* ... */ };
-  // ... rest of permission logic
+  const query = await buildPipelinePermissionQuery(user, isAll);
   
   if (boardId) {
     query.boardId = boardId;
   }

144-163: Optimize database queries in ticketsPipelineAssignedUsers.

The resolver makes multiple queries that could be optimized with aggregation.

 async ticketsPipelineAssignedUsers(
   _root: undefined,
   { _id }: { _id: string },
   { models }: IContext,
 ) {
-  const pipeline = await models.Pipelines.getPipeline(_id);
-
-  const stageIds = await models.Stages.find({
-    pipelineId: pipeline._id,
-  }).distinct('_id');
-
-  const assignedUserIds = await models.Tickets.find({
-    stageId: { $in: stageIds },
-  }).distinct('assignedUserIds');
+  const assignedUserIds = await models.Stages.aggregate([
+    { $match: { pipelineId: _id } },
+    { $lookup: {
+        from: 'tickets',
+        localField: '_id',
+        foreignField: 'stageId',
+        as: 'tickets'
+      }
+    },
+    { $unwind: '$tickets' },
+    { $unwind: '$tickets.assignedUserIds' },
+    { $group: { _id: null, userIds: { $addToSet: '$tickets.assignedUserIds' } } },
+    { $project: { _id: 0, userIds: 1 } }
+  ]);
+
+  const userIds = assignedUserIds[0]?.userIds || [];

-  return assignedUserIds.map((userId) => ({
+  return userIds.map((userId) => ({
     __typename: 'User',
     _id: userId || '',
   }));
 }
backend/plugins/frontline_api/src/modules/tickets/db/models/Pipelines.ts (1)

32-164: Consider refactoring static-only class to module functions.

While the current Mongoose class pattern is functional, consider refactoring to use module-level functions instead of a static-only class for better maintainability and following modern JavaScript patterns.

The pipeline operations logic is comprehensive and properly handles related entity cleanup. The integration with utility functions and proper error handling makes this a solid implementation.

Example refactor approach:

// Instead of static class methods, use module functions
export const getPipeline = async (models: IModels, _id: string) => {
  const pipeline = await models.Pipelines.findOne({ _id }).lean();
  if (!pipeline) {
    throw new Error('Pipeline not found');
  }
  return pipeline;
};

export const createPipeline = async (models: IModels, doc: IPipeline, stages?: IStageDocument[]) => {
  // implementation
};
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (2)

113-115: Apply optional chaining as suggested by static analysis.

The static analysis correctly identifies that this can be simplified using optional chaining.

-  if (_ids && _ids.length) {
+  if (_ids?.length) {
     filter._id = { $in: _ids };
   }

373-391: Remove commented code or implement the segment functionality.

There are large blocks of commented code related to segment functionality. If this is intended for future implementation, consider creating a TODO or moving it to documentation.

-  //   if (segmentData) {
-  //     const segment = JSON.parse(segmentData);
-  //     const itemIds = await fetchSegment(subdomain, '', {}, segment);
-  //     filter._id = { $in: itemIds };
-  //   }
-
-  //   if (segment) {
-  //     const segmentObj = await sendTRPCMessage({
-  //       pluginName: 'core',
-  //       method: 'query',
-  //       module: 'segment',
-  //       action: 'findOne',
-  //       input: { _id: segment },
-  //       defaultValue: {},
-  //     });
-  //     const itemIds = await fetchSegment(subdomain, segmentObj);
-
-  //     filter._id = { $in: itemIds };
-  //   }
+  // TODO: Implement segment filtering functionality
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/stage.ts (1)

54-63: Consider making the sort types configuration more maintainable.

The sortTypes object is hardcoded. Consider extracting it to a constants file for better maintainability and reusability.

+// In a constants file
+export const STAGE_SORT_TYPES = {
+  'created-asc': { createdAt: 1 },
+  'created-desc': { createdAt: -1 },
+  'modified-asc': { modifiedAt: 1 },
+  'modified-desc': { modifiedAt: -1 },
+  'close-asc': { closeDate: 1, order: 1 },
+  'close-desc': { closeDate: -1, order: 1 },
+  'alphabetically-asc': { name: 1 },
+};

// In the resolver
-    const sortTypes = {
-      'created-asc': { createdAt: 1 },
-      'created-desc': { createdAt: -1 },
-      'modified-asc': { modifiedAt: 1 },
-      'modified-desc': { modifiedAt: -1 },
-      'close-asc': { closeDate: 1, order: 1 },
-      'close-desc': { closeDate: -1, order: 1 },
-      'alphabetically-asc': { name: 1 },
-    };
+    import { STAGE_SORT_TYPES } from '~/modules/tickets/constants';
+    const sortTypes = STAGE_SORT_TYPES;
backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1)

45-47: Simplify the unnecessary conditional check.

The condition checklists && checklists.length === 0 can be simplified since an array will never be falsy if it exists.

-    if (checklists && checklists.length === 0) {
+    if (checklists.length === 0) {
       return;
     }
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts (1)

92-131: Consider optimizing the complex aggregation pipeline.

The inProcessTicketsTotalCount resolver uses a complex aggregation with multiple stages and lookups. Consider if this can be optimized or if the result should be cached for frequently accessed data.

The aggregation pipeline performs multiple operations that might be expensive:

  1. $match on stages
  2. $lookup with pipeline to tickets collection
  3. $unwind operation
  4. Another $match on unwound data

Consider adding indexes on the queried fields or implementing caching for this metric if it's frequently accessed.

// Consider adding these indexes:
// - stages: { pipelineId: 1, probability: 1, _id: 1 }
// - tickets: { stageId: 1, status: 1, initialStageId: 1 }
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/ticket.ts (4)

30-31: Apply optional chaining as suggested by static analysis.

The static analysis correctly identifies an opportunity for optional chaining.

-    modifiedBy: user && user._id,
-    userId: user ? user._id : doc.userId,
+    modifiedBy: user?._id,
+    userId: user?._id ?? doc.userId,

193-194: Apply optional chaining as suggested by static analysis.

Another valid suggestion for optional chaining.

-    if (doc.tagIds && doc.tagIds.length) {
+    if (doc.tagIds?.length) {
       doc.tagIds = doc.tagIds.filter((ti) => ti);
     }

468-468: Replace delete operator with undefined assignment for better performance.

The static analysis correctly suggests avoiding the delete operator for performance reasons.

-    delete doc.sourceConversationIds;
+    doc.sourceConversationIds = undefined;

57-68: Remove extensive commented code or implement the notification functionality.

There are large blocks of commented code related to notifications throughout the file. This should either be implemented or removed to improve code clarity.

If notifications are planned for future implementation, consider:

  1. Creating TODO comments instead of keeping commented code
  2. Moving the notification logic to a separate module
  3. Creating GitHub issues to track the implementation
-    // if (user) {
-    // //   const pipeline = await models.Pipelines.getPipeline(stage.pipelineId);
-    //   // sendNotifications(models, subdomain, {
-    //   //   ticket,
-    //   //   user,
-    //   //   type: `${type}Add`,
-    //   //   action: `invited you to the ${pipeline.name}`,
-    //   //   content: `'${ticket.name}'.`,
-    //   //   contentType: type
-    //   // });
-    // }
+    // TODO: Implement notification system for ticket creation

Also applies to: 133-191, 255-270, 322-355, 395-418

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/checklist.ts (1)

26-124: Consider adding error handling to mutation resolvers.

The mutation functions lack explicit error handling, which could lead to unhandled promise rejections or unclear error messages for clients.

Consider wrapping the database operations in try-catch blocks to provide better error handling and logging:

async ticketsChecklistsAdd(
  _root: undefined,
  args: IChecklist,
  { models, user }: IContext,
) {
+  try {
    const checklist = await models.CheckLists.createChecklist(args, user);
    
    checklistsChanged(checklist);
    
    return checklist;
+  } catch (error) {
+    console.error('Error creating checklist:', error);
+    throw error;
+  }
},
backend/plugins/frontline_api/src/modules/tickets/utils.ts (6)

846-886: Use optional chaining to simplify null checks.

Multiple null checks can be simplified using optional chaining for better readability.

Apply these diffs:

-  if (userIds && userIds.length) {
+  if (userIds?.length) {
     filter.userId = { $in: userIds };
   }

-  if (priorities && priorities.length) {
+  if (priorities?.length) {
     filter.priority = { $in: priorities };
   }

-  if (assignedUserIds && assignedUserIds.length) {
+  if (assignedUserIds?.length) {
     filter.assignedUserIds = { $in: assignedUserIds };
   }

-  if (labelIds && labelIds.length) {
+  if (labelIds?.length) {
     filter.labelIds = { $in: labelIds };
   }

-  if (productIds && productIds.length) {
+  if (productIds?.length) {
     filter['productsData.productId'] = { $in: productIds };
   }

-  if (sources && sources.length) {
+  if (sources?.length) {
     filter.source = { $in: sources };
   }

-  if (hackStages && hackStages.length) {
+  if (hackStages?.length) {
     filter.hackStages = { $in: hackStages };
   }

1062-1064: Remove unnecessary continue statement.

The continue statement at the end of a try-catch block is unnecessary.

Apply this diff:

    } catch (unused) {
-      continue;
+      // Silently ignore resolver errors
    }

897-897: Fix typo in function name.

The function name has a typo: orderHeler should be orderHelper.

-const orderHeler = (aboveOrder, belowOrder) => {
+const orderHelper = (aboveOrder, belowOrder) => {

And update the usage on line 941:

-  const order = orderHeler(aboveOrder, belowOrder);
+  const order = orderHelper(aboveOrder, belowOrder);

336-412: Remove or uncomment the large block of commented code.

There's a substantial amount of commented-out code that should either be removed if no longer needed or properly uncommented if it's still required.

This commented code appears to handle conformity relationships between tickets and companies/customers. If this functionality is still needed, uncomment and test it. If not, remove it to improve code maintainability:

-  // const ids = list.map((item) => item._id);
-
-  // const conformities = await sendCoreMessage({
-  //   subdomain,
-  //   action: 'conformities.getConformities',
-  //   data: {
-  //     mainType: type,
-  //     mainTypeIds: ids,
-  //     relTypes: ['company', 'customer'],
-  //   },
-  //   isRPC: true,
-  //   defaultValue: [],
-  // });
+  // Remove this entire block if conformity relationships are handled elsewhere

1045-1068: Improve error handling in ticketResolver function.

The silent error swallowing in the resolver function could hide important issues during development and debugging.

Consider logging errors or being more selective about which errors to ignore:

  for (const subResolver of Object.keys(resolver)) {
    try {
      additionInfo[subResolver] = await resolver[subResolver](
        ticket,
        {},
        { models, subdomain, user },
        { isSubscription: true },
      );
-    } catch (unused) {
-      continue;
+    } catch (error) {
+      // Log resolver errors for debugging but continue processing
+      console.warn(`Resolver ${subResolver} failed:`, error.message);
    }
  }

1-1068: Consider breaking down this large utility file.

This file contains 1068 lines with many different utility functions. Consider splitting it into smaller, more focused modules for better maintainability.

Possible organization:

  • ticket-filters.ts - for filtering utilities
  • ticket-permissions.ts - for permission checking utilities
  • ticket-ordering.ts - for order management utilities
  • ticket-numbering.ts - for number generation utilities
  • ticket-pipeline.ts - for pipeline-related utilities

This would improve code organization and make testing individual components easier.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 777a500 and dc5387f.

📒 Files selected for processing (63)
  • backend/plugins/frontline_api/src/apollo/resolvers/mutations.ts (1 hunks)
  • backend/plugins/frontline_api/src/apollo/resolvers/queries.ts (1 hunks)
  • backend/plugins/frontline_api/src/apollo/resolvers/resolvers.ts (1 hunks)
  • backend/plugins/frontline_api/src/apollo/schema/extensions.ts (1 hunks)
  • backend/plugins/frontline_api/src/apollo/schema/schema.ts (2 hunks)
  • backend/plugins/frontline_api/src/connectionResolvers.ts (5 hunks)
  • backend/plugins/frontline_api/src/modules/inbox/graphql/schemas/conversation.ts (0 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/board.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/comment.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/label.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/@types/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/constants.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/boards.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/checklists.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/comments.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/labels.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/pipelines.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/tickets.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Pipelines.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/board.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/index.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/board.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/index.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/label.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/board.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/index.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/label.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/board.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/common.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/index.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/label.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/resolver.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/utils.ts (1 hunks)
  • backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/checklists.ts (2 hunks)
  • backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts (13 hunks)
  • backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts (2 hunks)
  • backend/plugins/sales_api/src/modules/sales/utils.ts (6 hunks)
💤 Files with no reviewable changes (1)
  • backend/plugins/frontline_api/src/modules/inbox/graphql/schemas/conversation.ts
🧰 Additional context used
🪛 Biome (1.9.4)
backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts

[error] 27-142: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts

[error] 14-38: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts

[error] 18-100: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 47-47: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 57-57: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts

[error] 20-116: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts

[error] 29-96: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 121-197: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/ticket.ts

[error] 30-30: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 193-193: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 468-468: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts

[error] 26-162: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts

[error] 113-113: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

backend/plugins/sales_api/src/modules/sales/utils.ts

[error] 44-44: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

backend/plugins/frontline_api/src/modules/tickets/db/models/Pipelines.ts

[error] 32-164: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/utils.ts

[error] 184-184: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)


[error] 846-846: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 850-850: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 854-854: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 858-858: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 862-862: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 882-882: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 886-886: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 1063-1063: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (73)
backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/common.ts (1)

1-6: LGTM! Clean GraphQL input type definition.

The TicketsOrderItem input type is well-defined with appropriate field types and naming conventions. This will serve as a reusable input structure for ordering operations across the tickets module.

backend/plugins/frontline_api/src/apollo/resolvers/resolvers.ts (2)

2-2: LGTM! Proper import integration.

The import follows the established pattern and uses the appropriate module alias.


6-6: LGTM! Clean resolver integration.

The spread operator properly merges ticket resolvers with existing resolvers, following the established pattern used for inbox resolvers.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/index.ts (1)

1-13: LGTM! Well-structured resolver aggregation.

The file follows a clean modular pattern by aggregating all ticket-related resolvers into a single export point. The naming conventions are consistent and the structure makes it easy to manage and import resolvers.

backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/checklists.ts (2)

2-2: LGTM! Proper import for pub/sub functionality.

The import of graphqlPubsub enables real-time subscription notifications for checklist changes.


25-29: LGTM! Clean detail change notification.

The checklistDetailChanged function properly publishes detail-specific events with the appropriate event key and payload structure.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/index.ts (1)

1-13: LGTM! Clean resolver aggregation pattern.

The mutation resolver consolidation follows good modularity practices by importing and re-exporting related mutations from different domain modules. This provides a clean separation of concerns while offering a unified export interface.

backend/plugins/frontline_api/src/apollo/resolvers/mutations.ts (2)

5-5: Proper integration of ticket mutations.

The import correctly includes ticket mutations into the main resolver structure.


11-12: Clean mutation aggregation.

The spreading of ticketMutations alongside existing mutations properly integrates the ticketing functionality into the GraphQL API.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/index.ts (1)

1-15: Excellent query resolver organization.

The query consolidation pattern mirrors the mutations approach, providing clean modularity and a unified interface for all ticket-related GraphQL queries.

backend/plugins/frontline_api/src/modules/tickets/db/definitions/labels.ts (2)

4-15: Well-designed schema with appropriate constraints.

The schema properly defines pipeline labels with relevant fields and automatic timestamping. The field types and labels are appropriate for the use case.


17-20: Excellent use of compound unique index.

The compound unique index on pipelineId, name, and colorCode effectively prevents duplicate labels within the same pipeline while allowing the same label names/colors across different pipelines. This design supports good data integrity.

backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/stages.ts (2)

2-2: LGTM! Import addition is necessary for pub-sub functionality.

The import of graphqlPubsub is correctly placed and follows the established import pattern.


100-111: LGTM! Pub-sub activation properly implements real-time notifications.

The activated code correctly:

  • Fetches stage information to get the pipeline ID
  • Publishes a well-structured event with all necessary metadata
  • Follows consistent naming conventions for the event structure
backend/plugins/frontline_api/src/modules/tickets/@types/comment.ts (1)

1-17: LGTM! Well-structured TypeScript interfaces for comments.

The interfaces follow best practices:

  • Clean separation between base interface and document interface
  • Appropriate use of optional fields for flexible comment structure
  • Proper Mongoose document extension pattern
  • Support for hierarchical comments through parentId
backend/plugins/frontline_api/src/apollo/schema/extensions.ts (1)

1-31: LGTM! Proper GraphQL federation schema extensions.

The schema extensions correctly implement Apollo Federation patterns:

  • Consistent use of _id as federation keys across all extended types
  • Proper @external directive usage for federation
  • Logical addition of conversations field to User and Customer types
  • Clean template literal format for schema definition
backend/plugins/frontline_api/src/modules/tickets/@types/label.ts (1)

1-14: LGTM! Clean and well-structured label interfaces.

The interfaces are properly designed:

  • Essential fields for a pipeline label system (name, colorCode, pipelineId)
  • Optional createdBy field for audit tracking
  • Consistent Mongoose document extension pattern
  • Appropriate field types for the use case
backend/plugins/frontline_api/src/apollo/resolvers/queries.ts (2)

5-5: LGTM! Consistent import pattern for ticket queries.

The import follows the established pattern used by other query resolver imports in the file.


11-12: LGTM! Proper integration of ticket queries.

The spread operator integration maintains consistency with the existing resolver structure and follows the same pattern used for other query modules.

backend/plugins/frontline_api/src/modules/tickets/@types/pipeline.ts (1)

32-36: Good use of Mongoose Document pattern.

The IPipelineDocument interface correctly extends both the domain interface and Mongoose's Document, providing proper typing for MongoDB operations.

backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts (2)

9-56: Well-structured schema with good practices.

The schema follows good Mongoose patterns:

  • Uses proper field types and constraints
  • Implements enum validation
  • Includes timestamps
  • Uses descriptive labels
  • Properly configures the _id field

3-7: Verifying TypeScript path alias in the frontline_api plugin:

#!/bin/bash
# Check if frontline_api tsconfig extends a base config
grep -R '"extends"' -n backend/plugins/frontline_api/tsconfig.json || true

# Look for baseUrl and paths mappings
grep -R '"baseUrl"' -n backend/plugins/frontline_api/tsconfig.json || true
grep -R '"paths"' -n backend/plugins/frontline_api/tsconfig.json || true

# Specifically search for the "~" alias
grep -R '"~/*"' -n backend/plugins/frontline_api/tsconfig.json || true
backend/plugins/frontline_api/src/modules/tickets/@types/board.ts (1)

11-16: Good document interface design.

The IBoardDocument interface correctly extends the domain interface and includes the optional pipelines relationship, which is appropriate for a board that may or may not have loaded pipelines.

backend/plugins/frontline_api/src/apollo/schema/schema.ts (3)

32-54: Excellent GraphQL schema integration.

The tickets module integration follows the established pattern perfectly:

  • Consistent import naming convention
  • Proper template literal composition
  • Maintains the same order and structure as other modules

26-31: ```shell
#!/bin/bash
file="backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/index.ts"
echo "Export statements in $file:"
rg -n "export" "$file" || true

echo -e "\nOccurrences of 'mutations', 'queries', and 'types' in $file:"
rg -n "mutations" "$file" || true
rg -n "queries" "$file" || true
rg -n "types" "$file" || true


---

`6-6`: ```shell
#!/bin/bash
# Verify that TypeExtensions is exported in the new local extensions file
grep -R "export.*TypeExtensions" -n backend/plugins/frontline_api/src/apollo/schema/extensions.ts
backend/plugins/frontline_api/src/modules/tickets/@types/stage.ts (3)

5-5: Verify the data type for probability field.

The probability field is typed as string, which seems unusual for probability values that are typically numeric (0-1 or 0-100). Please confirm if this should be number instead, or if there's a specific reason for using string (e.g., storing textual probability descriptions).

</review_comment_end -->


3-20: Well-structured interface design.

The interface design follows good TypeScript practices with clear separation of concerns. The required fields (pipelineId, type) represent core functionality while optional fields provide flexibility for various stage configurations.

</review_comment_end -->


22-26: Proper Mongoose document extension.

The IStageDocument correctly extends both the base interface and Mongoose's Document, following standard patterns for typed MongoDB documents with explicit _id and createdAt fields.

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/checklist.ts (2)

5-13: Efficient and well-structured items resolver.

The resolver correctly fetches checklist items with proper sorting by order, ensuring consistent presentation. The query is efficient and follows GraphQL resolver patterns.

</review_comment_end -->


15-33: Robust percentage calculation with edge case handling.

The percentage calculation is mathematically correct and properly handles the edge case of empty checklists by returning 0. The logic correctly counts checked items and calculates the percentage.

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/board.ts (3)

11-13: Good optimization for cached pipelines.

The early return for existing board.pipelines is an effective optimization that avoids unnecessary database queries.

</review_comment_end -->


15-20: Efficient owner bypass logic.

The owner bypass logic is simple and efficient, providing full access to non-archived pipelines for admin users.

</review_comment_end -->


57-88: Complex but well-structured access control query.

The MongoDB query correctly implements role-based access control with proper visibility rules. The use of lean() is good for performance. The query structure handles public/private visibility and department/branch-based access appropriately.

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/checklist.ts (1)

7-16: Well-implemented list query with proper filtering and sorting.

The ticketsChecklists resolver correctly filters by contentTypeId and applies appropriate sorting for consistent ordering of results.

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/db/definitions/checklists.ts (2)

4-25: Well-designed checklist schema with proper indexing.

The schema structure is well-organized with appropriate field types, indexes on reference fields for query performance, and proper defaults. The use of custom string IDs and timestamps follows best practices.

</review_comment_end -->


27-39: Appropriate checklist item schema design.

The checklist item schema properly references the parent checklist with indexing and includes all necessary fields for functionality. The order field supports item sequencing, and the isChecked boolean enables completion tracking.

</review_comment_end -->

backend/plugins/frontline_api/src/modules/tickets/db/definitions/tickets.ts (1)

90-96: Excellent use of sparse unique index.

The combination of unique: true and sparse: true is the correct approach for optional unique fields, ensuring uniqueness only among documents that have this field set.

backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/board.ts (1)

1-16: Well-structured GraphQL types with proper Federation support.

The type definitions are clean and properly use Apollo Federation's @key directive for distributed schema composition. The relationship between boards and pipelines is correctly modeled.

backend/plugins/frontline_api/src/modules/tickets/@types/checklist.ts (1)

15-26: Consistent interface pattern for checklist items.

The checklist item interfaces follow the same well-designed pattern as the checklist interfaces. The inclusion of the order field in the document interface is appropriate for maintaining item ordering in the UI.

backend/plugins/frontline_api/src/modules/tickets/@types/ticket.ts (1)

54-54: ```shell
#!/bin/bash
echo "=== Ticket.ts (first 200 lines) ==="
sed -n '1,200p' backend/plugins/frontline_api/src/modules/tickets/@types/ticket.ts || echo "File not found or unreadable"

echo
echo "=== Searching for IStage definition in frontline_api ==="
rg -n "interface IStage" backend/plugins/frontline_api/src --type ts || echo "No IStage definition found in frontline_api"


</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/label.ts (1)</summary>

`1-29`: **Well-structured GraphQL schema for pipeline labels.**

The schema definition looks comprehensive and well-organized:
- Proper type definitions with federation support
- Complete CRUD operations through mutations
- Efficient parameter reuse
- Clear separation of concerns between types, queries, and mutations

</details>
<details>
<summary>backend/plugins/frontline_api/src/connectionResolvers.ts (2)</summary>

`99-107`: **Proper integration of ticket models into the IModels interface.**

The extension of the IModels interface to include all ticket-related models (Boards, Pipelines, Stages, Tickets, PipelineLabels, CheckLists, CheckListItems, Comments) is well-structured and follows the existing pattern.

---

`187-187`: **Clean integration of ticket model loading.**

The call to `loadTicketClasses(models, db)` properly integrates the ticket model loading into the existing model loading flow.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/stage.ts (1)</summary>

`1-68`: **Comprehensive and well-structured GraphQL schema for ticket stages.**

The schema definition is thorough and well-organized:
- Complete type definition with federation support via @key directive
- Reusable query parameters for consistent filtering across operations
- Comprehensive queries supporting various use cases including pagination
- Full mutation support for stage management and ticket sorting
- Clean separation of reusable components

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (5)</summary>

`37-50`: **LGTM!**

The uniqueness validation logic is correctly implemented with proper exclusion of the current document during updates.

---

`80-94`: **LGTM!**

The label creation logic is well-implemented with proper uniqueness validation and clear error handling.

---

`99-112`: **LGTM!**

The update logic correctly validates uniqueness and handles the update operation properly.

---

`117-131`: **LGTM!**

The removal logic properly handles cascading cleanup of label references while maintaining data integrity.

---

`27-142`: **Static-only class pattern is appropriate for Mongoose model loading.**

While Biome suggests avoiding static-only classes, this pattern is commonly used with Mongoose to load methods into schemas via `schema.loadClass()`. The pattern is appropriate for this use case.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/constants.ts (1)</summary>

`1-94`: **Well-structured constants module.**

The constants are well-organized with consistent patterns and helpful ALL arrays for validation purposes.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/db/definitions/pipelines.ts (1)</summary>

`9-92`: **Comprehensive and well-structured schema.**

The pipeline schema is well-designed with:
- Proper field types and validations
- Good use of constants for enums
- Comprehensive coverage of pipeline features
- Appropriate default values and optional fields

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts (2)</summary>

`24-48`: **LGTM!**

The basic CRUD operations are well-implemented with proper error handling and standard patterns.

---

`20-116`: **Static-only class pattern is appropriate for Mongoose model loading.**

Similar to the Labels model, this pattern is commonly used with Mongoose for loading methods into schemas.

</details>
<details>
<summary>backend/plugins/sales_api/src/modules/sales/utils.ts (3)</summary>

`1184-1235`: **Well-implemented GraphQL subscription publishing.**

The publishing logic properly handles different scenarios:
- Pipeline changes (itemRemove/itemAdd events)
- Regular updates (itemUpdate events)
- Uses itemResolver for consistent data enrichment

---

`1340-1393`: **LGTM!**

The status change publishing logic correctly handles different status transitions and maintains proper order for items being restored from archived state.

---

`44-44`: **Static analysis false positive - continue statement is necessary.**

The continue statement is required to skip to the next resolver when the current one fails. This is not unnecessary.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (1)</summary>

`22-30`: **LGTM! Core CRUD operations are well implemented.**

The getStage, createStage, updateStage, updateOrder, and removeStage methods (aside from the deletion logic issue) follow good patterns with proper error handling and cascading operations.




Also applies to: 45-50, 55-63, 68-70, 75-99

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/label.ts (1)</summary>

`4-55`: **LGTM! Clean and well-structured GraphQL mutations.**

The mutation resolvers follow GraphQL best practices with proper parameter destructuring, async/await usage, and delegation to model methods. The code is clean and maintainable.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts (1)</summary>

`6-77`: **LGTM! Well-implemented GraphQL field resolvers.**

The field resolvers are well-structured with:
- Proper early returns for null checks
- Clean date-based state computation logic
- Appropriate user permission checking
- Efficient database queries with proper filtering

The code follows GraphQL best practices and handles edge cases appropriately.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/pipeline.ts (1)</summary>

`1-79`: **LGTM! Comprehensive and well-structured GraphQL schema.**

The schema definition is excellent with:
- Comprehensive type coverage for TicketsPipeline
- Proper Apollo Federation support with @key directive
- Well-organized and reusable mutation parameters
- Consistent naming conventions following GraphQL best practices
- Good coverage of queries for different use cases

The schema provides a solid foundation for the ticketing system.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/board.ts (1)</summary>

`9-15`: **LGTM! Well-implemented query resolvers with good practices.**

The query resolvers demonstrate good practices with:
- Efficient use of lean() for performance
- Proper async/await patterns
- Good integration with TRPC for external service calls
- Clean data structure handling

The overall architecture is solid and follows GraphQL best practices.




Also applies to: 20-52, 57-67, 72-78, 98-163

</details>
<details>
<summary>backend/plugins/sales_api/src/modules/sales/graphql/resolvers/mutations/deals.ts (1)</summary>

`90-101`: **LGTM! Well-structured subscription event publishing.**

The subscription event publishing is properly implemented with the correct event structure and data enrichment.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/pipeline.ts (1)</summary>

`4-71`: **LGTM! Well-implemented security and permission checking.**

The permission logic properly handles different user roles, visibility settings, and department-based access control.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/stage.ts (1)</summary>

`36-66`: **Excellent permission filtering implementation.**

The visibility and membership-based filtering logic is well-implemented, properly handling public/private visibility, user membership, and department-based access control.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/board.ts (2)</summary>

`82-109`: **Efficient bulk operations for Gantt timeline updates.**

The use of MongoDB bulk operations for updating multiple tickets simultaneously is an excellent performance optimization. The logic correctly maps items to update operations and filters relations by start item ID.

---

`47-59`: **Proper cleanup of related field groups.**

The implementation correctly handles cleanup of board references in related field groups via TRPC communication, ensuring data integrity across the system.

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/pipeline.ts (2)</summary>

`125-154`: **Well-implemented pipeline duplication logic.**

The pipeline copying functionality correctly handles:
- Copying pipeline properties while excluding the original ID
- Adding "-copied" suffix to distinguish duplicates
- Recreating all associated stages with proper relationships
- Setting appropriate defaults for stage probability

---

`20-22`: **Good validation practices with checkNumberConfig.**

The consistent validation of number configuration before pipeline creation and editing ensures data integrity and prevents invalid configurations.




Also applies to: 38-40

</details>
<details>
<summary>backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (1)</summary>

`243-361`: ```shell
#!/bin/bash
TARGET="backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts"

echo "=== isCheckUser occurrences ==="
rg -n "isCheckUser" "$TARGET" || echo "None found"

echo "=== isCheckDepartment occurrences ==="
rg -n "isCheckDepartment" "$TARGET" || echo "None found"

echo "=== excludeCheckUserIds occurrences ==="
rg -n "excludeCheckUserIds" "$TARGET" || echo "None found"

echo "=== commonIds occurrences ==="
rg -n "commonIds" "$TARGET" || echo "None found"
backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1)

29-96: Consider the static analysis suggestion about static-only classes.

The static analysis correctly identifies that these classes contain only static methods. However, in this Mongoose context, this pattern is commonly used for model classes and provides good organization. The current implementation is acceptable, but you could consider using plain functions if preferred.

The static analysis suggestion is a false positive in this context. Mongoose model classes with static methods are a common and appropriate pattern for organizing database operations.

Also applies to: 121-197

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts (1)

170-220: Verify the aggregation logic in compareNextStageTicket.

The aggregation logic appears correct, but the mathematical calculation on lines 224-226 should be verified for edge cases, particularly division by zero.

   if (stages.length === 2) {
     const [first, second] = stages;
     result.count = first.currentTicketCount - second.currentTicketCount;
+    if (first.initialTicketCount === 0) {
+      result.percent = 0;
+    } else {
       result.percent =
         (second.initialTicketCount * 100) / first.initialTicketCount;
+    }
   }
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/ticket.ts (1)

434-551: Verify the complex ticket copying logic for data integrity.

The ticketsCopy function performs complex operations including checklist cloning. Ensure this maintains data integrity and consider wrapping it in a transaction.

The copying logic involves multiple database operations:

  1. Create ticket copy
  2. Clone checklists
  3. Clone checklist items
  4. Publish events

Consider implementing this within a database transaction to ensure atomicity:

// Consider wrapping the copy operation in a transaction
const session = await models.startSession();
try {
  await session.withTransaction(async () => {
    const clone = await models.Tickets.create(doc);
    // ... rest of the copying logic
  });
} finally {
  await session.endSession();
}

Comment on lines 5 to 17
export const commentSchema = new Schema({
_id: mongooseStringRandomId,
typeId: { type: String, label: 'Type Id' },
type: { type: String, label: 'Type' },

content: { type: String, label: 'Content' },
parentId: { type: String, label: 'Parent Id' },

userId: { type: String, label: 'User Id' },
userType: { type: String, enum: COMMENT_USER_TYPES.ALL, label: 'User Type' },

createdAt: { type: Date, label: 'Created at' },
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider schema improvements for better data integrity.

The comment schema could benefit from several enhancements:

  1. Inconsistent timestamp handling: You're manually defining createdAt but not using Mongoose's automatic timestamps. Consider using timestamps: true option for consistency and to get both createdAt and updatedAt.

  2. Missing field validations: Consider adding validation for critical fields like content (required, max length) and ensuring typeId/type consistency.

  3. Potential data integrity issues: No constraints to prevent orphaned comments or ensure valid parent-child relationships.

-export const commentSchema = new Schema({
+export const commentSchema = new Schema({
   _id: mongooseStringRandomId,
   typeId: { type: String, label: 'Type Id' },
   type: { type: String, label: 'Type' },

-  content: { type: String, label: 'Content' },
+  content: { type: String, required: true, maxlength: 10000, label: 'Content' },
   parentId: { type: String, label: 'Parent Id' },

   userId: { type: String, label: 'User Id' },
   userType: { type: String, enum: COMMENT_USER_TYPES.ALL, label: 'User Type' },

-  createdAt: { type: Date, label: 'Created at' },
-});
+}, {
+  timestamps: true,
+});
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const commentSchema = new Schema({
_id: mongooseStringRandomId,
typeId: { type: String, label: 'Type Id' },
type: { type: String, label: 'Type' },
content: { type: String, label: 'Content' },
parentId: { type: String, label: 'Parent Id' },
userId: { type: String, label: 'User Id' },
userType: { type: String, enum: COMMENT_USER_TYPES.ALL, label: 'User Type' },
createdAt: { type: Date, label: 'Created at' },
});
export const commentSchema = new Schema({
_id: mongooseStringRandomId,
typeId: { type: String, label: 'Type Id' },
type: { type: String, label: 'Type' },
content: { type: String, required: true, maxlength: 10000, label: 'Content' },
parentId: { type: String, label: 'Parent Id' },
userId: { type: String, label: 'User Id' },
userType: { type: String, enum: COMMENT_USER_TYPES.ALL, label: 'User Type' },
}, {
timestamps: true,
});
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/db/definitions/comments.ts
around lines 5 to 17, improve the commentSchema by enabling Mongoose's automatic
timestamps option to handle createdAt and updatedAt fields consistently. Add
validation rules such as making content required and setting a maximum length to
ensure data quality. Implement checks or constraints to maintain consistency
between typeId and type fields. Also, add logic or schema constraints to prevent
orphaned comments by validating parentId references to existing comments.

Comment on lines 3 to 30
export interface IPipeline {
name?: string;
boardId: string;
status?: string;
visibility?: string;
memberIds?: string[];
bgColor?: string;
watchedUserIds?: string[];
startDate?: Date;
endDate?: Date;
metric?: string;
hackScoringType?: string;
templateId?: string;
isCheckDate?: boolean;
isCheckUser?: boolean;
isCheckDepartment?: boolean;
excludeCheckUserIds?: string[];
numberConfig?: string;
numberSize?: string;
nameConfig?: string;
lastNum?: string;
departmentIds?: string[];
branchIds?: string[];
tagId?: string;
order?: number;
userId?: string;
type: string;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider refining the interface design for better type safety and maintainability.

The IPipeline interface has several areas for improvement:

  1. Large interface with many optional fields - This suggests the interface might be doing too much. Consider breaking it into smaller, more focused interfaces.

  2. Inconsistent field naming - hackScoringType seems oddly named for a production system. Consider more descriptive naming.

  3. Weak typing for configuration fields - Fields like numberConfig, nameConfig, and metric are typed as string but could benefit from more specific types or enums.

  4. Missing constraints on required fields - The type field is required but has no enum constraints, which could lead to invalid values.

Consider this improved typing approach:

+export enum PipelineType {
+  TICKET = 'ticket',
+  TASK = 'task',
+  DEAL = 'deal'
+}
+
+export enum PipelineVisibility {
+  PUBLIC = 'public',
+  PRIVATE = 'private'
+}

export interface IPipeline {
  name?: string;
  boardId: string;
  status?: string;
-  visibility?: string;
+  visibility?: PipelineVisibility;
  memberIds?: string[];
  bgColor?: string;
  watchedUserIds?: string[];
  startDate?: Date;
  endDate?: Date;
  metric?: string;
-  hackScoringType?: string;
+  scoringType?: string;
  templateId?: string;
  isCheckDate?: boolean;
  isCheckUser?: boolean;
  isCheckDepartment?: boolean;
  excludeCheckUserIds?: string[];
  numberConfig?: string;
  numberSize?: string;
  nameConfig?: string;
  lastNum?: string;
  departmentIds?: string[];
  branchIds?: string[];
  tagId?: string;
  order?: number;
  userId?: string;
-  type: string;
+  type: PipelineType;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface IPipeline {
name?: string;
boardId: string;
status?: string;
visibility?: string;
memberIds?: string[];
bgColor?: string;
watchedUserIds?: string[];
startDate?: Date;
endDate?: Date;
metric?: string;
hackScoringType?: string;
templateId?: string;
isCheckDate?: boolean;
isCheckUser?: boolean;
isCheckDepartment?: boolean;
excludeCheckUserIds?: string[];
numberConfig?: string;
numberSize?: string;
nameConfig?: string;
lastNum?: string;
departmentIds?: string[];
branchIds?: string[];
tagId?: string;
order?: number;
userId?: string;
type: string;
}
export enum PipelineType {
TICKET = 'ticket',
TASK = 'task',
DEAL = 'deal'
}
export enum PipelineVisibility {
PUBLIC = 'public',
PRIVATE = 'private'
}
export interface IPipeline {
name?: string;
boardId: string;
status?: string;
visibility?: PipelineVisibility;
memberIds?: string[];
bgColor?: string;
watchedUserIds?: string[];
startDate?: Date;
endDate?: Date;
metric?: string;
scoringType?: string;
templateId?: string;
isCheckDate?: boolean;
isCheckUser?: boolean;
isCheckDepartment?: boolean;
excludeCheckUserIds?: string[];
numberConfig?: string;
numberSize?: string;
nameConfig?: string;
lastNum?: string;
departmentIds?: string[];
branchIds?: string[];
tagId?: string;
order?: number;
userId?: string;
type: PipelineType;
}
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/@types/pipeline.ts from
lines 3 to 30, the IPipeline interface is too large with many optional fields,
has inconsistent naming, weak typing for config fields, and lacks enum
constraints on required fields. Refactor by splitting the interface into
smaller, focused interfaces for better maintainability. Rename hackScoringType
to a clearer, more descriptive name. Replace string types for numberConfig,
nameConfig, and metric with specific enums or union types to enforce valid
values. Define an enum for the type field and use it to constrain the allowed
values, improving type safety.

Comment on lines +4 to +9
export interface IBoard {
name?: string;
order?: number;
userId?: string;
type: string;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add type constraints for consistency.

Similar to the pipeline interface, the type field should have enum constraints to prevent invalid values.

+export enum BoardType {
+  TICKET = 'ticket',
+  TASK = 'task',
+  DEAL = 'deal'
+}

export interface IBoard {
  name?: string;
  order?: number;
  userId?: string;
-  type: string;
+  type: BoardType;
}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface IBoard {
name?: string;
order?: number;
userId?: string;
type: string;
}
export enum BoardType {
TICKET = 'ticket',
TASK = 'task',
DEAL = 'deal'
}
export interface IBoard {
name?: string;
order?: number;
userId?: string;
type: BoardType;
}
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/@types/board.ts between
lines 4 and 9, the IBoard interface's type field lacks enum constraints, which
can lead to invalid values. Define an enum for allowed type values similar to
the pipeline interface and update the type field to use this enum, ensuring only
valid predefined values are assignable.

Comment on lines +36 to +50
const supervisorDepartmentIds = await sendTRPCMessage({
pluginName: 'core',
method: 'query',
module: 'departments',
action: 'findWithChild',
input: {
query: {
supervisorId: user._id,
},
fields: {
_id: 1,
},
},
defaultValue: [],
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for supervisor departments TRPC call.

Similar to the user details call, this TRPC call also lacks error handling and could cause resolver failures.

Apply the same error handling pattern as suggested for the user details call.
</review_comment_end -->

🤖 Prompt for AI Agents
In
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/board.ts
around lines 36 to 50, the TRPC call to fetch supervisorDepartmentIds lacks
error handling, which may cause resolver failures. Wrap the sendTRPCMessage call
in a try-catch block or use a pattern consistent with the user details call to
catch and handle errors gracefully. Ensure that in case of failure, a default
empty array or appropriate fallback is returned to prevent the resolver from
breaking.

Comment on lines +16 to +587
export const ticketMutations = {
/**
* Create new ticket
*/
async ticketsAdd(
_root: undefined,
doc: ITicket & { proccessId: string; aboveItemId: string },
{ user, models }: IContext,
) {
doc.initialStageId = doc.stageId;
doc.watchedUserIds = user && [user._id];

const extendedDoc = {
...doc,
modifiedBy: user && user._id,
userId: user ? user._id : doc.userId,
order: await getNewOrder({
collection: models.Tickets,
stageId: doc.stageId,
aboveItemId: doc.aboveItemId,
}),
};

if (extendedDoc.customFieldsData) {
// clean custom field values

extendedDoc.customFieldsData = await sendTRPCMessage({
pluginName: 'core',
method: 'mutation',
module: 'fields',
action: 'prepareCustomFieldsData',
input: {
customFieldsData: extendedDoc.customFieldsData,
},
defaultValue: [],
});
}

const ticket = await models.Tickets.createTicket(extendedDoc);
const stage = await models.Stages.getStage(ticket.stageId);

// if (user) {
// // const pipeline = await models.Pipelines.getPipeline(stage.pipelineId);

// // sendNotifications(models, subdomain, {
// // ticket,
// // user,
// // type: `${type}Add`,
// // action: `invited you to the ${pipeline.name}`,
// // content: `'${ticket.name}'.`,
// // contentType: type
// // });
// }

graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId: doc.proccessId,
action: 'itemAdd',
data: {
item: ticket,
aboveItemId: doc.aboveItemId,
destinationStageId: stage._id,
},
},
});

return ticket;
},
/**
* Edit ticket
*/
async ticketsEdit(
_root: undefined,
{ _id, proccessId, ...doc }: ITicket & { _id: string; proccessId: string },
{ user, models, subdomain }: IContext,
) {
const ticket = await models.Tickets.getTicket(_id);

const extendedDoc = {
...doc,
modifiedAt: new Date(),
modifiedBy: user._id,
};

const stage = await models.Stages.getStage(ticket.stageId);

const { canEditMemberIds } = stage;

if (
canEditMemberIds &&
canEditMemberIds.length > 0 &&
!canEditMemberIds.includes(user._id)
) {
throw new Error('Permission denied');
}

if (extendedDoc.customFieldsData) {
// clean custom field values
extendedDoc.customFieldsData = await sendTRPCMessage({
pluginName: 'core',
method: 'mutation',
module: 'fields',
action: 'prepareCustomFieldsData',
input: {
customFieldsData: extendedDoc.customFieldsData,
},
defaultValue: [],
});
}

const updatedItem = await models.Tickets.updateTicket(_id, extendedDoc);
// labels should be copied to newly moved pipeline
if (doc.stageId) {
await copyPipelineLabels(models, { item: ticket, doc, user });
}

// const notificationDoc: IBoardNotificationParams = {
// item: updatedItem,
// user,
// type: `${type}Edit`,
// contentType: type,
// };

if (doc.status && ticket.status && ticket.status !== doc.status) {
const activityAction = doc.status === 'active' ? 'activated' : 'archived';

// order notification
if (activityAction === 'archived') {
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'itemRemove',
data: {
ticket,
oldStageId: ticket.stageId,
},
},
});

return;
}
}
if (doc.assignedUserIds) {
const { addedUserIds, removedUserIds } = checkUserIds(
ticket.assignedUserIds,
doc.assignedUserIds,
);

const activityContent = { addedUserIds, removedUserIds };

// notificationDoc.invitedUsers = addedUserIds;
// notificationDoc.removedUsers = removedUserIds;
}

// await sendNotifications(models, subdomain, notificationDoc);

// if (!notificationDoc.invitedUsers && !notificationDoc.removedUsers) {
// sendCoreMessage({
// subdomain: 'os',
// action: 'sendMobileNotification',
// data: {
// title: notificationDoc?.item?.name,
// body: `${
// user?.details?.fullName || user?.details?.shortName
// } has updated`,
// receivers: notificationDoc?.item?.assignedUserIds,
// data: {
// type,
// id: _id,
// },
// },
// });
// }

// exclude [null]
if (doc.tagIds && doc.tagIds.length) {
doc.tagIds = doc.tagIds.filter((ti) => ti);
}

const updatedStage = await models.Stages.getStage(updatedItem.stageId);

if (doc.tagIds || doc.startDate || doc.closeDate || doc.name) {
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
},
});
}

if (updatedStage.pipelineId !== stage.pipelineId) {
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'itemRemove',
data: {
item: ticket,
oldStageId: stage._id,
},
},
});
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: updatedStage.pipelineId,
proccessId,
action: 'itemAdd',
data: {
item: {
...updatedItem,
...(await ticketResolver(models, subdomain, user, updatedItem)),
},
aboveItemId: '',
destinationStageId: updatedStage._id,
},
},
});
} else {
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'itemUpdate',
data: {
item: {
...updatedItem,
...(await ticketResolver(models, subdomain, user, updatedItem)),
},
},
},
});
}

if (ticket.stageId === updatedItem.stageId) {
return updatedItem;
}

// if task moves between stages
// const { content, action } = await itemMover(
// models,
// user._id,
// ticket,
// updatedItem.stageId,
// );

// await sendNotifications(models, subdomain, {
// item: updatedItem,
// user,
// type: `${type}Change`,
// content,
// action,
// contentType: type
// });

return updatedItem;
},

/**
* Change ticket
*/
async ticketsChange(
_root: undefined,
doc: {
proccessId: string;
itemId: string;
aboveItemId?: string;
destinationStageId: string;
sourceStageId: string;
},
{ user, models, subdomain }: IContext,
) {
const {
proccessId,
itemId,
aboveItemId,
destinationStageId,
sourceStageId,
} = doc;

const ticket = await models.Tickets.getTicket(itemId);
const stage = await models.Stages.getStage(ticket.stageId);

const extendedDoc: ITicket = {
modifiedAt: new Date(),
modifiedBy: user._id,
stageId: destinationStageId,
order: await getNewOrder({
collection: models.Tickets,
stageId: destinationStageId,
aboveItemId,
}),
};

if (ticket.stageId !== destinationStageId) {
checkMovePermission(stage, user);

const destinationStage = await models.Stages.getStage(destinationStageId);

checkMovePermission(destinationStage, user);

extendedDoc.stageChangedDate = new Date();
}

const updatedItem = await models.Tickets.updateTicket(itemId, extendedDoc);

// const { content, action } = await itemMover(
// models,
// subdomain,
// user._id,
// ticket,
// destinationStageId,
// );

// await sendNotifications(models, subdomain, {
// item,
// user,
// type: `${type}Change`,
// content,
// action,
// contentType: type,
// });

// if (ticket?.assignedUserIds && ticket?.assignedUserIds?.length > 0) {
// sendCoreMessage({
// subdomain: 'os',
// action: 'sendMobileNotification',
// data: {
// title: `${ticket.name}`,
// body: `${user?.details?.fullName || user?.details?.shortName} ${
// action + content
// }`,
// receivers: ticket?.assignedUserIds,
// data: {
// type,
// id: ticket._id,
// },
// },
// });
// }

// order notification
const labels = await models.PipelineLabels.find({
_id: {
$in: ticket.labelIds,
},
});

graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'orderUpdated',
data: {
item: {
...ticket,
...(await ticketResolver(models, subdomain, user, ticket)),
labels,
},
aboveItemId,
destinationStageId,
oldStageId: sourceStageId,
},
},
});

return ticket;
},

/**
* Remove ticket
*/
async ticketsRemove(
_root: undefined,
{ _id }: { _id: string },
{ models }: IContext,
) {
const ticket = await models.Tickets.getTicket(_id);

// await sendNotifications(models, subdomain, {
// ticket,
// user,
// type: `ticketDelete`,
// action: `deleted ticket:`,
// content: `'${ticket.name}'`,
// contentType: 'ticket'
// });

// if (ticket?.assignedUserIds && ticket?.assignedUserIds?.length > 0) {
// sendCoreMessage({
// subdomain: "os",
// action: "sendMobileNotification",
// data: {
// title: `${ticket.name}`,
// body: `${user?.details?.fullName || user?.details?.shortName} deleted the ticket`,
// receivers: ticket?.assignedUserIds,
// data: {
// type: 'ticket',
// id: ticket._id
// }
// }
// });
// }

return await models.Tickets.findOneAndDelete({ _id: ticket._id });
},

/**
* Watch ticket
*/
async ticketsWatch(
_root: undefined,
{ _id, isAdd }: { _id: string; isAdd: boolean },
{ user, models }: IContext,
) {
return models.Tickets.watchTicket(_id, isAdd, user._id);
},

async ticketsCopy(
_root: undefined,
{ _id, proccessId }: { _id: string; proccessId: string },
{ user, models, subdomain }: IContext,
) {
const item = await models.Tickets.getTicket(_id);

const doc = {
...item,
_id: undefined,
userId: user._id,
modifiedBy: user._id,
watchedUserIds: [user._id],
assignedUserIds: item.assignedUserIds,
name: `${item.name}-copied`,
initialStageId: item.initialStageId,
stageId: item.stageId,
description: item.description,
priority: item.priority,
labelIds: item.labelIds,
order: await getNewOrder({
collection: models.Tickets,
stageId: item.stageId,
aboveItemId: item._id,
}),

attachments: (item.attachments || []).map((a) => ({
url: a.url,
name: a.name,
type: a.type,
size: a.size,
})),
};

delete doc.sourceConversationIds;

for (const param of ['source']) {
doc[param] = item[param];
}

const clone = await models.Tickets.create(doc);

// const companyIds = await getCompanyIds(subdomain, type, _id);
// const customerIds = await getCustomerIds(subdomain, type, _id);

const originalChecklists = await models.CheckLists.find({
contentTypeId: item._id,
}).lean();

const clonedChecklists = await models.CheckLists.insertMany(
originalChecklists.map((originalChecklist) => ({
contentTypeId: clone._id,
title: originalChecklist.title,
createdUserId: user._id,
createdDate: new Date(),
})),
{ ordered: true },
);

const originalChecklistIdToClonedId = new Map<string, string>();

for (let i = 0; i < originalChecklists.length; i++) {
originalChecklistIdToClonedId.set(
originalChecklists[i]._id,
clonedChecklists[i]._id,
);
}

const originalChecklistItems = await models.CheckListItems.find({
checklistId: { $in: originalChecklists.map((x) => x._id) },
}).lean();

await models.CheckListItems.insertMany(
originalChecklistItems.map(({ content, order, checklistId }) => ({
checklistId: originalChecklistIdToClonedId.get(checklistId),
isChecked: false,
createdUserId: user._id,
createdDate: new Date(),
content,
order,
})),
{ ordered: false },
);

// order notification
const stage = await models.Stages.getStage(clone.stageId);

graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'itemAdd',
data: {
item: {
...clone,
...(await ticketResolver(models, subdomain, user, clone)),
},
aboveItemId: _id,
destinationStageId: stage._id,
},
},
});

graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId: Math.random().toString(),
action: 'itemOfConformitiesUpdate',
data: {
item: {
...item,
},
},
},
});

return clone;
},

async ticketsArchive(
_root: undefined,
{ stageId, proccessId }: { stageId: string; proccessId: string },
{ models }: IContext,
) {
const tickets = await models.Tickets.find({
stageId,
status: { $ne: TICKET_STATUSES.ARCHIVED },
}).lean();

await models.Tickets.updateMany(
{ stageId },
{ $set: { status: TICKET_STATUSES.ARCHIVED } },
);

// order notification
const stage = await models.Stages.getStage(stageId);

for (const ticket of tickets) {
graphqlPubsub.publish(`ticketsPipelinesChanged:${stage.pipelineId}`, {
ticketsPipelinesChanged: {
_id: stage.pipelineId,
proccessId,
action: 'itemsRemove',
data: {
item: ticket,
destinationStageId: stage._id,
},
},
});
}

return 'ok';
},
};
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Consider breaking down the large ticketMutations object for better maintainability.

The file contains 587 lines with multiple complex mutation functions. Consider splitting this into separate files by functional area (e.g., ticketCreate.ts, ticketUpdate.ts, ticketCopy.ts).

Suggested structure:

mutations/
├── ticket/
│   ├── index.ts        // Re-exports all mutations
│   ├── create.ts       // ticketsAdd
│   ├── update.ts       // ticketsEdit, ticketsChange  
│   ├── copy.ts         // ticketsCopy
│   ├── archive.ts      // ticketsArchive
│   └── watch.ts        // ticketsWatch, ticketsRemove
🧰 Tools
🪛 Biome (1.9.4)

[error] 30-30: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 193-193: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 468-468: Avoid the delete operator which can impact performance.

Unsafe fix: Use an undefined assignment instead.

(lint/performance/noDelete)

🤖 Prompt for AI Agents
In
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/ticket.ts
spanning lines 16 to 587, the ticketMutations object is very large and complex,
making it hard to maintain. To fix this, split the mutations into separate files
by their functional areas, such as create.ts for ticketsAdd, update.ts for
ticketsEdit and ticketsChange, copy.ts for ticketsCopy, archive.ts for
ticketsArchive, and watch.ts for ticketsWatch and ticketsRemove. Then create an
index.ts file to re-export all these mutations, organizing them under a
mutations/ticket/ directory for better modularity and maintainability.

Comment on lines +117 to +123
async ticketsChecklistItemsOrder(
_root: undefined,
{ _id, destinationIndex }: { _id: string; destinationIndex: number },
{ models }: IContext,
) {
return models.CheckListItems.updateItemOrder(_id, destinationIndex);
},
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add subscription notification for real-time updates.

The ticketsChecklistItemsOrder function should publish subscription events to notify clients of order changes.

Apply this diff:

 async ticketsChecklistItemsOrder(
   _root: undefined,
   { _id, destinationIndex }: { _id: string; destinationIndex: number },
   { models }: IContext,
 ) {
-  return models.CheckListItems.updateItemOrder(_id, destinationIndex);
+  const result = await models.CheckListItems.updateItemOrder(_id, destinationIndex);
+  
+  ticketsChecklistDetailChanged(result.checklistId);
+  
+  return result;
 },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async ticketsChecklistItemsOrder(
_root: undefined,
{ _id, destinationIndex }: { _id: string; destinationIndex: number },
{ models }: IContext,
) {
return models.CheckListItems.updateItemOrder(_id, destinationIndex);
},
async ticketsChecklistItemsOrder(
_root: undefined,
{ _id, destinationIndex }: { _id: string; destinationIndex: number },
{ models }: IContext,
) {
const result = await models.CheckListItems.updateItemOrder(_id, destinationIndex);
ticketsChecklistDetailChanged(result.checklistId);
return result;
},
🤖 Prompt for AI Agents
In
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/checklist.ts
around lines 117 to 123, the ticketsChecklistItemsOrder function currently
updates the item order but does not notify clients of this change. Modify the
function to publish a subscription event after updating the order by using the
appropriate pubsub mechanism from the context or models. Ensure the event
includes relevant details about the updated checklist item order so subscribed
clients receive real-time updates.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (8)
backend/plugins/sales_api/src/modules/sales/utils.ts (1)

31-49: Address static analysis hint: Remove unnecessary continue statement.

The continue statement at line 44 is unnecessary since it's the last statement in the loop iteration.

Apply this diff to remove the unnecessary continue:

    } catch (unused) {
-      continue;
    }

Note: The error handling concerns in this function were already raised in previous reviews.

backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (1)

78-84: Fix critical logic error in ticket deletion.

The deletion query is incorrect - it's trying to match stageId against ticketIds (which contains ticket IDs), but should match _id against ticketIds.

Apply this fix:

     const ticketIds = await models.Tickets.find({
       stageId: { $eq: _id },
     }).distinct('_id');

     await models.CheckLists.removeChecklists(ticketIds);

-    await models.Tickets.deleteMany({ stageId: { $in: ticketIds } });
+    await models.Tickets.deleteMany({ _id: { $in: ticketIds } });
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/pipeline.ts (1)

116-116: Past review comment appears to be resolved.

The type annotation for _id parameter is now correctly specified as string. The destructuring parameter type looks proper.

backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts (2)

49-97: Add validation for required fields in createTicket.

The method should validate essential fields like ticket name before proceeding with creation.


145-151: Fix potential runtime error in watchTicket method.

The method doesn't check if the user exists in the array before removing, which could cause issues.

backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1)

178-196: Fix updateItemOrder method to handle order collisions properly.

The current implementation uses a simple swap which can cause issues if multiple items have the same order value.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (1)

22-465: Consider refactoring the large generateFilter function for better maintainability.

The function is 443 lines long, making it difficult to understand, test, and maintain.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/checklist.ts (1)

122-128: Add subscription notification for real-time updates.

The ticketsChecklistItemsOrder function should publish subscription events to notify clients of order changes, maintaining consistency with other checklist mutations.

Apply this diff:

 async ticketsChecklistItemsOrder(
   _root: undefined,
   { _id, destinationIndex }: { _id: string; destinationIndex: number },
   { models }: IContext,
 ) {
-  return models.CheckListItems.updateItemOrder(_id, destinationIndex);
+  const result = await models.CheckListItems.updateItemOrder(_id, destinationIndex);
+  
+  ticketsChecklistDetailChanged(result.checklistId);
+  
+  return result;
 },
🧹 Nitpick comments (6)
backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (1)

47-47: Replace 'this' with class name in static methods.

Using this in static context can be confusing and should be replaced with the explicit class name.

Apply these fixes:

     if (doc.code) {
-      await this.checkCodeDuplication(doc.code);
+      await Stage.checkCodeDuplication(doc.code);
     }
     if (doc.code) {
-      await this.checkCodeDuplication(doc.code);
+      await Stage.checkCodeDuplication(doc.code);
     }

Also applies to: 57-57

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/pipeline.ts (1)

142-151: Review stage copying logic for data consistency.

The stage copying logic may have an issue with probability handling. When copying stages, the probability defaults to '10%' if not present, but this might not be appropriate for all stage types.

Consider making the probability default more contextual:

      await models.Stages.createStage({
        ...rest,
-       probability: stage.probability || '10%',
+       probability: stage.probability || (copied.type === 'deal' ? '10%' : undefined),
        type: copied.type,
        pipelineId: copied._id,
      });
backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (2)

113-113: Use optional chaining for cleaner code.

The condition can be simplified using optional chaining.

-  if (_ids && _ids.length) {
+  if (_ids?.length) {

371-420: Remove commented code sections.

Large blocks of commented code should be removed to improve code readability and maintainability.

-  //   if (segmentData) {
-  //     const segment = JSON.parse(segmentData);
-  //     const itemIds = await fetchSegment(subdomain, '', {}, segment);
-  //     filter._id = { $in: itemIds };
-  //   }
-
-  //   if (segment) {
-  //     const segmentObj = await sendTRPCMessage({
-  //       pluginName: 'core',
-  //       method: 'query',
-  //       module: 'segment',
-  //       action: 'findOne',
-  //       input: { _id: segment },
-  //       defaultValue: {},
-  //     });
-  //     const itemIds = await fetchSegment(subdomain, segmentObj);
-
-  //     filter._id = { $in: itemIds };
-  //   }
-
-  //   if (vendorCustomerIds?.length > 0) {
-  //     const cards = await sendCommonMessage({
-  //       subdomain,
-  //       serviceName: 'clientportal',
-  //       action: 'clientPortalUserCards.find',
-  //       data: {
-  //         contentType: 'ticket',
-  //         cpUserId: { $in: vendorCustomerIds },
-  //       },
-  //       isRPC: true,
-  //       defaultValue: [],
-  //     });
-  //     const cardIds = cards.map((d) => d.contentTypeId);
-  //     if (filter._id) {
-  //       const ids = filter._id.$in;
-  //       const newIds = ids.filter((d) => cardIds.includes(d));
-  //       filter._id = { $in: newIds };
-  //     } else {
-  //       filter._id = { $in: cardIds };
-  //     }
-  //   }
backend/plugins/frontline_api/src/modules/tickets/utils.ts (2)

842-884: Use optional chaining for cleaner code.

Multiple conditions can be simplified using optional chaining operators for better readability.

Apply these improvements:

-  if (userIds && userIds.length) {
+  if (userIds?.length) {
     filter.userId = { $in: userIds };
   }

-  if (priorities && priorities.length) {
+  if (priorities?.length) {
     filter.priority = { $in: priorities };
   }

-  if (assignedUserIds && assignedUserIds.length) {
+  if (assignedUserIds?.length) {
     filter.assignedUserIds = { $in: assignedUserIds };
   }

-  if (labelIds && labelIds.length) {
+  if (labelIds?.length) {
     filter.labelIds = { $in: labelIds };
   }

-  if (productIds && productIds.length) {
+  if (productIds?.length) {
     filter['productsData.productId'] = { $in: productIds };
   }

-  if (sources && sources.length) {
+  if (sources?.length) {
     filter.source = { $in: sources };
   }

-  if (hackStages && hackStages.length) {
+  if (hackStages?.length) {
     filter.hackStages = { $in: hackStages };
   }

45-45: Add type annotations for better type safety.

The numberCalculator function parameters should have proper type annotations.

Apply this diff:

-const numberCalculator = (size: number, num?: any, skip?: boolean) => {
+const numberCalculator = (size: number, num?: number | string, skip?: boolean): string => {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc5387f and ee770fa.

📒 Files selected for processing (24)
  • backend/plugins/frontline_api/src/modules/tickets/@types/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/constants.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/comments.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/pipelines.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/tickets.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/stage.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/checklist.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/pipeline.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/utils.ts (1 hunks)
  • backend/plugins/sales_api/src/modules/sales/utils.ts (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (11)
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/comments.ts
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts
  • backend/plugins/frontline_api/src/modules/tickets/db/definitions/pipelines.ts
  • backend/plugins/frontline_api/src/modules/tickets/@types/pipeline.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts
  • backend/plugins/frontline_api/src/modules/tickets/constants.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/ticket.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/pipeline.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/schemas/checklist.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/stage.ts
  • backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/mutations/stage.ts
🧰 Additional context used
🪛 Biome (1.9.4)
backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts

[error] 14-85: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Tickets.ts

[error] 32-191: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts

[error] 29-96: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 121-197: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts

[error] 14-35: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts

[error] 27-146: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Stages.ts

[error] 18-100: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)


[error] 47-47: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)


[error] 57-57: Using this in a static context can be confusing.

this refers to the class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts

[error] 113-113: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

backend/plugins/frontline_api/src/modules/tickets/utils.ts

[error] 842-842: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 846-846: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 850-850: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 854-854: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 858-858: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 878-878: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 882-882: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)


[error] 1055-1055: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

backend/plugins/sales_api/src/modules/sales/utils.ts

[error] 44-44: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (13)
backend/plugins/sales_api/src/modules/sales/utils.ts (3)

1184-1235: LGTM! Well-implemented subscription publishing.

The GraphQL subscription publishing logic correctly handles different scenarios:

  • General pipeline changes for field updates
  • Cross-pipeline moves with proper remove/add event sequence
  • Same-pipeline updates with enriched data
  • Consistent use of itemResolver for complete subscription payloads

1340-1350: LGTM! Proper handling of item archiving.

The subscription event for archived items correctly publishes an itemRemove event, which aligns with expected UI behavior of removing archived items from active pipeline views.


1379-1393: LGTM! Proper handling of item activation.

The subscription event for activated items correctly publishes an itemAdd event with enriched data and proper positioning information, ensuring consistency with the pipeline UI state.

backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (2)

18-26: LGTM - Previous issues have been resolved.

The getComment method now correctly uses findOne() instead of find(), properly handling the single document return and error checking.


28-30: LGTM - Parameter type is correct.

The createComment method parameter type correctly uses IComment as declared in the interface.

backend/plugins/frontline_api/src/modules/tickets/db/models/Boards.ts (1)

47-84: LGTM - Cascade deletion logic is well implemented.

The removeBoard method correctly handles cascading deletions of related entities (pipelines, stages, tickets, checklists) to maintain data integrity. The sequential deletion approach ensures referential integrity is maintained.

backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1)

140-145: LGTM - Method signature now matches interface.

The labelsLabel method parameters now correctly match the interface declaration.

backend/plugins/frontline_api/src/modules/tickets/db/definitions/tickets.ts (2)

107-118: LGTM - Field labels have been corrected.

The labels for branchIds and departmentIds are now correctly set to "Branches" and "Departments" respectively, resolving the previous issue.


31-125: Excellent schema design for comprehensive ticket management.

The ticket schema is well-structured with:

  • Proper field types and indexing for performance
  • Support for time tracking, relations, and custom fields
  • Appropriate defaults and constraints
  • Legacy field handling for migration compatibility
backend/plugins/frontline_api/src/modules/tickets/db/models/Checklists.ts (1)

87-95: removeChecklist method appears to be fixed.

The method now correctly calls models.CheckLists.findOneAndDelete instead of the incorrect CheckListItems model reference.

backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/stage.ts (1)

7-231: Well-structured custom resolvers with appropriate separation of concerns.

The file demonstrates good practices with:

  • Clear function separation for different resolver concerns
  • Proper use of MongoDB aggregation pipelines for complex queries
  • Appropriate member visibility handling
  • Sound comparison logic between stages

The aggregation queries are complex but appear correctly structured for the ticket analytics requirements.

backend/plugins/frontline_api/src/modules/tickets/utils.ts (2)

152-154: Critical operation: Verify ticket deletion logic.

The function deletes all tickets associated with stages that are being removed. Ensure this behavior is intentional and properly documented, as it could result in data loss.

Consider adding logging or additional validation before deleting tickets:

+ // Log critical operation
+ console.warn(`Deleting ${stageIds.length} stages and their associated tickets`);
  await models.Tickets.deleteMany({ stageId: { $in: stageIds } });

1-1061: Well-structured utility module with comprehensive ticket management functionality.

The file provides a robust set of utilities for ticket management including filtering, ordering, permissions, and data manipulation. The code follows good async/await patterns and handles complex business logic effectively.

export interface ICommentModel extends Model<ICommentDocument> {
getComment(typeId: string): Promise<ICommentDocument>;
createComment(comment: IComment): Promise<ICommentDocument>;
updateComment(_id: string, comment: IComment): Promise<ICommentDocument>;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Implement missing updateComment method.

The interface declares an updateComment method but there's no implementation in the class. This will cause runtime errors if the method is called.

Add the missing implementation:

    public static async deleteComment(_id: string) {
      return models.Comments.deleteOne({ _id });
    }
+
+    public static async updateComment(_id: string, comment: IComment) {
+      await models.Comments.updateOne({ _id }, { $set: comment });
+      return models.Comments.findOne({ _id });
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
updateComment(_id: string, comment: IComment): Promise<ICommentDocument>;
public static async deleteComment(_id: string) {
return models.Comments.deleteOne({ _id });
}
public static async updateComment(_id: string, comment: IComment) {
await models.Comments.updateOne({ _id }, { $set: comment });
return models.Comments.findOne({ _id });
}
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts at
line 9, the updateComment method is declared in the interface but not
implemented in the class, which will cause runtime errors. Implement the
updateComment method in the class to accept an _id and a comment object, perform
the update operation on the database, and return the updated comment document as
a Promise.

return validSearchText([document.name || '', document.description || '']);
};

export const configReplacer = (config) => {
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add type annotation for better type safety.

The config parameter should have a proper type annotation.

Apply this diff:

-export const configReplacer = (config) => {
+export const configReplacer = (config: string) => {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const configReplacer = (config) => {
export const configReplacer = (config: string) => {
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/utils.ts at line 34, the
function parameter `config` lacks a type annotation, reducing type safety. Add
an appropriate type annotation to the `config` parameter based on its expected
structure or interface to improve type checking and maintainability.

Comment on lines +1054 to +1056
} catch (unused) {
continue;
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unnecessary continue and improve error handling.

The continue statement is unnecessary, and silently ignoring errors might hide important issues.

Apply this diff:

     } catch (unused) {
-      continue;
+      // Log error or handle appropriately if needed
     }

Consider logging errors for debugging purposes instead of silently ignoring them.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} catch (unused) {
continue;
}
} catch (unused) {
// Log error or handle appropriately if needed
}
🧰 Tools
🪛 Biome (1.9.4)

[error] 1055-1055: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/utils.ts around lines 1054
to 1056, remove the unnecessary continue statement inside the catch block and
replace it by logging the caught error to improve error visibility and
debugging. Ensure the error is logged with sufficient detail without
interrupting the flow.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed ee770fa in 13 minutes and 15 seconds. Click for details.
  • Reviewed 799 lines of code in 24 files
  • Skipped 0 files when reviewing.
  • Skipped posting 11 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/plugins/sales_api/src/modules/sales/utils.ts:93
  • Draft comment:
    In the watchItem function, the splice operation is called immediately after obtaining the index from indexOf. If the userId is not found, indexOf returns -1, causing splice(-1, 1) to remove the last element unexpectedly. Consider checking that the index is greater than or equal to 0 before splicing.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
2. backend/plugins/sales_api/src/modules/sales/utils.ts:182
  • Draft comment:
    In the createBoardItem function's catch block, when a duplicate key error is detected, the recursive call to createBoardItem is awaited but its result is not returned. This can lead to undefined being returned. Please add a 'return' statement before the recursive call.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
3. backend/plugins/sales_api/src/modules/sales/utils.ts:407
  • Draft comment:
    Typo detected: 'tommorrow' should be 'tomorrow' to improve code readability and consistency.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. backend/plugins/sales_api/src/modules/sales/utils.ts:975
  • Draft comment:
    The function name 'orderHeler' appears to be a typographical error. Consider renaming it to 'orderHelper' for clarity.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. backend/plugins/frontline_api/src/modules/tickets/constants.ts:24
  • Draft comment:
    Typographical error fixed: 'FOURTY' has been corrected to 'FORTY'.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only states that a typographical error has been fixed. It doesn't provide any actionable feedback or suggestions for improvement.
6. backend/plugins/frontline_api/src/modules/tickets/constants.ts:90
  • Draft comment:
    Typo fixed: changed 'Over due' to 'Overdue'. Looks good!
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only points out a typo correction and states that it looks good. It doesn't provide any actionable feedback or suggestions for improvement.
7. backend/plugins/frontline_api/src/modules/tickets/db/definitions/stages.ts:27
  • Draft comment:
    Typographical error: Corrected 'VISIBLITIES' to 'VISIBILITIES'. Please verify that the variable name is consistent across the codebase.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
8. backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts:16
  • Draft comment:
    Typo: In the doc comment, "Retreives comment" should be corrected to "Retrieves comment".
  • Reason this comment was not posted:
    Comment was on unchanged code.
9. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/customResolvers/pipeline.ts:3
  • Draft comment:
    Typo fixed: The identifier was corrected from 'VISIBLITIES' to 'VISIBILITIES'.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, as it only states that a typo was fixed. It does not provide any actionable feedback or suggestions for improvement.
10. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts:309
  • Draft comment:
    Typo detected: The variable name 'uqinueCheckUserIds' appears to be misspelled. Consider using 'uniqueCheckUserIds' for clarity.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
11. backend/plugins/frontline_api/src/modules/tickets/graphql/resolvers/queries/ticket.ts:348
  • Draft comment:
    Typo detected: The variable name 'uqinueCheckUserIds' is also misspelled here. Changing it to 'uniqueCheckUserIds' would improve readability.
  • Reason this comment was not posted:
    Comment was on unchanged code.

Workflow ID: wflow_QhDNtLp1eLsbJ8Ez

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed dc19858 in 1 minute and 43 seconds. Click for details.
  • Reviewed 29 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 2 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts:8
  • Draft comment:
    Removal of updateComment in ICommentModel: ensure all consumers are updated accordingly if update functionality is needed.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is asking the PR author to ensure that all consumers are updated accordingly, which violates the rule against asking the author to ensure compatibility or similar actions. It does not provide a specific suggestion or point out a specific issue with the code.
2. backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts:74
  • Draft comment:
    Replaced updateMany with updateOne in labelObject; confirm that targetId uniquely identifies a ticket.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% The comment violates the rule about not asking for confirmations or verifications. MongoDB _id fields are unique by definition. The interface and method signature clearly expect a single targetId. The change from updateMany to updateOne actually makes the code more correct since we're dealing with a single ticket. Perhaps there could be a legitimate reason to update multiple tickets with the same _id, though this would be highly unusual in MongoDB. MongoDB _ids are guaranteed to be unique, and the entire codebase structure treats targetId as a single ticket identifier. The change to updateOne is actually an improvement. Delete this comment as it violates the rule about asking for confirmations, and the change is actually an improvement given MongoDB's _id uniqueness guarantee.

Workflow ID: wflow_BpyOZUVlNUfalsUz

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (1)

15-15: Fix typo in comment.

"Retreives" should be "Retrieves".

-     * Retreives comment
+     * Retrieves comment
backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1)

66-72: Optimize label validation query.

The current approach uses find().countDocuments() which can be inefficient. Consider using countDocuments() directly for better performance.

-      const prevLabelsCount = await models.PipelineLabels.find({
-        _id: { $in: labelIds },
-      }).countDocuments();
+      const prevLabelsCount = await models.PipelineLabels.countDocuments({
+        _id: { $in: labelIds },
+      });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee770fa and dc19858.

📒 Files selected for processing (2)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (1 hunks)
  • backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts

[error] 13-34: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts

[error] 27-142: Avoid classes that contain only static members.

Prefer using simple functions instead of classes with only static members.

(lint/complexity/noStaticOnlyClass)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
backend/plugins/frontline_api/src/modules/tickets/db/models/Comments.ts (1)

13-34: Consider the static-only class pattern in context.

While the static analysis tool suggests avoiding classes with only static members, this pattern is appropriate here for Mongoose's loadClass functionality, which attaches static methods to schemas. The current implementation is consistent with the codebase's model definition approach.

backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts (3)

27-142: Pattern is appropriate for Mongoose loadClass.

While the static analysis suggests avoiding static-only classes, this pattern is correct for Mongoose's loadClass functionality and follows the established codebase conventions for model definitions.


87-87: Fix method call on wrong model.

The code calls models.PipelineLabels.validateUniqueness() but validateUniqueness is a static method on the class, not the model instance. This should be called on the class.

-      const isUnique = await models.PipelineLabels.validateUniqueness(filter);
+      const isUnique = await PipelineLabel.validateUniqueness(filter);

Likely an incorrect or invalid review comment.


100-103: Fix method call consistency.

Same issue as above - validateUniqueness should be called on the class, not the model instance.

-      const isUnique = await models.PipelineLabels.validateUniqueness(
-        { ...doc },
-        _id,
-      );
+      const isUnique = await PipelineLabel.validateUniqueness(
+        { ...doc },
+        _id,
+      );

Likely an incorrect or invalid review comment.

Comment on lines +137 to +140
await models.PipelineLabels.labelObject({
labelIds,
targetId,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix method call on wrong model.

The labelObject method should be called on the class, not the model instance.

-      await models.PipelineLabels.labelObject({
-        labelIds,
-        targetId,
-      });
+      await PipelineLabel.labelObject({
+        labelIds,
+        targetId,
+      });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await models.PipelineLabels.labelObject({
labelIds,
targetId,
});
await PipelineLabel.labelObject({
labelIds,
targetId,
});
🤖 Prompt for AI Agents
In backend/plugins/frontline_api/src/modules/tickets/db/models/Labels.ts around
lines 137 to 140, the method labelObject is incorrectly called on the model
instance models.PipelineLabels. Change the call to invoke labelObject on the
PipelineLabels class itself instead of the instance to fix the issue.

Copy link

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed 215f0c1 in 1 minute and 14 seconds. Click for details.
  • Reviewed 13 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 1 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. scripts/create-backend-plugin.js:248
  • Draft comment:
    Updated import path now uses the capitalized module name for consistency. Please verify that the actual file in 'db/models' is also named accordingly to avoid runtime path issues.
  • Reason this comment was not posted:
    Confidence changes required: 33% <= threshold 50% None

Workflow ID: wflow_jUTxMQtYPtbKY1iE

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

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.

2 participants