Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Task<PageResult<Gathering>> GetGatherings(
DateTimeOffset? endDateBefore,
DateTimeOffset? endDateAfter,
bool? isCancelled,
HashSet<long>? categoryIds,
int? offset,
int? limit);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,11 @@ public async Task<ActionResult<Booking>> CreateBooking([FromBody] BookingReqDto
await emailQueue.WriteAsync(booking.BookingId);
return Ok(booking);
}

[HttpPut("{bookingId}", Name = "UpdateBooking")]
public async Task<ActionResult> UpdateBooking(string bookingId,
[FromBody] BookingReqDto bookingReqDto) {

[HttpPatch("{bookingId}/cancel", Name = "CancelBooking")]
public async Task<ActionResult> CancelBooking(string bookingId) {
Booking? booking = await bookingService.GetBooking(bookingId);
if (booking is null) {
if (booking?.Gathering is null) {
return NotFound();
}

Expand All @@ -78,7 +77,8 @@ public async Task<ActionResult> UpdateBooking(string bookingId,
return Forbid();
}

booking = await bookingService.UpdateBooking(bookingId, bookingReqDto);
booking.CancellationDateTime = DateTimeOffset.UtcNow;
booking = await bookingService.UpdateBooking(bookingId, bookingReqDto: booking.ToBookingDto());
Comment on lines +80 to +81
Copy link

Copilot AI Aug 30, 2025

Choose a reason for hiding this comment

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

The CancelBooking method is still calling UpdateBooking internally after setting CancellationDateTime. Consider creating a dedicated CancelBooking method in the service layer to maintain separation of concerns and make the intent clearer.

Suggested change
booking.CancellationDateTime = DateTimeOffset.UtcNow;
booking = await bookingService.UpdateBooking(bookingId, bookingReqDto: booking.ToBookingDto());
booking = await bookingService.CancelBooking(bookingId);

Copilot uses AI. Check for mistakes.
Comment on lines +80 to +81
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

The parameter name 'bookingReqDto' is explicitly specified but the method is still using the generic UpdateBooking. Consider creating a dedicated CancelBooking method in the service layer for better semantic clarity.

Suggested change
booking.CancellationDateTime = DateTimeOffset.UtcNow;
booking = await bookingService.UpdateBooking(bookingId, bookingReqDto: booking.ToBookingDto());
booking = await bookingService.CancelBooking(bookingId);

Copilot uses AI. Check for mistakes.
return Ok(booking);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Evently.Server.Features.Gatherings.Controllers;
[ApiController]
[Route("api/v1/[controller]")]
public sealed class GatheringsController(
ILogger<GatheringsController> logger,
IGatheringService gatheringService,
IFileStorageService fileStorageService) : ControllerBase {
[HttpGet("{gatheringId:long}", Name = "GetGathering")]
Expand All @@ -31,7 +32,9 @@ public async Task<ActionResult<List<Gathering>>> GetGatherings(string? attendeeI
string? name,
DateTimeOffset? startDateBefore, DateTimeOffset? startDateAfter, DateTimeOffset? endDateBefore, DateTimeOffset? endDateAfter,
bool? isCancelled,
long[]? categoryIds,
int? offset, int? limit) {
logger.LogInformation("categoryIds: {}", string.Join(",", categoryIds ?? []));
Copy link

Copilot AI Sep 2, 2025

Choose a reason for hiding this comment

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

This debug logging statement should be removed or moved to a more appropriate log level (Debug/Trace) as it will log on every request in production.

Suggested change
logger.LogInformation("categoryIds: {}", string.Join(",", categoryIds ?? []));
logger.LogDebug("categoryIds: {}", string.Join(",", categoryIds ?? []));

Copilot uses AI. Check for mistakes.
PageResult<Gathering> result = await gatheringService.GetGatherings(attendeeId,
organiserId,
name,
Expand All @@ -40,6 +43,7 @@ public async Task<ActionResult<List<Gathering>>> GetGatherings(string? attendeeI
endDateBefore,
endDateAfter,
isCancelled,
categoryIds?.ToHashSet(),
offset,
limit);
List<Gathering> exhibitions = result.Items;
Expand Down Expand Up @@ -105,7 +109,6 @@ public async Task<ActionResult<Gathering>> DeleteGathering(long gatheringId) {
}

private async Task<Uri> UploadCoverImage(long gatheringId, IFormFile coverImg) {
string t = coverImg.FileName;
string fileName = $"gatherings/{gatheringId}/cover-image{Path.GetExtension(coverImg.FileName)}";
BinaryData binaryData = await coverImg.ToBinaryData();
return await fileStorageService.UploadFile(fileName,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public async Task<PageResult<Gathering>> GetGatherings(
DateTimeOffset? endDateBefore,
DateTimeOffset? endDateAfter,
bool? isCancelled,
HashSet<long>? categoryIds,
int? offset,
int? limit) {
IQueryable<Gathering> query = db.Gatherings
Expand All @@ -37,6 +38,7 @@ public async Task<PageResult<Gathering>> GetGatherings(
.Where((gathering) => endDateAfter == null || gathering.End >= endDateAfter)
.Where((gathering) => organiserId == null || gathering.OrganiserId == organiserId)
.Where(gathering => isCancelled == null || gathering.CancellationDateTime.HasValue == isCancelled)
.Where((gathering) => categoryIds == null || gathering.GatheringCategoryDetails.Any(detail => categoryIds.Contains(detail.CategoryId)))
.Where((gathering) =>
attendeeId == null || gathering.Bookings.Any((be) => be.AttendeeId == attendeeId))
.Include(gathering => gathering.Bookings.Where((be) => be.AttendeeId == attendeeId))
Expand Down
3 changes: 3 additions & 0 deletions src/evently.client/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export default tseslint.config([
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser
},
rules: {
"@typescript-eslint/no-explicit-any": "off"
}
}
]);
1 change: 1 addition & 0 deletions src/evently.client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"preview": "vite preview"
},
"dependencies": {
"@oddbird/css-anchor-positioning": "^0.6.1",
"@tailwindcss/vite": "^4.1.11",
"@tanstack/react-form": "^1.19.2",
"@tanstack/react-query": "^5.84.2",
Expand Down
83 changes: 83 additions & 0 deletions src/evently.client/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 16 additions & 13 deletions src/evently.client/src/lib/components/-card.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
import { Card } from "~/lib/components/card.tsx";
import { Gathering } from "~/lib/domains/entities";
import { getMockGathering } from "~/lib/services/gathering-service.mock";
import { TestWrapper, WrapperDataTestId } from "~/lib/components/test-wrapper.tsx";
import {
TestWrappers,
WrapperDataTestId
} from "~/lib/components/test-wrappers.tsx";

describe("Card Component", () => {
let mockGathering: Gathering;
Expand All @@ -12,19 +15,19 @@ describe("Card Component", () => {

it("renders gathering name", async () => {
render(
<TestWrapper>
<TestWrappers>
<Card gathering={mockGathering} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));
expect(screen.getByText("Tech Conference 2024")).toBeInTheDocument();
});

it("renders gathering description", async () => {
render(
<TestWrapper>
<TestWrappers>
<Card gathering={mockGathering} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));

Expand All @@ -35,9 +38,9 @@ describe("Card Component", () => {

it("renders gathering location", async () => {
render(
<TestWrapper>
<TestWrappers>
<Card gathering={mockGathering} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));

Expand All @@ -46,9 +49,9 @@ describe("Card Component", () => {

it("renders cover image when coverSrc is provided", async () => {
render(
<TestWrapper>
<TestWrappers>
<Card gathering={mockGathering} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));

Expand All @@ -64,9 +67,9 @@ describe("Card Component", () => {
};

render(
<TestWrapper>
<TestWrappers>
<Card gathering={cancelledGathering} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));
expect(screen.getByText(/cancelled/i)).toBeInTheDocument();
Expand All @@ -76,9 +79,9 @@ describe("Card Component", () => {
const gatheringWithMultipleCategories: Gathering = await getMockGathering(1);

render(
<TestWrapper>
<TestWrappers>
<Card gathering={gatheringWithMultipleCategories} />
</TestWrapper>
</TestWrappers>
);
await waitFor(() => screen.findByTestId(WrapperDataTestId));

Expand Down
7 changes: 5 additions & 2 deletions src/evently.client/src/lib/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ export { Card } from "./card";
export { Tabs } from "./tabs";
export * from "./tab-state.ts";
export { FieldErrMsg } from "./field-err-msg.tsx";
export { TestWrapper, WrapperDataTestId } from "./test-wrapper.tsx";
export * from "./render-with-test-providers.tsx";
export {
TestWrappers,
WrapperDataTestId,
TestRouteWrapper
} from "./test-wrappers.tsx";
Loading