creae update devices
This commit is contained in:
parent
d9c3bfa35e
commit
65b924c174
|
@ -0,0 +1,6 @@
|
|||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="DBE_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="@localhost" uuid="bfd16a45-57c4-4cdd-9553-7a07934e4c31">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://localhost:5432/</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Yuna.iml" filepath="$PROJECT_DIR$/.idea/Yuna.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/bfd16a45-57c4-4cdd-9553-7a07934e4c31/console_1.sql" dialect="PostgreSQL" />
|
||||
<file url="PROJECT" dialect="PostgreSQL" />
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
|
@ -80,5 +80,27 @@ namespace Yuna.Tests.Repositories
|
|||
Assert.NotNull(result);
|
||||
Assert.True(result.All(x => x.UserId == 1));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Update_Updates()
|
||||
{
|
||||
//Arrange
|
||||
Settings.Init();
|
||||
var _context = new DapperContext(true);
|
||||
var repo = new DeviceRepository(_context);
|
||||
|
||||
//Act
|
||||
var initial = await repo.GetById(1);
|
||||
|
||||
initial!.Name += "1";
|
||||
var returnResult = await repo.Update(initial);
|
||||
|
||||
var realChanged = await repo.GetById(1);
|
||||
|
||||
|
||||
//Assert
|
||||
Assert.Equal(initial.Name, returnResult!.Name);
|
||||
Assert.Equal(initial!.Name, realChanged!.Name);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,9 +16,6 @@ namespace Yuna.Website.Server.API
|
|||
app.MapPost("/api/device", CreateDevice)
|
||||
.WithTags("device");
|
||||
|
||||
app.MapDelete("/api/device/{id:long}", () => { })
|
||||
.WithTags("device");
|
||||
|
||||
app.MapGet("/api/device/{deviceId:long}", GetById)
|
||||
.WithTags("device");
|
||||
|
||||
|
@ -32,6 +29,12 @@ namespace Yuna.Website.Server.API
|
|||
|
||||
app.MapPut("/api/device/{deviceId:long}", AddSkillsToDevice)
|
||||
.WithTags("device");
|
||||
|
||||
app.MapPut("/api/device", Update)
|
||||
.WithTags("device");
|
||||
|
||||
app.MapDelete("/api/device/{deviceId:long}", Delete)
|
||||
.WithTags("device");
|
||||
}
|
||||
|
||||
|
||||
|
@ -99,10 +102,18 @@ namespace Yuna.Website.Server.API
|
|||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> Delete(IDeviceService deviceService, long id)
|
||||
public async Task<IResult> Delete(IDeviceService deviceService, HttpContext context, long deviceId)
|
||||
{
|
||||
var result = await deviceService.Delete(id);
|
||||
if (result is null) return Results.NotFound();
|
||||
var isAdmin = context.GetRoleFromCookie();
|
||||
var userId = context.GetUserIdFromCookie();
|
||||
|
||||
var deviceToDelete = await deviceService.GetById(deviceId);
|
||||
if(deviceToDelete is null) return Results.NotFound();
|
||||
|
||||
if (userId != deviceToDelete.UserId && !isAdmin) return Results.Forbid();
|
||||
|
||||
var result = await deviceService.Delete(deviceId);
|
||||
if (result is null) return Results.Problem(statusCode: 500);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
|
@ -129,5 +140,40 @@ namespace Yuna.Website.Server.API
|
|||
|
||||
return Results.Ok(result);
|
||||
}
|
||||
|
||||
|
||||
public class UpdateDeviceRequest
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; set; } = null!;
|
||||
|
||||
[JsonPropertyName("description")]
|
||||
public string Description { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("deviceUrl")]
|
||||
public string DeviceUrl { get; set; } = null!;
|
||||
}
|
||||
|
||||
[Authorize]
|
||||
public async Task<IResult> Update([FromBody] UpdateDeviceRequest request, HttpContext context, IDeviceService deviceService)
|
||||
{
|
||||
var userId = context.GetUserIdFromCookie();
|
||||
var isAdmin = context.GetRoleFromCookie();
|
||||
|
||||
var device = await deviceService.GetById(request.Id);
|
||||
|
||||
if (device is null) return Results.NotFound();
|
||||
if (device.UserId != userId && !isAdmin) return Results.Forbid();
|
||||
|
||||
device.DeviceUrl = request.DeviceUrl;
|
||||
device.Name = request.Name;
|
||||
device.Description = request.Description;
|
||||
|
||||
var result = await deviceService.Update(device);
|
||||
return Results.Ok(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,11 @@ namespace Yuna.Website.Server.Infrastructure
|
|||
|
||||
private static void LoadConnectionStrs(JsonElement connectionStrs)
|
||||
{
|
||||
var env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
DbConnectionStr = connectionStrs.GetProperty("Db").GetString()!;
|
||||
}
|
||||
|
||||
|
|
|
@ -107,9 +107,6 @@ namespace Yuna.Website.Server.Services.DeviceService
|
|||
|
||||
public async Task<Device?> Delete(long id)
|
||||
{
|
||||
var prop = await _deviceRepository.GetById(id);
|
||||
if (prop is null) return null;
|
||||
|
||||
var result = await _deviceRepository.Delete(id);
|
||||
return result;
|
||||
}
|
||||
|
@ -130,5 +127,11 @@ namespace Yuna.Website.Server.Services.DeviceService
|
|||
var result = await _deviceRepository.GetList(userId);
|
||||
return result ?? [];
|
||||
}
|
||||
|
||||
public async Task<Device?> Update(Device device)
|
||||
{
|
||||
var result = await _deviceRepository.Update(device);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,5 +12,6 @@ namespace Yuna.Website.Server.Services.DeviceService
|
|||
public Task<Device?> Delete(long id);
|
||||
public Task<Device?> AddProps(IReadOnlyList<Prop> props, long deviceId);
|
||||
public Task<Dictionary<long, string>?> FetchPropsData(Device device);
|
||||
public Task<Device?> Update(Device device);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Dapper;
|
||||
using System.Data;
|
||||
using Yuna.Website.Server.Model;
|
||||
|
||||
namespace Yuna.Website.Server.Storage.Repositories.Device
|
||||
{
|
||||
|
@ -36,9 +37,18 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
return result;
|
||||
}
|
||||
|
||||
public Task<Model.Device?> Delete(long id)
|
||||
public async Task<Model.Device?> Delete(long id)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
var query =
|
||||
$@"
|
||||
UPDATE ""Yuna_Devices""
|
||||
SET ""IsDeleted"" = TRUE
|
||||
WHERE ""Id"" = {id}
|
||||
AND NOT ""IsDeleted""
|
||||
returning *
|
||||
";
|
||||
|
||||
return await _context.Connection.QuerySingleOrDefaultAsync<Model.Device>(query);
|
||||
}
|
||||
|
||||
public async Task<Model.Device?> GetById(long id)
|
||||
|
@ -58,7 +68,8 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
FROM ""Yuna_Devices"" d
|
||||
LEFT JOIN ""Yuna_Props_In_Devices"" pd ON d.""Id"" = pd.""DeviceId""
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""
|
||||
WHERE d.""Id"" = {id}";
|
||||
WHERE d.""Id"" = {id}
|
||||
AND NOT d.""IsDeleted""";
|
||||
|
||||
var deviceDict = new Dictionary<long, Model.Device>();
|
||||
var result = await _context.Connection.QueryAsync<Model.Device, Model.Prop, Model.Device>(
|
||||
|
@ -96,7 +107,9 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
p.""Type"" as {nameof(Model.Prop.Type)}
|
||||
FROM ""Yuna_Devices"" d
|
||||
LEFT JOIN ""Yuna_Props_In_Devices"" pd ON d.""Id"" = pd.""DeviceId""
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""";
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""
|
||||
WHERE NOT d.""IsDeleted""
|
||||
ORDER BY d.""Id""";
|
||||
|
||||
var deviceDict = new Dictionary<long, Model.Device>();
|
||||
var result = await _context.Connection.QueryAsync<Model.Device, Model.Prop, Model.Device>(
|
||||
|
@ -135,7 +148,9 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
FROM ""Yuna_Devices"" d
|
||||
LEFT JOIN ""Yuna_Props_In_Devices"" pd ON d.""Id"" = pd.""DeviceId""
|
||||
LEFT JOIN ""Yuna_Props"" p ON pd.""PropId"" = p.""Id""
|
||||
WHERE d.""UserId"" = {userId};";
|
||||
WHERE d.""UserId"" = {userId}
|
||||
AND NOT d.""IsDeleted""
|
||||
ORDER BY d.""Id""";
|
||||
|
||||
var deviceDict = new Dictionary<long, Model.Device>();
|
||||
var result = await _context.Connection.QueryAsync<Model.Device, Model.Prop, Model.Device>(
|
||||
|
@ -156,5 +171,22 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
|
||||
return result.Distinct().ToList();
|
||||
}
|
||||
|
||||
public async Task<Model.Device?> Update(Model.Device device)
|
||||
{
|
||||
var query =
|
||||
$@"
|
||||
UPDATE public.""Yuna_Devices""
|
||||
SET ""Name"" = @Name,
|
||||
""Description"" = @Description,
|
||||
""DeviceUrl"" = @DeviceUrl,
|
||||
""UserId"" = @UserId
|
||||
WHERE ""Id"" = @Id
|
||||
AND NOT ""IsDeleted""
|
||||
returning *
|
||||
";
|
||||
|
||||
return await _context.Connection.QuerySingleOrDefaultAsync<Model.Device>(query, device);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace Yuna.Website.Server.Storage.Repositories.Device
|
|||
public Task<IReadOnlyList<Model.Device>> GetList();
|
||||
public Task<IReadOnlyList<Model.Device>> GetList(long userId);
|
||||
public Task<Model.Device?> Create(Model.Device device);
|
||||
//public Task<User?> Update(User user);
|
||||
public Task<Model.Device?> Update(Model.Device device);
|
||||
public Task<Model.Device?> Delete(long id);
|
||||
public Task AddProps(IReadOnlyList<Model.Prop> skills, long deviceId);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import { Header } from "../../components/Header"
|
||||
import { DeviceList } from "./components/DeviceList";
|
||||
import { EditModal } from "./components/modals/EditModal";
|
||||
|
||||
export const HomePage = () => {
|
||||
return (
|
||||
<>
|
||||
|
||||
<Header />
|
||||
<DeviceList />
|
||||
<EditModal />
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -2,8 +2,10 @@ import { useEffect, useMemo } from "react";
|
|||
import "../../../resources/styles/home.scss"
|
||||
import { IDeviceDto, IPropDto } from "../types";
|
||||
import { DeviceCardStore } from "../model/DeviceCardStore";
|
||||
import { Button, Center, Skeleton, useInterval } from "@chakra-ui/react";
|
||||
import { Button, Center, IconButton, Skeleton, Spacer, useInterval } from "@chakra-ui/react";
|
||||
import { observer } from "mobx-react";
|
||||
import { MdOutlineDelete, MdOutlineSettings } from "react-icons/md";
|
||||
import { homePageStore } from "../model/HomePageStore";
|
||||
|
||||
|
||||
|
||||
|
@ -36,11 +38,25 @@ export const DeviceCard = observer((props: { data: IDeviceDto }) => {
|
|||
<div className="device_card">
|
||||
<div className="h1_container">
|
||||
<h1>{props.data.name}</h1>
|
||||
<Button>sdfs</Button>
|
||||
<div className="button_container">
|
||||
<IconButton
|
||||
colorScheme="red"
|
||||
onClick={() => homePageStore.deleteDevice(props.data.id)}
|
||||
icon={<MdOutlineDelete />}
|
||||
aria-label={"delete"}
|
||||
fontSize={"90%"}
|
||||
size={'sm'} />
|
||||
<IconButton
|
||||
onClick={() => homePageStore.setEditModalOpened(props.data.id, true)}
|
||||
icon={<MdOutlineSettings />}
|
||||
aria-label={"edit"}
|
||||
fontSize={"90%"}
|
||||
size={'sm'} />
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ textAlign: 'center' }}>{props.data.description}</p>
|
||||
<ul>
|
||||
{getPropsList(props.data.props, deviceCardStore.isLoading)}
|
||||
{getPropsList(props.data.props ?? [], deviceCardStore.isLoading)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import { observer } from "mobx-react";
|
||||
import { homePageStore } from "../../model/HomePageStore";
|
||||
import { Button, Center, Input, Modal, ModalBody, ModalCloseButton, ModalContent, ModalHeader, ModalOverlay, Stack, Textarea } from "@chakra-ui/react";
|
||||
import { IDeviceDto } from "../../types";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export const EditModal = observer(() => {
|
||||
const currentDevice = homePageStore.currentEditDevice
|
||||
|
||||
const [name, setName] = useState(currentDevice?.name);
|
||||
const [description, setDescription] = useState(currentDevice?.description);
|
||||
const [url, setUrl] = useState(currentDevice?.deviceUrl);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
setName(currentDevice?.name);
|
||||
setDescription(currentDevice?.description);
|
||||
setUrl(currentDevice?.deviceUrl)
|
||||
},
|
||||
[currentDevice]
|
||||
)
|
||||
|
||||
const onUpdateDevice = () => {
|
||||
homePageStore.setEditModalOpened(currentDevice!.id, false);
|
||||
homePageStore.updateDevice({
|
||||
id: currentDevice?.id,
|
||||
name: name,
|
||||
description: description,
|
||||
deviceUrl: url
|
||||
} as IDeviceDto);
|
||||
}
|
||||
|
||||
return (<Modal
|
||||
onClose={() => homePageStore.setEditModalOpened(homePageStore.editModalCurrentId!, false)}
|
||||
isOpen={homePageStore.isEditModalOpened}
|
||||
isCentered
|
||||
blockScrollOnMount={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>Редактировать</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Stack spacing={3}>
|
||||
<label>Название:</label>
|
||||
<Input
|
||||
value={name ?? ""}
|
||||
onChange={e => setName(e.target.value)}
|
||||
disabled={homePageStore.isLoading} />
|
||||
<label>Описание:</label>
|
||||
<Textarea
|
||||
value={description ?? ""}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
disabled={homePageStore.isLoading} />
|
||||
<label>URL:</label>
|
||||
<Input
|
||||
value={url ?? ""}
|
||||
onChange={e => setUrl(e.target.value)}
|
||||
disabled={homePageStore.isLoading} />
|
||||
</Stack>
|
||||
<Center>
|
||||
<Button
|
||||
colorScheme="green"
|
||||
marginTop={10}
|
||||
onClick={() => onUpdateDevice()}
|
||||
isLoading={homePageStore.isLoading}>Сохранить</Button>
|
||||
</Center>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>);
|
||||
});
|
|
@ -1,15 +1,29 @@
|
|||
import { makeAutoObservable } from "mobx";
|
||||
import { IDeviceDto } from "../types";
|
||||
import { HomePageService } from "../services/HomePageService";
|
||||
import { error, info } from "../../../utils/ToastHelper";
|
||||
|
||||
class HomePageStore {
|
||||
|
||||
devices: IDeviceDto[] = []
|
||||
isLoading: boolean = false;
|
||||
isEditModalOpened: boolean = false;
|
||||
editModalCurrentId: number | null = null;
|
||||
currentEditDevice: IDeviceDto | undefined;
|
||||
|
||||
|
||||
constructor() {
|
||||
makeAutoObservable(this);
|
||||
}
|
||||
|
||||
setEditModalOpened(id: number, isOpened: boolean): void {
|
||||
this.currentEditDevice = { ...this.devices.find(device => device.id === id) } as IDeviceDto;
|
||||
this.isEditModalOpened = isOpened;
|
||||
}
|
||||
|
||||
setCurrentEditDevice(value: IDeviceDto | undefined) {
|
||||
this.currentEditDevice = value;
|
||||
}
|
||||
|
||||
setDevices(value: IDeviceDto[]) {
|
||||
this.devices = value;
|
||||
|
@ -21,6 +35,29 @@ class HomePageStore {
|
|||
this.setDevices(result);
|
||||
this.isLoading = false;
|
||||
}
|
||||
|
||||
async updateDevice(value: IDeviceDto) {
|
||||
const result = await HomePageService.updateDevice(value);
|
||||
if (result === null) {
|
||||
error("Ошибка", "Запрос не был выполнен");
|
||||
return;
|
||||
}
|
||||
|
||||
this.setCurrentEditDevice(undefined);
|
||||
info("Успешно", "Изменения были сохранены")
|
||||
await this.loadDevices();
|
||||
}
|
||||
|
||||
async deleteDevice(value: number) {
|
||||
const result = await HomePageService.deleteDevice(value);
|
||||
if (result === null) {
|
||||
error("Ошибка", "Запрос не был выполнен");
|
||||
return;
|
||||
}
|
||||
|
||||
info("Успешно", "Устройство было удалено");
|
||||
await this.loadDevices();
|
||||
}
|
||||
}
|
||||
|
||||
export const homePageStore = new HomePageStore();
|
|
@ -21,4 +21,30 @@ export class HomePageService {
|
|||
if (response.status === 200) return response.data;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async updateDevice(dto: IDeviceDto): Promise<IDeviceDto | null> {
|
||||
try {
|
||||
const response = await api.put<IDeviceDto>(`device`, dto);
|
||||
if (response.status === 200) return response.data;
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
console.error("Error: ", error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static async deleteDevice(id: number): Promise<IDeviceDto | null> {
|
||||
try {
|
||||
const response = await api.delete<IDeviceDto>(`device/${id}`);
|
||||
if (response.status === 200) return response.data;
|
||||
}
|
||||
|
||||
catch (error) {
|
||||
console.error("Error: ", error);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
export interface IDeviceDto {
|
||||
id: number;
|
||||
name: string;
|
||||
description: string;
|
||||
props: IPropDto[]
|
||||
deviceUrl: string;
|
||||
description?: string;
|
||||
props?: IPropDto[]
|
||||
deviceUrl?: string;
|
||||
}
|
||||
|
||||
export enum propType {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
width: 80%;
|
||||
height: 70px;
|
||||
box-shadow: 5px 5px 0px #319795;
|
||||
z-index: 1;
|
||||
/* x-offset y-offset blur-radius color */
|
||||
|
||||
h1 {
|
||||
|
|
|
@ -15,19 +15,26 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
padding: 0px 50px;
|
||||
padding: 0px 0px;
|
||||
|
||||
.h1_container {
|
||||
overflow: hidden;
|
||||
height: 80px;
|
||||
|
||||
h1 {}
|
||||
|
||||
button {
|
||||
position: relative;
|
||||
top: -50px;
|
||||
width: 10px;
|
||||
float: right;
|
||||
overflow: visible;
|
||||
width: 100%;
|
||||
|
||||
h1 {
|
||||
max-width: 450px;
|
||||
display: block;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
.button_container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 5px;
|
||||
position: absolute;
|
||||
top: 25px;
|
||||
right: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"mobx-react": "^9.1.1"
|
||||
"mobx-react": "^9.1.1",
|
||||
"react-icons": "^5.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/js-tokens": {
|
||||
|
@ -96,6 +97,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.2.1.tgz",
|
||||
"integrity": "sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"dependencies": {
|
||||
"mobx-react": "^9.1.1"
|
||||
"mobx-react": "^9.1.1",
|
||||
"react-icons": "^5.2.1"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue