Skip to content

Conversation

@arif-u-ahmed
Copy link
Contributor

@arif-u-ahmed arif-u-ahmed commented Nov 17, 2025

Summary

Implements pause listing functionality for sharers, allowing them to pause active listings which removes them from search results while retaining reservation history.

Changes

  • Added pause application service (pause.ts) following cancel pattern
  • Added pauseItemListing GraphQL mutation and resolver
  • Updated UI components with pause button and confirmation modal (Popconfirm)
  • Added state mapping for domain states to UI statuses
  • Filtered paused listings from reservers' search results in itemListings query

Implementation Details

  • Application service uses Unit of Work pattern with transaction scoping
  • GraphQL mutation includes authentication checks
  • UI uses Ant Design Popconfirm for confirmation modal
  • Paused listings are automatically excluded from search results for reservers
  • State mapping converts domain states (Published → Active, etc.) for UI display

Testing

  • All files pass linting
  • GraphQL types generated successfully
  • Unit tests and Storybook updates added

Related

Closes #42

Checklist

  • Code follows project patterns and conventions
  • GraphQL types generated
  • No linting errors
  • Follows instruction markdown requirements
  • Unit tests added
  • Storybook stories updated

Summary by Sourcery

Enable sharers to pause active listings by adding backend service, GraphQL mutation, and UI controls while filtering paused listings from search results and standardizing status display.

New Features:

  • Implement pause listing application service with transactional Unit of Work
  • Add pauseItemListing GraphQL mutation and resolver with authentication
  • Introduce pause button and confirmation modal in My Listings UI

Bug Fixes:

  • Exclude paused listings from itemListings query results for reservers

Enhancements:

  • Map domain listing states to consistent UI statuses

- Add pause application service following cancel pattern
- Add pauseItemListing GraphQL mutation and resolver
- Update UI components with pause button and confirmation modal
- Filter paused listings from reservers' search results
- Add state mapping for domain to UI status conversion

Implements pause listing feature per issue #42 requirements.
Allows sharers to pause active listings, removing them from search
results while retaining reservation history.
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Nov 17, 2025

Reviewer's Guide

This PR adds a ‘pause listing’ feature by introducing a new application service with transaction scoping, extending the GraphQL API with a pauseItemListing mutation and filtering logic, and integrating the pause action into the client UI with state mapping and confirmation dialogs.

Sequence diagram for pausing a listing via GraphQL mutation

sequenceDiagram
actor User
participant UI
participant GraphQL
participant ApplicationService
participant DataSource
User->>UI: Clicks "Pause" button
UI->>GraphQL: pauseItemListing(id)
GraphQL->>ApplicationService: pause({ id })
ApplicationService->>DataSource: withScopedTransaction(repo)
DataSource->>ApplicationService: getById(id)
ApplicationService->>DataSource: listing.pause() & repo.save(listing)
DataSource-->>ApplicationService: Paused listing entity
ApplicationService-->>GraphQL: Paused listing entity
GraphQL-->>UI: Paused listing entity
UI-->>User: Show success message
Loading

Class diagram for ItemListingApplicationService and pause command

classDiagram
    class ItemListingApplicationService {
      +cancel(command)
      +pause(command)
      +queryPaged(command)
      +update(command)
      // ... other methods
    }
    class ItemListingPauseCommand {
      +id: string
    }
    ItemListingApplicationService --> ItemListingPauseCommand
    class ItemListing {
      +pause()
      +state: string
      // ... other fields and methods
    }
    ItemListingApplicationService --> ItemListing
    ItemListingPauseCommand --> ItemListing
Loading

File-Level Changes

Change Details Files
Introduce pause functionality in application service
  • Create ItemListingPauseCommand and pause handler using Unit of Work pattern
  • Throw errors if listing not found or pause fails
  • Expose pause method in ItemListing application service index
packages/sthrift/application-services/src/contexts/listing/item/pause.ts
packages/sthrift/application-services/src/contexts/listing/item/index.ts
Extend GraphQL API with pauseItemListing and filter logic
  • Add pauseItemListing mutation to schema
  • Implement pauseItemListing resolver with authentication check
  • Filter out listings with state ‘Paused’ in itemListings query
packages/sthrift/graphql/src/schema/types/listing/item-listing.graphql
packages/sthrift/graphql/src/schema/types/listing/item-listing.resolvers.ts
Integrate pauseItemListing mutation on client
  • Add pauseItemListing GraphQL mutation fragment
  • Use useMutation hook with success and error handlers
  • Refetch listings after pause completes
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.tsx
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.graphql
Add Pause action UI with confirmation modal
  • Wrap Pause button in Ant Design Popconfirm for both table and card views
  • Trigger onAction('pause', id) on confirm
  • Display success and error messages on pause completion
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-card.tsx
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.tsx
Map domain listing states to UI statuses
  • Implement mapDomainStateToUIStatus function for state-to-status translation
  • Use mapped status in listings table container
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.tsx

Assessment against linked issues

Issue Objective Addressed Explanation
#42 Implement pause listing functionality so that a Sharer can pause their own listing from the management UI, with a confirmation modal, and only the owner can perform this action.
#42 When paused, the listing must change state from active to paused, be removed from search/browse results for reservers, and retain all reservation history.
#42 Integrate with backend API to update listing state, send notification, and show success/error feedback using Ant Design message, following accessibility, error, and loading patterns as for Edit Listing.

Possibly linked issues

  • Listing - Pause Listing #42: The PR implements the core pause listing functionality, including UI, backend mutations, state changes, and removal from search results, directly addressing the issue's requirements.

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

Copy link
Contributor

@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 there - I've reviewed your changes - here's some feedback:

  • Add an ownership check in pauseItemListing (resolver or service) to ensure only the sharer who owns the listing can pause it, not just any authenticated user.
  • Instead of filtering out paused listings in memory in the itemListings resolver, push that filter into your data source query or service to avoid loading all listings and improve performance.
  • The Popconfirm + Button logic is duplicated in both card and table components—consider extracting a reusable action component to reduce duplication and keep the UI code DRY.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Add an ownership check in pauseItemListing (resolver or service) to ensure only the sharer who owns the listing can pause it, not just any authenticated user.
- Instead of filtering out paused listings in memory in the itemListings resolver, push that filter into your data source query or service to avoid loading all listings and improve performance.
- The Popconfirm + Button logic is duplicated in both card and table components—consider extracting a reusable action component to reduce duplication and keep the UI code DRY.

## Individual Comments

### Comment 1
<location> `packages/sthrift/graphql/src/schema/types/listing/item-listing.resolvers.ts:173-177` </location>
<code_context>
			const result =
				await context.applicationServices.Listing.ItemListing.pause({
					id: args.id,
				});
			return result;

</code_context>

<issue_to_address>
**suggestion (code-quality):** Inline variable that is immediately returned ([`inline-immediately-returned-variable`](https://docs.sourcery.ai/Reference/Rules-and-In-Line-Suggestions/TypeScript/Default-Rules/inline-immediately-returned-variable))

```suggestion
			return await context.applicationServices.Listing.ItemListing.pause({
   					id: args.id,
   				});

```

<br/><details><summary>Explanation</summary>Something 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.
</details>
</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.

@arif-u-ahmed arif-u-ahmed marked this pull request as draft November 17, 2025 18:08
@arif-u-ahmed arif-u-ahmed self-assigned this Nov 17, 2025
- Add GraphQL resolver tests for pauseItemListing mutation
- Add application service unit tests for pause functionality
- Add container component tests with state mapping verification
- Update Storybook stories with pause action scenarios
- Add feature file scenarios for pause listing resolver

Completes test coverage requirements per instruction markdowns.
Copy link
Contributor

Copilot AI left a comment

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 implements pause listing functionality for sharers, allowing them to temporarily remove active listings from search results while preserving reservation history. The implementation follows the established cancel pattern and adheres to the DDD architecture with proper separation between domain, application, and presentation layers.

Key Changes:

  • Added pause application service following the Unit of Work pattern with transactional scoping
  • Implemented pauseItemListing GraphQL mutation with authentication checks and comprehensive test coverage
  • Enhanced UI with Popconfirm modal for pause confirmation and state mapping from domain states to UI statuses
  • Applied filtering logic to exclude paused listings from reserver search results

Reviewed Changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/sthrift/application-services/src/contexts/listing/item/pause.ts New application service implementing pause functionality with UoW pattern, matching cancel service structure
packages/sthrift/application-services/src/contexts/listing/item/pause.test.ts Comprehensive unit tests covering success case, not-found error, save failure, and repository errors
packages/sthrift/application-services/src/contexts/listing/item/index.ts Registered pause service in ItemListing application service interface and implementation
packages/sthrift/graphql/src/schema/types/listing/item-listing.graphql Added pauseItemListing mutation to GraphQL schema following cancelItemListing pattern
packages/sthrift/graphql/src/schema/types/listing/item-listing.resolvers.ts Implemented pauseItemListing mutation resolver with authentication and filtering logic for itemListings query
packages/sthrift/graphql/src/schema/types/listing/item-listing.resolvers.test.ts Added BDD-style test scenarios for pause mutation covering authentication, errors, and edge cases
packages/sthrift/graphql/src/schema/types/listing/features/item-listing.resolvers.feature Added feature scenarios documenting pause mutation behavior for successful operations, authentication failures, and errors
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.tsx Replaced plain button with Popconfirm component for pause action with user-friendly confirmation message
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-card.tsx Added Popconfirm to pause button in card view matching table implementation
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.tsx Implemented pauseListing mutation hook, added domain-to-UI state mapping function, and integrated pause action handling
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.test.tsx Added tests verifying component rendering and state mapping for Published and Paused listings
apps/ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.graphql Added pauseItemListing mutation document requesting id, state, and updatedAt fields
apps/ui-sharethrift/src/components/layouts/home/my-listings/stories/all-listings-table.stories.tsx Added Storybook stories demonstrating pause action, active listings filtering, and paused listings display

Comment on lines 49 to 51
// Filter out paused listings from search results for reservers
// Paused listings should not be visible to reservers
return allListings.filter(listing => listing.state !== 'Paused');
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

The itemListings query filters only 'Paused' listings, but should also filter out other non-active states like 'Cancelled', 'Drafted', 'Expired', 'Blocked', and 'Appeal Requested'. According to the domain value objects, only 'Published' listings should be visible to reservers in search results.

Consider filtering to only show 'Published' listings:

return allListings.filter(listing => listing.state === 'Published');

This aligns better with the isActive property in the ListingState value object which returns true only for 'Published' state.

Suggested change
// Filter out paused listings from search results for reservers
// Paused listings should not be visible to reservers
return allListings.filter(listing => listing.state !== 'Paused');
// Only show listings in 'Published' state to reservers in search results
return allListings.filter(listing => listing.state === 'Published');

Copilot uses AI. Check for mistakes.
await context.applicationServices.Listing.ItemListing.pause({
id: args.id,
});
return result;
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Missing semicolon at the end of the return statement. This is inconsistent with the code style used in the cancelItemListing mutation above (line 160) and throughout the codebase.

Copilot uses AI. Check for mistakes.
/**
* Maps domain listing state to UI status
* Domain states: Published, Paused, Cancelled, Drafted, Expired, Blocked, Appeal Requested
* UI statuses: Active, Paused, Cancelled, Draft, Expired, Blocked, Cancelled
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

The comment in line 16 lists "Cancelled" twice in the "UI statuses" list. It should only appear once.

Suggested change
* UI statuses: Active, Paused, Cancelled, Draft, Expired, Blocked, Cancelled
* UI statuses: Active, Paused, Cancelled, Draft, Expired, Blocked

Copilot uses AI. Check for mistakes.
onAction: (action: string, listingId: string) => {
if (action === 'pause') {
console.log('Pause action triggered for listing:', listingId);
alert(`Pausing listing ${listingId}. In real app, this would show a confirmation modal.`);
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

The alert message says "In real app, this would show a confirmation modal", but in the actual implementation (lines 62-73 in all-listings-table.tsx), a Popconfirm component is already used as the confirmation modal. This comment is misleading.

Consider updating the message to:

alert(`Listing ${listingId} paused. The Popconfirm modal would appear in the actual component.`);
Suggested change
alert(`Pausing listing ${listingId}. In real app, this would show a confirmation modal.`);
alert(`Listing ${listingId} paused. The Popconfirm modal would appear in the actual component.`);

Copilot uses AI. Check for mistakes.
success: vi.fn(),
error: vi.fn(),
},
});
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Error: ';' expected.

Suggested change
});
};

Copilot uses AI. Check for mistakes.
success: vi.fn(),
error: vi.fn(),
},
});
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Error: Declaration or statement expected.

Suggested change
});
};

Copilot uses AI. Check for mistakes.
success: vi.fn(),
error: vi.fn(),
},
});
Copy link

Copilot AI Nov 17, 2025

Choose a reason for hiding this comment

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

Error: Declaration or statement expected.

Suggested change
});
};

Copilot uses AI. Check for mistakes.
Resolved conflicts in pnpm-lock.yaml:
- Kept rimraf@^6.1.0 (security upgrade from feature branch)
- Kept only glob@11.1.0 (removed glob@10.4.5 per feature branch security override)
Resolved conflicts in:
- application-services/src/contexts/listing/item/index.ts
  * Merged pause, delete, update, and unblock methods
- graphql/src/schema/types/listing/item-listing.graphql
  * Added pauseItemListing alongside cancelItemListing and deleteItemListing
  * Updated cancelItemListing and deleteItemListing to return ItemListingMutationResult
- graphql/src/schema/types/listing/item-listing.resolvers.ts
  * Added pauseItemListing mutation resolver
  * Fixed cancelItemListing and deleteItemListing to return proper mutation result structure
  * Kept filtering of paused listings in itemListings query
- graphql/src/schema/types/listing/item-listing.resolvers.test.ts
  * Merged mock context to include all methods (pause, deleteListings, unblock)
  * Added pause listing test scenarios
- ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.graphql
  * Merged pauseItemListing, cancelItemListing, and deleteItemListing mutations
- ui-sharethrift/src/components/layouts/home/my-listings/components/all-listings-table.container.tsx
  * Integrated pause, cancel, and delete mutations
  * Kept mapDomainStateToUIStatus helper for state mapping

Regenerated GraphQL types via pnpm run gen
- Removed unused mapDomainStateToUIStatus function causing TS6133 error
- Function was not being used anywhere in the component
- Fixes Azure Pipeline build failure
- Inline immediately returned variable in pauseItemListing resolver
- Add ownership verification to pause listing functionality
  - Only the listing owner (sharer) can pause their listing
  - Prevents unauthorized users from pausing listings
- Optimize paused listing filtering by pushing to database layer
  - Move filter from in-memory to MongoDB query using $nin operator
  - Improves performance by avoiding loading all listings into memory
  - Add excludeStates parameter to ItemListingQueryAllCommand

Security: Implements authorization check for pause action
Performance: Database-level filtering instead of application-level
…atures

- Update queryAll expectations to include excludeStates: ['Paused']
- Update pause expectations to include userEmail parameter
- Fixes tests after implementing Sourcery code review suggestions
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.

Listing - Pause Listing

2 participants