Skip to content

[#36] Add rating for resolved ticket#6

Merged
sksingh2005 merged 1 commit into
mainfrom
feat/ratings
May 19, 2026
Merged

[#36] Add rating for resolved ticket#6
sksingh2005 merged 1 commit into
mainfrom
feat/ratings

Conversation

@sksingh2005

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a customer satisfaction rating workflow for resolved tickets, spanning DB schema, API responses, UI display/input, and an automated closure policy for unrated resolved tickets.

Changes:

  • Add rating, ratingComment, and resolvedAt to the Ticket model and expose them in ticket detail/workbench APIs.
  • Add a /tickets/{id}/rating endpoint and a scheduled job to auto-close unrated resolved tickets after a configurable number of days.
  • Add a reusable Ratings UI component and surface rating display/submission in ticket detail/workbench pages; update the tickets manual.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/frontend/src/types/domain/tickets.ts Extends ticket domain types with rating and ratingComment.
src/frontend/src/pages/TicketWorkbenchFormPage.tsx Displays rating (read-only) on the workbench edit form when present.
src/frontend/src/pages/SupportTicketDetailPage.tsx Adds rating submission UI for resolved tickets and read-only display when already rated.
src/frontend/src/components/ui/ratings.tsx New star-rating UI component used by pages.
src/backend/main/resources/application.properties Adds config for auto-close window (ticket.rating.auto-close-days).
src/backend/main/java/ai/mnemosyne_systems/service/TicketAutoCloseService.java New scheduled service to auto-close unrated resolved tickets.
src/backend/main/java/ai/mnemosyne_systems/resource/UserTicketApiResource.java Includes rating fields in user ticket detail response payload.
src/backend/main/java/ai/mnemosyne_systems/resource/TicketWorkbenchApiResource.java Includes rating fields in workbench bootstrap/form payload.
src/backend/main/java/ai/mnemosyne_systems/resource/TicketResource.java Tracks resolvedAt when status transitions to/from Resolved.
src/backend/main/java/ai/mnemosyne_systems/resource/TicketRatingApiResource.java New endpoint for submitting ticket ratings.
src/backend/main/java/ai/mnemosyne_systems/resource/SupportTicketApiResource.java Includes rating fields in support ticket detail response payload.
src/backend/main/java/ai/mnemosyne_systems/resource/SuperuserTicketApiResource.java Includes rating fields in superuser ticket detail response payload.
src/backend/main/java/ai/mnemosyne_systems/model/Ticket.java Adds persisted fields: rating, rating comment, and resolved timestamp.
doc/manual/en/11-tickets.md Documents rating flow and auto-close behavior.

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

Comment on lines +44 to +58
User user = AuthHelper.findUser(auth);
if (user == null || (!User.TYPE_USER.equalsIgnoreCase(user.type) && !AuthHelper.isSupport(user)
&& !AuthHelper.isSuperuser(user) && !AuthHelper.isTam(user))) {
throw new WebApplicationException(Response.seeOther(URI.create("/login")).build());
}

Ticket ticket = Ticket.findById(id);
if (ticket == null) {
throw new NotFoundException();
}

// Only the requester or someone from the same company can logically rate, but we enforce typical ticket access.
if (!MessageVisibilitySupport.canAccessTicket(user, ticket)) {
throw new WebApplicationException(Response.seeOther(URI.create("/")).build());
}
Comment thread src/backend/main/java/ai/mnemosyne_systems/resource/TicketRatingApiResource.java Outdated
Comment on lines +69 to +71
ticket.rating = rating;
ticket.ratingComment = ratingComment;

Comment thread src/backend/main/java/ai/mnemosyne_systems/resource/TicketResource.java Outdated
for (Ticket ticket : ticketsToClose) {
LOGGER.infof("Auto-closing ticket %d (%s) as it has been resolved for %d days with no rating.", ticket.id,
ticket.name, autoCloseDays);
String previousStatus = ticket.status;
Comment thread src/backend/main/resources/application.properties Outdated
Comment thread src/frontend/src/components/ui/ratings.tsx Outdated
Comment on lines +141 to +148
<div
style={{
position: "absolute",
top: 0,
overflow: "hidden",
width: `${fillPercentage * 100}%`,
}}
>
Comment on lines +62 to +66
const rating = value || 0;
const fullStars = Math.floor(rating);
const hasPartial = rating % 1 > 0;
const emptyCount = Math.max(0, totalStars - fullStars - (hasPartial ? 1 : 0));

Comment on lines +37 to +43
@POST
@Path("/{id}/rating")
@Transactional
public Response submitRating(@CookieParam(AuthHelper.AUTH_COOKIE) String auth,
@HeaderParam("X-Billetsys-Client") String client, @PathParam("id") Long id,
@FormParam("rating") Integer rating, @FormParam("ratingComment") String ratingComment) {

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 5 comments.

Comment thread src/frontend/src/components/ui/ratings.tsx Outdated
Comment thread src/frontend/src/components/ui/ratings.tsx
Comment on lines +231 to +233
if (!sameStatus(previousStatus, "Resolved") && sameStatus(status, "Resolved")) {
ticket.resolvedAt = java.time.LocalDateTime.now();
} else if (!sameStatus(status, "Resolved") && !sameStatus(status, "Closed")) {
Comment on lines +408 to +410
if (!sameStatus(previousStatus, "Resolved") && sameStatus(status, "Resolved")) {
ticket.resolvedAt = java.time.LocalDateTime.now();
} else if (!sameStatus(status, "Resolved") && !sameStatus(status, "Closed")) {
Comment on lines +39 to +79
@POST
@Path("/{id}/rating")
@Transactional
public Response submitRating(@CookieParam(AuthHelper.AUTH_COOKIE) String auth,
@HeaderParam("X-Billetsys-Client") String client, @PathParam("id") Long id,
@FormParam("rating") Integer rating, @FormParam("ratingComment") String ratingComment) {

User user = AuthHelper.findUser(auth);
if (user == null || (!User.TYPE_USER.equalsIgnoreCase(user.type) && !AuthHelper.isSuperuser(user))) {
throw new WebApplicationException(Response.seeOther(URI.create("/login")).build());
}

Ticket ticket = Ticket.findById(id);
if (ticket == null) {
throw new NotFoundException();
}

if (!MessageVisibilitySupport.canAccessTicket(user, ticket)) {
throw new WebApplicationException(Response.seeOther(URI.create("/")).build());
}

if (!"Resolved".equalsIgnoreCase(ticket.status)) {
throw new BadRequestException("Ticket must be resolved to be rated.");
}

if (ticket.rating != null) {
throw new BadRequestException("This ticket has already been rated.");
}

if (rating == null || rating < 1 || rating > 10) {
throw new BadRequestException("Rating must be between 1 and 10.");
}

ticket.rating = rating;
ticket.ratingComment = normalizeComment(ratingComment);

if ("react".equalsIgnoreCase(client)) {
return Response.ok(java.util.Map.of("redirectTo", "")).type(MediaType.APPLICATION_JSON).build();
}
return ReactRedirectSupport.redirect(client, "/tickets/" + ticket.id);
}

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

Comment on lines +942 to +964
<div className="space-y-2">
<label className="text-sm font-semibold">
How would you rate the resolution of this ticket?
</label>
<Ratings
value={ratingValue}
onValueChange={setRatingValue}
asInput
variant="yellow"
/>
</div>
<div className="space-y-2 pt-2">
<label className="text-sm font-semibold">
Optional comment
</label>
<Textarea
value={ratingComment}
onChange={(e) => setRatingComment(e.target.value)}
placeholder="Tell us about your experience..."
maxLength={2000}
className="min-h-[100px]"
/>
</div>
Comment thread doc/manual/en/11-tickets.md Outdated

* **Users** see the rating on their own tickets.
* **Superusers** see ratings on all tickets within their company.
* **TAMs** see ratings on tickets for the companies they are assigned to. TAMs can only see their own company's tickets.
@sksingh2005 sksingh2005 force-pushed the feat/ratings branch 3 times, most recently from ba71158 to d1995f8 Compare May 17, 2026 18:16
@sksingh2005 sksingh2005 merged commit a50b870 into main May 19, 2026
2 checks passed
@sksingh2005 sksingh2005 deleted the feat/ratings branch June 15, 2026 15:50
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