Skip to content

Conversation

@chiol
Copy link
Contributor

@chiol chiol commented Oct 30, 2025

  • Added sorting capabilities to the feedback table.
  • Refactored feedback table to improve state management and pagination.
  • Introduced separate components for feedback deletion and AI processing actions.
  • Updated date range handling and channel selection logic.
  • Improved loading states and error handling in feedback management.
  • Enhanced table pagination to reset page index on page size change.
  • Updated dependencies in pnpm-lock.yaml for better compatibility.

Summary by CodeRabbit

  • New Features

    • Client-side sorting in the feedback table.
    • Channel-aware feedback UI with automatic channel selection.
    • Modularized delete and AI action buttons with clearer loading/toast feedback.
  • Bug Fixes

    • Reset pagination and selection when filters, date range, or channel change.
    • Changing page size now resets to the first page.
  • Chores

    • Removed React strict/build config flags.
    • Increased default query cache stale time to 60s.

- Added sorting capabilities to the feedback table.
- Refactored feedback table to improve state management and pagination.
- Introduced separate components for feedback deletion and AI processing actions.
- Updated date range handling and channel selection logic.
- Improved loading states and error handling in feedback management.
- Enhanced table pagination to reset page index on page size change.
- Updated dependencies in pnpm-lock.yaml for better compatibility.
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Walkthrough

Refactors feedback UI to be channel-aware, integrates client-side sorting into data fetches, modularizes delete/AI actions, changes query caching defaults (60s), adjusts pagination behavior to reset on page-size changes, and simplifies Next.js config and route type import.

Changes

Cohort / File(s) Summary
Next.js config & env
apps/web/next-env.d.ts, apps/web/next.config.js
Switched route types import from ambient reference to module import; removed reactStrictMode: true and eslint: { ignoreDuringBuilds: true } from Next config.
Global query defaults
apps/web/src/pages/_app.tsx
QueryClient now initialized with defaultOptions setting queries.staleTime = 60000 (60s).
Feedback query converter
apps/web/src/entities/feedback/lib/use-feedback-query-converter.ts
Fixed internal typo (onChageTableFiltersonChangeTableFilters); introduced filtered collections to constrain queries/tableFilters to selected filterFields; returned objects updated to use filtered collections.
Feedback table UI
apps/web/src/entities/feedback/ui/feedback-table.ui.tsx
Added useSort integration (sorting forwarded to data fetch); new handlers reset pagination/selection on channel/filter/date changes; replaced inline delete/AI actions with FeedbackDeleteButton and RunAIButton components; adjusted query usage and staleTime.
Feedback page & channel flow
apps/web/src/pages/main/project/[projectId]/feedback.tsx
Added channel fetching and auto-select first channel; introduced EmptyChannel and FeedbackContainer to derive filter fields from channel data; updated rendering/loading/error branching.
Table pagination behavior
apps/web/src/shared/ui/tables/table-pagination.tsx
Page-size selection now calls table.setPagination({ pageIndex: 0, pageSize }) to reset to first page on size change.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant FeedbackPage as feedback.tsx
    participant ChannelService
    participant FeedbackContainer
    participant FeedbackTable
    participant QueryConverter
    participant API as DataFetch

    User->>FeedbackPage: open feedback page
    FeedbackPage->>ChannelService: fetch channels for project
    ChannelService-->>FeedbackPage: channels
    FeedbackPage->>FeedbackPage: set currentChannelId (auto-select first)
    FeedbackPage->>FeedbackContainer: mount with currentChannelId

    FeedbackContainer->>API: fetch channelData for channelId
    API-->>FeedbackContainer: channelData
    FeedbackContainer->>QueryConverter: compute filterFields from channelData

    User->>FeedbackTable: change sort / filters / date / channel
    FeedbackTable->>FeedbackTable: reset pageIndex & clear selection
    FeedbackTable->>QueryConverter: build queries (includes sort + filtered fields)
    QueryConverter->>API: fetch feedback list (staleTime=60s)
    API-->>FeedbackTable: results
    FeedbackTable->>User: render updated table

    User->>FeedbackTable: click delete or Run AI
    FeedbackTable->>FeedbackDeleteButton: open delete flow / confirm
    FeedbackTable->>RunAIButton: trigger AI mutation
    FeedbackDeleteButton->>API: delete mutation
    RunAIButton->>API: run-ai mutation
    API-->>FeedbackTable: mutation results
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus review on: feedback-table.ui.tsx (sorting integration, new handlers, modular action components), feedback.tsx channel-loading and state transitions, and use-feedback-query-converter.ts filtered query logic.
  • Verify QueryClient global default (60s) impact across app.
  • Confirm pagination reset behavior in table-pagination.tsx and interactions with table state.

Possibly related PRs

Suggested reviewers

  • jihun

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "feat: enhance feedback table functionality and UI" is directly related to the changeset's primary focus. The raw summary shows that the majority of substantive changes center on improving the feedback table through sorting capabilities, state management refactoring, modular component extraction (FeedbackDeleteButton, RunAIButton), and pagination enhancements. While the title is somewhat umbrella-like and doesn't capture specific technical improvements such as the sorting integration or the modularization of delete/AI actions, it accurately conveys that the feedback table and its UI are being enhanced. The title is descriptive and specific enough that a teammate reviewing the commit history would understand the general nature of the changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/feedback-table

📜 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 6a392bb and 5d8b474.

📒 Files selected for processing (1)
  • apps/web/next-env.d.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/web/next-env.d.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: integration-test

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

Comment @coderabbitai help to get the list of available commands and usage tips.

@chiol chiol requested a review from jihun October 30, 2025 05:38
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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/src/entities/feedback/lib/use-feedback-query-converter.ts (1)

164-185: Fix stale dependencies in onChangeTableFilters.

onChangeTableFilters closes over projectId and defaultDateRange, but the dependency list only contains dateRange. When feedbackSearchMaxDays or the active channel changes, the callback keeps using the previous default range and project ID, so clearing filters reinstates the wrong date window and issues get created against the wrong project. Add all reactive values the function reads so the memoized callback stays in sync.(react.dev)

-  const onChangeTableFilters = useCallback(
+  const onChangeTableFilters = useCallback(
     async (tableFilters: TableFilter[], operator: TableFilterOperator) => {
       const result = await Promise.all(
         tableFilters.map(async ({ condition, format, key, value }) => ({
           key,
           value: await toQuery(projectId)[format](value),
           condition,
         })),
       );

       if (result.length === 0 && !dateRange) {
         await setDefaultQueries(defaultDateRange ? [defaultDateRange] : []);
         await setQueries([]);
         await setOperator('AND');
         return;
       }

       await setOperator(operator);
       await setQueries(result);
     },
-    [dateRange],
+    [dateRange, defaultDateRange, projectId, setDefaultQueries, setOperator, setQueries],
   );
🧹 Nitpick comments (1)
apps/web/package.json (1)

40-40: Use the stable React Compiler package.

[email protected] predates the 1.0.0 stable release and has already surfaced install regressions in downstream tooling. Pinning the stable 1.0.0 build keeps us aligned with the React team’s guidance and avoids the RC-only breakages we’ve seen reported in the ecosystem.(ko.react.dev)

-    "babel-plugin-react-compiler": "19.1.0-rc.3",
+    "babel-plugin-react-compiler": "1.0.0",
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a75da85 and c622827.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (8)
  • apps/web/next-env.d.ts (1 hunks)
  • apps/web/next.config.js (1 hunks)
  • apps/web/package.json (1 hunks)
  • apps/web/src/entities/feedback/lib/use-feedback-query-converter.ts (2 hunks)
  • apps/web/src/entities/feedback/ui/feedback-table.ui.tsx (8 hunks)
  • apps/web/src/pages/_app.tsx (1 hunks)
  • apps/web/src/pages/main/project/[projectId]/feedback.tsx (5 hunks)
  • apps/web/src/shared/ui/tables/table-pagination.tsx (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/src/entities/feedback/ui/feedback-table.ui.tsx (1)
apps/web/src/assets/index.tsx (1)
  • AISparklingIcon (99-109)

Comment on lines +379 to +405
const { mutateAsync: processAI, isPending: isPendingAIProcess } =
useOAIMutation({
method: 'post',
path: '/api/admin/projects/{projectId}/ai/process',
pathParams: { projectId },
queryOptions: {
onSuccess() {
toast.success(t('v2.toast.success'));
},
onSettled() {
afterProcess();
setLoadingFeedbackIds([]);
},
onError(error) {
toast.error(error.message);
},
},
});
return (
<Button
variant="outline"
onClick={() => {
setLoadingFeedbackIds(selectedRowIds);
toast.promise(processAI({ feedbackIds: selectedRowIds }), {
loading: 'Loading',
success: () => 'Success',
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Avoid double toasts when using toast.promise

toast.promise already handles the loading/success/error lifecycle, but the mutation still emits separate toast.success/toast.error, so the user gets two notifications for each outcome. Let toast.promise own the messaging and keep the mutation focused on state cleanup.

   const { mutateAsync: processAI, isPending: isPendingAIProcess } =
     useOAIMutation({
       method: 'post',
       path: '/api/admin/projects/{projectId}/ai/process',
       pathParams: { projectId },
       queryOptions: {
-        onSuccess() {
-          toast.success(t('v2.toast.success'));
-        },
         onSettled() {
           afterProcess();
           setLoadingFeedbackIds([]);
         },
-        onError(error) {
-          toast.error(error.message);
-        },
       },
     });
@@
         toast.promise(processAI({ feedbackIds: selectedRowIds }), {
-          loading: 'Loading',
-          success: () => 'Success',
+          loading: 'Loading',
+          success: () => t('v2.toast.success'),
+          error: (error: Error) => error.message,
         });
       }}
       disabled={isPendingAIProcess || !perms.includes('feedback_update')}

(react-hot-toast.com)

📝 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 { mutateAsync: processAI, isPending: isPendingAIProcess } =
useOAIMutation({
method: 'post',
path: '/api/admin/projects/{projectId}/ai/process',
pathParams: { projectId },
queryOptions: {
onSuccess() {
toast.success(t('v2.toast.success'));
},
onSettled() {
afterProcess();
setLoadingFeedbackIds([]);
},
onError(error) {
toast.error(error.message);
},
},
});
return (
<Button
variant="outline"
onClick={() => {
setLoadingFeedbackIds(selectedRowIds);
toast.promise(processAI({ feedbackIds: selectedRowIds }), {
loading: 'Loading',
success: () => 'Success',
});
const { mutateAsync: processAI, isPending: isPendingAIProcess } =
useOAIMutation({
method: 'post',
path: '/api/admin/projects/{projectId}/ai/process',
pathParams: { projectId },
queryOptions: {
onSettled() {
afterProcess();
setLoadingFeedbackIds([]);
},
},
});
return (
<Button
variant="outline"
onClick={() => {
setLoadingFeedbackIds(selectedRowIds);
toast.promise(processAI({ feedbackIds: selectedRowIds }), {
loading: 'Loading',
success: () => t('v2.toast.success'),
error: (error: Error) => error.message,
});
🤖 Prompt for AI Agents
In apps/web/src/entities/feedback/ui/feedback-table.ui.tsx around lines 379 to
405, the mutation uses both toast.promise and also emits
toast.success/toast.error in its queryOptions which causes duplicate
notifications; remove the toast.success and toast.error calls from the
mutation's onSuccess/onError handlers (leave onSettled to call afterProcess()
and setLoadingFeedbackIds([]) for cleanup) and let the toast.promise call supply
the loading/success/error messages instead (ensure toast.promise options include
appropriate success/error text).

Comment on lines +51 to +54
if (!channels) return;
void setCurrentChannelId(channels.items[0]?.id ?? null);
}, [channels]);

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don’t overwrite the selected channel on refetch.

Every time useAllChannels refetches, this effect writes the first channel ID back into query state. That means the user loses their selection as soon as the list refreshes (e.g., background stale-time refresh or mutation). Gate the assignment so we only seed the default when no valid selection exists.

-  useEffect(() => {
-    if (!channels) return;
-    void setCurrentChannelId(channels.items[0]?.id ?? null);
-  }, [channels]);
+  useEffect(() => {
+    if (!channels) return;
+
+    const firstChannelId = channels.items[0]?.id ?? null;
+    const currentExists = channels.items.some(
+      (channel) => channel.id === currentChannelId,
+    );
+
+    if (currentChannelId !== -1 && currentExists) return;
+
+    void setCurrentChannelId(firstChannelId);
+  }, [channels, currentChannelId, setCurrentChannelId]);
📝 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 (!channels) return;
void setCurrentChannelId(channels.items[0]?.id ?? null);
}, [channels]);
useEffect(() => {
if (!channels) return;
const firstChannelId = channels.items[0]?.id ?? null;
const currentExists = channels.items.some(
(channel) => channel.id === currentChannelId,
);
if (currentChannelId !== -1 && currentExists) return;
void setCurrentChannelId(firstChannelId);
}, [channels, currentChannelId, setCurrentChannelId]);
🤖 Prompt for AI Agents
In apps/web/src/pages/main/project/[projectId]/feedback.tsx around lines 51 to
54, the effect unconditionally seeds the current channel with
channels.items[0]?.id on every refetch which clobbers a user’s existing
selection; change the logic to only set the current channel when there is no
valid selection (e.g., currentChannelId is null/undefined or the id is not
present in channels.items), and skip assignment when channels is empty or the
existing selected id is still valid so background refetches won’t overwrite the
user’s choice.

@chiol chiol self-assigned this Oct 30, 2025
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