Skip to content

Conversation

Copy link

Copilot AI commented Jul 11, 2025

This PR enhances the suspended users and suspended mods management pages by adding search functionality and pagination, making it easier for moderators to find specific suspended users or mods in large lists.

Changes Made

🔍 Search Functionality

  • Suspended Users: Search by username with case-insensitive partial matching
  • Suspended Mods: Search by display name with case-insensitive partial matching
  • URL Integration: Search terms are reflected in URL parameters for bookmarkable results
  • Real-time filtering: Results update immediately as user types

📄 Pagination

  • Added LoadMore component to both lists with 15 items per page
  • Proper "end of results" detection and messaging
  • Maintains search filters when loading additional pages

🛠 Technical Implementation

  • Created filter utilities: getSuspendedUserFilterValuesFromParams.ts and getSuspendedModFilterValuesFromParams.ts
  • Added new GraphQL queries: GET_SUSPENDED_USERS_WITH_SEARCH and GET_SUSPENDED_MODS_WITH_SEARCH
  • Implemented client-side filtering for robust search experience
  • Enhanced error handling and empty state messaging

📁 Files Modified

  • pages/forums/[forumId]/edit/suspended-users.vue - Added search bar and pagination
  • components/channel/form/SuspendedModList.vue - Added search bar and pagination
  • graphQLData/mod/queries.js - Added new paginated queries
  • Added utility functions and unit tests

Usage Examples

Before

Lists showed all suspended users/mods without search or pagination, making it difficult to find specific entries in large lists.

After

  • Navigate to /forums/example/edit/suspended-users?searchInput=alice to search for users
  • Navigate to /forums/example/edit/suspended-mods?searchInput=mod to search for mods
  • Search results are bookmarkable and shareable
  • LoadMore button appears when additional results are available

Benefits

  • Improved UX: Quick search through large lists
  • Scalable: Pagination handles large datasets efficiently
  • Bookmarkable: Search results can be shared and bookmarked
  • Consistent: Follows existing patterns from discussion filtering
  • Accessible: Maintains existing styling and accessibility standards

Fixes #7.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@vercel
Copy link

vercel bot commented Jul 11, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
open-forum-frontend ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jul 11, 2025 8:03pm

Co-authored-by: gennitdev <114821397+gennitdev@users.noreply.github.com>
Copilot AI changed the title [WIP] Enhancements for lists of suspended users and suspended mods Add search and pagination to suspended users and mods lists Jul 11, 2025
Copilot AI requested a review from gennitdev July 11, 2025 19:05
Copy link
Owner

@gennitdev gennitdev left a comment

Choose a reason for hiding this comment

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

There's a problem with the async calls, yielding this backend error. can you fix it?

🚨 GraphQL Error Details: {
  errorId: 'err_1752261241569_7cmjlkpyp',
  timestamp: '2025-07-11T19:14:01.569Z',
  message: 'Variable "$searchInput" is never used in operation "getSuspendedUsersWithSearch".',
  code: 'UNKNOWN_ERROR',
  path: undefined,
  locations: [ { line: 1, column: 64 } ],
  operationName: 'getSuspendedUsersWithSearch',
  variables: { channelUniqueName: 'cats', limit: 15, offset: 0 },
  query: 'query getSuspendedUsersWithSearch($channelUniqueName: String!, $searchInput: String, $limit: Int!, $offset: Int!) {\n' +
    '  channels(where: {uniqueName: $channelUniqueName}) {\n' +
    '    uniqueName\n' +
    '    SuspendedUsersAggregate {\n' +
    '      count\n' +
    '      __typename\n' +
    '    }\n' +
    '    SuspendedUsers(\n' +
    '      options: {limit: $limit, offset: $offset, sort: {createdAt: DESC}}\n' +
    '    ) {\n' +
    '      id\n' +
    '      username\n' +
    '      createdAt\n' +
    '      suspendedUntil\n' +
    '      suspendedIndefinitely\n' +
    '      SuspendedUser {\n' +
    '        username\n' +
    '        displayName\n' +
    '        profilePicURL\n' +
    '        commentKarma\n' +
    '        discussionKarma\n' +
    '        __typename\n' +
    '      }\n' +
    '      RelatedIssue {\n' +
    '        id\n' +
    '        __typename\n' +
    '      }\n' +
    '      __typename\n' +
    '    }\n' +
    '    __typename\n' +
    '  }\n' +
    '}',
  userAgent: undefined,
  ip: undefined,
  stack: 'GraphQLError: Variable "$searchInput" is never used in operation "getSuspendedUsersWithSearch".\n' +
    '    at Object.leave (/Users/catherineluse/gennit/gennit-backend/node_modules/graphql/validation/rules/NoUnusedVariablesRule.js:39:15)\n' +
    '    at Object.leave (/Users/catherineluse/gennit/gennit-backend/node_modules/graphql/language/visitor.js:324:32)\n' +
    '    at Object.leave (/Users/catherineluse/gennit/gennit-backend/node_modules/graphql/utilities/TypeInfo.js:411:21)\n' +
    '    at visit (/Users/catherineluse/gennit/gennit-backend/node_modules/graphql/language/visitor.js:197:21)\n' +
    '    at validate (/Users/catherineluse/gennit/gennit-backend/node_modules/graphql/validation/validate.js:91:24)\n' +
    '    at validate (/Users/catherineluse/gennit/gennit-backend/node_modules/apollo-server-core/dist/requestPipeline.js:188:39)\n' +
    '    at processGraphQLRequest (/Users/catherineluse/gennit/gennit-backend/node_modules/apollo-server-core/dist/requestPipeline.js:99:38)\n' +
    '    at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n' +
    '    at async processHTTPRequest (/Users/catherineluse/gennit/gennit-backend/node_modules/apollo-server-core/dist/runHttpQuery.js:222:30)',

Co-authored-by: gennitdev <114821397+gennitdev@users.noreply.github.com>
Copy link
Author

Copilot AI commented Jul 11, 2025

There's a problem with the async calls, yielding this backend error. can you fix it?

...

Fixed the GraphQL error by removing the unused $searchInput parameter from both queries. The search functionality continues to work through client-side filtering. See commit c196ae2.

Copilot AI requested a review from gennitdev July 11, 2025 19:26
Copy link
Owner

@gennitdev gennitdev left a comment

Choose a reason for hiding this comment

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

I don't want client-side filtering. I want the searchInput passed into the graphql queries on both lists. Here is an example of searchInput being used in a query:

export const GET_CHANNELS = gql`
  query getSortedChannels(
    $offset: Int
    $limit: Int
    $tags: [String]
    $searchInput: String
    $now: DateTime = "${now}"
  ) {
    getSortedChannels(
      offset: $offset
      limit: $limit
      tags: $tags
      searchInput: $searchInput
    ) {
      channels {
        uniqueName
        displayName
        channelIconURL
        description
        Tags {
          text
        }
        EventChannelsAggregate(
          where: {
            NOT: { 
              archived: true,
              Event: null
            }
            Event: { 
              canceled: false, 
              endTime_GT: $now,
            }
          }
        ) {
          count
        }
        DiscussionChannelsAggregate(
          where: {
            AND: [
              { NOT: { archived: true } }
              { NOT: { Discussion: null } }
              { NOT: { Discussion: { hasDownload: true } } }
            ]
          }
        ) {
          count
        }
      }
      aggregateChannelCount
    }
  }
`;

Copy link
Owner

@gennitdev gennitdev left a comment

Choose a reason for hiding this comment

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

Actually a better example would be this query:

import { gql } from "@apollo/client/core";

export const GET_TAGS = gql`
  query getTags($where: TagWhere) {
    tags(where: $where, options: { limit: 10 }) {
      text
    }
  }
`;

the search input will be needed to be passed into the where object. for the type of the where object, you will need to check the auto-generated type in the generated/graphql.ts.

…iltering

- Add $searchInput parameter back to both GraphQL queries
- Use where clauses for server-side filtering:
  - SuspendedUser: { username_CONTAINS: $searchInput }
  - SuspendedMod: { displayName_CONTAINS: $searchInput }
- Remove client-side filtering logic from Vue components
- Add watchers to refetch data when search input changes
- Update fetchMore functions to include searchInput parameter

Co-authored-by: gennitdev <114821397+gennitdev@users.noreply.github.com>
Copy link
Author

Copilot AI commented Jul 11, 2025

I don't want client-side filtering. I want the searchInput passed into the graphql queries on both lists. Here is an example of searchInput being used in a query:

...

I've updated both GraphQL queries to implement server-side filtering instead of client-side filtering. The queries now use proper where clauses:

  • For suspended users: SuspendedUser: { username_CONTAINS: $searchInput }
  • For suspended mods: SuspendedMod: { displayName_CONTAINS: $searchInput }

The Vue components have been updated to pass the searchInput parameter to the queries and remove the client-side filtering logic. This provides better performance and accurate aggregate counts. See commit 8562038.

Co-authored-by: gennitdev <114821397+gennitdev@users.noreply.github.com>
Copy link
Author

Copilot AI commented Jul 11, 2025

Actually a better example would be this query:

...

I've updated both GraphQL queries to follow the pattern you suggested. The queries now accept a $where: SuspensionWhere parameter instead of $searchInput, and the Vue components conditionally build the where clause only when there's a search term:

  • For suspended users: { SuspendedUser: { username_CONTAINS: searchInput } }
  • For suspended mods: { SuspendedMod: { displayName_CONTAINS: searchInput } }

When there's no search input, undefined is passed for the where clause, which eliminates the GraphQL validation errors. See commit 4aea66d.

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.

Enhancements for lists of suspended users and suspended mods

2 participants