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
@@ -0,0 +1,4 @@
namespace Evently.Server.Common.Domains.Models;

// [{"categoryId":1,"gatheringId":22},{"categoryId":2,"gatheringId":22}]
public sealed record GatheringCategoryDetailDto(long GatheringId, long CategoryId);
3 changes: 2 additions & 1 deletion src/Evently.Server/Common/Domains/Models/GatheringReqDto.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ public sealed record GatheringReqDto(
DateTimeOffset? CancellationDateTime,
string Location,
string OrganiserId,
string? CoverSrc
string? CoverSrc,
List<GatheringCategoryDetailDto> GatheringCategoryDetails
);
13 changes: 12 additions & 1 deletion src/Evently.Server/Common/Extensions/MapperExtension.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ namespace Evently.Server.Common.Extensions;

public static class MapperExtension {
public static Gathering ToGathering(this GatheringReqDto gatheringReqDto) {
List<GatheringCategoryDetail> gatheringCategoryDetails = gatheringReqDto.GatheringCategoryDetails
.Select((detail) => new GatheringCategoryDetail {
GatheringId = gatheringReqDto.GatheringId,
CategoryId = detail.CategoryId,
})
.ToList();
Gathering gathering = new() {
GatheringId = gatheringReqDto.GatheringId,
Name = gatheringReqDto.Name,
Expand All @@ -15,11 +21,15 @@ public static Gathering ToGathering(this GatheringReqDto gatheringReqDto) {
Location = gatheringReqDto.Location,
OrganiserId = gatheringReqDto.OrganiserId,
CoverSrc = gatheringReqDto.CoverSrc,
GatheringCategoryDetails = gatheringCategoryDetails,
};
return gathering;
}

public static GatheringReqDto ToGatheringDto(this Gathering gathering) {
List<GatheringCategoryDetailDto> gatheringCategoryDetails = gathering.GatheringCategoryDetails
.Select(detail => new GatheringCategoryDetailDto(detail.GatheringId, detail.CategoryId))
.ToList();
GatheringReqDto reqDto = new(
gathering.GatheringId,
gathering.Name,
Expand All @@ -29,7 +39,8 @@ public static GatheringReqDto ToGatheringDto(this Gathering gathering) {
gathering.CancellationDateTime,
gathering.Location,
gathering.OrganiserId,
gathering.CoverSrc
gathering.CoverSrc,
gatheringCategoryDetails
);
return reqDto;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace Evently.Server.Features.Accounts;
namespace Evently.Server.Features.Accounts.Controllers;

// Based on https://tinyurl.com/26arz8vk
[ApiController]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using System.Globalization;
using System.Threading.Channels;

namespace Evently.Server.Features.Bookings;
namespace Evently.Server.Features.Bookings.Controllers;

[ApiController]
[Route("api/v1/[controller]")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public async Task<Booking> CreateBooking(BookingReqDto bookingReqDto) {
if (!validationResult.IsValid) {
throw new ArgumentException($"Account has already booked this gathering (GatheringId: {booking.GatheringId})");
}

booking.BookingId = $"book_{await Nanoid.GenerateAsync(size: 10)}";
await db.Bookings.AddAsync(booking);
await db.SaveChangesAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Globalization;

namespace Evently.Server.Features.Categories;
namespace Evently.Server.Features.Categories.Controllers;

[ApiController]
[Route("api/v1/[controller]")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
using Evently.Server.Common.Domains.Models;
using Evently.Server.Common.Extensions;
using Evently.Server.Features.Accounts.Services;
using FluentValidation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using MimeKit;
using System.Globalization;

namespace Evently.Server.Features.Gatherings;
namespace Evently.Server.Features.Gatherings.Controllers;

[ApiController]
[Route("api/v1/[controller]")]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ public async Task<Gathering> UpdateGathering(long gatheringId, GatheringReqDto g
}

Gathering current = await db.Gatherings.AsTracking()
.Include((g) => g.GatheringCategoryDetails)
.FirstOrDefaultAsync((ex) => ex.GatheringId == gatheringId)
?? throw new KeyNotFoundException($"{gatheringId} not found");

Expand All @@ -87,6 +88,7 @@ public async Task<Gathering> UpdateGathering(long gatheringId, GatheringReqDto g
current.End = gathering.End;
current.Location = gathering.Location;
current.CoverSrc = gathering.CoverSrc;
current.GatheringCategoryDetails = gathering.GatheringCategoryDetails;

await db.SaveChangesAsync();
return current;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;

namespace Evently.Server.Features.HealthChecks;
namespace Evently.Server.Features.HealthChecks.Controllers;

[ApiController]
[Route("api/v1/[controller]")]
Expand Down
2 changes: 1 addition & 1 deletion src/evently.client/src/lib/components/card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function Card({ gathering, accountId }: CardProps): JSX.Element {
<Icon icon="mingcute:time-fill" width="24" height="24" />
<span>{`${start.toLocaleString(DateTime.DATETIME_MED)} — ${end.toLocaleString(DateTime.DATETIME_MED)}`}</span>
</div>
<div className="flex flex-row flex-wrap space-x-2">
<div className="flex flex-row flex-wrap gap-x-2 gap-y-1">
{categories.map((category) => (
<div className="badge badge-soft badge-info" key={category.categoryId}>
{category.categoryName}
Expand Down
38 changes: 22 additions & 16 deletions src/evently.client/src/lib/domains/models/upsert-dtos.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,35 @@
export class BookingReqDto {
public bookingId = "";
public attendeeId = "";
public gatheringId = 0;
public creationDateTime = new Date();
public checkInDateTime: Date | null = null;
public checkoutDateTime: Date | null = null;
public cancellationDateTime: Date | null = null;
bookingId = "";
attendeeId = "";
gatheringId = 0;
creationDateTime = new Date();
checkInDateTime: Date | null = null;
checkoutDateTime: Date | null = null;
cancellationDateTime: Date | null = null;

constructor(partial: Partial<BookingReqDto> = {}) {
Object.assign(this, partial);
}
}

export class GatheringReqDto {
public gatheringId = 0;
public name = "";
public description = "";
public start = new Date();
public end = new Date();
public location = "";
public organiserId = "";
public coverSrc?: string | null = null;
public cancellationDateTime: Date | null = null;
gatheringId = 0;
name = "";
description = "";
start = new Date();
end = new Date();
location = "";
organiserId = "";
coverSrc?: string | null = null;
cancellationDateTime: Date | null = null;
gatheringCategoryDetails: GatheringCategoryDetailReqDto[] = [];

constructor(partial: Partial<GatheringReqDto> = {}) {
Object.assign(this, partial);
}
}

export class GatheringCategoryDetailReqDto {
gatheringId = 0;
categoryId = 0;
}
7 changes: 7 additions & 0 deletions src/evently.client/src/lib/services/category-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Category } from "~/lib/domains/entities";
import axios from "axios";

export async function getCategories(): Promise<Category[]> {
const response = await axios.get<Category[]>("/api/v1/Categories");
return response.data;
}
14 changes: 11 additions & 3 deletions src/evently.client/src/lib/services/gathering-service.mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Booking, Gathering, Category } from "~/lib/domains/entities";
import { Booking, Gathering, Category, GatheringCategoryDetail } from "~/lib/domains/entities";
import { GatheringReqDto } from "~/lib/domains/models";
import type { GetGatheringsParams } from "./gathering-service";
import type { PageResult } from "~/lib/domains/interfaces";
Expand Down Expand Up @@ -101,7 +101,11 @@ export async function createMockGathering(
): Promise<Gathering> {
return {
...new Gathering(),
...gatheringDto
...gatheringDto,
gatheringCategoryDetails: gatheringDto.gatheringCategoryDetails.map((detail) => ({
...detail,
...new GatheringCategoryDetail()
}))
};
}

Expand All @@ -114,6 +118,10 @@ export async function updateMockGathering(
return {
...new Gathering(),
...gatheringDto,
gatheringId: gatheringId
gatheringId: gatheringId,
gatheringCategoryDetails: gatheringDto.gatheringCategoryDetails.map((detail) => ({
...detail,
...new GatheringCategoryDetail()
}))
};
}
38 changes: 21 additions & 17 deletions src/evently.client/src/lib/services/gathering-service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Gathering } from "~/lib/domains/entities";
import axios from "axios";
import { GatheringReqDto } from "~/lib/domains/models";
import { GatheringCategoryDetailReqDto, GatheringReqDto } from "~/lib/domains/models";
import type { PageResult } from "~/lib/domains/interfaces";

export interface GetGatheringsParams {
Expand Down Expand Up @@ -43,17 +43,7 @@ export async function createGathering(
gatheringDto: GatheringReqDto,
coverImg?: File | null
): Promise<Gathering> {
const formData = new FormData();
for (const [key, value] of Object.entries(gatheringDto)) {
if (value != null) {
formData.set(key, value);
}
}
if (coverImg != null) {
formData.set("coverImg", coverImg, coverImg.name);
}
formData.set("start", gatheringDto.start.toISOString());
formData.set("end", gatheringDto.end.toISOString());
const formData: FormData = toFormData(gatheringDto, coverImg);

const response = await axios.post<Gathering>(`/api/v1/Gatherings`, formData, {
headers: { "Content-Type": "multipart/form-data" }
Expand All @@ -66,12 +56,30 @@ export async function updateGathering(
gatheringDto: GatheringReqDto,
coverImg?: File | null
): Promise<Gathering> {
const formData: FormData = toFormData(gatheringDto, coverImg);

const response = await axios.put<Gathering>(`/api/v1/Gatherings/${gatheringId}`, formData, {
headers: { "Content-Type": "multipart/form-data" }
});
return response.data;
}

function toFormData(gatheringDto: GatheringReqDto, coverImg?: File | null): FormData {
const formData = new FormData();
for (const [key, value] of Object.entries(gatheringDto)) {
if (value != null) {
formData.set(key, value);
}
}
formData.delete("gatheringCategoryDetails");

for (let i = 0; i < gatheringDto.gatheringCategoryDetails.length; i++) {
const detail: GatheringCategoryDetailReqDto = gatheringDto.gatheringCategoryDetails[i];
for (const [key, value] of Object.entries(detail)) {
formData.set(`gatheringCategoryDetails[${i}].${key}`, value);
}
}

if (coverImg != null) {
formData.set("coverImg", coverImg, coverImg.name);
}
Expand All @@ -80,9 +88,5 @@ export async function updateGathering(
if (gatheringDto.cancellationDateTime != null) {
formData.set("cancellationDateTime", gatheringDto.cancellationDateTime.toISOString());
}

const response = await axios.put<Gathering>(`/api/v1/Gatherings/${gatheringId}`, formData, {
headers: { "Content-Type": "multipart/form-data" }
});
return response.data;
return formData;
}
1 change: 1 addition & 0 deletions src/evently.client/src/lib/services/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./auth-service";
export * from "./store";
export * from "./gathering-service.ts";
export * from "./booking-service";
export * from "./category-service.ts";
15 changes: 15 additions & 0 deletions src/evently.client/src/lib/services/util-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,18 @@ export function toIsoString(date: Date | null): string {
const dateTime: DateTime = DateTime.fromJSDate(date);
return dateTime.toFormat("yyyy-MM-dd'T'HH:mm");
}

export async function fetchFile(src: string, fileName?: string): Promise<File> {
const urlObj = new URL(src);
if (fileName == null) {
fileName = urlObj.pathname.split("/").pop() ?? "";
}

const response = await fetch(src);
if (!response.ok) {
throw new Error(`Failed to fetch ${src}`);
}
const dataBlob: Blob = await response.blob();
const file: File = new File([dataBlob], fileName, { type: dataBlob.type });
return file;
}
Loading