Skip to content

Feature/supportcomponent#1050

Merged
Gerviba merged 4 commits into
stagingfrom
feature/supportcomponent
Jul 11, 2026
Merged

Feature/supportcomponent#1050
Gerviba merged 4 commits into
stagingfrom
feature/supportcomponent

Conversation

@Gerviba

@Gerviba Gerviba commented Jul 11, 2026

Copy link
Copy Markdown
Member

Summary by SlopRabbit

  • New Features
    • Added a complete support center for creating, viewing, and replying to support threads.
    • Added secure access for logged-out visitors, thread status tracking, message limits, and closed-thread handling.
    • Added an administration interface for replying, claiming, closing, reopening, and managing support schedules.
    • Added configurable support settings, notifications, incoming email handling, automatic assignment, and statistics.
    • Added customizable support content and default responder names.
  • Improvements
    • Email notifications can now use dynamically rendered or overridden subjects.
    • Corrected the responsible-person label in product overviews.

@vercel

vercel Bot commented Jul 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmsch-cst Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-felezobal Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-golyakorte Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-seniortabor Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-skktv Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-snyt Ready Ready Preview, Comment Jul 11, 2026 7:48pm
cmsch-vitorlaskupa Ready Ready Preview, Comment Jul 11, 2026 7:48pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
Not Found - https://docs.github.com/rest/issues/comments#update-an-issue-comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (7)
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt (2)

182-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add @Valid to the IncomingEmailDto parameter for defense-in-depth input validation.

The webhook endpoint deserializes IncomingEmailDto without triggering bean validation. Even though the payload comes from an email service, adding @Valid ensures malformed payloads are rejected early rather than causing downstream errors in supportService.processIncomingEmail.

♻️ Proposed fix
-    `@RequestBody` dto: IncomingEmailDto
+    `@Valid` `@RequestBody` dto: IncomingEmailDto
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`
around lines 182 - 198, Add the `@Valid` annotation to the IncomingEmailDto
parameter of SupportApiController.incomingEmail so bean validation runs
immediately after deserialization, before supportService.processIncomingEmail is
called.

182-198: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Add @Valid to the IncomingEmailDto parameter for defense-in-depth input validation.

The webhook endpoint deserializes IncomingEmailDto without triggering bean validation. Even though the payload comes from an email service, adding @Valid ensures malformed payloads are rejected early rather than causing downstream errors in supportService.processIncomingEmail.

♻️ Proposed fix
-    `@RequestBody` dto: IncomingEmailDto
+    `@Valid` `@RequestBody` dto: IncomingEmailDto
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`
around lines 182 - 198, Add the `@Valid` annotation to the `IncomingEmailDto`
parameter in `incomingEmail` so bean validation runs during request
deserialization before `supportService.processIncomingEmail` is called.
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt (1)

67-71: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

resolveDisplayName is duplicated in SupportService.kt.

The same resolveDisplayName logic exists in both SupportStatsDashboard (lines 67-71) and SupportService (lines 383-387). Consider extracting it to a shared helper or service to avoid divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`
around lines 67 - 71, The resolveDisplayName logic is duplicated across
SupportStatsDashboard and SupportService. Extract it into a shared helper or
service, then update both classes to delegate to that implementation while
preserving the existing fallback order and behavior.
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt (2)

389-411: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

pickAvailableSupportUser duplicates schedule filtering logic from SupportStatsDashboard.buildCurrentScheduleCard.

The schedule availability filtering logic (lines 392-397) is nearly identical to SupportStatsDashboard lines 77-82. Consider extracting a shared isScheduleActive(schedule) helper to avoid divergence.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 389 - 411, Extract the duplicated schedule availability predicate
from pickAvailableSupportUser into a shared isScheduleActive(schedule) helper,
alongside the equivalent logic in
SupportStatsDashboard.buildCurrentScheduleCard. Update both callers to use the
helper while preserving the existing validation of supportUserId, from/to
values, timezone conversion, and active time-range behavior.

307-317: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low value

decodeSrsEmail uses a simplified SRS parsing strategy.

The decoder splits on = and assumes the last two parts are domain and user. Real SRS format is SRS=hash=timestamp=domain=user, but some implementations use SRS0=hash=timestamp=domain=user or encode the separator differently. If the user's email address contains =, the parsing would break. This is acceptable as a best-effort decoder but should be documented as such.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 307 - 317, The decodeSrsEmail method uses best-effort simplified
SRS parsing; document this limitation directly near the method or parsing logic.
Note that it supports the expected SRS forms only heuristically, may not handle
alternate separators or “=” characters in the original address, and returns the
raw address when decoding is uncertain.
backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt (1)

52-59: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Redundant enum persistence annotations.

Both @Enumerated(EnumType.STRING) and @JdbcTypeCode(SqlTypes.VARCHAR) are present on the status field. In Hibernate 6, @JdbcTypeCode(SqlTypes.VARCHAR) is the preferred approach and makes @Enumerated redundant. Consider removing @Enumerated to reduce confusion, or keep both if the project requires backward compatibility with Hibernate 5.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt`
around lines 52 - 59, Remove the redundant `@Enumerated` annotation from the
status property in SupportThreadEntity, retaining
`@JdbcTypeCode`(SqlTypes.VARCHAR) so the enum continues to persist as a string.
Keep the existing column, JSON view, and generated input/overview annotations
unchanged.
frontend/src/pages/support/supportThread.page.tsx (1)

92-92: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix prettier formatting violations.

Static analysis reports formatting issues on lines 92, 120, and 130 where multi-line JSX attributes should be collapsed to single lines per the project's prettier configuration.

♻️ Proposed formatting fixes
           <div
-            key={msg.id}
-            className={cn(
-              'rounded-lg border p-4',
-              msg.fromAdmin ? 'bg-primary/5 border-primary/20' : 'bg-card'
-            )}
+            key={msg.id} className={cn('rounded-lg border p-4', msg.fromAdmin ? 'bg-primary/5 border-primary/20' : 'bg-card')}
           >
           <Button
-            onClick={handleReply}
-            disabled={addMessageMutation.isPending || !replyText.trim()}
-            className="self-start"
+            onClick={handleReply} disabled={addMessageMutation.isPending || !replyText.trim()} className="self-start"
           >
       {thread.status === SupportThreadStatus.DONE && (
-        <p className="text-muted-foreground text-sm mt-2">Ez az üzenetváltás le van zárva.</p>
+        <p className="text-muted-foreground text-sm mt-2">Ez az üzenetváltás le van zárva.</p>
       )}

Also applies to: 120-120, 130-130

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/support/supportThread.page.tsx` at line 92, Apply the
project’s Prettier formatting to the JSX in the support thread page,
specifically the multi-line attributes near the div elements at the referenced
locations. Collapse attributes that fit on one line while preserving the
existing JSX structure and behavior.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt`:
- Around line 24-33: Update getCardFormHtml to avoid calling userEntity.get()
when the repository result is empty, and escape the fallback full name before
interpolating it into the placeholder attribute. Derive the placeholder safely
from the Optional with an empty/default value, while preserving the existing
currentName handling and form output.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`:
- Around line 79-100: Update createThread and ThreadListView to return
SupportThreadView DTOs instead of serializing SupportThreadEntity, ensuring
uuidSecret and userInternalId are not exposed; follow the existing getThread
mapping, or use a dedicated create-response DTO only if the secret is required
by the creator.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`:
- Around line 258-280: Guard the admin-reply notification flow in SupportService
before calling emailService.sendTemplatedEmail: only send when thread.userEmail
is non-blank. Preserve the existing template lookup, email values, recipient,
and missing-template warning behavior, while skipping the send entirely for
blank addresses.
- Around line 319-354: Update processIncomingEmail to validate the resolved user
and email content before calling addCustomerMessage or createThread: reject
blocked users via isBlockedUser, reject bodies exceeding limits via
isContentTooLong, and enforce hasTooManyConsecutiveCustomerResponses for
existing threads. Return without modifying support data when any validation
fails, while preserving the existing routing and creation behavior for valid
emails.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`:
- Around line 77-82: Update the inSchedule filtering logic to compare the
schedule’s full start and end timestamps as instants rather than converting them
to LocalTime. Preserve the existing validation and timezone handling while
ensuring schedules spanning midnight or multiple days are classified correctly.

In `@backend/src/main/resources/templates/settings.html`:
- Line 59: Update SupportAdminSettingsExtension’s formHtml construction to
HTML-escape userEntity.get().fullName before interpolating it, matching the
existing escaping applied to currentName; preserve the raw th:utext rendering
while ensuring the generated placeholder cannot contain unescaped user input.

In `@backend/src/main/resources/templates/supportThreadView.html`:
- Around line 116-117: Update the Thymeleaf condition on the message author
display span to compare realAuthorName and authorName null-safely, replacing the
direct msg.realAuthorName.equals(...) call with Thymeleaf’s null-safe strings
comparison while preserving the existing display behavior.

In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts`:
- Around line 19-23: Reformat the axios.post call in
useAddSupportMessageMutation so the method invocation and its arguments are on a
single line, matching the repository’s Prettier output while preserving the
existing URL, payload, and params.
- Around line 19-23: Reformat the axios.post call in
useAddSupportMessageMutation so the complete invocation, including its URL,
payload, and params arguments, is on a single line as required by Prettier.
Preserve the existing request behavior and values.

In `@frontend/src/pages/support/supportList.page.tsx`:
- Around line 23-27: Update the status label lookup using statusLabel and ensure
unmapped thread.status values fall back to displaying thread.status instead of
rendering undefined; preserve the existing labels for the three
SupportThreadStatus values.

---

Nitpick comments:
In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`:
- Around line 182-198: Add the `@Valid` annotation to the IncomingEmailDto
parameter of SupportApiController.incomingEmail so bean validation runs
immediately after deserialization, before supportService.processIncomingEmail is
called.
- Around line 182-198: Add the `@Valid` annotation to the `IncomingEmailDto`
parameter in `incomingEmail` so bean validation runs during request
deserialization before `supportService.processIncomingEmail` is called.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`:
- Around line 389-411: Extract the duplicated schedule availability predicate
from pickAvailableSupportUser into a shared isScheduleActive(schedule) helper,
alongside the equivalent logic in
SupportStatsDashboard.buildCurrentScheduleCard. Update both callers to use the
helper while preserving the existing validation of supportUserId, from/to
values, timezone conversion, and active time-range behavior.
- Around line 307-317: The decodeSrsEmail method uses best-effort simplified SRS
parsing; document this limitation directly near the method or parsing logic.
Note that it supports the expected SRS forms only heuristically, may not handle
alternate separators or “=” characters in the original address, and returns the
raw address when decoding is uncertain.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`:
- Around line 67-71: The resolveDisplayName logic is duplicated across
SupportStatsDashboard and SupportService. Extract it into a shared helper or
service, then update both classes to delegate to that implementation while
preserving the existing fallback order and behavior.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt`:
- Around line 52-59: Remove the redundant `@Enumerated` annotation from the status
property in SupportThreadEntity, retaining `@JdbcTypeCode`(SqlTypes.VARCHAR) so
the enum continues to persist as a string. Keep the existing column, JSON view,
and generated input/overview annotations unchanged.

In `@frontend/src/pages/support/supportThread.page.tsx`:
- Line 92: Apply the project’s Prettier formatting to the JSX in the support
thread page, specifically the multi-line attributes near the div elements at the
referenced locations. Collapse attributes that fit on one line while preserving
the existing JSX structure and behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 308f6672-3e8b-4a6e-b4c5-02c13eecf89e

📥 Commits

Reviewing files that changed from the base of the PR and between ccb666d and 561db3d.

📒 Files selected for processing (42)
  • .coderabbit.yaml
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/debt/SoldProductEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/email/EmailService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/location/LocationService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponent.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponentController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportComponentEntityConfiguration.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportMessageEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportMessageRepository.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleAdminController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleRepository.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadRepository.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadViewExtension.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/dto/IncomingEmailDto.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/dto/SiteContext.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/repository/EntityPageDataSource.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/repository/ManualRepository.kt
  • backend/src/main/kotlin/hu/bme/sch/cmsch/service/PermissionsService.kt
  • backend/src/main/resources/config/application.properties
  • backend/src/main/resources/templates/settings.html
  • backend/src/main/resources/templates/supportThreadView.html
  • frontend/src/App.tsx
  • frontend/src/api/contexts/config/types.ts
  • frontend/src/api/hooks/queryKeys.ts
  • frontend/src/api/hooks/support/useAddSupportMessageMutation.ts
  • frontend/src/api/hooks/support/useCreateSupportThreadMutation.ts
  • frontend/src/api/hooks/support/useSupportThreadQuery.ts
  • frontend/src/api/hooks/support/useSupportThreadsQuery.ts
  • frontend/src/pages/support/supportList.page.tsx
  • frontend/src/pages/support/supportNew.page.tsx
  • frontend/src/pages/support/supportThread.page.tsx
  • frontend/src/util/paths.ts
  • frontend/src/util/views/support.view.ts

Comment on lines +24 to +33
override fun getCardFormHtml(user: CmschUser): String {
val userEntity = userRepository.findByInternalId(user.internalId)
val currentName = userEntity
.map { userService.resolveConfig(it.config).supportDefaultName }
.orElse("")
return """
<form action="/admin/control/support/settings/default-name" method="post">
<div class="field-group">
<label>Megjelenő név</label>
<input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

userEntity.get() will throw NoSuchElementException when the user is not found.

On line 25, userRepository.findByInternalId(user.internalId) returns an Optional. Lines 26–28 correctly handle the empty case for currentName via .orElse(""), but line 33 calls userEntity.get().fullName directly in the placeholder without an isPresent check. If the repository returns an empty Optional, this will crash with NoSuchElementException.

Additionally, userEntity.get().fullName is interpolated into the placeholder attribute without escapeHtml(), creating an XSS vector if the full name contains characters like " or <.

🐛 Proposed fix
     override fun getCardFormHtml(user: CmschUser): String {
         val userEntity = userRepository.findByInternalId(user.internalId)
         val currentName = userEntity
             .map { userService.resolveConfig(it.config).supportDefaultName }
             .orElse("")
+        val fallbackName = userEntity.map { it.fullName }.orElse(user.fullName)
         return """
             <form action="/admin/control/support/settings/default-name" method="post">
                 <div class="field-group">
                     <label>Megjelenő név</label>
-                    <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255">
+                    <input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${escapeHtml(fallbackName)}" maxlength="255">
                 </div>
📝 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
override fun getCardFormHtml(user: CmschUser): String {
val userEntity = userRepository.findByInternalId(user.internalId)
val currentName = userEntity
.map { userService.resolveConfig(it.config).supportDefaultName }
.orElse("")
return """
<form action="/admin/control/support/settings/default-name" method="post">
<div class="field-group">
<label>Megjelenő név</label>
<input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${userEntity.get().fullName}" maxlength="255">
override fun getCardFormHtml(user: CmschUser): String {
val userEntity = userRepository.findByInternalId(user.internalId)
val currentName = userEntity
.map { userService.resolveConfig(it.config).supportDefaultName }
.orElse("")
val fallbackName = userEntity.map { it.fullName }.orElse(user.fullName)
return """
<form action="/admin/control/support/settings/default-name" method="post">
<div class="field-group">
<label>Megjelenő név</label>
<input type="text" name="supportDefaultName" value="${escapeHtml(currentName)}" placeholder="${escapeHtml(fallbackName)}" maxlength="255">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt`
around lines 24 - 33, Update getCardFormHtml to avoid calling userEntity.get()
when the repository result is empty, and escape the fallback full name before
interpolating it into the placeholder attribute. Derive the placeholder safely
from the Optional with an empty/default value, while preserving the existing
currentName handling and form output.

Comment on lines +79 to +100
fun createThread(@RequestBody request: CreateThreadRequest, auth: Authentication?): ResponseEntity<SupportThreadEntity> {
if (request.title.isBlank()) return ResponseEntity.badRequest().build()
val user = auth?.getUserOrNull()
return if (user != null && supportComponent.minRole.isAvailableForRole(user.role)) {
val email = getEmail(user)
if (supportService.isBlockedUser(user.internalId, email))
return ResponseEntity.status(HttpStatus.FORBIDDEN).build()
if (supportService.countOpenThreadsForUser(user.internalId, "") >= supportComponent.maxOpenThreads)
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build()
ResponseEntity.ok(supportService.createThread(request.title, request.content, user.internalId, email, user.userName))
} else {
if (request.authorEmail.isBlank()) return ResponseEntity.badRequest().build()
if (supportService.isBlockedUser("", request.authorEmail))
return ResponseEntity.status(HttpStatus.FORBIDDEN).build()
if (supportService.countOpenThreadsForUser("", request.authorEmail) >= supportComponent.maxOpenThreads)
return ResponseEntity.status(HttpStatus.TOO_MANY_REQUESTS).build()
ResponseEntity.ok(supportService.createThread(
request.title, request.content, "", request.authorEmail,
request.authorName.ifBlank { request.authorEmail }
))
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify which fields SupportThreadEntity exposes and whether `@JsonIgnore` or `@JsonView` is applied
fd "SupportThreadEntity.kt" --exec cat -n

Repository: kir-dev/cmsch

Length of output: 7482


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Map relevant symbols and inspect the controller + DTOs that shape support-thread responses.
fd "SupportApiController.kt" "SupportThreadView.kt" "ThreadListView.kt" "CreateThreadRequest.kt" --exec sh -c 'printf "\n== %s ==\n" "$1"; ast-grep outline "$1" --view expanded' sh {}

for f in \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadView.kt \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/ThreadListView.kt
do
  if [ -f "$f" ]; then
    printf "\n===== %s =====\n" "$f"
    cat -n "$f" | sed -n '1,260p'
  fi
done

Repository: kir-dev/cmsch

Length of output: 392


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find all places where SupportThreadEntity is returned from controllers or used in JSON views.
rg -n --hidden --glob '!**/build/**' \
  'SupportThreadEntity|SupportThreadView|ThreadListView|JsonView|ResponseEntity<SupportThreadEntity>|List<SupportThreadEntity>' \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support

Repository: kir-dev/cmsch

Length of output: 11458


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the support API controller and DTOs with line numbers.
for f in \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportThreadEntity.kt
do
  printf "\n===== %s =====\n" "$f"
  cat -n "$f" | sed -n '1,240p'
done

Repository: kir-dev/cmsch

Length of output: 18624


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check whether this app configures Jackson JSON views globally or on these endpoints.
rg -n --hidden --glob '!**/build/**' \
  'DEFAULT_VIEW_INCLUSION|MappingJacksonValue|`@JsonView`|JsonView|Jackson2ObjectMapperBuilder|ObjectMapper|SerializationView|MappingJackson2HttpMessageConverter' \
  backend/src/main/kotlin backend/src/main/resources

printf "\n===== SupportApiController annotations =====\n"
sed -n '1,220p' backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt | nl -ba | sed -n '1,140p'

Repository: kir-dev/cmsch

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the thread creation path to see which fields are populated and returned.
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt | sed -n '150,220p'

printf "\n===== JSON view usage in support APIs =====\n"
rg -n --hidden --glob '!**/build/**' '`@JsonView`|MappingJacksonValue|ResponseEntity<SupportThreadEntity>|ThreadListView|SupportThreadView\.from' \
  backend/src/main/kotlin/hu/bme/sch/cmsch/component/support

Repository: kir-dev/cmsch

Length of output: 4752


Return support threads as DTOs, not SupportThreadEntity (SupportApiController.kt:79-114). createThread and ThreadListView serialize the entity directly, which can expose uuidSecret and userInternalId. getThread already uses SupportThreadView; use that here too, or return a dedicated create-response DTO if the secret must be returned to the creator.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportApiController.kt`
around lines 79 - 100, Update createThread and ThreadListView to return
SupportThreadView DTOs instead of serializing SupportThreadEntity, ensuring
uuidSecret and userInternalId are not exposed; follow the existing getThread
mapping, or use a dedicated create-response DTO only if the secret is required
by the creator.

Comment on lines +258 to +280
if (!internalOnly && supportComponent.sendEmailOnAdminReply) {
val threadUrl = buildThreadUrl(thread)
val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector)
if (template != null) {
emailService.sendTemplatedEmail(
responsible = adminUser,
template = template,
values = mapOf(
"title" to thread.title,
"message" to content,
"messageHtml" to toMessageHtml(content),
"solver" to displayName,
"threadUrl" to threadUrl,
"creationDate" to formatHungarianDate(thread.createdAt),
"lastAnswerDate" to formatHungarianDate(now)
),
to = listOf(thread.userEmail),
subjectOverride = "RE: {{title}}"
)
} else {
log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Admin reply email sent even when thread.userEmail is blank.

emailService.sendTemplatedEmail is called with to = listOf(thread.userEmail) without checking if the email is non-blank. Threads created via the email webhook always have an email, but threads created through the UI by unauthenticated users may have a blank email. This could cause the email service to attempt sending to an empty address.

🛡️ Proposed fix: guard against blank email
-        if (!internalOnly && supportComponent.sendEmailOnAdminReply) {
+        if (!internalOnly && supportComponent.sendEmailOnAdminReply && thread.userEmail.isNotBlank()) {
             val threadUrl = buildThreadUrl(thread)
📝 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
if (!internalOnly && supportComponent.sendEmailOnAdminReply) {
val threadUrl = buildThreadUrl(thread)
val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector)
if (template != null) {
emailService.sendTemplatedEmail(
responsible = adminUser,
template = template,
values = mapOf(
"title" to thread.title,
"message" to content,
"messageHtml" to toMessageHtml(content),
"solver" to displayName,
"threadUrl" to threadUrl,
"creationDate" to formatHungarianDate(thread.createdAt),
"lastAnswerDate" to formatHungarianDate(now)
),
to = listOf(thread.userEmail),
subjectOverride = "RE: {{title}}"
)
} else {
log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector)
}
}
if (!internalOnly && supportComponent.sendEmailOnAdminReply && thread.userEmail.isNotBlank()) {
val threadUrl = buildThreadUrl(thread)
val template = emailService.getTemplateBySelector(supportComponent.answerEmailTemplateSelector)
if (template != null) {
emailService.sendTemplatedEmail(
responsible = adminUser,
template = template,
values = mapOf(
"title" to thread.title,
"message" to content,
"messageHtml" to toMessageHtml(content),
"solver" to displayName,
"threadUrl" to threadUrl,
"creationDate" to formatHungarianDate(thread.createdAt),
"lastAnswerDate" to formatHungarianDate(now)
),
to = listOf(thread.userEmail),
subjectOverride = "RE: {{title}}"
)
} else {
log.warn("Email template '{}' not found, skipping notification", supportComponent.answerEmailTemplateSelector)
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 258 - 280, Guard the admin-reply notification flow in
SupportService before calling emailService.sendTemplatedEmail: only send when
thread.userEmail is non-blank. Preserve the existing template lookup, email
values, recipient, and missing-template warning behavior, while skipping the
send entirely for blank addresses.

Comment on lines +319 to +354
@Transactional
fun processIncomingEmail(dto: IncomingEmailDto) {
val rawFrom = dto.addresses.from.address.trim()
val toAddress = dto.addresses.to.address.trim()
val resentFrom = dto.addresses.resentFrom.address.trim()
val subject = dto.subject.trim()
val body = stripEmailQuotes(dto.body.text.ifBlank { dto.body.html })

if (rawFrom.isBlank() || subject.isBlank()) {
log.warn("Incoming email ignored: from='{}', subject='{}'", rawFrom, subject)
return
}

val allowedTo = supportComponent.allowedToAddress.trim()
if (allowedTo.isNotBlank() && !toAddress.equals(allowedTo, ignoreCase = true)) {
log.warn("Incoming email rejected: 'To' address '{}' does not match configured '{}'", toAddress, allowedTo)
return
}

val allowedResentFrom = supportComponent.allowedResentFromAddress.trim()
if (allowedResentFrom.isNotBlank() && !resentFrom.equals(allowedResentFrom, ignoreCase = true)) {
log.warn("Incoming email rejected: 'Resent-From' '{}' does not match configured '{}'", resentFrom, allowedResentFrom)
return
}

val realEmail = decodeSrsEmail(rawFrom)

val normalized = normalizeSubject(subject)
val existing = findMatchingThread(normalized, realEmail)
val user = userRepository.findByEmailIgnoreCase(realEmail).orElse(null)
if (existing != null) {
addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail)
} else {
createThread(subject, body, user?.internalId ?: "", realEmail, user?.fullName ?: realEmail)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Blocked users and content limits are not enforced in the email processing path.

processIncomingEmail does not call isBlockedUser or isContentTooLong before creating a thread or appending a message. This means blocked emails/user IDs can still submit support requests via the incoming email webhook, bypassing the blocklist. Similarly, hasTooManyConsecutiveCustomerResponses is not checked, allowing rate limit bypass via email.

🔒️ Proposed fix: add validation checks in `processIncomingEmail`
     val realEmail = decodeSrsEmail(rawFrom)
+    val userInternalId = user?.internalId ?: ""
+
+    if (isBlocked(userInternalId, realEmail)) {
+        log.warn("Incoming email from blocked user/email '{}', ignoring", realEmail)
+        return
+    }
+    if (isContentTooLong(body)) {
+        log.warn("Incoming email body too long from '{}', ignoring", realEmail)
+        return
+    }
 
     val normalized = normalizeSubject(subject)
     val existing = findMatchingThread(normalized, realEmail)
-    val user = userRepository.findByEmailIgnoreCase(realEmail).orElse(null)
+    if (existing != null) {
+        if (hasTooManyConsecutiveCustomerResponses(existing.uuid)) {
+            log.warn("Too many consecutive customer responses for thread '{}', ignoring", existing.uuid)
+            return
+        }
+        addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail)
+    } else {
-    if (existing != null) {
-        addCustomerMessage(existing.uuid, body, user?.fullName ?: realEmail, realEmail)
-    } else {
-        createThread(subject, body, user?.internalId ?: "", realEmail, user?.fullName ?: realEmail)
+        createThread(subject, body, userInternalId, realEmail, user?.fullName ?: realEmail)
    }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt`
around lines 319 - 354, Update processIncomingEmail to validate the resolved
user and email content before calling addCustomerMessage or createThread: reject
blocked users via isBlockedUser, reject bodies exceeding limits via
isContentTooLong, and enforce hasTooManyConsecutiveCustomerResponses for
existing threads. Return without modifying support data when any validation
fails, while preserving the existing routing and creation behavior for valid
emails.

Comment on lines +77 to +82
val inSchedule = schedules.filter { schedule ->
if (schedule.supportUserId.isBlank() || schedule.from <= 0 || schedule.to <= 0) return@filter false
val fromTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(schedule.from), clock.timeZone).toLocalTime()
val toTime = ZonedDateTime.ofInstant(Instant.ofEpochSecond(schedule.to), clock.timeZone).toLocalTime()
nowTime >= fromTime && nowTime <= toTime
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleEntity.kt

Repository: kir-dev/cmsch

Length of output: 3707


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SupportStatsDashboard =="
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt

echo
echo "== Search for SupportScheduleEntity field usage =="
rg -n "SupportScheduleEntity|supportSchedules|supportUserId|supportUserName|from_time|to_time|\.from\b|\.to\b" backend/src/main/kotlin -g'*.kt'

Repository: kir-dev/cmsch

Length of output: 13626


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== SupportScheduleAdminController =="
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportScheduleAdminController.kt

echo
echo "== SupportService schedule selection =="
sed -n '380,410p' backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportService.kt

echo
echo "== Search for schedule descriptions or docs =="
rg -n "Elérhető:|support schedule|supportSchedules|from_time|to_time|InputType.DATE" backend/src/main/kotlin backend/src/main/resources -g'*.kt' -g'*.md' -g'*.yml' -g'*.yaml'

Repository: kir-dev/cmsch

Length of output: 12141


Compare full timestamps instead of LocalTime The date component is dropped here, so any schedule that spans midnight or multiple days will be classified incorrectly. If these fields are real start/end timestamps, compare instants directly; if they’re meant to be daily windows, store time-of-day and handle overnight wraparound explicitly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportStatsDashboard.kt`
around lines 77 - 82, Update the inSchedule filtering logic to compare the
schedule’s full start and end timestamps as instants rather than converting them
to LocalTime. Preserve the existing validation and timezone handling while
ensuring schedules spanning midnight or multiple days are classified correctly.

<div class="card wide-false settings-extension-card" th:each="ext : ${settingsExtensions}">
<h4 th:text="${ext.title}">Bővítmény</h4>
<div class="desc" th:text="${ext.description}"></div>
<div th:utext="${ext.formHtml}"></div>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Trace where formHtml is populated in settings extensions
rg -rn "formHtml" --type kotlin -g '!**/test/**'
rg -rn "settingsExtensions" --type kotlin -g '!**/test/**'

Repository: kir-dev/cmsch

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the relevant template and likely model/population code.
git ls-files | rg 'settings\.html|formHtml|settingsExtensions|extension|Settings|settings'

# Search broadly for the symbols in the repository.
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions' .

Repository: kir-dev/cmsch

Length of output: 1881


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files | rg 'settings\.html|formHtml|settingsExtensions|extension|Settings|settings'
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions' .

Repository: kir-dev/cmsch

Length of output: 1881


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify the source of ext.formHtml in the template context.
rg -n --hidden --glob '!**/test/**' 'ext\.formHtml|formHtml|settingsExtensions' backend src .

Repository: kir-dev/cmsch

Length of output: 2275


🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files | rg 'settings\.html|formHtml|settingsExtensions|Settings'
rg -n --hidden --glob '!**/test/**' 'formHtml|settingsExtensions|th:utext|formHtml' backend src .

Repository: kir-dev/cmsch

Length of output: 5732


🏁 Script executed:

#!/bin/bash
set -euo pipefail

ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt --view expanded
ast-grep outline backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/SettingsApiController.kt --view expanded

Repository: kir-dev/cmsch

Length of output: 1361


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/AdminSettingsExtension.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/controller/admin/SettingsController.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/support/SupportAdminSettingsExtension.kt
printf '\n---\n'
cat -n backend/src/main/kotlin/hu/bme/sch/cmsch/component/app/SettingsApiController.kt

Repository: kir-dev/cmsch

Length of output: 8818


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect where fullName comes from and whether other AdminSettingsExtension implementations exist.
rg -n --hidden --glob '!**/test/**' 'class .*AdminSettingsExtension|: AdminSettingsExtension|fullName|supportDefaultName' backend/src/main/kotlin/hu/bme/sch/cmsch

Repository: kir-dev/cmsch

Length of output: 32283


Escape userEntity.get().fullName in SupportAdminSettingsExtension. settings.html renders ext.formHtml raw via th:utext, and this form fragment still interpolates the placeholder value without escaping. currentName is already escaped; apply the same here or stop building HTML by string concatenation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/resources/templates/settings.html` at line 59, Update
SupportAdminSettingsExtension’s formHtml construction to HTML-escape
userEntity.get().fullName before interpolating it, matching the existing
escaping applied to currentName; preserve the raw th:utext rendering while
ensuring the generated placeholder cannot contain unescaped user input.

Comment on lines +116 to +117
<span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

NPE risk: msg.realAuthorName.equals() may throw if realAuthorName is null.

If msg.realAuthorName is null, calling .equals(msg.authorName) will throw a NullPointerException in the Thymeleaf expression. Use a null-safe comparison instead.

🐛 Proposed fix using null-safe comparison
-                         <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
+                         <span th:if="${msg.fromAdmin && msg.realAuthorName != null && !msg.realAuthorName.equals(msg.authorName)}"

Alternatively, use Thymeleaf's #strings.equals() utility which handles nulls safely:

-                         <span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
+                         <span th:if="${msg.fromAdmin && !`#strings.equals`(msg.realAuthorName, msg.authorName)}"
📝 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
<span th:if="${msg.fromAdmin && !msg.realAuthorName.equals(msg.authorName)}"
th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span>
<span th:if="${msg.fromAdmin && msg.realAuthorName != null && !msg.realAuthorName.equals(msg.authorName)}"
th:text="| (megjelenítve: ${msg.authorName})|" style="font-style: italic;"></span>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/src/main/resources/templates/supportThreadView.html` around lines 116
- 117, Update the Thymeleaf condition on the message author display span to
compare realAuthorName and authorName null-safely, replacing the direct
msg.realAuthorName.equals(...) call with Thymeleaf’s null-safe strings
comparison while preserving the existing display behavior.

Comment on lines +19 to +23
const res = await axios.post<SupportMessageView>(
`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
{ content, authorName },
{ params }
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix linter failure: reformat axios.post call to a single line.

The run-linters check fails because Prettier expects the axios.post arguments on a single line. This will block CI.

🎨 Proposed fix
-      const res = await axios.post<SupportMessageView>(
-        `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
-        { content, authorName },
-        { params }
-      )
+      const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })
📝 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
const res = await axios.post<SupportMessageView>(
`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
{ content, authorName },
{ params }
)
const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })
🧰 Tools
🪛 GitHub Check: run-linters

[failure] 19-19:
Replace ⏎········${ApiPaths.SUPPORT_THREAD}/${uuid}/message,⏎········{·content,·authorName·},⏎········{·params·}⏎······ with ``${ApiPaths.SUPPORT_THREAD}/${uuid}/message,·{·content,·authorName·},·{·params·}

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts` around lines
19 - 23, Reformat the axios.post call in useAddSupportMessageMutation so the
method invocation and its arguments are on a single line, matching the
repository’s Prettier output while preserving the existing URL, payload, and
params.

Source: Linters/SAST tools


📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix linter failure: reformat axios.post call to a single line.

The run-linters check fails because Prettier expects the axios.post arguments on a single line. This will block CI.

🎨 Proposed fix
-      const res = await axios.post<SupportMessageView>(
-        `${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
-        { content, authorName },
-        { params }
-      )
+      const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })
📝 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
const res = await axios.post<SupportMessageView>(
`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`,
{ content, authorName },
{ params }
)
const res = await axios.post<SupportMessageView>(`${ApiPaths.SUPPORT_THREAD}/${uuid}/message`, { content, authorName }, { params })
🧰 Tools
🪛 GitHub Check: run-linters

[failure] 19-19:
Replace ⏎········${ApiPaths.SUPPORT_THREAD}/${uuid}/message,⏎········{·content,·authorName·},⏎········{·params·}⏎······ with ``${ApiPaths.SUPPORT_THREAD}/${uuid}/message,·{·content,·authorName·},·{·params·}

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/api/hooks/support/useAddSupportMessageMutation.ts` around lines
19 - 23, Reformat the axios.post call in useAddSupportMessageMutation so the
complete invocation, including its URL, payload, and params arguments, is on a
single line as required by Prettier. Preserve the existing request behavior and
values.

Source: Linters/SAST tools

Comment on lines +23 to +27
const statusLabel: Record<string, string> = {
[SupportThreadStatus.WAITING_FOR_ADMIN]: `Rendező válaszára vár`,
[SupportThreadStatus.WAITING_FOR_CUSTOMER]: 'A te válaszodra vár',
[SupportThreadStatus.DONE]: 'Lezárva'
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

statusLabel[thread.status] may return undefined.

If thread.status contains a value not present in the three mapped keys, the Badge will render undefined (nothing visible). Consider adding a fallback label (e.g., statusLabel[thread.status] ?? thread.status).

💚 Proposed fix
-                {statusLabel[thread.status]}
+                {statusLabel[thread.status] ?? thread.status}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@frontend/src/pages/support/supportList.page.tsx` around lines 23 - 27, Update
the status label lookup using statusLabel and ensure unmapped thread.status
values fall back to displaying thread.status instead of rendering undefined;
preserve the existing labels for the three SupportThreadStatus values.

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.

1 participant