Skip to content

Commit

Permalink
Merge pull request #131 from martinjonsson01/87-show-personalized-fav…
Browse files Browse the repository at this point in the history
…orites-2

87 show personalized favorites
  • Loading branch information
JacobBredin authored May 14, 2022
2 parents 30abc31 + 137d604 commit d38d743
Show file tree
Hide file tree
Showing 35 changed files with 1,807 additions and 118 deletions.
6 changes: 0 additions & 6 deletions src/Application/Contracts/ContractService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,6 @@ public IEnumerable<ContractPreviewDto> SearchUnauthorized(string query)
return ConvertToPreviews(results);
}

/// <inheritdoc />
public IEnumerable<Contract> FetchFavorites()
{
return _repo.Favorites;
}

/// <inheritdoc />
public void UpdateContract(Contract contract)
{
Expand Down
6 changes: 0 additions & 6 deletions src/Application/Contracts/IContractRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ public interface IContractRepository
/// </summary>
IEnumerable<Contract> Recent { get; }

/// <summary>
/// Gets all contracts marked as favorites.
/// </summary>
/// <returns>The contract with a favorite mark.</returns>
IEnumerable<Contract> Favorites { get; }

/// <summary>
/// Adds a new contract to store.
/// </summary>
Expand Down
6 changes: 0 additions & 6 deletions src/Application/Contracts/IContractService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,6 @@ public interface IContractService
/// <returns>Whether the removal was successful.</returns>
bool Remove(Guid id);

/// <summary>
/// Gets contracts marked as favorites.
/// </summary>
/// <returns>All favorite marked contracts.</returns>
IEnumerable<Contract> FetchFavorites();

/// <summary>
/// Updates the contract in the repository.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions src/Application/Users/FavoriteContractDto.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Application.Users;

/// <summary>
/// Used to send information about making a Contract a users favorite.
/// </summary>
public class FavoriteContractDto
{
/// <summary>
/// Gets the id of the user.
/// </summary>
public string UserName { get; init; } = string.Empty;

/// <summary>
/// Gets the id of the contract.
/// </summary>
public Guid ContractId { get; init; }

/// <summary>
/// Gets a value indicating whether gets if the Contract should be a favorite or not.
/// </summary>
public bool IsFavorite { get; init; }
}
15 changes: 15 additions & 0 deletions src/Application/Users/IUserRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,19 @@ public interface IUserRepository
/// Ensures that an admin user is created.
/// </summary>
void EnsureAdminCreated();

/// <summary>
/// Adds a favorite contract to store, for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
void AddFavorite(string userName, Guid contractId);

/// <summary>
/// Removes a stored favorite contract, for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>If the removal was successful.</returns>
bool RemoveFavorite(string userName, Guid contractId);
}
31 changes: 31 additions & 0 deletions src/Application/Users/IUserService.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Domain.Contracts;
using Domain.Users;

namespace Application.Users;
Expand Down Expand Up @@ -40,4 +41,34 @@ public interface IUserService
/// <param name="password">The password of the <see cref="User"/> to validate.</param>
/// <returns>A response containing the generated token.</returns>
AuthenticateResponse Authenticate(string username, string password);

/// <summary>
/// Gets all contracts marked as favorite by a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <returns>All contracts marked as favorite by the user.</returns>
IEnumerable<Contract> FetchAllFavorites(string userName);

/// <summary>
/// Checks if the contract is marked as favorite by the user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>Whether the contract was marked as favorite.</returns>
bool IsFavorite(string userName, Guid contractId);

/// <summary>
/// Adds a favorite contract for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
void AddFavorite(string userName, Guid contractId);

/// <summary>
/// Removes a favorite contract for a certain user.
/// </summary>
/// <param name="userName">The name of the user.</param>
/// <param name="contractId">The id of the contract.</param>
/// <returns>Whether the removal was successful.</returns>
bool RemoveFavorite(string userName, Guid contractId);
}
36 changes: 35 additions & 1 deletion src/Application/Users/UserService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using Application.Configuration;
using Application.Exceptions;

using Domain.Contracts;
using Domain.Users;

using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -71,6 +71,30 @@ public IEnumerable<User> FetchAllUsers()
return _repo.All;
}

/// <inheritdoc />
public IEnumerable<Contract> FetchAllFavorites(string userName)
{
return FetchUser(userName).Favorites;
}

/// <inheritdoc />
public bool IsFavorite(string userName, Guid contractId)
{
return FetchUser(userName).Favorites.Any(c => c.Id == contractId);
}

/// <inheritdoc />
public void AddFavorite(string userName, Guid contractId)
{
_repo.AddFavorite(userName, contractId);
}

/// <inheritdoc />
public bool RemoveFavorite(string userName, Guid contractId)
{
return _repo.RemoveFavorite(userName, contractId);
}

private static IEnumerable<Claim> CreateClaims(User user)
{
var claims = new List<Claim> { new("id", user.Id.ToString()), };
Expand Down Expand Up @@ -102,4 +126,14 @@ private string GenerateJwtToken(User user)
SecurityToken? token = tokenHandler.CreateToken(tokenDescriptor);
return tokenHandler.WriteToken(token);
}

private User FetchUser(string username)
{
User? user = _repo.Fetch(username);

if (user == null)
throw new UserDoesNotExistException(username);

return user;
}
}
9 changes: 4 additions & 5 deletions src/Client/Pages/Contracts/ContractCard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<h4 class="card-title text-truncate m-0" id="contract-name">@Contract.Name</h4>
</div>
<div class="d-flex justify-content-end width-10">
<FavoriteButton @ref="_favoriteButton" Contract="@Contract" OnFavoriteChange="@FavoriteChange"></FavoriteButton>
<FavoriteButton @ref="_favoriteButton" Contract="@Contract" OnFavoriteChange="@((args)=> ChangeFavorite(((Guid, bool))args))"></FavoriteButton>
</div>
<div class="pe-3 pointer" data-bs-toggle="modal" data-bs-target="#@("id_" + Contract.Id)" @onclick="AddToRecentlyViewed"></div>
</div>
Expand Down Expand Up @@ -50,7 +50,7 @@
/// Called when a contract has its favorite status changed.
/// </summary>
[Parameter]
public EventCallback<Contract> OnFavoriteChange { get; set; } = EventCallback<Contract>.Empty;
public EventCallback<(Guid, bool)> OnFavoriteChange { get; set; } = EventCallback<(Guid, bool)>.Empty;

private async Task AddToRecentlyViewed()
{
Expand All @@ -61,10 +61,9 @@
}
}

private async Task FavoriteChange(Contract contract)
private async Task ChangeFavorite((Guid Id, bool IsFavorite)args)
{
Contract = contract;
await OnFavoriteChange.InvokeAsync(contract);
await OnFavoriteChange.InvokeAsync(args);
}

}
48 changes: 34 additions & 14 deletions src/Client/Pages/Contracts/FavoriteButton.razor
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@
@using Newtonsoft.Json
@using Domain.Contracts
@using System.Text
@using Application.Users
@inject HttpClient _http
@inject ISessionService _session

<button class="btn p-0 white-text" @onclick="ChangeFavorite">
@if (Contract.IsFavorite)
@if (isFavorite)
{
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor" class="bi bi-heart-fill" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M8 1.314C12.438-3.248 23.534 4.735 8 15-7.534 4.736 3.562-3.248 8 1.314z"/>
Expand All @@ -31,23 +33,41 @@
/// Called when a contract has its favorite status changed.
/// </summary>
[Parameter]
public EventCallback<Contract> OnFavoriteChange { get; set; } = EventCallback<Contract>.Empty;
public EventCallback<(Guid, bool)> OnFavoriteChange { get; set; } = EventCallback<(Guid, bool)>.Empty;

private async Task ChangeFavorite()
{
var patchDocument = new JsonPatchDocument<Contract>();
patchDocument.Replace(contract => contract.IsFavorite, !Contract.IsFavorite);
var serializedDocument = JsonConvert.SerializeObject(patchDocument);
var content = new StringContent(serializedDocument, Encoding.UTF8, "application/json-patch+json");
/// <summary>
/// Whether the contract is marked as favorite.
/// </summary>
private bool isFavorite { get; set; }

var response = await _http.PatchAsync($"/api/v1/Contracts/{Contract.Id}", content);
if (response.IsSuccessStatusCode)
protected override async void OnInitialized()
{
if (_session.IsAuthenticated)
{
HttpResponseMessage response = await _http.GetAsync($"/api/v1/users/{_session.Username}/favorites/{Contract.Id}");
isFavorite = response.IsSuccessStatusCode;
}
else
{
var contract = await response.Content.ReadFromJsonAsync<Contract>();
if (contract != null) Contract = contract;
await InvokeAsync(StateHasChanged);
await OnFavoriteChange.InvokeAsync(Contract);
isFavorite = false;
}
await InvokeAsync(StateHasChanged);
base.OnInitialized();
}

private async Task ChangeFavorite()
{
if (!string.IsNullOrEmpty(_session.Username))
{
FavoriteContractDto favoriteContract = new() { UserName = _session.Username, ContractId = Contract.Id, IsFavorite = !isFavorite };

HttpResponseMessage response = await _http.PostAsJsonAsync($"/api/v1/users/{_session.Username}/favorites", favoriteContract);
if (response.IsSuccessStatusCode)
{
isFavorite = !isFavorite;
await InvokeAsync(StateHasChanged);
await OnFavoriteChange.InvokeAsync((Contract.Id, isFavorite));
}
}
}
}
11 changes: 6 additions & 5 deletions src/Client/Pages/Dashboard/FavoriteCards.razor
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
@using Domain.Contracts
@using Newtonsoft.Json
@inject HttpClient _http
@inject ISessionService _session

<div id="favorite-contracts" style="padding-top: 30px;">
<h3>Favoriter</h3>
<FetchData @ref="_dataFetcher"
TData="List<Contract>"
Url="/api/v1/Contracts/favorites"
Url=@($"/api/v1/users/{_session.Username}/favorites")
Context="contracts">
@{
var contractsList = contracts.ToList();
Expand All @@ -16,7 +17,7 @@
<div id="favorite-cards-container" class="d-flex flex-row justify-content-around flex-wrap">
@foreach (var contract in contractsList)
{
<ContractCard Contract=@contract OnFavoriteChange="OnFavoriteChange"/>
<ContractCard Contract=@contract OnFavoriteChange="@((args)=> OnFavoriteChange(((Guid, bool))args))"/>
}
</div>
}
Expand All @@ -34,10 +35,10 @@
@code {
private FetchData<List<Contract>> _dataFetcher = null!;

private async Task OnFavoriteChange(Contract contract)
private async Task OnFavoriteChange((Guid Id, bool IsFavorite)args)
{
if (contract.IsFavorite) return;
_dataFetcher.Data?.RemoveAll(other => other.Id == contract.Id);
if (args.IsFavorite) return;
_dataFetcher.Data?.RemoveAll(other => other.Id == args.Id);
await InvokeAsync(StateHasChanged);
}

Expand Down
15 changes: 9 additions & 6 deletions src/Domain/Contracts/Contract.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace Domain.Contracts;
using Domain.Users;

namespace Domain.Contracts;

/// <summary>
/// A document containing negotiated discounts and benefits.
Expand All @@ -10,11 +12,6 @@ public class Contract
/// </summary>
public Guid Id { get; init; } = Guid.NewGuid();

/// <summary>
/// Gets or sets a value indicating whether the contract is a favorite or not.
/// </summary>
public bool IsFavorite { get; set; }

/// <summary>
/// Gets or sets the name of the contract.
/// </summary>
Expand Down Expand Up @@ -69,4 +66,10 @@ public class Contract
/// Gets or sets the contact information for the supplier.
/// </summary>
public string SupplierContactInfo { get; set; } = "Kontaktinformation till leverantör saknas.";

// Entity Framework requires that a navigation property exists in both
// classes (User and Contract) when a many-to-many relation is to be genereated,
// and exists only for this reason.
// See the Entity Framework documentation for more information.
private ICollection<User> FavoritedBy { get; } = new List<User>();
}
7 changes: 7 additions & 0 deletions src/Domain/Users/User.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Domain.Contracts;

namespace Domain.Users;

/// <summary>
Expand Down Expand Up @@ -34,4 +36,9 @@ public class User
/// Gets or sets the date of the latest payment by the user. It is set to 1/1/0001 12:00:00 AM by default.
/// </summary>
public DateTime LatestPaymentDate { get; set; } = DateTime.MinValue;

/// <summary>
/// Gets favorite contracts.
/// </summary>
public ICollection<Contract> Favorites { get; init; } = new List<Contract>();
}
3 changes: 0 additions & 3 deletions src/Infrastructure/Databases/EFContractRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@ public EFContractRepository(EFDatabaseContext context, ILogger<EFContractReposit
/// <inheritdoc />
public IEnumerable<Contract> Recent => _recent.FetchRecentContracts();

/// <inheritdoc />
public IEnumerable<Contract> Favorites => Contracts.Where(contract => contract.IsFavorite);

private DbSet<Contract> Contracts => _context.Contracts;

/// <inheritdoc />
Expand Down
6 changes: 4 additions & 2 deletions src/Infrastructure/Databases/EFDatabaseContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public EFDatabaseContext(
/// <inheritdoc />
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
_ = modelBuilder.Entity<User>()
.HasKey(user => user.Id);
_ = modelBuilder
.Entity<User>()
.HasMany(p => p.Favorites)
.WithMany("FavoritedBy");
}
}
Loading

0 comments on commit d38d743

Please sign in to comment.