School Commit Init
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
*.swp
|
||||
*.*~
|
||||
project.lock.json
|
||||
.DS_Store
|
||||
*.pyc
|
||||
nupkg/
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/
|
||||
|
||||
# Rider
|
||||
.idea/
|
||||
|
||||
# Visual Studio
|
||||
.vs/
|
||||
|
||||
# Fleet
|
||||
.fleet/
|
||||
|
||||
# Code Rush
|
||||
.cr/
|
||||
|
||||
# User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
build/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Oo]ut/
|
||||
msbuild.log
|
||||
msbuild.err
|
||||
msbuild.wrn
|
||||
@@ -0,0 +1,120 @@
|
||||
// using AutoMapper;
|
||||
// using Silk_Road_Api.Models;
|
||||
// using Silk_Road_Api.Profiles;
|
||||
// using Silk_Road_Api.Repositories;
|
||||
// using Silk_Road_Api.Services;
|
||||
// using Silk_Road_Api.ViewModels;
|
||||
|
||||
// namespace Silk_Road_Api_Test;
|
||||
|
||||
// public class DrugServiceTest
|
||||
// {
|
||||
// [Fact]
|
||||
// public async void GetDrug_ValidId_ReturnsADrug()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
|
||||
// // Act
|
||||
// var drug = await drugService.GetDrug(1);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(drug);
|
||||
// Assert.Equal(1, drug.id);
|
||||
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void GetDrug_InvalidId_ThrowsError()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
|
||||
// // Act
|
||||
// var act = async () => await drugService.GetDrug(100);
|
||||
|
||||
// // Assert
|
||||
// await Assert.ThrowsAsync<Exception>(act);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void GetDrugs_WithPageSize10andPage1_Returns10Drugs()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
// SearchInput searchInput = new SearchInput
|
||||
// {
|
||||
// PageSize = 10,
|
||||
// Page = 1
|
||||
// };
|
||||
// // Act
|
||||
// var drugs = await drugService.GetDrugs(searchInput);
|
||||
// // Assert
|
||||
// Assert.NotNull(drugs);
|
||||
// Assert.Equal(16, drugs.Total);
|
||||
// Assert.Equal(10, drugs.Items.Count);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void AddDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
// DrugViewModel drug = new DrugViewModel
|
||||
// {
|
||||
// name = "Test Drug",
|
||||
// description = "Test Description",
|
||||
// price = 100,
|
||||
// stock = 10,
|
||||
// manufacturer = "Pfizer"
|
||||
// };
|
||||
// // Act
|
||||
// var result = await drugService.AddDrug(drug);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void UpdateDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
// DrugViewModel drug = new DrugViewModel
|
||||
// {
|
||||
// id = 1,
|
||||
// name = "Test Drug",
|
||||
// description = "Test Description",
|
||||
// price = 100,
|
||||
// stock = 10,
|
||||
// manufacturer = "Pfizer"
|
||||
// };
|
||||
// // Act
|
||||
// var result = await drugService.UpdateDrug(drug);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void DeleteDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
// // Act
|
||||
// var result = await drugService.DeleteDrug(1);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void DeleteDrugs_InvalidId_ReturnsFalse()
|
||||
// {
|
||||
// // Arrange
|
||||
// IDrugService drugService = new DrugService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<DrugProfile>())));
|
||||
// // Act
|
||||
// var result = await drugService.DeleteDrug(100);
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
|
||||
// }
|
||||
@@ -0,0 +1 @@
|
||||
global using Xunit;
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
// using AutoMapper;
|
||||
// using Silk_Road_Api;
|
||||
// using Silk_Road_Api.Models;
|
||||
// using Silk_Road_Api.Repositories;
|
||||
// using Silk_Road_Api.Services;
|
||||
// using Silk_Road_Api.ViewModels;
|
||||
|
||||
// namespace Silk_Road_Api_Test;
|
||||
|
||||
// public class ManufacturerServiceTest
|
||||
// {
|
||||
// [Fact]
|
||||
// public async void GetManufacturer_ValidId_ReturnsAManufacturer()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
|
||||
// // Act
|
||||
// var drug = await manufacturerService.GetManufacturer(1);
|
||||
|
||||
// // Assert
|
||||
// Assert.NotNull(drug);
|
||||
// Assert.Equal(1, drug.id);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void GetManufacturer_InvalidId_ThrowsError()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
|
||||
// // Act
|
||||
// var act = async () => await manufacturerService.GetManufacturer(100);
|
||||
|
||||
// // Assert
|
||||
// await Assert.ThrowsAsync<Exception>(act);
|
||||
// }
|
||||
// [Fact]
|
||||
// public async void GetManufacturers_WithPageSize1andPage1_Returns1Drugs()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
// SearchInput searchInput = new SearchInput
|
||||
// {
|
||||
// PageSize = 1,
|
||||
// Page = 1
|
||||
// };
|
||||
// // Act
|
||||
// var manufacturers = await manufacturerService.GetManufacturers(searchInput);
|
||||
// // Assert
|
||||
// Assert.NotNull(manufacturers);
|
||||
// Assert.Equal(1, manufacturers.Total);
|
||||
// Assert.Equal(1, manufacturers.Items.Count);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void AddDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
// ManufacturerViewModel manufacturer = new ManufacturerViewModel
|
||||
// {
|
||||
// name = "Azteca",
|
||||
// contact = "mail@azteca.com",
|
||||
// address = "test",
|
||||
// };
|
||||
// // Act
|
||||
// var result = await manufacturerService.AddManufacturer(manufacturer);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void UpdateDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
// ManufacturerViewModel manufacturer = new ManufacturerViewModel
|
||||
// {
|
||||
// id = 1,
|
||||
// name = "Azteca",
|
||||
// contact = "mail@azteca.com",
|
||||
// address = "test",
|
||||
// };
|
||||
// // Act
|
||||
// var result = await manufacturerService.UpdateManufacturer(manufacturer);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void DeleteDrugs_Succesful()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
// // Act
|
||||
// var result = await manufacturerService.DeleteManufacturer(1);
|
||||
// // Assert
|
||||
// Assert.True(result);
|
||||
// }
|
||||
|
||||
// [Fact]
|
||||
// public async void DeleteDrugs_InvalidId_ReturnsFalse()
|
||||
// {
|
||||
// // Arrange
|
||||
// IManufacturerService manufacturerService = new ManufacturerService(new MemoryRepository(), new MemoryManufacturerRepository(), new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<ManufacturerProfile>())));
|
||||
// // Act
|
||||
// var result = await manufacturerService.DeleteManufacturer(100);
|
||||
// // Assert
|
||||
// Assert.False(result);
|
||||
// }
|
||||
|
||||
// }
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RootNamespace>Silk_Road_Api_Test</RootNamespace>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Silk Road Api\Silk Road Api.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,28 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk Road Api", "Silk Road Api\Silk Road Api.csproj", "{424EB0D7-2D26-458F-B242-330792AC9265}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk Road Api Test", "Silk Road Api Test\Silk Road Api Test.csproj", "{D14A6B3E-FD52-42CF-9468-5782E269FCBD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{424EB0D7-2D26-458F-B242-330792AC9265}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{424EB0D7-2D26-458F-B242-330792AC9265}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{424EB0D7-2D26-458F-B242-330792AC9265}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{424EB0D7-2D26-458F-B242-330792AC9265}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{D14A6B3E-FD52-42CF-9468-5782E269FCBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{D14A6B3E-FD52-42CF-9468-5782E269FCBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{D14A6B3E-FD52-42CF-9468-5782E269FCBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{D14A6B3E-FD52-42CF-9468-5782E269FCBD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.Services;
|
||||
using Timer = System.Timers.Timer;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Http.HttpResults;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class DrugController : ControllerBase
|
||||
{
|
||||
private readonly IDrugService _drugService;
|
||||
|
||||
public DrugController(IDrugService drugService)
|
||||
{
|
||||
_drugService = drugService;
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("get-drugs", Name = "GetDrugs")]
|
||||
public async Task<IActionResult> GetDrugs([FromQuery] SearchInput searchInput)
|
||||
{
|
||||
var drugs = await _drugService.GetDrugs(this, searchInput);
|
||||
return Ok(drugs);
|
||||
}
|
||||
|
||||
[HttpGet("get-drug/{id}", Name = "GetDrug")]
|
||||
public async Task<IActionResult> GetDrug(int id)
|
||||
{
|
||||
var drug = await _drugService.GetDrug(this,id);
|
||||
if (drug == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(drug);
|
||||
}
|
||||
|
||||
[HttpPost("add-drug", Name = "AddDrug")]
|
||||
public async Task<IActionResult> AddDrug([FromBody] DrugViewModel drug)
|
||||
{
|
||||
if (await _drugService.AddDrug(this,drug))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[HttpPut("update-drug", Name = "UpdateDrug")]
|
||||
public async Task<IActionResult> UpdateDrug([FromBody] DrugViewModel drug)
|
||||
{
|
||||
if (await _drugService.UpdateDrug(this, drug))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[HttpDelete("delete-drug/{id}", Name = "DeleteDrug")]
|
||||
public async Task<IActionResult> DeleteDrug(int id)
|
||||
{
|
||||
if (await _drugService.DeleteDrug(this, id))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[Route("/ws")]
|
||||
[ApiExplorerSettings(IgnoreApi = true)]
|
||||
public async Task Get()
|
||||
{
|
||||
if (HttpContext.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
var timer = new Timer(5000);
|
||||
timer.Elapsed += async (sender, e) =>
|
||||
{
|
||||
await SendMessage(webSocket, timer);
|
||||
};
|
||||
timer.Start();
|
||||
while (webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
var result = await webSocket.ReceiveAsync(new ArraySegment<byte>(new byte[1024]), CancellationToken.None);
|
||||
if (result.MessageType == WebSocketMessageType.Close)
|
||||
{
|
||||
await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Done", CancellationToken.None);
|
||||
timer.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("get-chart-data", Name = "GetChartData")]
|
||||
public async Task<IActionResult> GetChartData()
|
||||
{
|
||||
var chartData = await _drugService.GetChartData();
|
||||
return Ok(chartData);
|
||||
}
|
||||
|
||||
private async Task SendMessage(WebSocket webSocket, Timer timer)
|
||||
{
|
||||
_drugService.GenerateDrugs();
|
||||
if (webSocket.State == WebSocketState.Open)
|
||||
{
|
||||
var buffer = Encoding.UTF8.GetBytes("Drugs generated");
|
||||
await webSocket.SendAsync(new ArraySegment<byte>(buffer, 0, buffer.Length), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Silk_Road_Api.Extensions;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Services;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class ManufacturerController : ControllerBase
|
||||
{
|
||||
private readonly IManufacturerService _manufacturerService;
|
||||
|
||||
public ManufacturerController(IManufacturerService manufacturerService)
|
||||
{
|
||||
_manufacturerService = manufacturerService;
|
||||
}
|
||||
|
||||
[HttpGet("get-manufacturer", Name = "GetManufacturers")]
|
||||
public async Task<IActionResult> GetManufacturers([FromQuery] SearchInput searchInput)
|
||||
{
|
||||
var manufacturers = await _manufacturerService.GetManufacturers(this, searchInput);
|
||||
return Ok(manufacturers);
|
||||
}
|
||||
|
||||
[HttpGet("get-manufacturer/{id}", Name = "GetManufacturer")]
|
||||
public async Task<IActionResult> GetManufacturer(int id)
|
||||
{
|
||||
var manufacturer = await _manufacturerService.GetManufacturer(this, id);
|
||||
if (manufacturer == null)
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
return Ok(manufacturer);
|
||||
}
|
||||
|
||||
[HttpPost("add-manufacturer", Name = "AddManufacturer")]
|
||||
public async Task<IActionResult> AddManufacturer([FromBody] ManufacturerViewModel manufacturer)
|
||||
{
|
||||
if (await _manufacturerService.AddManufacturer(this, manufacturer))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[HttpPut("update-manufacturer", Name = "UpdateManufacturer")]
|
||||
public async Task<IActionResult> UpdateManufacturer([FromBody] ManufacturerViewModel manufacturer)
|
||||
{
|
||||
if (await _manufacturerService.UpdateManufacturer(this, manufacturer))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
|
||||
[HttpDelete("delete-manufacturer/{id}", Name = "DeleteManufacturer")]
|
||||
public async Task<IActionResult> DeleteManufacturer(int id)
|
||||
{
|
||||
if (await _manufacturerService.DeleteManufacturer(this, id))
|
||||
return Ok();
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
+49
@@ -0,0 +1,49 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Silk_Road_Api.Extensions;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Services;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("user")]
|
||||
public class UserController : ControllerBase
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
|
||||
public UserController(IUserService userService)
|
||||
{
|
||||
_userService = userService;
|
||||
}
|
||||
|
||||
[HttpPost("register-user", Name ="RegisterUser")]
|
||||
public async Task<IActionResult> RegisterUserAsync([FromBody] RegistrationViewModel user)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _userService.RegisterUser(user);
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
[HttpPost("login-user", Name = "LoginUser")]
|
||||
public IActionResult LoginUser([FromBody] LoginViewModel loginViewModel)
|
||||
{
|
||||
try
|
||||
{
|
||||
var user = _userService.LoginUser(loginViewModel.username, loginViewModel.password);
|
||||
HttpContext.Session.Set<User>("user", user);
|
||||
return Ok();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return BadRequest(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.DatabaseContext;
|
||||
|
||||
public class SilkRoadApiContext : DbContext
|
||||
{
|
||||
public SilkRoadApiContext(DbContextOptions options) : base(options)
|
||||
{
|
||||
this.Options = options;
|
||||
}
|
||||
|
||||
internal DbContextOptions Options { get; }
|
||||
|
||||
public DbSet<Drug> Drugs { get; set; }
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
public DbSet<Manufacturer> Manufacturers { get; set; }
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<Drug>().ToTable("Drugs");
|
||||
modelBuilder.Entity<Manufacturer>().ToTable("Manufacturers");
|
||||
modelBuilder.Entity<User>().ToTable("Users");
|
||||
}
|
||||
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Silk_Road_Api.Extensions
|
||||
{
|
||||
public static class SessionExtensions
|
||||
{
|
||||
public static void Set<T>(this ISession session, string key, T value)
|
||||
{
|
||||
session.SetString(key, JsonSerializer.Serialize(value));
|
||||
}
|
||||
|
||||
public static T? Get<T>(this ISession session, string key)
|
||||
{
|
||||
var value = session.GetString(key);
|
||||
return value == null ? default : JsonSerializer.Deserialize<T>(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Silk_Road_Api.Models;
|
||||
|
||||
public class ChartModel
|
||||
{
|
||||
public int ManufacturerId { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Silk_Road_Api.Models;
|
||||
|
||||
public class Drug
|
||||
{
|
||||
public int id { get; set; } = 0;
|
||||
public string name { get; set; }
|
||||
public decimal price { get; set; }
|
||||
public int stock { get; set; }
|
||||
public string description { get; set; }
|
||||
public int manufacturerId { get; set; }
|
||||
public string sideEffects { get; set; }
|
||||
public string interactions { get; set; }
|
||||
public string dosage { get; set; }
|
||||
public string contraindications { get; set; }
|
||||
public string warnings { get; set; }
|
||||
public string storage { get; set; }
|
||||
public string pregnancy { get; set; }
|
||||
public string breastfeeding { get; set; }
|
||||
public string overdose { get; set; }
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Silk_Road_Api;
|
||||
|
||||
public class Manufacturer
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string name { get; set; }
|
||||
public string address { get; set; }
|
||||
|
||||
public string contact { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Silk_Road_Api.Models;
|
||||
|
||||
public class SearchInput
|
||||
{
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public string? Sorting { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Silk_Road_Api;
|
||||
|
||||
public class SearchOutput<T>
|
||||
{
|
||||
public int Page { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int Total { get; set; }
|
||||
public List<T> Items { get; set; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Silk_Road_Api.Models
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public int id { get; set; }
|
||||
public string firstname { get; set; }
|
||||
public string lastname { get; set; }
|
||||
public string username { get; set; }
|
||||
public string email { get; set; }
|
||||
public string password { get; set; }
|
||||
public int companyid { get; set; }
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using AutoMapper;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Profiles;
|
||||
|
||||
public class DrugProfile : Profile
|
||||
{
|
||||
public DrugProfile()
|
||||
{
|
||||
CreateMap<Drug, DrugViewModel>();
|
||||
CreateMap<DrugViewModel, Drug>();
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using AutoMapper;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api;
|
||||
|
||||
public class ManufacturerProfile : Profile
|
||||
{
|
||||
public ManufacturerProfile()
|
||||
{
|
||||
CreateMap<Manufacturer, ManufacturerViewModel>();
|
||||
CreateMap<ManufacturerViewModel, Manufacturer>();
|
||||
CreateMap<RegistrationViewModel, Manufacturer>();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using AutoMapper;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Profiles
|
||||
{
|
||||
public class UserProfile : Profile
|
||||
{
|
||||
public UserProfile()
|
||||
{
|
||||
CreateMap<RegistrationViewModel, User>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.Services;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Silk_Road_Api;
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
|
||||
builder.Services.AddControllers();
|
||||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddDbContext<SilkRoadApiContext>(
|
||||
options => options.UseSqlServer(builder.Configuration.GetConnectionString("SilkRoadApiContext"))
|
||||
);
|
||||
builder.Services.AddScoped<IUserRepository, UserRepository>();
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IDrugRepository, DrugRepository>();
|
||||
builder.Services.AddScoped<IManufacturerRepository, ManufacturerRepository>();
|
||||
builder.Services.AddScoped<IDrugService, DrugService>();
|
||||
builder.Services.AddScoped<IManufacturerService, ManufacturerService>();
|
||||
builder.Services.AddAutoMapper(typeof(Program));
|
||||
|
||||
builder.Services.AddDistributedMemoryCache();
|
||||
|
||||
builder.Services.AddSession(options =>
|
||||
{
|
||||
options.Cookie.Name = "SessionId";
|
||||
options.Cookie.HttpOnly = true;
|
||||
options.Cookie.IsEssential = true;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
//if (app.Environment.IsDevelopment())
|
||||
//{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
//}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseAuthorization();
|
||||
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader());
|
||||
app.UseWebSockets(new WebSocketOptions
|
||||
{
|
||||
KeepAliveInterval = TimeSpan.FromSeconds(1),
|
||||
});
|
||||
app.MapControllers();
|
||||
app.UseSession();
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:25677",
|
||||
"sslPort": 44376
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "http://localhost:5176",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:7260;http://localhost:5176",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System.Linq.Expressions;
|
||||
using Bogus;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class DrugRepository : Repository, IDrugRepository
|
||||
{
|
||||
private static readonly List<string> prefixes = ["Neuro", "Bio", "Cyber", "Synth", "Nano", "Micro", "Hyper", "Psycho"];
|
||||
private static readonly List<string> roots = ["zym", "plex", "morp", "cyto", "gen", "nerv", "neuro", "bio", "pharm", "morph"];
|
||||
private static readonly List<string> suffixes = ["x", "z", "on", "ex", "ol", "ine", "ium", "ane", "ide", "os"];
|
||||
public DrugRepository(SilkRoadApiContext context) : base(context)
|
||||
{
|
||||
//GenerateDrugs(10);
|
||||
}
|
||||
|
||||
public IQueryable<Drug> GetDrugs()
|
||||
{
|
||||
return AsQueryable<Drug>();
|
||||
}
|
||||
|
||||
public async Task<Drug> GetDrug(int id)
|
||||
{
|
||||
return await GetAsync<Drug>(id);
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteCount(IQueryable<Drug> query)
|
||||
{
|
||||
Expression<Func<IQueryable<Drug>>> expression = () => query;
|
||||
return await _context.FromExpression(expression)
|
||||
.AsNoTracking()
|
||||
.Select(x => 1)
|
||||
.CountAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void GenerateDrugs(int n)
|
||||
{
|
||||
var drugs = new Faker<Drug>()
|
||||
.RuleFor(d => d.name, f => f.PickRandom(prefixes) + f.PickRandom(roots) + f.PickRandom(suffixes))
|
||||
.RuleFor(d => d.price, f => f.Random.Number(5, 100))
|
||||
.RuleFor(d => d.stock, f => f.Random.Number(1, 100))
|
||||
.RuleFor(d => d.manufacturerId, f => f.Random.Number(1, 10))
|
||||
.RuleFor(d => d.description, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.sideEffects, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.interactions, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.dosage, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.contraindications, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.warnings, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.storage, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.pregnancy, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.breastfeeding, f => f.Lorem.Letter(30))
|
||||
.RuleFor(d => d.overdose, f => f.Lorem.Letter(30))
|
||||
.Generate(n);
|
||||
_context.AddRange(drugs);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public async Task<IQueryable<ChartModel>> GetChartData()
|
||||
{
|
||||
var chartData = GetDrugs().GroupBy(d => d.manufacturerId)
|
||||
.Select(g => new ChartModel
|
||||
{
|
||||
ManufacturerId = g.Key,
|
||||
Count = g.Count()
|
||||
});
|
||||
return chartData;
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public interface IDrugRepository : IRepository
|
||||
{
|
||||
public IQueryable<Drug> GetDrugs();
|
||||
public Task<Drug> GetDrug(int id);
|
||||
public Task<int> ExecuteCount(IQueryable<Drug> query);
|
||||
public void GenerateDrugs(int n = 5);
|
||||
public Task<IQueryable<ChartModel>> GetChartData();
|
||||
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public interface IManufacturerRepository : IRepository
|
||||
{
|
||||
public IQueryable<Manufacturer> GetManufacturers();
|
||||
public Task<Manufacturer> GetManufacturer(int id);
|
||||
public Task<int> GetManufacturerByName(string name);
|
||||
public Task<int> ExecuteCount(IQueryable<Manufacturer> query);
|
||||
public void GenerateManufacturers(int n = 5);
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public interface IRepository
|
||||
{
|
||||
public Task AddAsync<T>(T entity);
|
||||
public void Update<T>(T entity) where T : class;
|
||||
public void UpdateRange<T>(List<T> entities) where T : class;
|
||||
public Task<bool> SaveChangesAsync();
|
||||
Task<T> GetAsync<T>(params object[] ids) where T : class;
|
||||
IQueryable<T> AsQueryable<T>() where T : class;
|
||||
public void Delete<T>(T entity) where T : class;
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
namespace Silk_Road_Api;
|
||||
|
||||
public interface IRepositoryBase
|
||||
{
|
||||
public Task<T> GetAsync<T>(params object[] ids) where T : class;
|
||||
public IQueryable<T> AsQueryable<T>() where T : class;
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Repositories;
|
||||
|
||||
namespace Silk_Road_Api.Repositories
|
||||
{
|
||||
public interface IUserRepository : IRepository
|
||||
{
|
||||
public User? GetUserByUsernameAndPassword(string username, string password);
|
||||
public User? GetUserByUsername(string username);
|
||||
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
using System.Linq.Expressions;
|
||||
using Bogus;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class ManufacturerRepository : Repository, IManufacturerRepository
|
||||
{
|
||||
public ManufacturerRepository(SilkRoadApiContext context) : base(context)
|
||||
{
|
||||
//GenerateManufacturers(10);
|
||||
}
|
||||
|
||||
public IQueryable<Manufacturer> GetManufacturers()
|
||||
{
|
||||
return AsQueryable<Manufacturer>();
|
||||
}
|
||||
|
||||
public async Task<Manufacturer> GetManufacturer(int id)
|
||||
{
|
||||
return await GetAsync<Manufacturer>(id);
|
||||
}
|
||||
|
||||
public async Task<int> GetManufacturerByName(string name)
|
||||
{
|
||||
return await AsQueryable<Manufacturer>().Where(x => x.name == name).Select(x => x.id).FirstOrDefaultAsync();
|
||||
}
|
||||
|
||||
public void GenerateManufacturers(int n)
|
||||
{
|
||||
var manufacturers = new Faker<Manufacturer>()
|
||||
.RuleFor(m => m.name, f => f.Company.CompanyName())
|
||||
.RuleFor(m => m.address, f => f.Address.FullAddress())
|
||||
.RuleFor(m => m.contact, f => f.Internet.Email())
|
||||
.Generate(n);
|
||||
_context.AddRange(manufacturers);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteCount(IQueryable<Manufacturer> query)
|
||||
{
|
||||
Expression<Func<IQueryable<Manufacturer>>> expression = () => query;
|
||||
return await _context.FromExpression(expression)
|
||||
.AsNoTracking()
|
||||
.Select(x => 1)
|
||||
.CountAsync()
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class MemoryManufacturerRepository : IRepository, IManufacturerRepository
|
||||
{
|
||||
private readonly List<Manufacturer> _manufacturers = new List<Manufacturer>{
|
||||
new Manufacturer { id = 1, name = "Pfizer", address = "New York", contact ="contact@Pfizer.com"},
|
||||
new Manufacturer { id = 2, name = "Moderna", address = "New Jersey", contact ="contact@Moderna.com"}
|
||||
};
|
||||
|
||||
public MemoryManufacturerRepository()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public new async Task AddAsync<T>(T entity)
|
||||
{
|
||||
(entity as Manufacturer).id = _manufacturers.Max(d => d.id) + 1;
|
||||
_manufacturers.Add(entity as Manufacturer);
|
||||
}
|
||||
|
||||
public new void Update<T>(T entity) where T : class
|
||||
{
|
||||
var manufacturerId = (entity as Manufacturer).id;
|
||||
var manufacturer = _manufacturers.FirstOrDefault(d => d.id == manufacturerId);
|
||||
if (manufacturer == null)
|
||||
throw new Exception("Manufacturer not found");
|
||||
manufacturer.name = (entity as Manufacturer).name;
|
||||
manufacturer.address = (entity as Manufacturer).address;
|
||||
manufacturer.contact = (entity as Manufacturer).contact;
|
||||
|
||||
}
|
||||
|
||||
public new void UpdateRange<T>(List<T> entities) where T : class
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Update(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public new async Task<bool> SaveChangesAsync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public new async Task<Manufacturer> GetAsync<T>(params object[] ids) where T : class
|
||||
{
|
||||
return _manufacturers.FirstOrDefault(d => d.id == (int)ids.FirstOrDefault());
|
||||
}
|
||||
|
||||
public new IQueryable<T> AsQueryable<T>() where T : class
|
||||
{
|
||||
return _manufacturers.AsQueryable() as IQueryable<T>;
|
||||
}
|
||||
|
||||
public new void Delete<T>(T entity) where T : class
|
||||
{
|
||||
if (!_manufacturers.Remove(entity as Manufacturer))
|
||||
throw new Exception("Failed to delete drug");
|
||||
}
|
||||
|
||||
public IQueryable<Manufacturer> GetManufacturers()
|
||||
{
|
||||
return _manufacturers.AsQueryable();
|
||||
}
|
||||
|
||||
public Task<Manufacturer> GetManufacturer(int id)
|
||||
{
|
||||
return Task.FromResult(_manufacturers.Where(d => d.id == id).FirstOrDefault());
|
||||
}
|
||||
|
||||
public Task<int> GetManufacturerByName(string name)
|
||||
{
|
||||
return Task.FromResult(_manufacturers.Where(d => d.name == name).Select(d => d.id).FirstOrDefault());
|
||||
}
|
||||
|
||||
public void GenerateManufacturers(int n = 5)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<T> IRepository.GetAsync<T>(params object[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteCount(IQueryable<Manufacturer> query)
|
||||
{
|
||||
return query.Count();
|
||||
}
|
||||
}
|
||||
+404
@@ -0,0 +1,404 @@
|
||||
using Bogus;
|
||||
using Bogus.Healthcare;
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class MemoryRepository : IDrugRepository
|
||||
{
|
||||
private static readonly List<Drug> _drugs = new List<Drug>{
|
||||
new Drug{
|
||||
id =1,
|
||||
name= "Paracetamol",
|
||||
price= 5,
|
||||
stock= 100,
|
||||
manufacturerId=1,
|
||||
description= "Paracetamol is a painkiller and can be used to relieve mild to moderate pain. It can also reduce high temperatures (fever).",
|
||||
sideEffects= "Paracetamol is a very safe medicine when used correctly. Side effects are rare if you take it at the right dosage.",
|
||||
interactions= "Paracetamol can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose of paracetamol is one or two 500mg tablets at a time. Do not take more than 4 doses in 24 hours.",
|
||||
contraindications= "Do not take paracetamol if you are allergic to it.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep paracetamol in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Paracetamol is safe to take in pregnancy and while breastfeeding, at recommended doses.",
|
||||
breastfeeding= "Paracetamol is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of paracetamol can cause serious harm. The maximum amount of paracetamol for adults is 1 gram (1000 mg) per dose and 4 grams (4000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 2,
|
||||
name= "Ibuprofen",
|
||||
price= 10,
|
||||
stock= 50,
|
||||
manufacturerId=1,
|
||||
description= "Ibuprofen is a painkiller available over the counter without a prescription. It\"s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.",
|
||||
sideEffects= "The most common side effects of ibuprofen are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Ibuprofen can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 200mg tablets 3 times a day. Do not take more than 6 tablets (1200mg) in 24 hours.",
|
||||
contraindications= "Do not take ibuprofen if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep ibuprofen in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Ibuprofen is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Ibuprofen is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of ibuprofen can cause serious harm. The maximum amount of ibuprofen for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 3,
|
||||
name= "Aspirin",
|
||||
price= 15,
|
||||
stock= 25,
|
||||
manufacturerId=1,
|
||||
description= "Aspirin is a painkiller and anti-inflammatory medicine. It can be used to relieve pain and inflammation caused by many conditions such as headaches, toothache, period pain, and joint pain.",
|
||||
sideEffects= "The most common side effects of aspirin are indigestion and stomach irritation.",
|
||||
interactions= "Aspirin can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 or 4 times a day. Do not take more than 12 tablets (3600mg) in 24 hours.",
|
||||
contraindications= "Do not take aspirin if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep aspirin in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Aspirin is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Aspirin is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of aspirin can cause serious harm. The maximum amount of aspirin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 4,
|
||||
name= "Codeine",
|
||||
price= 20,
|
||||
stock= 10,
|
||||
manufacturerId=1,
|
||||
description= "Codeine is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.",
|
||||
sideEffects= "The most common side effects of codeine are constipation, feeling sick, and drowsiness.",
|
||||
interactions= "Codeine can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 30mg tablets 3 or 4 times a day. Do not take more than 12 tablets (360mg) in 24 hours.",
|
||||
contraindications= "Do not take codeine if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep codeine in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Codeine is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Codeine is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of codeine can cause serious harm. The maximum amount of codeine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 5,
|
||||
name= "Morphine",
|
||||
price= 25,
|
||||
stock= 5,
|
||||
manufacturerId=1,
|
||||
description= "Morphine is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.",
|
||||
sideEffects= "The most common side effects of morphine are constipation, feeling sick, and drowsiness.",
|
||||
interactions= "Morphine can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 10mg tablets 3 or 4 times a day. Do not take more than 12 tablets (120mg) in 24 hours.",
|
||||
contraindications= "Do not take morphine if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep morphine in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Morphine is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Morphine is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of morphine can cause serious harm. The maximum amount of morphine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 6,
|
||||
name= "Tramadol",
|
||||
price= 30,
|
||||
stock= 2,
|
||||
manufacturerId=1,
|
||||
description= "Tramadol is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.",
|
||||
sideEffects= "The most common side effects of tramadol are constipation, feeling sick, and drowsiness.",
|
||||
interactions= "Tramadol can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 50mg tablets 3 or 4 times a day. Do not take more than 12 tablets (600mg) in 24 hours.",
|
||||
contraindications= "Do not take tramadol if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep tramadol in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Tramadol is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Tramadol is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of tramadol can cause serious harm. The maximum amount of tramadol for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 7,
|
||||
name= "Cocodamol",
|
||||
price= 35,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Cocodamol is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.",
|
||||
sideEffects= "The most common side effects of cocodamol are constipation, feeling sick, and drowsiness.",
|
||||
interactions= "Cocodamol can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 30mg tablets 3 or 4 times a day. Do not take more than 12 tablets (360mg) in 24 hours.",
|
||||
contraindications= "Do not take cocodamol if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep cocodamol in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Cocodamol is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Cocodamol is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of cocodamol can cause serious harm. The maximum amount of cocodamol for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 8,
|
||||
name= "Diclofenac",
|
||||
price= 40,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Diclofenac is a painkiller available over the counter without a prescription. It\"s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.",
|
||||
sideEffects= "The most common side effects of diclofenac are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Diclofenac can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 50mg tablets 3 times a day. Do not take more than 6 tablets (300mg) in 24 hours.",
|
||||
contraindications= "Do not take diclofenac if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep diclofenac in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Diclofenac is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Diclofenac is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of diclofenac can cause serious harm. The maximum amount of diclofenac for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 9,
|
||||
name= "Naproxen",
|
||||
price= 45,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Naproxen is a painkiller available over the counter without a prescription. It\"s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.",
|
||||
sideEffects= "The most common side effects of naproxen are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Naproxen can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 250mg tablets 3 times a day. Do not take more than 6 tablets (1500mg) in 24 hours.",
|
||||
contraindications= "Do not take naproxen if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep naproxen in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Naproxen is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Naproxen is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of naproxen can cause serious harm. The maximum amount of naproxen for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 10,
|
||||
name= "Mefenamic Acid",
|
||||
price= 50,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Mefenamic Acid is a painkiller available over the counter without a prescription. It\"s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.",
|
||||
sideEffects= "The most common side effects of mefenamic acid are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Mefenamic acid can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 250mg tablets 3 times a day. Do not take more than 6 tablets (1500mg) in 24 hours.",
|
||||
contraindications= "Do not take mefenamic acid if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep mefenamic acid in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Mefenamic acid is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Mefenamic acid is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of mefenamic acid can cause serious harm. The maximum amount of mefenamic acid for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 11,
|
||||
name= "Gabapentin",
|
||||
price= 55,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Gabapentin is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of gabapentin are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Gabapentin can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take gabapentin if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep gabapentin in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Gabapentin is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Gabapentin is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of gabapentin can cause serious harm. The maximum amount of gabapentin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 12,
|
||||
name= "Pregabalin",
|
||||
price= 60,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Pregabalin is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of pregabalin are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Pregabalin can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take pregabalin if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep pregabalin in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Pregabalin is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Pregabalin is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of pregabalin can cause serious harm. The maximum amount of pregabalin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 13,
|
||||
name= "Amitriptyline",
|
||||
price= 65,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Amitriptyline is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of amitriptyline are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Amitriptyline can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take amitriptyline if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep amitriptyline in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Amitriptyline is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Amitriptyline is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of amitriptyline can cause serious harm. The maximum amount of amitriptyline for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 14,
|
||||
name= "Duloxetine",
|
||||
price= 70,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Duloxetine is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of duloxetine are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Duloxetine can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take duloxetine if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep duloxetine in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Duloxetine is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Duloxetine is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of duloxetine can cause serious harm. The maximum amount of duloxetine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 15,
|
||||
name= "Venlafaxine",
|
||||
price= 75,
|
||||
stock= 1,
|
||||
manufacturerId=1,
|
||||
description= "Venlafaxine is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of venlafaxine are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Venlafaxine can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take venlafaxine if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep venlafaxine in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Venlafaxine is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Venlafaxine is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of venlafaxine can cause serious harm. The maximum amount of venlafaxine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
new Drug{
|
||||
id= 16,
|
||||
name= "Sertraline",
|
||||
price= 80,
|
||||
stock= 1,
|
||||
manufacturerId= 1,
|
||||
description= "Sertraline is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.",
|
||||
sideEffects= "The most common side effects of sertraline are headaches, dizziness, and drowsiness.",
|
||||
interactions= "Sertraline can be taken with other medicines, including prescription medicines such as antibiotics.",
|
||||
dosage= "The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.",
|
||||
contraindications= "Do not take sertraline if you have a stomach ulcer, or have had one in the past.",
|
||||
warnings= "Do not take more than the recommended dose.",
|
||||
storage= "Keep sertraline in a cool, dry place. Do not store it above 25C.",
|
||||
pregnancy= "Sertraline is not recommended in pregnancy, especially in the third trimester.",
|
||||
breastfeeding= "Sertraline is safe to take while breastfeeding, at recommended doses.",
|
||||
overdose= "An overdose of sertraline can cause serious harm. The maximum amount of sertraline for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day."
|
||||
},
|
||||
};
|
||||
private static readonly List<string> prefixes = ["Neuro", "Bio", "Cyber", "Synth", "Nano", "Micro", "Hyper", "Psycho"];
|
||||
private static readonly List<string> roots = ["zym", "plex", "morp", "cyto", "gen", "nerv", "neuro", "bio", "pharm", "morph"];
|
||||
private static readonly List<string> suffixes = ["x", "z", "on", "ex", "ol", "ine", "ium", "ane", "ide", "os"];
|
||||
public MemoryRepository()
|
||||
{
|
||||
// _drugs.Clear();
|
||||
// GenerateDrugs();
|
||||
}
|
||||
|
||||
public new async Task AddAsync<T>(T entity)
|
||||
{
|
||||
(entity as Drug).id = _drugs.Max(d => d.id) + 1;
|
||||
_drugs.Add(entity as Drug);
|
||||
}
|
||||
|
||||
public new void Update<T>(T entity) where T : class
|
||||
{
|
||||
var drug = entity as Drug;
|
||||
var existingDrug = _drugs.FirstOrDefault(d => d.id == drug.id);
|
||||
if (existingDrug != null)
|
||||
{
|
||||
existingDrug.name = drug.name;
|
||||
existingDrug.price = drug.price;
|
||||
existingDrug.stock = drug.stock;
|
||||
existingDrug.description = drug.description;
|
||||
existingDrug.manufacturerId = drug.manufacturerId;
|
||||
existingDrug.sideEffects = drug.sideEffects;
|
||||
existingDrug.interactions = drug.interactions;
|
||||
existingDrug.dosage = drug.dosage;
|
||||
existingDrug.contraindications = drug.contraindications;
|
||||
existingDrug.warnings = drug.warnings;
|
||||
existingDrug.storage = drug.storage;
|
||||
existingDrug.pregnancy = drug.pregnancy;
|
||||
existingDrug.breastfeeding = drug.breastfeeding;
|
||||
existingDrug.overdose = drug.overdose;
|
||||
return;
|
||||
}
|
||||
throw new Exception("Failed to update drug");
|
||||
}
|
||||
|
||||
public new void UpdateRange<T>(List<T> entities) where T : class
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
Update(entity);
|
||||
}
|
||||
}
|
||||
|
||||
public new async Task<bool> SaveChangesAsync()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public new async Task<Drug> GetAsync<T>(params object[] ids) where T : class
|
||||
{
|
||||
return _drugs.FirstOrDefault(d => d.id == (int)ids.FirstOrDefault());
|
||||
}
|
||||
|
||||
public new IQueryable<T> AsQueryable<T>() where T : class
|
||||
{
|
||||
return _drugs.AsQueryable() as IQueryable<T>;
|
||||
}
|
||||
|
||||
public new void Delete<T>(T entity) where T : class
|
||||
{
|
||||
if (!_drugs.Remove(entity as Drug))
|
||||
throw new Exception("Failed to delete drug");
|
||||
}
|
||||
|
||||
public IQueryable<Drug> GetDrugs()
|
||||
{
|
||||
return _drugs.AsQueryable();
|
||||
}
|
||||
|
||||
public async Task<Drug> GetDrug(int id)
|
||||
{
|
||||
return _drugs.FirstOrDefault(d => d.id == id);
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteCount(IQueryable<Drug> query)
|
||||
{
|
||||
return query.Count();
|
||||
}
|
||||
|
||||
Task<T> IRepository.GetAsync<T>(params object[] ids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void GenerateDrugs(int n = 5)
|
||||
{
|
||||
var drugs = new Faker<Drug>()
|
||||
.RuleFor(d => d.id, f => _drugs.Count > 0 ? _drugs.Max(d => d.id) + f.IndexFaker + 1 : f.IndexFaker + 1)
|
||||
.RuleFor(d => d.name, f => f.PickRandom(prefixes) + f.PickRandom(roots) + f.PickRandom(suffixes))
|
||||
.RuleFor(d => d.price, f => f.Random.Number(5, 100))
|
||||
.RuleFor(d => d.stock, f => f.Random.Number(1, 100))
|
||||
.RuleFor(d => d.manufacturerId, f => f.Random.Number(1, 10))
|
||||
.RuleFor(d => d.description, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.sideEffects, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.interactions, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.dosage, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.contraindications, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.warnings, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.storage, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.pregnancy, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.breastfeeding, f => f.Lorem.Paragraph())
|
||||
.RuleFor(d => d.overdose, f => f.Lorem.Paragraph())
|
||||
.Generate(n);
|
||||
_drugs.AddRange(drugs);
|
||||
}
|
||||
|
||||
public async Task<IQueryable<ChartModel>> GetChartData()
|
||||
{
|
||||
return _drugs.GroupBy(d => d.manufacturerId)
|
||||
.Select(g => new ChartModel
|
||||
{
|
||||
ManufacturerId = g.Key,
|
||||
Count = g.Count()
|
||||
}).AsQueryable();
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class Repository : RepositoryBase, IRepository
|
||||
{
|
||||
public Repository(SilkRoadApiContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task AddAsync<T>(T entity)
|
||||
{
|
||||
await _context.AddAsync(entity);
|
||||
}
|
||||
|
||||
public void Update<T>(T entity) where T : class
|
||||
{
|
||||
var trackedEntity = _context.Entry(entity);
|
||||
trackedEntity.State = EntityState.Modified;
|
||||
_context.Update(entity);
|
||||
}
|
||||
|
||||
public void UpdateRange<T>(List<T> entities) where T : class
|
||||
{
|
||||
_context.UpdateRange(entities);
|
||||
}
|
||||
|
||||
public async Task<bool> SaveChangesAsync()
|
||||
{
|
||||
var saved = false;
|
||||
int numberOfRetries = 0;
|
||||
do
|
||||
{
|
||||
try
|
||||
{
|
||||
await _context.SaveChangesAsync();
|
||||
saved = true;
|
||||
}
|
||||
catch (DbUpdateConcurrencyException ex)
|
||||
{
|
||||
foreach (var entry in ex.Entries)
|
||||
{
|
||||
await entry.ReloadAsync();
|
||||
}
|
||||
numberOfRetries++;
|
||||
}
|
||||
}
|
||||
while (!saved && numberOfRetries < 3);
|
||||
return saved;
|
||||
}
|
||||
|
||||
public void Delete<T>(T entity) where T : class
|
||||
{
|
||||
_context.Remove(entity);
|
||||
}
|
||||
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
|
||||
namespace Silk_Road_Api.Repositories;
|
||||
|
||||
public class RepositoryBase : IRepositoryBase
|
||||
{
|
||||
protected readonly SilkRoadApiContext _context;
|
||||
|
||||
public RepositoryBase(SilkRoadApiContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public async Task<T> GetAsync<T>(params object[] ids) where T : class
|
||||
{
|
||||
return await _context.FindAsync<T>(ids);
|
||||
}
|
||||
|
||||
public IQueryable<T> AsQueryable<T>() where T : class
|
||||
{
|
||||
return _context.Set<T>().AsQueryable();
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
using Silk_Road_Api.DatabaseContext;
|
||||
using Silk_Road_Api.Models;
|
||||
|
||||
namespace Silk_Road_Api.Repositories
|
||||
{
|
||||
public class UserRepository : Repository, IUserRepository
|
||||
{
|
||||
|
||||
public UserRepository(SilkRoadApiContext context) : base(context) { }
|
||||
|
||||
public User? GetUserByUsername(string username)
|
||||
{
|
||||
return _context.Users.Where(u => u.username.Equals(username)).FirstOrDefault();
|
||||
}
|
||||
|
||||
public User? GetUserByUsernameAndPassword(string username, string password)
|
||||
{
|
||||
return _context.Users.Where(u => u.username.Equals(username) && u.password.Equals(password)).FirstOrDefault(); }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Org.BouncyCastle.Tls;
|
||||
using Silk_Road_Api.Extensions;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.Utils;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services;
|
||||
|
||||
public class DrugService : IDrugService
|
||||
{
|
||||
private readonly IDrugRepository _drugRepository;
|
||||
private readonly IManufacturerRepository _manufacturerRepository;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public DrugService(IDrugRepository drugRepository, IManufacturerRepository manufacturerRepository, IMapper mapper)
|
||||
{
|
||||
_drugRepository = drugRepository;
|
||||
_manufacturerRepository = manufacturerRepository;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<DrugViewModel> GetDrug(ControllerBase controller, int id)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if(user == null)
|
||||
{
|
||||
throw new Exception("User not logged in");
|
||||
}
|
||||
var drug = await _drugRepository.GetDrug(id);
|
||||
if (drug == null)
|
||||
throw new Exception("Drug not found");
|
||||
if (user.id != Constants.Admin && drug.manufacturerId != user.companyid)
|
||||
throw new Exception("Drug does not belong to user");
|
||||
var manufacturer = await _manufacturerRepository.GetManufacturer(drug.manufacturerId);
|
||||
if (manufacturer == null)
|
||||
throw new Exception("Manufacturer not found");
|
||||
var viewModel = _mapper.Map<DrugViewModel>(drug);
|
||||
viewModel.manufacturer = manufacturer.name;
|
||||
return viewModel;
|
||||
|
||||
}
|
||||
|
||||
public async Task<SearchOutput<DrugViewModel>> GetDrugs(ControllerBase controller, SearchInput input)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null)
|
||||
{
|
||||
throw new Exception("User not logged in");
|
||||
}
|
||||
var output = new SearchOutput<DrugViewModel>
|
||||
{
|
||||
Page = input.Page,
|
||||
PageSize = input.PageSize
|
||||
};
|
||||
var manufacturer = _manufacturerRepository.GetManufacturers();
|
||||
if (user.id != Constants.Admin)
|
||||
{
|
||||
manufacturer = manufacturer.Where(m => m.id == user.companyid);
|
||||
}
|
||||
var drugs = _drugRepository.GetDrugs();
|
||||
var drugViewModels = drugs.Join(manufacturer, d => d.manufacturerId, m => m.id, (d, m) => new DrugViewModel
|
||||
{
|
||||
id = d.id,
|
||||
name = d.name,
|
||||
price = d.price,
|
||||
stock = d.stock,
|
||||
manufacturer = m.name,
|
||||
description = d.description,
|
||||
sideEffects = d.sideEffects,
|
||||
interactions = d.interactions,
|
||||
dosage = d.dosage,
|
||||
contraindications = d.contraindications,
|
||||
warnings = d.warnings,
|
||||
storage = d.storage,
|
||||
pregnancy = d.pregnancy,
|
||||
breastfeeding = d.breastfeeding,
|
||||
overdose = d.overdose
|
||||
});
|
||||
output.Total = await _drugRepository.ExecuteCount(drugs);
|
||||
drugViewModels = input.Sorting switch
|
||||
{
|
||||
"name" => drugViewModels.OrderBy(d => d.name),
|
||||
"price" => drugViewModels.OrderBy(d => d.price),
|
||||
"manufacturer" => drugViewModels.OrderBy(d => d.manufacturer),
|
||||
"name_desc" => drugViewModels.OrderByDescending(d => d.name),
|
||||
"price_desc" => drugViewModels.OrderByDescending(d => d.price),
|
||||
"manufacturer_desc" => drugViewModels.OrderByDescending(d => d.manufacturer),
|
||||
_ => drugViewModels.OrderBy(d => d.id),
|
||||
};
|
||||
drugViewModels = drugViewModels.Skip(input.PageSize * (input.Page - 1)).Take(input.PageSize);
|
||||
output.Items = drugViewModels.ToList();
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> AddDrug(ControllerBase controller, DrugViewModel drugViewModel)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var manufacturerId = await _manufacturerRepository.GetManufacturerByName(drugViewModel.manufacturer);
|
||||
if (manufacturerId == 0)
|
||||
throw new Exception("Manufacturer not found");
|
||||
if (user.id != Constants.Admin && user.companyid != manufacturerId)
|
||||
throw new Exception("Illegal operation");
|
||||
var drug = _mapper.Map<Drug>(drugViewModel);
|
||||
drug.manufacturerId = manufacturerId;
|
||||
await _drugRepository.AddAsync(drug);
|
||||
return await _drugRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateDrug(ControllerBase controller, DrugViewModel drugViewModel)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var manufacturerId = await _manufacturerRepository.GetManufacturerByName(drugViewModel.manufacturer);
|
||||
if (manufacturerId == 0)
|
||||
throw new Exception("Manufacturer not found");
|
||||
if (user.id != Constants.Admin && user.companyid != manufacturerId)
|
||||
throw new Exception("Illegal operation");
|
||||
var drug = _mapper.Map<Drug>(drugViewModel);
|
||||
drug.manufacturerId = manufacturerId;
|
||||
_drugRepository.Update(drug);
|
||||
return await _drugRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteDrug(ControllerBase controller, int id)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
var drug = await _drugRepository.GetDrug(id);
|
||||
if (user.id != Constants.Admin && user.companyid != drug.manufacturerId)
|
||||
throw new Exception("Illegal operation");
|
||||
_drugRepository.Delete(drug);
|
||||
return await _drugRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateDrugs()
|
||||
{
|
||||
_drugRepository.GenerateDrugs();
|
||||
}
|
||||
|
||||
public async Task<List<ChartViewModel>> GetChartData()
|
||||
{
|
||||
return (await _drugRepository.GetChartData()).Join(_manufacturerRepository.GetManufacturers(), d => d.ManufacturerId, m => m.id, (d, m) => new ChartViewModel
|
||||
{
|
||||
Manufacturer = m.name,
|
||||
Count = d.Count
|
||||
}).ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services;
|
||||
|
||||
public interface IDrugService
|
||||
{
|
||||
public Task<DrugViewModel> GetDrug(ControllerBase controller, int id);
|
||||
public Task<SearchOutput<DrugViewModel>> GetDrugs(ControllerBase controller, SearchInput input);
|
||||
public Task<bool> AddDrug(ControllerBase controller, DrugViewModel drugViewModel);
|
||||
public Task<bool> UpdateDrug(ControllerBase controller, DrugViewModel drugViewModel);
|
||||
public Task<bool> DeleteDrug(ControllerBase controller, int id);
|
||||
public void GenerateDrugs();
|
||||
|
||||
public Task<List<ChartViewModel>> GetChartData();
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services;
|
||||
|
||||
public interface IManufacturerService
|
||||
{
|
||||
public Task<ManufacturerViewModel> GetManufacturer(ControllerBase controller, int id);
|
||||
public Task<SearchOutput<ManufacturerViewModel>> GetManufacturers(ControllerBase controller, SearchInput input);
|
||||
public Task<bool> AddManufacturer(ControllerBase controller, ManufacturerViewModel manufacturerViewModel);
|
||||
public Task<bool> UpdateManufacturer(ControllerBase controller, ManufacturerViewModel manufacturerViewModel);
|
||||
public Task<bool> DeleteManufacturer(ControllerBase controller, int id);
|
||||
public void GenerateManufacturer();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
public Task RegisterUser(RegistrationViewModel user);
|
||||
public User LoginUser(string username, string password);
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
using AutoMapper;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Silk_Road_Api.Extensions;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.Utils;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services;
|
||||
|
||||
public class ManufacturerService : IManufacturerService
|
||||
{
|
||||
private readonly IManufacturerRepository _manufacturerRepository;
|
||||
private readonly IDrugRepository _drugRepository;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public ManufacturerService(IDrugRepository drugRepository, IManufacturerRepository manufacturerRepository, IMapper mapper)
|
||||
{
|
||||
_drugRepository = drugRepository;
|
||||
_manufacturerRepository = manufacturerRepository;
|
||||
_mapper = mapper;
|
||||
}
|
||||
public async Task<bool> AddManufacturer(ControllerBase controller, ManufacturerViewModel manufacturerViewModel)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null || user.id != Constants.Admin)
|
||||
{
|
||||
throw new Exception("Unauthorized");
|
||||
}
|
||||
try
|
||||
{
|
||||
var manufacturer = _mapper.Map<Manufacturer>(manufacturerViewModel);
|
||||
await _manufacturerRepository.AddAsync(manufacturer);
|
||||
return await _manufacturerRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteManufacturer(ControllerBase controller, int id)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null || user.id != Constants.Admin)
|
||||
{
|
||||
throw new Exception("Unauthorized");
|
||||
}
|
||||
try
|
||||
{
|
||||
var manufacturer = await _manufacturerRepository.GetManufacturer(id);
|
||||
_manufacturerRepository.Delete(manufacturer);
|
||||
return await _manufacturerRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateManufacturer()
|
||||
{
|
||||
_manufacturerRepository.GenerateManufacturers();
|
||||
}
|
||||
|
||||
public async Task<ManufacturerViewModel> GetManufacturer(ControllerBase controller, int id)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null || user.id != Constants.Admin)
|
||||
{
|
||||
throw new Exception("Unauthorized");
|
||||
}
|
||||
var manufacturer = await _manufacturerRepository.GetManufacturer(id);
|
||||
if (manufacturer == null)
|
||||
throw new Exception("Manufacturer not found");
|
||||
var viewModel = _mapper.Map<ManufacturerViewModel>(manufacturer);
|
||||
viewModel.totalCount = _drugRepository.GetDrugs().Where(d => d.manufacturerId == id)?.Count() ?? 0;
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
public async Task<SearchOutput<ManufacturerViewModel>> GetManufacturers(ControllerBase controller, SearchInput input)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null || user.id != Constants.Admin)
|
||||
{
|
||||
throw new Exception("Unauthorized");
|
||||
}
|
||||
var output = new SearchOutput<ManufacturerViewModel>
|
||||
{
|
||||
Page = input.Page,
|
||||
PageSize = input.PageSize
|
||||
};
|
||||
var drugs = _drugRepository.GetDrugs();
|
||||
var viewModel = _manufacturerRepository.GetManufacturers().GroupJoin(drugs, m => m.id, d => d.manufacturerId, (m, d) => new
|
||||
{
|
||||
manufacturer = m,
|
||||
drugs = d.Count()
|
||||
|
||||
}).Select(
|
||||
x => new ManufacturerViewModel
|
||||
{
|
||||
id = x.manufacturer.id,
|
||||
name = x.manufacturer.name,
|
||||
address = x.manufacturer.address,
|
||||
contact = x.manufacturer.contact,
|
||||
totalCount = x.drugs
|
||||
}
|
||||
);
|
||||
output.Total = viewModel.Count();
|
||||
viewModel = viewModel.Skip(input.PageSize * (input.Page - 1)).Take(input.PageSize);
|
||||
output.Items = viewModel.ToList();
|
||||
return output;
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateManufacturer(ControllerBase controller, ManufacturerViewModel manufacturerViewModel)
|
||||
{
|
||||
var user = controller.HttpContext.Session.Get<User>("user");
|
||||
if (user == null || user.id != Constants.Admin)
|
||||
{
|
||||
throw new Exception("Unauthorized");
|
||||
}
|
||||
try
|
||||
{
|
||||
var manufacturer = _mapper.Map<Manufacturer>(manufacturerViewModel);
|
||||
_manufacturerRepository.Update(manufacturer);
|
||||
return await _manufacturerRepository.SaveChangesAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using AutoMapper;
|
||||
using Silk_Road_Api.Models;
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.Repositories;
|
||||
using Silk_Road_Api.ViewModels;
|
||||
|
||||
namespace Silk_Road_Api.Services
|
||||
{
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly IManufacturerRepository _manufacturerRepository;
|
||||
private readonly IMapper _mapper;
|
||||
public UserService(IUserRepository userRepository, IManufacturerRepository manufacturerRepository, IMapper mapper) {
|
||||
_userRepository = userRepository;
|
||||
_manufacturerRepository = manufacturerRepository;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public User? LoginUser(string username, string password)
|
||||
{
|
||||
var user = _userRepository.GetUserByUsernameAndPassword(username, password);
|
||||
if (user == null)
|
||||
{
|
||||
throw new Exception("Username and password don't match");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
public async Task RegisterUser(RegistrationViewModel user)
|
||||
{
|
||||
var dbUser = _userRepository.GetUserByUsername(user.username);
|
||||
if (dbUser != null)
|
||||
{
|
||||
throw new Exception("Username already taken");
|
||||
}
|
||||
var company = await _manufacturerRepository.GetManufacturerByName(user.name);
|
||||
if (company != 0)
|
||||
{
|
||||
throw new Exception("Company already has an account associated, please contact support");
|
||||
}
|
||||
var userToBeAdded = _mapper.Map<User>(user);
|
||||
var companyToBeAdded = _mapper.Map<Manufacturer>(user);
|
||||
await _manufacturerRepository.AddAsync(companyToBeAdded);
|
||||
await _manufacturerRepository.SaveChangesAsync();
|
||||
userToBeAdded.companyid = await _manufacturerRepository.GetManufacturerByName(user.name);
|
||||
await _userRepository.AddAsync(userToBeAdded);
|
||||
await _userRepository.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Silk_Road_Api</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="Bogus.Healthcare" Version="35.5.0" />
|
||||
<PackageReference Include="Faker.Net" Version="2.0.163" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" Version="8.0.4" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.3" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="8.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,6 @@
|
||||
@Silk_Road_Api_HostAddress = http://localhost:5176
|
||||
|
||||
GET {{Silk_Road_Api_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Silk_Road_Api.Utils
|
||||
{
|
||||
public static class Constants
|
||||
{
|
||||
public const int Admin = 1234;
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
namespace Silk_Road_Api.ViewModels;
|
||||
|
||||
public class ChartViewModel
|
||||
{
|
||||
public string Manufacturer { get; set; }
|
||||
public int Count { get; set; }
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Silk_Road_Api.ViewModels;
|
||||
|
||||
public class DrugViewModel
|
||||
{
|
||||
public int id { get; set; } = 0;
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string name { get; set; }
|
||||
[Range(0, 999.99)]
|
||||
public decimal price { get; set; }
|
||||
[Range(0, 20000)]
|
||||
public int stock { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string description { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string manufacturer { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string sideEffects { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string interactions { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string dosage { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string contraindications { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string warnings { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string storage { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string pregnancy { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string breastfeeding { get; set; }
|
||||
[RegularExpression(@"^[a-zA-Z''-'\s]{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string overdose { get; set; }
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
namespace Silk_Road_Api.ViewModels
|
||||
{
|
||||
public class LoginViewModel
|
||||
{
|
||||
public required string username { get; set; }
|
||||
public required string password { get; set; }
|
||||
}
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace Silk_Road_Api.ViewModels;
|
||||
|
||||
public class ManufacturerViewModel
|
||||
{
|
||||
public int id { get; set; } = 0;
|
||||
[RegularExpression(@".{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string name { get; set; }
|
||||
[RegularExpression(@".{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string address { get; set; }
|
||||
[RegularExpression(@".{3,40}$", ErrorMessage = "Characters are not allowed.")]
|
||||
public string contact { get; set; }
|
||||
public int totalCount { get; set; } = 0;
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
namespace Silk_Road_Api.ViewModels
|
||||
{
|
||||
public class RegistrationViewModel
|
||||
{
|
||||
public required string name { get; set; }
|
||||
public required string address { get; set; }
|
||||
|
||||
public required string contact { get; set; }
|
||||
public required string firstname { get; set; }
|
||||
public required string lastname { get; set; }
|
||||
public required string username { get; set; }
|
||||
public required string email { get; set; }
|
||||
public required string password { get; set; }
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"ConnectionStrings": {
|
||||
"SilkRoadApiContext": "Server=tcp:silksongdb.database.windows.net,1433;Initial Catalog=SilkRoadDb;Persist Security Info=False;User ID=silksongdb;Password=Ni7BN6stuSTChkU;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs', 'node_modules', '.prettierrc.json'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh', '@typescript-eslint', 'import'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
'prefer-const': 'error',
|
||||
'import/prefer-default-export': 'error',
|
||||
'@typescript-eslint/no-unused-vars': 'warn',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
coverage
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"jsxSingleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"useTabs": false
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Silk Road</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"name": "silk-road-web",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"lint-staged": "lint-staged",
|
||||
"preview": "vite preview",
|
||||
"prepare": "husky",
|
||||
"test": "vitest",
|
||||
"coverage": "vitest --coverage ",
|
||||
"ui": "vitest --ui"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.4",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/x-charts": "^6.19.8",
|
||||
"axios": "^1.6.8",
|
||||
"husky": "^9.0.11",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-use-websockets": "^2.3.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mui/icons-material": "^5.15.13",
|
||||
"@mui/material": "^5.15.13",
|
||||
"@reduxjs/toolkit": "^2.2.2",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.1",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
"@types/styled-components": "^5.1.34",
|
||||
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
||||
"@typescript-eslint/parser": "^7.1.1",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"@vitest/coverage-v8": "^1.4.0",
|
||||
"@vitest/ui": "^1.4.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"jest": "^29.7.0",
|
||||
"jsdom": "^24.0.0",
|
||||
"lint-staged": "^15.2.2",
|
||||
"react-redux": "^9.1.0",
|
||||
"react-router-dom": "^6.22.3",
|
||||
"styled-components": "^6.1.8",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.1.6",
|
||||
"vitest": "^1.4.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
"eslint --report-unused-disable-directives --max-warnings 0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,42 @@
|
||||
#root {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: filter 300ms;
|
||||
}
|
||||
.logo:hover {
|
||||
filter: drop-shadow(0 0 2em #646cffaa);
|
||||
}
|
||||
.logo.react:hover {
|
||||
filter: drop-shadow(0 0 2em #61dafbaa);
|
||||
}
|
||||
|
||||
@keyframes logo-spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
a:nth-of-type(2) .logo {
|
||||
animation: logo-spin infinite 20s linear;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
}
|
||||
|
||||
.read-the-docs {
|
||||
color: #888;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import routes from './constants/routes';
|
||||
|
||||
function App() {
|
||||
return <RouterProvider router={routes} />;
|
||||
}
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
+131
@@ -0,0 +1,131 @@
|
||||
import Card from '@mui/material/Card';
|
||||
import CardActions from '@mui/material/CardActions';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getDrugById } from '../../redux/selectors';
|
||||
import { RootState } from '../../redux/store';
|
||||
import axios from 'axios';
|
||||
import Drug from '../../interfaces/Drug';
|
||||
|
||||
export default function DrugDetails() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const id = parseInt(useParams().id ?? '0');
|
||||
|
||||
const [drug, setDrug] = useState<Drug | null>(null);
|
||||
const potentialDrug = useSelector((state: RootState) =>
|
||||
getDrugById(state, id)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDrugById = async (id: number) => {
|
||||
try {
|
||||
const drug = await axios.get<Drug>(
|
||||
`https://silkroadapi.azurewebsites.net/Drug/get-drug/${id}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setDrug(drug.data);
|
||||
console.log(drug);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
navigate('/error');
|
||||
}
|
||||
};
|
||||
if (potentialDrug) setDrug(potentialDrug);
|
||||
else fetchDrugById(id);
|
||||
}, [id, navigate, potentialDrug]);
|
||||
|
||||
const handleBackButton = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
!!drug && (
|
||||
<div style={{ marginLeft: '15%', marginRight: '15%' }}>
|
||||
<Card sx={{ maxWidth: 1600 }}>
|
||||
<CardContent>
|
||||
<Typography variant='h3' gutterBottom>
|
||||
{drug?.name}
|
||||
</Typography>
|
||||
<Typography variant='h4' component='div'>
|
||||
${drug?.price}
|
||||
</Typography>
|
||||
<Typography sx={{ mb: 1.5 }} color='text.secondary'>
|
||||
{drug?.description}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Manufacturer:
|
||||
<br />
|
||||
{drug?.manufacturer}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Side Effects:
|
||||
<br />
|
||||
{drug?.sideEffects}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Interactions:
|
||||
<br />
|
||||
{drug?.interactions}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Dosage:
|
||||
<br />
|
||||
{drug?.dosage}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Contraindications:
|
||||
<br />
|
||||
{drug?.contraindications}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Warnings:
|
||||
<br />
|
||||
{drug?.warnings}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Storage:
|
||||
<br />
|
||||
{drug?.storage}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Pregnacy:
|
||||
<br />
|
||||
{drug?.pregnancy}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Breastfeeding:
|
||||
<br />
|
||||
{drug?.breastfeeding}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Overdose:
|
||||
<br />
|
||||
{drug?.overdose}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button size='small' onClick={handleBackButton}>
|
||||
Back
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import Card from '@mui/material/Card';
|
||||
import CardActions from '@mui/material/CardActions';
|
||||
import CardContent from '@mui/material/CardContent';
|
||||
import Button from '@mui/material/Button';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import axios from 'axios';
|
||||
import Manufacturer from '../../interfaces/Manufacturer';
|
||||
|
||||
export default function DrugDetails() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const id = parseInt(useParams().id ?? '0');
|
||||
|
||||
const [drug, setDrug] = useState<Manufacturer | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDrugById = async (id: number) => {
|
||||
try {
|
||||
const drug = await axios.get<Manufacturer>(
|
||||
`https://silkroadapi.azurewebsites.net/Manufacturer/get-manufacturer/${id}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setDrug(drug.data);
|
||||
console.log(drug);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
navigate('/error');
|
||||
}
|
||||
};
|
||||
fetchDrugById(id);
|
||||
}, [id, navigate]);
|
||||
|
||||
const handleBackButton = () => {
|
||||
navigate(-1);
|
||||
};
|
||||
|
||||
return (
|
||||
!!drug && (
|
||||
<div style={{ marginLeft: '15%', marginRight: '15%' }}>
|
||||
<Card sx={{ maxWidth: 1600 }}>
|
||||
<CardContent>
|
||||
<Typography variant='h3' gutterBottom>
|
||||
{drug?.name}
|
||||
</Typography>
|
||||
<Typography variant='h4' component='div'>
|
||||
{drug?.address}
|
||||
</Typography>
|
||||
<Typography sx={{ mb: 1.5 }} color='text.secondary'>
|
||||
{drug?.contact}
|
||||
</Typography>
|
||||
<Divider />
|
||||
<Typography variant='body1'>
|
||||
Total Count:
|
||||
<br />
|
||||
{drug?.totalCount}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
<CardActions>
|
||||
<Button size='small' onClick={handleBackButton}>
|
||||
Back
|
||||
</Button>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
import * as React from 'react';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableFooter from '@mui/material/TableFooter';
|
||||
import TablePagination from '@mui/material/TablePagination';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import FirstPageIcon from '@mui/icons-material/FirstPage';
|
||||
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft';
|
||||
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
|
||||
import LastPageIcon from '@mui/icons-material/LastPage';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
TableHead
|
||||
} from '@mui/material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DeleteDialog from '../Shared/DeleteDialog';
|
||||
import Drug from '../../interfaces/Drug';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import axios from 'axios';
|
||||
import { loadDrugs } from '../../redux/slices/drugSlice';
|
||||
import { getWebSocket } from '../../redux/selectors';
|
||||
|
||||
interface TablePaginationActionsProps {
|
||||
count: number;
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
onPageChange: (
|
||||
event: React.MouseEvent<HTMLButtonElement>,
|
||||
newPage: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
function TablePaginationActions(props: TablePaginationActionsProps) {
|
||||
const theme = useTheme();
|
||||
const { count, page, rowsPerPage, onPageChange } = props;
|
||||
|
||||
const handleFirstPageButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, 0);
|
||||
};
|
||||
|
||||
const handleBackButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, page - 1);
|
||||
};
|
||||
|
||||
const handleNextButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, page + 1);
|
||||
};
|
||||
|
||||
const handleLastPageButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
|
||||
<IconButton
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label='first page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label='previous page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowRight />
|
||||
) : (
|
||||
<KeyboardArrowLeft />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label='next page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowLeft />
|
||||
) : (
|
||||
<KeyboardArrowRight />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label='last page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const DrugTable = () => {
|
||||
const dispatch = useDispatch();
|
||||
const [page, setPage] = useState(
|
||||
sessionStorage.getItem('page')
|
||||
? parseInt(sessionStorage.getItem('page') as string)
|
||||
: 0
|
||||
);
|
||||
const ws = useSelector(getWebSocket);
|
||||
|
||||
const [sort, setSort] = useState('');
|
||||
const [open, setOpen] = useState(-1);
|
||||
const [drugs, setDrugs] = useState<Drug[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleViewClick = (id: number) => {
|
||||
navigate(`/details/${id}`);
|
||||
};
|
||||
const handleUpdateClick = (id: number) => {
|
||||
navigate(`/update/${id}`);
|
||||
};
|
||||
|
||||
const handleDeleteClick = (id: number) => {
|
||||
setOpen(id);
|
||||
};
|
||||
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(
|
||||
sessionStorage.getItem('rowsPerPage')
|
||||
? parseInt(sessionStorage.getItem('rowsPerPage') as string, 10)
|
||||
: 5
|
||||
);
|
||||
|
||||
const emptyRows =
|
||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - total) : 0;
|
||||
|
||||
const handleChangePage = (
|
||||
_event: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
sessionStorage.setItem('page', newPage.toString());
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
sessionStorage.setItem('rowsPerPage', event.target.value);
|
||||
};
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://silkroadapi.azurewebsites.net/Drug/get-drugs?Page=${
|
||||
page + 1
|
||||
}&PageSize=${rowsPerPage}&sorting=${sort}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
dispatch(loadDrugs(response.data.items));
|
||||
setDrugs(response.data.items);
|
||||
setTotal(response.data.total);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError(true);
|
||||
}
|
||||
}, [dispatch, page, rowsPerPage, sort]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [page, sort, dispatch, rowsPerPage, fetchData]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log(ws);
|
||||
ws?.addEventListener('message', (event) => {
|
||||
console.log(event.data);
|
||||
fetchData();
|
||||
});
|
||||
}, [fetchData, ws]);
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 500 }} aria-label='custom pagination table'>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell
|
||||
data-asc={'false'}
|
||||
onClick={(e) => {
|
||||
setSort(
|
||||
e.currentTarget.dataset.asc === 'false' ? 'name' : 'name_desc'
|
||||
);
|
||||
e.currentTarget.dataset.asc =
|
||||
e.currentTarget.dataset.asc === 'false' ? 'true' : 'false';
|
||||
}}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
>
|
||||
Name
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align='right'
|
||||
onClick={(e) => {
|
||||
setSort(
|
||||
e.currentTarget.dataset.asc === 'false'
|
||||
? 'manufacturer'
|
||||
: 'manufacturer_desc'
|
||||
);
|
||||
e.currentTarget.dataset.asc =
|
||||
e.currentTarget.dataset.asc === 'false' ? 'true' : 'false';
|
||||
}}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
>
|
||||
Manufacturer
|
||||
</TableCell>
|
||||
<TableCell
|
||||
align='right'
|
||||
onClick={(e) => {
|
||||
setSort(
|
||||
e.currentTarget.dataset.asc === 'false'
|
||||
? 'price'
|
||||
: 'price_desc'
|
||||
);
|
||||
e.currentTarget.dataset.asc =
|
||||
e.currentTarget.dataset.asc === 'false' ? 'true' : 'false';
|
||||
}}
|
||||
sx={{ cursor: 'pointer' }}
|
||||
>
|
||||
Price (USD)
|
||||
</TableCell>
|
||||
<TableCell align='right'>View</TableCell>
|
||||
<TableCell align='right'>Update</TableCell>
|
||||
<TableCell align='right'>Delete</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{drugs.map((drug) => (
|
||||
<TableRow key={drug.id}>
|
||||
<TableCell component='th' scope='row'>
|
||||
{drug.name}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
{drug.manufacturer}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
${drug.price}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleViewClick(drug.id);
|
||||
}}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleUpdateClick(drug.id);
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleDeleteClick(drug.id);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow style={{ height: 53 * emptyRows }}>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
colSpan={3}
|
||||
count={total}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
slotProps={{
|
||||
select: {
|
||||
inputProps: {
|
||||
'aria-label': 'rows per page'
|
||||
},
|
||||
native: true
|
||||
}
|
||||
}}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
<DeleteDialog
|
||||
id={open}
|
||||
handleClose={() => {
|
||||
setOpen(-1);
|
||||
}}
|
||||
handleAgree={async () => {
|
||||
await axios.delete(
|
||||
`https://silkroadapi.azurewebsites.net/Drug/delete-drug/${open}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
await fetchData();
|
||||
setOpen(-1);
|
||||
}}
|
||||
/>
|
||||
<Dialog
|
||||
open={error}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle id='alert-dialog-title'>{'Error'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='alert-dialog-description'>
|
||||
Error while connecting
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setError(false);
|
||||
}}
|
||||
color='primary'
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default DrugTable;
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
import * as React from 'react';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import Box from '@mui/material/Box';
|
||||
import Table from '@mui/material/Table';
|
||||
import TableBody from '@mui/material/TableBody';
|
||||
import TableCell from '@mui/material/TableCell';
|
||||
import TableContainer from '@mui/material/TableContainer';
|
||||
import TableFooter from '@mui/material/TableFooter';
|
||||
import TablePagination from '@mui/material/TablePagination';
|
||||
import TableRow from '@mui/material/TableRow';
|
||||
import Paper from '@mui/material/Paper';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import FirstPageIcon from '@mui/icons-material/FirstPage';
|
||||
import KeyboardArrowLeft from '@mui/icons-material/KeyboardArrowLeft';
|
||||
import KeyboardArrowRight from '@mui/icons-material/KeyboardArrowRight';
|
||||
import LastPageIcon from '@mui/icons-material/LastPage';
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle,
|
||||
TableHead
|
||||
} from '@mui/material';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import DeleteDialog from '../Shared/DeleteDialog';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import axios from 'axios';
|
||||
import Manufacturer from '../../interfaces/Manufacturer';
|
||||
|
||||
interface TablePaginationActionsProps {
|
||||
count: number;
|
||||
page: number;
|
||||
rowsPerPage: number;
|
||||
onPageChange: (
|
||||
event: React.MouseEvent<HTMLButtonElement>,
|
||||
newPage: number
|
||||
) => void;
|
||||
}
|
||||
|
||||
function TablePaginationActions(props: TablePaginationActionsProps) {
|
||||
const theme = useTheme();
|
||||
const { count, page, rowsPerPage, onPageChange } = props;
|
||||
|
||||
const handleFirstPageButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, 0);
|
||||
};
|
||||
|
||||
const handleBackButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, page - 1);
|
||||
};
|
||||
|
||||
const handleNextButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, page + 1);
|
||||
};
|
||||
|
||||
const handleLastPageButtonClick = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
onPageChange(event, Math.max(0, Math.ceil(count / rowsPerPage) - 1));
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ flexShrink: 0, ml: 2.5 }}>
|
||||
<IconButton
|
||||
onClick={handleFirstPageButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label='first page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? <LastPageIcon /> : <FirstPageIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleBackButtonClick}
|
||||
disabled={page === 0}
|
||||
aria-label='previous page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowRight />
|
||||
) : (
|
||||
<KeyboardArrowLeft />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleNextButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label='next page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? (
|
||||
<KeyboardArrowLeft />
|
||||
) : (
|
||||
<KeyboardArrowRight />
|
||||
)}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
onClick={handleLastPageButtonClick}
|
||||
disabled={page >= Math.ceil(count / rowsPerPage) - 1}
|
||||
aria-label='last page'
|
||||
>
|
||||
{theme.direction === 'rtl' ? <FirstPageIcon /> : <LastPageIcon />}
|
||||
</IconButton>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const ManufacturerTable = () => {
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [page, setPage] = useState(
|
||||
sessionStorage.getItem('page')
|
||||
? parseInt(sessionStorage.getItem('page') as string)
|
||||
: 0
|
||||
);
|
||||
|
||||
const [sort] = useState('');
|
||||
const [open, setOpen] = useState(-1);
|
||||
const [manufacturers, setDrugs] = useState<Manufacturer[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
const [rowsPerPage, setRowsPerPage] = React.useState(
|
||||
sessionStorage.getItem('rowsPerPage')
|
||||
? parseInt(sessionStorage.getItem('rowsPerPage') as string, 10)
|
||||
: 5
|
||||
);
|
||||
|
||||
const emptyRows =
|
||||
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - total) : 0;
|
||||
|
||||
const handleChangePage = (
|
||||
_event: React.MouseEvent<HTMLButtonElement> | null,
|
||||
newPage: number
|
||||
) => {
|
||||
setPage(newPage);
|
||||
sessionStorage.setItem('page', newPage.toString());
|
||||
};
|
||||
|
||||
const handleChangeRowsPerPage = (
|
||||
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
||||
) => {
|
||||
setRowsPerPage(parseInt(event.target.value, 10));
|
||||
setPage(0);
|
||||
sessionStorage.setItem('rowsPerPage', event.target.value);
|
||||
};
|
||||
|
||||
const fetchData = useCallback(async () => {
|
||||
try {
|
||||
const response = await axios.get(
|
||||
`https://silkroadapi.azurewebsites.net/Manufacturer/get-manufacturer?Page=${
|
||||
page + 1
|
||||
}&PageSize=${rowsPerPage}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setDrugs(response.data.items);
|
||||
setTotal(response.data.total);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
setError(true);
|
||||
}
|
||||
}, [page, rowsPerPage]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchData();
|
||||
}, [page, sort, dispatch, rowsPerPage, fetchData]);
|
||||
|
||||
const handleViewClick = (id: number) => {
|
||||
navigate(`/manufacturer/details/${id}`);
|
||||
};
|
||||
const handleUpdateClick = (id: number) => {
|
||||
navigate(`/manufacturer/update/${id}`);
|
||||
};
|
||||
|
||||
const handleDeleteClick = (id: number) => {
|
||||
setOpen(id);
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer component={Paper}>
|
||||
<Table sx={{ minWidth: 500 }} aria-label='custom pagination table'>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell align='right'>Andress</TableCell>
|
||||
<TableCell align='right'>Contact</TableCell>
|
||||
<TableCell align='right'>Total Drugs</TableCell>
|
||||
<TableCell align='right'>View</TableCell>
|
||||
<TableCell align='right'>Update</TableCell>
|
||||
<TableCell align='right'>Delete</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{manufacturers.map((manufacturer) => (
|
||||
<TableRow key={manufacturer.id}>
|
||||
<TableCell component='th' scope='row'>
|
||||
{manufacturer.name}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
{manufacturer.address}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
{manufacturer.contact}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
{manufacturer.totalCount}
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleViewClick(manufacturer.id ?? 0);
|
||||
}}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleUpdateClick(manufacturer.id ?? 0);
|
||||
}}
|
||||
>
|
||||
Update
|
||||
</Button>
|
||||
</TableCell>
|
||||
<TableCell style={{ width: 160 }} align='right'>
|
||||
<Button
|
||||
variant='outlined'
|
||||
onClick={() => {
|
||||
handleDeleteClick(manufacturer.id ?? 0);
|
||||
}}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow style={{ height: 53 * emptyRows }}>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
colSpan={3}
|
||||
count={total}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
slotProps={{
|
||||
select: {
|
||||
inputProps: {
|
||||
'aria-label': 'rows per page'
|
||||
},
|
||||
native: true
|
||||
}
|
||||
}}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
ActionsComponent={TablePaginationActions}
|
||||
/>
|
||||
</TableRow>
|
||||
</TableFooter>
|
||||
</Table>
|
||||
<DeleteDialog
|
||||
id={open}
|
||||
handleClose={() => {
|
||||
setOpen(-1);
|
||||
}}
|
||||
handleAgree={async () => {
|
||||
await axios.delete(
|
||||
`https://silkroadapi.azurewebsites.net/Manufacturer/delete-manufacturer/${open}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
await fetchData();
|
||||
setOpen(-1);
|
||||
}}
|
||||
/>
|
||||
<Dialog
|
||||
open={error}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle id='alert-dialog-title'>{'Error'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='alert-dialog-description'>
|
||||
Error while connecting
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setError(false);
|
||||
}}
|
||||
color='primary'
|
||||
>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default ManufacturerTable;
|
||||
@@ -0,0 +1,41 @@
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogActions,
|
||||
Button
|
||||
} from '@mui/material';
|
||||
|
||||
const DeleteDialog = (props: {
|
||||
id: number;
|
||||
handleClose: () => void;
|
||||
handleAgree: () => void;
|
||||
}) => {
|
||||
const { id, handleClose, handleAgree } = props;
|
||||
return (
|
||||
<Dialog
|
||||
open={id >= 0}
|
||||
onClose={handleClose}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'
|
||||
>
|
||||
<DialogTitle id='alert-dialog-title'>{'Delete?'}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='alert-dialog-description'>
|
||||
Are you sure you want to delete this item?
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={handleClose} color='primary'>
|
||||
No
|
||||
</Button>
|
||||
<Button onClick={handleAgree} color='primary' autoFocus>
|
||||
Yes
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default DeleteDialog;
|
||||
@@ -0,0 +1,216 @@
|
||||
import { Button, FormLabel } from '@mui/material';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import { ChangeEvent, FormEvent, useState } from 'react';
|
||||
import Drug from '../../interfaces/Drug';
|
||||
import getEmptyDrug from '../../utils/DrugUtils';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
|
||||
export default function DrugFrom(props: { updateDrug?: Drug }) {
|
||||
const [drug, setDrug] = useState(props.updateDrug ?? getEmptyDrug());
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setDrug({
|
||||
...drug,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e: FormEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
if (drug.id === undefined || drug.id === -1) {
|
||||
addDrug(drug);
|
||||
} else {
|
||||
updateDrug(drug);
|
||||
}
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
const addDrug = async (drug: Drug) => {
|
||||
try {
|
||||
axios.post('https://silkroadapi.azurewebsites.net/Drug/add-drug', drug, {
|
||||
withCredentials: true
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDrug = async (drug: Drug) => {
|
||||
try {
|
||||
axios.put(
|
||||
'https://silkroadapi.azurewebsites.net/Drug/update-drug',
|
||||
drug,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<FormControl
|
||||
sx={{
|
||||
width: '70%',
|
||||
marginLeft: '15%',
|
||||
marginRight: '15%',
|
||||
marginBottom: '30px',
|
||||
marginTop: '30px'
|
||||
}}
|
||||
>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='name'
|
||||
title='Name'
|
||||
value={drug.name}
|
||||
/>
|
||||
<FormLabel>Stock</FormLabel>
|
||||
<TextField
|
||||
type='number'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='stock'
|
||||
title='Stock'
|
||||
value={drug.stock}
|
||||
/>
|
||||
<FormLabel>Price</FormLabel>
|
||||
<TextField
|
||||
type='number'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='price'
|
||||
title='Price'
|
||||
value={drug.price}
|
||||
/>
|
||||
<FormLabel>Manufacturer</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='manufacturer'
|
||||
title='Manufacturer'
|
||||
value={drug.manufacturer}
|
||||
/>
|
||||
<FormLabel>Description</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='description'
|
||||
title='Description'
|
||||
value={drug.description}
|
||||
/>
|
||||
<FormLabel>Side Effects</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='sideEffects'
|
||||
title='Side Effects'
|
||||
value={drug.sideEffects}
|
||||
/>
|
||||
<FormLabel>Interactions</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='interactions'
|
||||
title='Interactions'
|
||||
value={drug.interactions}
|
||||
/>
|
||||
<FormLabel>Dosage</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='dosage'
|
||||
title='Dosage'
|
||||
value={drug.dosage}
|
||||
/>
|
||||
<FormLabel>Contraindications</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='contraindications'
|
||||
title='Contraindications'
|
||||
value={drug.contraindications}
|
||||
/>
|
||||
<FormLabel>Warnings</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='warnings'
|
||||
title='Warnings'
|
||||
value={drug.warnings}
|
||||
/>
|
||||
<FormLabel>Storage</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='storage'
|
||||
title='Storage'
|
||||
value={drug.storage}
|
||||
/>
|
||||
<FormLabel>Pregnancy</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='pregnancy'
|
||||
title='Pregnancy'
|
||||
value={drug.pregnancy}
|
||||
/>
|
||||
<FormLabel>Breastfeeding</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='breastfeeding'
|
||||
title='Breastfeeding'
|
||||
value={drug.breastfeeding}
|
||||
/>
|
||||
<FormLabel>Overdose</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
multiline
|
||||
rows={4}
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='overdose'
|
||||
title='Overdose'
|
||||
value={drug.overdose}
|
||||
/>
|
||||
<Button variant='outlined' color='secondary' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
</FormControl>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
import * as React from 'react';
|
||||
import AppBar from '@mui/material/AppBar';
|
||||
import Box from '@mui/material/Box';
|
||||
import Toolbar from '@mui/material/Toolbar';
|
||||
import IconButton from '@mui/material/IconButton';
|
||||
import Typography from '@mui/material/Typography';
|
||||
import Menu from '@mui/material/Menu';
|
||||
import MenuIcon from '@mui/icons-material/Menu';
|
||||
import Container from '@mui/material/Container';
|
||||
import Avatar from '@mui/material/Avatar';
|
||||
import Button from '@mui/material/Button';
|
||||
import Tooltip from '@mui/material/Tooltip';
|
||||
import MenuItem from '@mui/material/MenuItem';
|
||||
import AdbIcon from '@mui/icons-material/Adb';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { getWebSocket } from '../../redux/selectors';
|
||||
import { setWebSocket } from '../../redux/slices/websocketSlice';
|
||||
|
||||
function Header() {
|
||||
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null);
|
||||
const [anchorElUser, setAnchorElUser] = useState<null | HTMLElement>(null);
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
const ws = useSelector(getWebSocket);
|
||||
const setWebSocketClick = () => {
|
||||
if (ws === null || ws.readyState === ws.CLOSED) {
|
||||
dispatch(setWebSocket(new WebSocket('ws://localhost:5176/ws')));
|
||||
console.log('WebSocket created');
|
||||
} else {
|
||||
ws.close();
|
||||
console.log('WebSocket closed');
|
||||
}
|
||||
};
|
||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorElNav(event.currentTarget);
|
||||
};
|
||||
const handleOpenUserMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorElUser(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseNavMenu = () => {
|
||||
setAnchorElNav(null);
|
||||
};
|
||||
|
||||
const handleCloseUserMenu = () => {
|
||||
setAnchorElUser(null);
|
||||
};
|
||||
|
||||
const handleAddDrug = () => {
|
||||
navigate('/add');
|
||||
};
|
||||
|
||||
const handleLogoClick = () => {
|
||||
navigate('/');
|
||||
};
|
||||
|
||||
function handleStatistics(): void {
|
||||
navigate('/stats');
|
||||
}
|
||||
|
||||
function handleAddManu(): void {
|
||||
navigate('/manufacturer/add');
|
||||
}
|
||||
|
||||
return (
|
||||
<AppBar position='static'>
|
||||
<Container maxWidth='xl'>
|
||||
<Toolbar disableGutters>
|
||||
<AdbIcon sx={{ display: { xs: 'none', md: 'flex' }, mr: 1 }} />
|
||||
<Typography
|
||||
variant='h6'
|
||||
noWrap
|
||||
component='a'
|
||||
onClick={handleLogoClick}
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: 'none', md: 'flex' },
|
||||
fontFamily: 'monospace',
|
||||
fontWeight: 700,
|
||||
letterSpacing: '.3rem',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none',
|
||||
cursor: 'pointer'
|
||||
}}
|
||||
>
|
||||
Silk Road
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'flex', md: 'none' } }}>
|
||||
<IconButton
|
||||
size='large'
|
||||
aria-label='account of current user'
|
||||
aria-controls='menu-appbar'
|
||||
aria-haspopup='true'
|
||||
onClick={handleOpenNavMenu}
|
||||
color='inherit'
|
||||
>
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
<Menu
|
||||
id='menu-appbar'
|
||||
anchorEl={anchorElNav}
|
||||
anchorOrigin={{
|
||||
vertical: 'bottom',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left'
|
||||
}}
|
||||
open={Boolean(anchorElNav)}
|
||||
onClose={handleCloseNavMenu}
|
||||
sx={{
|
||||
display: { xs: 'block', md: 'none' }
|
||||
}}
|
||||
>
|
||||
<MenuItem key='add-drug' onClick={handleAddDrug}>
|
||||
<Typography textAlign='center'>Add Drug</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem key='add-manu' onClick={handleAddManu}>
|
||||
<Typography textAlign='center'>Add Manufacturer</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem key='statistics' onClick={handleStatistics}>
|
||||
<Typography textAlign='center'>Statistics</Typography>
|
||||
</MenuItem>
|
||||
<MenuItem key='create-ws' onClick={setWebSocketClick}>
|
||||
<Typography textAlign='center'>Start Generating</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
<AdbIcon sx={{ display: { xs: 'flex', md: 'none' }, mr: 1 }} />
|
||||
<Typography
|
||||
variant='h5'
|
||||
noWrap
|
||||
component='a'
|
||||
onClick={handleLogoClick}
|
||||
sx={{
|
||||
mr: 2,
|
||||
display: { xs: 'flex', md: 'none' },
|
||||
flexGrow: 1,
|
||||
fontFamily: 'monospace',
|
||||
fontWeight: 700,
|
||||
letterSpacing: '.3rem',
|
||||
color: 'inherit',
|
||||
textDecoration: 'none'
|
||||
}}
|
||||
>
|
||||
Silk Road
|
||||
</Typography>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
<Button
|
||||
key='add-drug'
|
||||
onClick={handleAddDrug}
|
||||
sx={{ my: 2, color: 'white', display: 'block' }}
|
||||
>
|
||||
Add Drug
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
<Button
|
||||
key='add-manu'
|
||||
onClick={handleAddManu}
|
||||
sx={{ my: 2, color: 'white', display: 'block' }}
|
||||
>
|
||||
Add Manufacturer
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
<Button
|
||||
key='statistics'
|
||||
onClick={handleStatistics}
|
||||
sx={{ my: 2, color: 'white', display: 'block' }}
|
||||
>
|
||||
Statistics
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 1, display: { xs: 'none', md: 'flex' } }}>
|
||||
<Button
|
||||
key='create-ws'
|
||||
onClick={setWebSocketClick}
|
||||
sx={{ my: 2, color: 'white', display: 'block' }}
|
||||
>
|
||||
Start Generating
|
||||
</Button>
|
||||
</Box>
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title='Open settings'>
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<Avatar alt='Login' />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{ mt: '45px' }}
|
||||
id='menu-appbar'
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right'
|
||||
}}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
<MenuItem key='add-drug' onClick={handleCloseUserMenu}>
|
||||
<Typography textAlign='center'>Login</Typography>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
</AppBar>
|
||||
);
|
||||
}
|
||||
export default Header;
|
||||
@@ -0,0 +1,101 @@
|
||||
import { Button, FormLabel } from '@mui/material';
|
||||
import FormControl from '@mui/material/FormControl';
|
||||
import TextField from '@mui/material/TextField';
|
||||
import { ChangeEvent, FormEvent, useState } from 'react';
|
||||
import { getEmptyManufacturer } from '../../utils/DrugUtils';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import axios from 'axios';
|
||||
import Manufacturer from '../../interfaces/Manufacturer';
|
||||
|
||||
export default function DrugFrom(props: { updateDrug?: Manufacturer }) {
|
||||
const [drug, setDrug] = useState(props.updateDrug ?? getEmptyManufacturer());
|
||||
const navigate = useNavigate();
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setDrug({
|
||||
...drug,
|
||||
[e.target.name]: e.target.value
|
||||
});
|
||||
};
|
||||
|
||||
const handleSubmit = (e: FormEvent<HTMLElement>) => {
|
||||
e.preventDefault();
|
||||
if (drug.id === undefined || drug.id === -1) {
|
||||
addDrug(drug);
|
||||
} else {
|
||||
updateDrug(drug);
|
||||
}
|
||||
};
|
||||
|
||||
const addDrug = async (drug: Manufacturer) => {
|
||||
try {
|
||||
drug.id = 0;
|
||||
axios.post(
|
||||
'https://silkroadapi.azurewebsites.net/Manufacturer/add-manufacturer',
|
||||
drug,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
navigate('/');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDrug = async (drug: Manufacturer) => {
|
||||
try {
|
||||
axios.put(
|
||||
'https://silkroadapi.azurewebsites.net/Manufacturer/update-manufacturer',
|
||||
drug,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
navigate('/');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit}>
|
||||
<FormControl
|
||||
sx={{
|
||||
width: '70%',
|
||||
marginLeft: '15%',
|
||||
marginRight: '15%',
|
||||
marginBottom: '30px',
|
||||
marginTop: '30px'
|
||||
}}
|
||||
>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='name'
|
||||
title='Name'
|
||||
value={drug.name}
|
||||
/>
|
||||
<FormLabel>Address</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='address'
|
||||
title='Address'
|
||||
value={drug.address}
|
||||
/>
|
||||
<FormLabel>Contact</FormLabel>
|
||||
<TextField
|
||||
type='text'
|
||||
required
|
||||
onChange={handleChange}
|
||||
name='contact'
|
||||
title='Contact'
|
||||
value={drug.contact}
|
||||
/>
|
||||
<Button variant='outlined' color='secondary' type='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
</FormControl>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import * as React from 'react';
|
||||
import { BarChart } from '@mui/x-charts/BarChart';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getWebSocket } from '../../redux/selectors';
|
||||
import axios from 'axios';
|
||||
|
||||
const chartSetting = {
|
||||
xAxis: [
|
||||
{
|
||||
label: 'Drugs By Company'
|
||||
}
|
||||
],
|
||||
width: 500,
|
||||
height: 400
|
||||
};
|
||||
type data = { manufacturer: string; count: number };
|
||||
|
||||
// const dataset: data[] = [];
|
||||
// drugs.forEach((drug) => {
|
||||
// const manufacturer = drug.manufacturer;
|
||||
// const index = dataset.findIndex((data) => data.manufacturer === manufacturer);
|
||||
// if (index === -1) {
|
||||
// dataset.push({ manufacturer, count: 1 });
|
||||
// } else {
|
||||
// dataset[index].count++;
|
||||
// }
|
||||
// });
|
||||
|
||||
const valueFormatter = (value: number) => `${value} #`;
|
||||
|
||||
export default function DrugBarChart() {
|
||||
const ws = useSelector(getWebSocket);
|
||||
const [data, setData] = React.useState<data[]>([]);
|
||||
const [newitems, setNewItems] = React.useState<number>(1);
|
||||
|
||||
const fetchData = async () => {
|
||||
const data = await axios.get(
|
||||
'https://silkroadapi.azurewebsites.net/Drug/get-chart-data',
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setData(data.data);
|
||||
setNewItems(0);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
ws?.addEventListener('message', (event) => {
|
||||
console.log(event.data);
|
||||
setNewItems(1);
|
||||
fetchData();
|
||||
});
|
||||
}, [ws]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (newitems === 0) return;
|
||||
fetchData();
|
||||
console.log(data);
|
||||
}, [data, newitems]);
|
||||
|
||||
// const groupBy = () => {
|
||||
// const dataset: data[] = [];
|
||||
// const arrayDrug = [...drugs];
|
||||
// arrayDrug.forEach((drug: Drug) => {
|
||||
// const manufacturer = drug.manufacturer;
|
||||
// const index = dataset.findIndex(
|
||||
// (data) => data.manufacturer === manufacturer
|
||||
// );
|
||||
// if (index === -1) {
|
||||
// dataset.push({ manufacturer, count: 1 });
|
||||
// } else {
|
||||
// dataset[index].count++;
|
||||
// }
|
||||
// });
|
||||
// return dataset;
|
||||
// };
|
||||
|
||||
// const [dataset] = React.useState<data[]>(groupBy());
|
||||
console.log(data);
|
||||
return (
|
||||
data.length > 0 && (
|
||||
<BarChart
|
||||
dataset={data}
|
||||
yAxis={[{ scaleType: 'band', dataKey: 'manufacturer' }]}
|
||||
series={[{ dataKey: 'count', label: 'Total Drugs', valueFormatter }]}
|
||||
layout='horizontal'
|
||||
{...chartSetting}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,278 @@
|
||||
import Drug from "../interfaces/Drug";
|
||||
|
||||
const drugs: Drug[] = [
|
||||
{
|
||||
id: 1,
|
||||
name: 'Paracetamol',
|
||||
price: 5,
|
||||
stock: 100,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Paracetamol is a painkiller and can be used to relieve mild to moderate pain. It can also reduce high temperatures (fever).',
|
||||
sideEffects: 'Paracetamol is a very safe medicine when used correctly. Side effects are rare if you take it at the right dosage.',
|
||||
interactions: 'Paracetamol can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose of paracetamol is one or two 500mg tablets at a time. Do not take more than 4 doses in 24 hours.',
|
||||
contraindications: 'Do not take paracetamol if you are allergic to it.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep paracetamol in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Paracetamol is safe to take in pregnancy and while breastfeeding, at recommended doses.',
|
||||
breastfeeding: 'Paracetamol is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of paracetamol can cause serious harm. The maximum amount of paracetamol for adults is 1 gram (1000 mg) per dose and 4 grams (4000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: 'Ibuprofen',
|
||||
price: 10,
|
||||
stock: 50,
|
||||
manufacturer: 'Pfizer',
|
||||
description: 'Ibuprofen is a painkiller available over the counter without a prescription. It\'s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.',
|
||||
sideEffects: 'The most common side effects of ibuprofen are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Ibuprofen can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 200mg tablets 3 times a day. Do not take more than 6 tablets (1200mg) in 24 hours.',
|
||||
contraindications: 'Do not take ibuprofen if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep ibuprofen in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Ibuprofen is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Ibuprofen is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of ibuprofen can cause serious harm. The maximum amount of ibuprofen for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: 'Aspirin',
|
||||
price: 15,
|
||||
stock: 25,
|
||||
manufacturer: 'Bayer',
|
||||
description: 'Aspirin is a painkiller and anti-inflammatory medicine. It can be used to relieve pain and inflammation caused by many conditions such as headaches, toothache, period pain, and joint pain.',
|
||||
sideEffects: 'The most common side effects of aspirin are indigestion and stomach irritation.',
|
||||
interactions: 'Aspirin can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 or 4 times a day. Do not take more than 12 tablets (3600mg) in 24 hours.',
|
||||
contraindications: 'Do not take aspirin if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep aspirin in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Aspirin is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Aspirin is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of aspirin can cause serious harm. The maximum amount of aspirin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: 'Codeine',
|
||||
price: 20,
|
||||
stock: 10,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Codeine is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.',
|
||||
sideEffects: 'The most common side effects of codeine are constipation, feeling sick, and drowsiness.',
|
||||
interactions: 'Codeine can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 30mg tablets 3 or 4 times a day. Do not take more than 12 tablets (360mg) in 24 hours.',
|
||||
contraindications: 'Do not take codeine if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep codeine in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Codeine is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Codeine is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of codeine can cause serious harm. The maximum amount of codeine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
name: 'Morphine',
|
||||
price: 25,
|
||||
stock: 5,
|
||||
manufacturer: 'Pfizer',
|
||||
description: 'Morphine is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.',
|
||||
sideEffects: 'The most common side effects of morphine are constipation, feeling sick, and drowsiness.',
|
||||
interactions: 'Morphine can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 10mg tablets 3 or 4 times a day. Do not take more than 12 tablets (120mg) in 24 hours.',
|
||||
contraindications: 'Do not take morphine if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep morphine in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Morphine is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Morphine is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of morphine can cause serious harm. The maximum amount of morphine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
name: 'Tramadol',
|
||||
price: 30,
|
||||
stock: 2,
|
||||
manufacturer: 'Bayer',
|
||||
description: 'Tramadol is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.',
|
||||
sideEffects: 'The most common side effects of tramadol are constipation, feeling sick, and drowsiness.',
|
||||
interactions: 'Tramadol can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 50mg tablets 3 or 4 times a day. Do not take more than 12 tablets (600mg) in 24 hours.',
|
||||
contraindications: 'Do not take tramadol if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep tramadol in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Tramadol is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Tramadol is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of tramadol can cause serious harm. The maximum amount of tramadol for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
name: 'Cocodamol',
|
||||
price: 35,
|
||||
stock: 1,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Cocodamol is a strong painkiller and is part of a group of medicines called opioids. Opioids are derived from the poppy plant, and are known for their pain-relieving properties.',
|
||||
sideEffects: 'The most common side effects of cocodamol are constipation, feeling sick, and drowsiness.',
|
||||
interactions: 'Cocodamol can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 30mg tablets 3 or 4 times a day. Do not take more than 12 tablets (360mg) in 24 hours.',
|
||||
contraindications: 'Do not take cocodamol if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep cocodamol in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Cocodamol is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Cocodamol is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of cocodamol can cause serious harm. The maximum amount of cocodamol for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
name: 'Diclofenac',
|
||||
price: 40,
|
||||
stock: 1,
|
||||
manufacturer: 'Pfizer',
|
||||
description: 'Diclofenac is a painkiller available over the counter without a prescription. It\'s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.',
|
||||
sideEffects: 'The most common side effects of diclofenac are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Diclofenac can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 50mg tablets 3 times a day. Do not take more than 6 tablets (300mg) in 24 hours.',
|
||||
contraindications: 'Do not take diclofenac if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep diclofenac in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Diclofenac is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Diclofenac is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of diclofenac can cause serious harm. The maximum amount of diclofenac for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
name: 'Naproxen',
|
||||
price: 45,
|
||||
stock: 1,
|
||||
manufacturer: 'Bayer',
|
||||
description: 'Naproxen is a painkiller available over the counter without a prescription. It\'s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.',
|
||||
sideEffects: 'The most common side effects of naproxen are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Naproxen can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 250mg tablets 3 times a day. Do not take more than 6 tablets (1500mg) in 24 hours.',
|
||||
contraindications: 'Do not take naproxen if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep naproxen in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Naproxen is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Naproxen is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of naproxen can cause serious harm. The maximum amount of naproxen for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
name: 'Mefenamic Acid',
|
||||
price: 50,
|
||||
stock: 1,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Mefenamic Acid is a painkiller available over the counter without a prescription. It\'s one of a group of painkillers called non-steroidal anti-inflammatory drugs (NSAIDs) and can be used to ease mild to moderate pain, such as period pain, toothache, and migraines.',
|
||||
sideEffects: 'The most common side effects of mefenamic acid are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Mefenamic acid can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 250mg tablets 3 times a day. Do not take more than 6 tablets (1500mg) in 24 hours.',
|
||||
contraindications: 'Do not take mefenamic acid if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep mefenamic acid in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Mefenamic acid is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Mefenamic acid is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of mefenamic acid can cause serious harm. The maximum amount of mefenamic acid for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
name: 'Gabapentin',
|
||||
price: 55,
|
||||
stock: 1,
|
||||
manufacturer: 'Pfizer',
|
||||
description: 'Gabapentin is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of gabapentin are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Gabapentin can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take gabapentin if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep gabapentin in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Gabapentin is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Gabapentin is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of gabapentin can cause serious harm. The maximum amount of gabapentin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
name: 'Pregabalin',
|
||||
price: 60,
|
||||
stock: 1,
|
||||
manufacturer: 'Bayer',
|
||||
description: 'Pregabalin is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of pregabalin are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Pregabalin can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take pregabalin if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep pregabalin in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Pregabalin is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Pregabalin is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of pregabalin can cause serious harm. The maximum amount of pregabalin for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
name: 'Amitriptyline',
|
||||
price: 65,
|
||||
stock: 1,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Amitriptyline is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of amitriptyline are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Amitriptyline can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take amitriptyline if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep amitriptyline in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Amitriptyline is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Amitriptyline is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of amitriptyline can cause serious harm. The maximum amount of amitriptyline for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
name: 'Duloxetine',
|
||||
price: 70,
|
||||
stock: 1,
|
||||
manufacturer: 'Pfizer',
|
||||
description: 'Duloxetine is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of duloxetine are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Duloxetine can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take duloxetine if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep duloxetine in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Duloxetine is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Duloxetine is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of duloxetine can cause serious harm. The maximum amount of duloxetine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
name: 'Venlafaxine',
|
||||
price: 75,
|
||||
stock: 1,
|
||||
manufacturer: 'Bayer',
|
||||
description: 'Venlafaxine is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of venlafaxine are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Venlafaxine can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take venlafaxine if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep venlafaxine in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Venlafaxine is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Venlafaxine is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of venlafaxine can cause serious harm. The maximum amount of venlafaxine for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
name: 'Sertraline',
|
||||
price: 80,
|
||||
stock: 1,
|
||||
manufacturer: 'GSK',
|
||||
description: 'Sertraline is a medicine used to treat nerve pain, such as epilepsy, and can also be used to treat anxiety.',
|
||||
sideEffects: 'The most common side effects of sertraline are headaches, dizziness, and drowsiness.',
|
||||
interactions: 'Sertraline can be taken with other medicines, including prescription medicines such as antibiotics.',
|
||||
dosage: 'The usual dose for adults is one or two 300mg tablets 3 times a day. Do not take more than 6 tablets (1800mg) in 24 hours.',
|
||||
contraindications: 'Do not take sertraline if you have a stomach ulcer, or have had one in the past.',
|
||||
warnings: 'Do not take more than the recommended dose.',
|
||||
storage: 'Keep sertraline in a cool, dry place. Do not store it above 25C.',
|
||||
pregnancy: 'Sertraline is not recommended in pregnancy, especially in the third trimester.',
|
||||
breastfeeding: 'Sertraline is safe to take while breastfeeding, at recommended doses.',
|
||||
overdose: 'An overdose of sertraline can cause serious harm. The maximum amount of sertraline for adults is 1 gram (1000 mg) per dose and 3 grams (3000 mg) per day.'
|
||||
},
|
||||
];
|
||||
|
||||
export default drugs;
|
||||
@@ -0,0 +1,55 @@
|
||||
import Add from '../views/Add';
|
||||
import Update from '../views/Update';
|
||||
import Details from '../views/Details';
|
||||
import Search from '../views/Search';
|
||||
import Error from '../views/Error';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import Statistics from '../views/Statistics';
|
||||
import DetailsManu from '../views/DetailsManu';
|
||||
import AddManu from '../views/AddManu';
|
||||
import UpdateManu from '../views/UpdateManu';
|
||||
|
||||
const routes = createBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <Search />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/details/:id',
|
||||
element: <Details />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/add',
|
||||
element: <Add />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/update/:id',
|
||||
element: <Update />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: 'stats',
|
||||
element: <Statistics />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/manufacturer/details/:id',
|
||||
element: <DetailsManu />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/manufacturer/add',
|
||||
element: <AddManu />,
|
||||
errorElement: <Error />
|
||||
},
|
||||
{
|
||||
path: '/manufacturer/update/:id',
|
||||
element: <UpdateManu />,
|
||||
errorElement: <Error />
|
||||
}
|
||||
]);
|
||||
|
||||
export default routes;
|
||||
@@ -0,0 +1,20 @@
|
||||
type Drug = {
|
||||
id: number;
|
||||
name: string;
|
||||
price: number;
|
||||
stock: number;
|
||||
manufacturer: string;
|
||||
description: string;
|
||||
sideEffects: string;
|
||||
interactions: string;
|
||||
dosage: string;
|
||||
contraindications: string;
|
||||
warnings: string;
|
||||
storage: string;
|
||||
pregnancy: string;
|
||||
breastfeeding: string;
|
||||
overdose: string;
|
||||
|
||||
};
|
||||
|
||||
export default Drug;
|
||||
@@ -0,0 +1,9 @@
|
||||
type Manufacturer = {
|
||||
id: number | null;
|
||||
name: string;
|
||||
address: string;
|
||||
contact: string;
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export default Manufacturer;
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App.tsx';
|
||||
import GlobalStyle from './styled/global.ts';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './redux/store.ts';
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<GlobalStyle />
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
@@ -0,0 +1,8 @@
|
||||
import { RootState } from "./store";
|
||||
|
||||
export const getDrugs = (state: RootState) => state.drugs.drugs;
|
||||
export const getDrugById = (state: RootState, id: number) =>
|
||||
state.drugs.drugs.find((drug) => drug.id === id);
|
||||
export const getWebSocket = (state: RootState) => state.websocket.ws;
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
import Drug from '../../interfaces/Drug'
|
||||
|
||||
export interface DrugState {
|
||||
drugs: Drug[]
|
||||
}
|
||||
|
||||
const initialState: DrugState = {
|
||||
drugs: [],
|
||||
}
|
||||
|
||||
export const drugsSlice = createSlice({
|
||||
name: 'drugs',
|
||||
initialState,
|
||||
reducers: {
|
||||
loadDrugs: (state, action: PayloadAction<Drug[]>) => {
|
||||
state.drugs = action.payload
|
||||
},
|
||||
addDrug: (state, action: PayloadAction<Drug>) => {
|
||||
state.drugs.push(action.payload)
|
||||
},
|
||||
removeDrug: (state, action: PayloadAction<number>) => {
|
||||
state.drugs = state.drugs.filter((drug) => drug.id !== action.payload)
|
||||
},
|
||||
updateDrug: (state, action: PayloadAction<Drug>) => {
|
||||
const index = state.drugs.findIndex((drug) => drug.id === action.payload.id)
|
||||
if (index !== -1) {
|
||||
state.drugs[index] = action.payload
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// Action creators are generated for each case reducer function
|
||||
export const { loadDrugs, addDrug, removeDrug, updateDrug } = drugsSlice.actions
|
||||
|
||||
export default drugsSlice.reducer
|
||||
@@ -0,0 +1,24 @@
|
||||
import { createSlice } from '@reduxjs/toolkit'
|
||||
import type { PayloadAction } from '@reduxjs/toolkit'
|
||||
|
||||
export interface WebSocketState {
|
||||
ws: WebSocket | null
|
||||
}
|
||||
|
||||
const initialState: WebSocketState = {
|
||||
ws: null,
|
||||
}
|
||||
|
||||
export const websocketSlice = createSlice({
|
||||
name: 'websocket',
|
||||
initialState,
|
||||
reducers: {
|
||||
setWebSocket: (state, action: PayloadAction<WebSocket>) => {
|
||||
state.ws = action.payload
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
export const { setWebSocket } = websocketSlice.actions
|
||||
|
||||
export default websocketSlice.reducer
|
||||
@@ -0,0 +1,18 @@
|
||||
import { configureStore } from '@reduxjs/toolkit'
|
||||
import drugSlice from './slices/drugSlice'
|
||||
import websocketSlice from './slices/websocketSlice'
|
||||
|
||||
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
drugs: drugSlice,
|
||||
websocket: websocketSlice
|
||||
},
|
||||
middleware: (getDefaultMiddleware) => getDefaultMiddleware({
|
||||
serializableCheck: false,
|
||||
})
|
||||
})
|
||||
|
||||
export type RootState = ReturnType<typeof store.getState>
|
||||
export type AppDispatch = typeof store.dispatch
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
html {
|
||||
margin: 0;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
export default GlobalStyle;
|
||||
@@ -0,0 +1,34 @@
|
||||
import Drug from "../interfaces/Drug";
|
||||
import Manufacturer from "../interfaces/Manufacturer";
|
||||
|
||||
export const getEmptyDrug = (): Drug => {
|
||||
return {
|
||||
id: -1,
|
||||
name: '',
|
||||
stock: 0,
|
||||
price: 0,
|
||||
manufacturer: '',
|
||||
description: '',
|
||||
sideEffects: '',
|
||||
interactions: '',
|
||||
dosage: '',
|
||||
contraindications: '',
|
||||
warnings: '',
|
||||
storage: '',
|
||||
pregnancy: '',
|
||||
breastfeeding: '',
|
||||
overdose: '',
|
||||
} as Drug;
|
||||
}
|
||||
|
||||
export const getEmptyManufacturer = (): Manufacturer => {
|
||||
return {
|
||||
id: -1,
|
||||
name: '',
|
||||
address: '',
|
||||
contact: '',
|
||||
totalCount: 0,
|
||||
} as Manufacturer;
|
||||
}
|
||||
|
||||
export default getEmptyDrug;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DrugForm from '../components/Shared/DrugForm';
|
||||
import Header from '../components/Shared/Header';
|
||||
|
||||
const Add = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<DrugForm />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Add;
|
||||
@@ -0,0 +1,13 @@
|
||||
import ManuForm from '../components/Shared/ManuForm';
|
||||
import Header from '../components/Shared/Header';
|
||||
|
||||
const AddManu = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<ManuForm />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddManu;
|
||||
@@ -0,0 +1,13 @@
|
||||
import DrugDetails from '../components/Details/DrugDetails';
|
||||
import Header from '../components/Shared/Header';
|
||||
|
||||
const Details = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<DrugDetails />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Details;
|
||||
@@ -0,0 +1,13 @@
|
||||
import ManuDetails from '../components/Details/ManuDetails';
|
||||
import Header from '../components/Shared/Header';
|
||||
|
||||
const DetailsManu = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<ManuDetails />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default DetailsManu;
|
||||
@@ -0,0 +1,9 @@
|
||||
const Error = () => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Error</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Error;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Divider } from '@mui/material';
|
||||
import DrugTable from '../components/Search/DrugTable';
|
||||
import Header from '../components/Shared/Header';
|
||||
import ManufacturerTable from '../components/Search/ManufacturerTable';
|
||||
|
||||
const Search = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<DrugTable />
|
||||
<Divider sx={{ padding: 10 }} />
|
||||
<ManufacturerTable />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Search;
|
||||
@@ -0,0 +1,13 @@
|
||||
import Header from '../components/Shared/Header';
|
||||
import DrugBarChart from '../components/Statistics/DrugBarChart';
|
||||
|
||||
const Statistics = () => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<DrugBarChart />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Statistics;
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import DrugForm from '../components/Shared/DrugForm';
|
||||
import Header from '../components/Shared/Header';
|
||||
import axios from 'axios';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import Drug from '../interfaces/Drug';
|
||||
import { getDrugById } from '../redux/selectors';
|
||||
import { RootState } from '../redux/store';
|
||||
|
||||
const Update = () => {
|
||||
const id = parseInt(useParams().id ?? '-1');
|
||||
const [drug, setDrug] = useState<Drug | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const potentialDrug = useSelector((state: RootState) =>
|
||||
getDrugById(state, id)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDrugById = async (id: number) => {
|
||||
try {
|
||||
const drug = await axios.get<Drug>(
|
||||
`https://silkroadapi.azurewebsites.net/Drug/get-drug/${id}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setDrug(drug.data);
|
||||
console.log(drug);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
navigate('/error');
|
||||
}
|
||||
};
|
||||
if (potentialDrug) setDrug(potentialDrug);
|
||||
else fetchDrugById(id);
|
||||
}, [id, navigate, potentialDrug]);
|
||||
return (
|
||||
drug && (
|
||||
<>
|
||||
<Header />
|
||||
<DrugForm updateDrug={drug} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default Update;
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import ManuForm from '../components/Shared/ManuForm';
|
||||
import Header from '../components/Shared/Header';
|
||||
import axios from 'axios';
|
||||
import { useEffect, useState } from 'react';
|
||||
import Manufacturer from '../interfaces/Manufacturer';
|
||||
|
||||
const UpdateManu = () => {
|
||||
const id = parseInt(useParams().id ?? '-1');
|
||||
const [drug, setDrug] = useState<Manufacturer | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
const fetchDrugById = async (id: number) => {
|
||||
try {
|
||||
const drugcox = await axios.get<Manufacturer>(
|
||||
`https://silkroadapi.azurewebsites.net/Manufacturer/get-manufacturer/${id}`,
|
||||
{ withCredentials: true }
|
||||
);
|
||||
setDrug(drugcox.data);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
navigate('/error');
|
||||
}
|
||||
};
|
||||
fetchDrugById(id);
|
||||
}, [id, navigate]);
|
||||
return (
|
||||
drug && (
|
||||
<>
|
||||
<Header />
|
||||
<ManuForm updateDrug={drug} />
|
||||
</>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default UpdateManu;
|
||||
@@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@@ -0,0 +1,55 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import DrugDetails from '../src/components/Details/DrugDetails';
|
||||
import React from 'react';
|
||||
import routes from '../src/constants/routes';
|
||||
import drugs from '../src/constants/mock_data';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from '../src/redux/store';
|
||||
|
||||
const mockedUseNavigate = vi.fn(() => {
|
||||
return (path: string) => {
|
||||
return routes[path];
|
||||
};
|
||||
});
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-router-dom')>(
|
||||
'react-router-dom'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useNavigate: () => mockedUseNavigate,
|
||||
useParams: () => ({ id: '1' })
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('react-redux', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-redux')>(
|
||||
'react-redux'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useSelector: () => drugs[0]
|
||||
};
|
||||
});
|
||||
|
||||
test('DrugDetails renders correctly', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<DrugDetails />
|
||||
</Provider>
|
||||
);
|
||||
expect(screen.getByText('Paracetamol')).toBeTruthy();
|
||||
expect(screen.getByText('$5')).toBeTruthy();
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Paracetamol is a painkiller and can be used to relieve mild to moderate pain. It can also reduce high temperatures (fever).'
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
|
||||
test('DrugDetails back button works correctly', () => {
|
||||
render(<DrugDetails />);
|
||||
fireEvent.click(screen.getByText('Back'));
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith(-1);
|
||||
});
|
||||
@@ -0,0 +1,182 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import drugs from '../src/constants/mock_data';
|
||||
import React from 'react';
|
||||
import routes from '../src/constants/routes';
|
||||
import DrugFrom from '../src/components/Shared/DrugForm';
|
||||
import axios from 'axios';
|
||||
import Drug from '../src/interfaces/Drug';
|
||||
|
||||
const mockedUseNavigate = vi.fn(() => {
|
||||
return (path: string) => {
|
||||
return routes[path];
|
||||
};
|
||||
});
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-router-dom')>(
|
||||
'react-router-dom'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useNavigate: () => mockedUseNavigate
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('react-redux', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-redux')>(
|
||||
'react-redux'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useDispatch: () => vi.fn()
|
||||
};
|
||||
});
|
||||
|
||||
test('DrugForm renders correctly - Empty', () => {
|
||||
render(<DrugFrom />);
|
||||
expect(screen.getByTitle('Name')).toBeTruthy();
|
||||
expect(screen.getByTitle('Stock')).toBeTruthy();
|
||||
expect(screen.getByTitle('Price')).toBeTruthy();
|
||||
|
||||
expect(
|
||||
screen.getByTitle('Name').getElementsByTagName('input')[0].value
|
||||
).not.toBeTruthy();
|
||||
expect(screen.getByTitle('Stock').getElementsByTagName('input')[0].value).eq(
|
||||
'0'
|
||||
);
|
||||
expect(screen.getByTitle('Price').getElementsByTagName('input')[0].value).eq(
|
||||
'0'
|
||||
);
|
||||
});
|
||||
|
||||
test('DrugForm renders correctly - Update', () => {
|
||||
render(<DrugFrom updateDrug={drugs[0]} />);
|
||||
expect(screen.getByTitle('Name')).toBeTruthy();
|
||||
expect(screen.getByTitle('Stock')).toBeTruthy();
|
||||
expect(screen.getByTitle('Price')).toBeTruthy();
|
||||
|
||||
expect(screen.getByTitle('Name').getElementsByTagName('input')[0].value).eq(
|
||||
drugs[0].name
|
||||
);
|
||||
expect(screen.getByTitle('Stock').getElementsByTagName('input')[0].value).eq(
|
||||
drugs[0].stock.toString()
|
||||
);
|
||||
expect(screen.getByTitle('Price').getElementsByTagName('input')[0].value).eq(
|
||||
drugs[0].price.toString()
|
||||
);
|
||||
});
|
||||
|
||||
test('DrugForm renders correctly - Create', () => {
|
||||
render(<DrugFrom />);
|
||||
expect(drugs.length).eq(16);
|
||||
fireEvent.change(screen.getByTitle('Name').getElementsByTagName('input')[0], {
|
||||
target: { value: 'Test' }
|
||||
});
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Stock').getElementsByTagName('input')[0],
|
||||
{
|
||||
target: { value: '10' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Price').getElementsByTagName('input')[0],
|
||||
{
|
||||
target: { value: '100' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Manufacturer').getElementsByTagName('input')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Description').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Side Effects').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Interactions').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Dosage').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Contraindications').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Warnings').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Storage').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Pregnancy').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Breastfeeding').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
fireEvent.change(
|
||||
screen.getByTitle('Overdose').getElementsByTagName('textarea')[0],
|
||||
{
|
||||
target: { value: 'Test' }
|
||||
}
|
||||
);
|
||||
vi.spyOn(axios, 'post').mockImplementation((url: string, data?: unknown) => {
|
||||
drugs.push(data as Drug);
|
||||
return Promise.resolve({ data: data });
|
||||
});
|
||||
fireEvent.click(screen.getByText('Submit'));
|
||||
expect(drugs.length).eq(17);
|
||||
expect(drugs[16].name).eq('Test');
|
||||
});
|
||||
|
||||
test('DrugForm renders correctly - Update', () => {
|
||||
render(<DrugFrom updateDrug={drugs[0]} />);
|
||||
expect(screen.getByTitle('Name')).toBeTruthy();
|
||||
expect(screen.getByTitle('Stock')).toBeTruthy();
|
||||
expect(screen.getByTitle('Price')).toBeTruthy();
|
||||
|
||||
expect(screen.getByTitle('Name').getElementsByTagName('input')[0].value).eq(
|
||||
drugs[0].name
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByTitle('Name').getElementsByTagName('input')[0], {
|
||||
target: { value: 'cox' }
|
||||
});
|
||||
vi.spyOn(axios, 'put').mockImplementation((url: string, data?: unknown) => {
|
||||
const index = drugs.findIndex((d) => d.id === (data as Drug).id);
|
||||
drugs[index] = data as Drug;
|
||||
return Promise.resolve({ data: data });
|
||||
});
|
||||
fireEvent.click(screen.getByText('Submit'));
|
||||
expect(drugs[0].name).eq('cox');
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import DrugTable from '../src/components/Search/DrugTable';
|
||||
import drugs from '../src/constants/mock_data';
|
||||
import React from 'react';
|
||||
import routes from '../src/constants/routes';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from '../src/redux/store';
|
||||
import axios from 'axios';
|
||||
|
||||
const mockedUseNavigate = vi.fn(() => {
|
||||
return (path: string) => {
|
||||
return routes[path];
|
||||
};
|
||||
});
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-router-dom')>(
|
||||
'react-router-dom'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useNavigate: () => mockedUseNavigate
|
||||
};
|
||||
});
|
||||
|
||||
test('DrugTable renders correctly', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<DrugTable />
|
||||
</Provider>
|
||||
);
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { Page: 1, PageSize: 10, Total: 16, Items: drugs }
|
||||
});
|
||||
expect(screen.getByText('Name')).toBeTruthy();
|
||||
expect(screen.getByText('Price (USD)')).toBeTruthy();
|
||||
expect(screen.getByText('Manufacturer')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('DrugTable renders correct number of rows', async () => {
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { Page: 1, PageSize: 10, total: 16, items: drugs.slice(0, 10) }
|
||||
});
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<DrugTable />
|
||||
</Provider>
|
||||
);
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
// Header row + 10 data rows + 1 row for pagination
|
||||
expect(screen.getAllByRole('row')).toHaveLength(12);
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { Page: 2, PageSize: 10, total: 16, items: drugs.slice(9, 16) }
|
||||
});
|
||||
fireEvent.click(screen.getByLabelText('next page'));
|
||||
// Header row + 6 data rows + 1 empty row to keep style + 1 row for pagination
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
expect(screen.getAllByRole('row')).toHaveLength(9);
|
||||
});
|
||||
|
||||
test('DrugTable buttons work correctly', async () => {
|
||||
vi.spyOn(axios, 'get').mockResolvedValue({
|
||||
data: { Page: 1, PageSize: 10, total: 16, items: drugs.slice(0, 10) }
|
||||
});
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<DrugTable />
|
||||
</Provider>
|
||||
);
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
fireEvent.click(screen.getAllByText('View').pop()!);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/details/10');
|
||||
fireEvent.click(screen.getAllByText('Update').pop()!);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/update/10');
|
||||
fireEvent.click(screen.getAllByText('Delete').pop()!);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/update/10');
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { expect, test, vi } from 'vitest';
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import routes from '../src/constants/routes';
|
||||
import Header from '../src/components/Shared/Header';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from '../src/redux/store';
|
||||
|
||||
const mockedUseNavigate = vi.fn(() => {
|
||||
return (path: string) => {
|
||||
return routes[path];
|
||||
};
|
||||
});
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const mod = await vi.importActual<typeof import('react-router-dom')>(
|
||||
'react-router-dom'
|
||||
);
|
||||
return {
|
||||
...mod,
|
||||
useNavigate: () => mockedUseNavigate
|
||||
};
|
||||
});
|
||||
|
||||
test('Header - test links', () => {
|
||||
render(
|
||||
<Provider store={store}>
|
||||
<Header />
|
||||
</Provider>
|
||||
);
|
||||
const homeLink = screen.getAllByText('Silk Road');
|
||||
const firstLink = homeLink[0];
|
||||
expect(firstLink).toBeTruthy();
|
||||
fireEvent.click(firstLink);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/');
|
||||
const secondLink = homeLink[1];
|
||||
expect(secondLink).toBeTruthy();
|
||||
fireEvent.click(secondLink);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/');
|
||||
const addLink = screen.getAllByText('Add Drug');
|
||||
const add = addLink[0];
|
||||
expect(add).toBeTruthy();
|
||||
fireEvent.click(add);
|
||||
expect(mockedUseNavigate).toHaveBeenCalledWith('/add');
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react-swc'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
}
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user