[FEAT] continue integration

This commit is contained in:
Edouard DUPIN 2024-09-12 00:34:28 +02:00
parent 091390e025
commit dc7131843d
25 changed files with 1004 additions and 194 deletions

View File

@ -25,12 +25,12 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
<version>2.1.0-alpha1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.16.1</version>
<version>2.18.0-rc1</version>
</dependency>
<!--
************************************************************
@ -40,24 +40,24 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.1</version>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.1</version>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId>
<version>2.24.0</version>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.3.1</version>
<version>3.5.0</version>
</dependency>
</dependencies>
<build>

View File

@ -1,6 +1,11 @@
package org.kar.karusic;
import java.net.URI;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.ImageWriter;
import org.glassfish.grizzly.http.server.HttpServer;
import org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpServerFactory;
@ -10,6 +15,7 @@ import org.glassfish.jersey.server.ResourceConfig;
import org.kar.archidata.GlobalConfiguration;
import org.kar.archidata.UpdateJwtPublicKey;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.catcher.GenericCatcher;
import org.kar.archidata.filter.CORSFilter;
import org.kar.archidata.filter.OptionFilter;
@ -28,6 +34,7 @@ import org.kar.karusic.migration.Initialization;
import org.kar.karusic.migration.Migration20231126;
import org.kar.karusic.migration.Migration20240225;
import org.kar.karusic.migration.Migration20240226;
import org.kar.karusic.migration.Migration20240907;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -55,6 +62,7 @@ public class WebLauncher {
migrationEngine.add(new Migration20231126());
migrationEngine.add(new Migration20240225());
migrationEngine.add(new Migration20240226());
migrationEngine.add(new Migration20240907());
WebLauncher.LOGGER.info("Migrate the DB [START]");
migrationEngine.migrateWaitAdmin(GlobalConfiguration.dbConfig);
WebLauncher.LOGGER.info("Migrate the DB [STOP]");
@ -64,7 +72,7 @@ public class WebLauncher {
WebLauncher.LOGGER.info("[START] application wake UP");
final WebLauncher launcher = new WebLauncher();
launcher.migrateDB();
launcher.process();
WebLauncher.LOGGER.info("end-configure the server & wait finish process:");
Thread.currentThread().join();
@ -73,7 +81,34 @@ public class WebLauncher {
WebLauncher.LOGGER.info("STOP the REST server:");
}
public void plop(final String aaa) {
// List available Image Readers
System.out.println("Available Image Readers:");
final Iterator<ImageReader> readers = ImageIO.getImageReadersByFormatName(aaa);
while (readers.hasNext()) {
final ImageReader reader = readers.next();
System.out.println("Reader: " + reader.getOriginatingProvider().getDescription(null));
System.out.println("Reader CN: " + reader.getOriginatingProvider().getPluginClassName());
//ImageIO.deregisterServiceProvider(reader.getOriginatingProvider());
}
// List available Image Writers
System.out.println("\nAvailable Image Writers:");
final Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(aaa);
while (writers.hasNext()) {
final ImageWriter writer = writers.next();
System.out.println("Writer: " + writer.getOriginatingProvider().getDescription(null));
System.out.println("Writer CN: " + writer.getOriginatingProvider().getPluginClassName());
}
}
public void process() throws InterruptedException {
ImageIO.scanForPlugins();
plop("jpeg");
plop("png");
plop("webmp");
plop("webp");
// ===================================================================
// Configure resources
// ===================================================================
@ -97,6 +132,7 @@ public class WebLauncher {
rc.register(PlaylistResource.class);
rc.register(TrackResource.class);
rc.register(DataResource.class);
rc.register(ProxyResource.class);
rc.register(HealthCheck.class);
rc.register(Front.class);

View File

@ -3,6 +3,7 @@ package org.kar.karusic;
import java.util.List;
import org.kar.archidata.api.DataResource;
import org.kar.archidata.api.ProxyResource;
import org.kar.archidata.externalRestApi.AnalyzeApi;
import org.kar.archidata.externalRestApi.TsGenerateApi;
import org.kar.archidata.tools.ConfigBaseVariable;
@ -19,20 +20,20 @@ import org.slf4j.LoggerFactory;
public class WebLauncherLocal extends WebLauncher {
private static final Logger LOGGER = LoggerFactory.getLogger(WebLauncherLocal.class);
private WebLauncherLocal() {}
public static void generateObjects() throws Exception {
LOGGER.info("Generate APIs");
final List<Class<?>> listOfResources = List.of(AlbumResource.class, ArtistResource.class, Front.class,
GenderResource.class, HealthCheck.class, PlaylistResource.class, UserResource.class,
TrackResource.class, DataResource.class);
TrackResource.class, DataResource.class, ProxyResource.class);
final AnalyzeApi api = new AnalyzeApi();
api.addAllApi(listOfResources);
TsGenerateApi.generateApi(api, "../front2/src/back-api/");
LOGGER.info("Generate APIs (DONE)");
}
public static void main(final String[] args) throws Exception {
generateObjects();
final WebLauncherLocal launcher = new WebLauncherLocal();
@ -41,7 +42,7 @@ public class WebLauncherLocal extends WebLauncher {
Thread.currentThread().join();
launcher.LOGGER.info("STOP the REST server:");
}
@Override
public void process() throws InterruptedException {
if (true) {

View File

@ -100,13 +100,18 @@ public class AlbumResource {
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@Operation(description = "Add a cover on a specific album")
@AsyncType(Album.class)
@TypeScriptProgress
public void uploadCover(
public Album uploadCover(
@PathParam("id") final Long id,
@FormDataParam("uri") final String uri,
@FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Album.class, id, fileInputStream, fileMetaData);
if (uri != null) {
DataTools.uploadCoverFromUri(Album.class, id, uri);
} else {
DataTools.uploadCover(Album.class, id, fileInputStream, fileMetaData);
}
return DataAccess.get(Album.class, id);
}
@DELETE

View File

@ -30,27 +30,27 @@ import jakarta.ws.rs.core.MediaType;
@Produces({ MediaType.APPLICATION_JSON })
public class ArtistResource {
private static final Logger LOGGER = LoggerFactory.getLogger(ArtistResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Artist get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Artist.class, id);
}
@GET
@RolesAllowed("USER")
public List<Artist> gets() throws Exception {
return DataAccess.gets(Artist.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Artist post(@AsyncType(Artist.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Artist.class, jsonRequest);
}
@PATCH
@Path("{id}")
@RolesAllowed("ADMIN")
@ -60,27 +60,32 @@ public class ArtistResource {
DataAccess.updateWithJson(Artist.class, id, jsonRequest);
return DataAccess.get(Artist.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Artist.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Artist.class)
@TypeScriptProgress
public void uploadCover(
public Artist uploadCover(
@PathParam("id") final Long id,
@FormDataParam("uri") final String uri,
@FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Artist.class, id, fileInputStream, fileMetaData);
if (uri != null) {
DataTools.uploadCoverFromUri(Artist.class, id, uri);
} else {
DataTools.uploadCover(Artist.class, id, fileInputStream, fileMetaData);
}
return DataAccess.get(Artist.class, id);
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")

View File

@ -72,13 +72,18 @@ public class GenderResource {
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Gender.class)
@TypeScriptProgress
public void uploadCover(
public Gender uploadCover(
@PathParam("id") final Long id,
@FormDataParam("uri") final String uri,
@FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Gender.class, id, fileInputStream, fileMetaData);
if (uri != null) {
DataTools.uploadCoverFromUri(Gender.class, id, uri);
} else {
DataTools.uploadCover(Gender.class, id, fileInputStream, fileMetaData);
}
return DataAccess.get(Gender.class, id);
}
@DELETE

View File

@ -41,27 +41,27 @@ import jakarta.ws.rs.core.Response;
@Produces({ MediaType.APPLICATION_JSON })
public class TrackResource {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackResource.class);
@GET
@Path("{id}")
@RolesAllowed("USER")
public static Track get(@PathParam("id") final Long id) throws Exception {
return DataAccess.get(Track.class, id);
}
@GET
@RolesAllowed("USER")
public List<Track> gets() throws Exception {
return DataAccess.gets(Track.class);
}
@POST
@RolesAllowed("ADMIN")
@Consumes(MediaType.APPLICATION_JSON)
public Track post(@AsyncType(Track.class) final String jsonRequest) throws Exception {
return DataAccess.insertWithJson(Track.class, jsonRequest);
}
@PATCH
@Path("{id}")
@RolesAllowed("ADMIN")
@ -71,14 +71,14 @@ public class TrackResource {
DataAccess.updateWithJson(Track.class, id, jsonRequest);
return DataAccess.get(Track.class, id);
}
@DELETE
@Path("{id}")
@RolesAllowed("ADMIN")
public void remove(@PathParam("id") final Long id) throws Exception {
DataAccess.delete(Track.class, id);
}
@POST
@Path("{id}/artist/{artistId}")
@RolesAllowed("ADMIN")
@ -87,7 +87,7 @@ public class TrackResource {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId);
return DataAccess.get(Track.class, id);
}
@DELETE
@Path("{id}/artist/{trackId}")
@RolesAllowed("ADMIN")
@ -96,20 +96,25 @@ public class TrackResource {
AddOnManyToMany.removeLink(Track.class, id, "artist", artistId);
return DataAccess.get(Track.class, id);
}
@POST
@Path("{id}/cover")
@RolesAllowed("ADMIN")
@Consumes({ MediaType.MULTIPART_FORM_DATA })
@AsyncType(Track.class)
@TypeScriptProgress
public void uploadCover(
public Track uploadCover(
@PathParam("id") final Long id,
@FormDataParam("uri") final String uri,
@FormDataParam("file") final InputStream fileInputStream,
@FormDataParam("file") final FormDataContentDisposition fileMetaData) throws Exception {
DataTools.uploadCover(Track.class, id, fileInputStream, fileMetaData);
if (uri != null) {
DataTools.uploadCoverFromUri(Track.class, id, uri);
} else {
DataTools.uploadCover(Track.class, id, fileInputStream, fileMetaData);
}
return DataAccess.get(Track.class, id);
}
@DELETE
@Path("{id}/cover/{coverId}")
@RolesAllowed("ADMIN")
@ -118,7 +123,7 @@ public class TrackResource {
AddOnDataJson.removeLink(Track.class, id, "covers", coverId);
return DataAccess.get(Track.class, id);
}
@POST
@Path("upload/")
@RolesAllowed("ADMIN")
@ -146,7 +151,7 @@ public class TrackResource {
album = DataTools.multipartCorrection(album);
trackId = DataTools.multipartCorrection(trackId);
title = DataTools.multipartCorrection(title);
//public NodeSmall uploadFile(final FormDataMultiPart form) {
LOGGER.info("Upload media file: " + fileMetaData);
LOGGER.info(" > fileName: " + fileName);
@ -165,13 +170,13 @@ public class TrackResource {
build();
}
*/
final long tmpUID = DataTools.getTmpDataId();
final String sha512 = DataTools.saveTemporaryFile(fileInputStream, tmpUID);
Data data = DataTools.getWithSha512(sha512);
if (data == null) {
LOGGER.info("Need to add the data in the BDD ... ");
try {
data = DataTools.createNewData(tmpUID, fileName, sha512);
} catch (final IOException ex) {
@ -207,7 +212,7 @@ public class TrackResource {
// return Response.notModified("TypeId does not exist ...").build();
// }
LOGGER.info(" ==> genderElem={}", genderElem);
Artist artistElem = null;
if (artist != null) {
LOGGER.info(" Try to find Artist: '{}'", artist);
@ -220,7 +225,7 @@ public class TrackResource {
}
}
LOGGER.info(" ==> artistElem={}", artistElem);
Album albumElem = null;
if (album != null) {
albumElem = DataAccess.getWhere(Album.class, new Condition(new QueryCondition("name", "=", album)));
@ -231,9 +236,9 @@ public class TrackResource {
}
}
LOGGER.info(" ==> " + album);
LOGGER.info("add media");
Track trackElem = new Track();
trackElem.name = title;
trackElem.track = trackId != null ? Long.parseLong(trackId) : null;
@ -259,5 +264,5 @@ public class TrackResource {
return Response.status(417).entity("Back-end error : " + ex.getMessage()).type("text/plain").build();
}
}
}

View File

@ -17,6 +17,29 @@ services:
start_period: 1m
start_interval: 1m
kar_mongodb_service:
image: mongo:latest
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: base_db_password
ports:
- 27017:27017
volumes:
- ./dataMongo:/data/db
mongo_express_service:
image: mongo-express
restart: always
ports:
- 8081:8081
links:
- kar_mongodb_service:db
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: base_db_password
ME_CONFIG_MONGODB_URL: mongodb://root:base_db_password@db:27017/
ME_CONFIG_BASICAUTH: false
kar_adminer_service:
image: adminer:latest
restart: always
@ -25,7 +48,7 @@ services:
condition: service_healthy
links:
- kar_db_service:db
- kar_mongodb_service:dbm
ports:
- 4079:8080
mem_limit: 50m

View File

@ -41,6 +41,9 @@ importers:
'@formiz/core':
specifier: 2.3.1
version: 2.3.1(react@18.3.1)
'@formiz/validations':
specifier: 2.0.1
version: 2.0.1
allotment:
specifier: 1.20.2
version: 1.20.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@ -71,6 +74,9 @@ importers:
react-dom:
specifier: 18.3.1
version: 18.3.1(react@18.3.1)
react-dropzone:
specifier: 14.2.3
version: 14.2.3(react@18.3.1)
react-error-boundary:
specifier: 4.0.13
version: 4.0.13(react@18.3.1)
@ -102,8 +108,8 @@ importers:
specifier: 0.4.7
version: 0.4.7(react@18.3.1)
react-virtuoso:
specifier: 4.10.1
version: 4.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
specifier: 4.10.2
version: 4.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ts-pattern:
specifier: 5.3.1
version: 5.3.1
@ -140,7 +146,7 @@ importers:
version: 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)
'@storybook/react-vite':
specifier: 8.2.9
version: 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.21.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))
version: 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.21.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))
'@storybook/theming':
specifier: 8.2.9
version: 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))
@ -160,8 +166,8 @@ importers:
specifier: 29.5.12
version: 29.5.12
'@types/node':
specifier: 22.5.1
version: 22.5.1
specifier: 22.5.2
version: 22.5.2
'@types/react':
specifier: 18.3.5
version: 18.3.5
@ -179,7 +185,7 @@ importers:
version: 8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)
'@vitejs/plugin-react':
specifier: 4.3.1
version: 4.3.1(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))
version: 4.3.1(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))
eslint:
specifier: 9.9.1
version: 9.9.1(jiti@1.21.6)
@ -200,13 +206,13 @@ importers:
version: 0.8.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)
jest:
specifier: 29.7.0
version: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
version: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
jest-environment-jsdom:
specifier: 29.7.0
version: 29.7.0
knip:
specifier: 5.27.5
version: 5.27.5(@types/node@22.5.1)(typescript@5.5.4)
specifier: 5.29.1
version: 5.29.1(@types/node@22.5.2)(typescript@5.5.4)
lint-staged:
specifier: 15.2.9
version: 15.2.9
@ -227,16 +233,16 @@ importers:
version: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7))
ts-node:
specifier: 10.9.2
version: 10.9.2(@types/node@22.5.1)(typescript@5.5.4)
version: 10.9.2(@types/node@22.5.2)(typescript@5.5.4)
typescript:
specifier: 5.5.4
version: 5.5.4
vite:
specifier: 5.4.2
version: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
version: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
vitest:
specifier: 2.0.5
version: 2.0.5(@types/node@22.5.1)(jsdom@20.0.3)(terser@5.31.1)
version: 2.0.5(@types/node@22.5.2)(jsdom@20.0.3)(terser@5.31.1)
packages:
@ -1824,6 +1830,9 @@ packages:
peerDependencies:
react: '>=18'
'@formiz/validations@2.0.1':
resolution: {integrity: sha512-NZGluw3iWt7zw3oTiOwnttg7DCgB1SR0AMWVpdTYsaXDS54HwukwteaUqxii/FeGNliuRLYxtawnhMwa8YIhyA==}
'@humanwhocodes/module-importer@1.0.1':
resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
engines: {node: '>=12.22'}
@ -2419,8 +2428,8 @@ packages:
'@types/node@18.19.39':
resolution: {integrity: sha512-nPwTRDKUctxw3di5b4TfT3I0sWDiWoPQCZjXhvdkINntwr8lcoVCKsTgnXeRubKIlfnV+eN/HYk6Jb40tbcEAQ==}
'@types/node@22.5.1':
resolution: {integrity: sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==}
'@types/node@22.5.2':
resolution: {integrity: sha512-acJsPTEqYqulZS/Yp/S3GgeE6GZ0qYODUR8aVr/DkhHQ8l9nd4j5x1/ZJy9/gHrRlFMqkO6i0I3E27Alu4jjPg==}
'@types/parse-json@4.0.2':
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
@ -2774,6 +2783,10 @@ packages:
asynckit@0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
attr-accept@2.2.2:
resolution: {integrity: sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==}
engines: {node: '>=4'}
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@ -3676,6 +3689,10 @@ packages:
resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
engines: {node: '>=16.0.0'}
file-selector@0.6.0:
resolution: {integrity: sha512-QlZ5yJC0VxHxQQsQhXvBaC7VRJ2uaxTf+Tfpu4Z/OcVQJVpZO+DGU0rkoVW5ce2SccxugvpBJoMvUs59iILYdw==}
engines: {node: '>= 12'}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
@ -4455,8 +4472,8 @@ packages:
resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
engines: {node: '>=6'}
knip@5.27.5:
resolution: {integrity: sha512-YkhFv/udMNGf6YUPU6enbDH2iPi7jq5d3K6egS0A2Qqk3M7+CuSF44f1uwQZH9chCyi3JJoJNxqyB8B0m/guxw==}
knip@5.29.1:
resolution: {integrity: sha512-l8qFtRqNpCk8xf46VOwhBUva7LBwanoGPJ4KQNwVRl6hmEXStf1BJlfbYRZ+yQpbilbIV6LN+ztX6LaGtyd4TQ==}
engines: {node: '>=18.6.0'}
hasBin: true
peerDependencies:
@ -4731,10 +4748,6 @@ packages:
micromark@4.0.0:
resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==}
micromatch@4.0.5:
resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
engines: {node: '>=8.6'}
micromatch@4.0.7:
resolution: {integrity: sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==}
engines: {node: '>=8.6'}
@ -5242,6 +5255,12 @@ packages:
peerDependencies:
react: ^18.3.1
react-dropzone@14.2.3:
resolution: {integrity: sha512-O3om8I+PkFKbxCukfIR3QAGftYXDZfOE2N1mr/7qebQJHs7U+/RSL/9xomJNpRg9kM5h9soQSdf0Gc7OHF5Fug==}
engines: {node: '>= 10.13'}
peerDependencies:
react: '>= 16.8 || 18.0.0'
react-element-to-jsx-string@15.0.0:
resolution: {integrity: sha512-UDg4lXB6BzlobN60P8fHWVPX3Kyw8ORrTeBtClmIlGdkOOE+GYQSFvmEU5iLLpwp/6v42DINwNcwOhOLfQ//FQ==}
peerDependencies:
@ -5378,8 +5397,8 @@ packages:
react: '*'
react-dom: '*'
react-virtuoso@4.10.1:
resolution: {integrity: sha512-vDBt9AarmCjPNshw3VxPXW355ZQKSO0p9vrAJ0pi04TB6aXk+qHWTu8NuaQ3ppcd/Ub1r5ryRA4fJ2QGuH0H0g==}
react-virtuoso@4.10.2:
resolution: {integrity: sha512-os6n9QKeKRF+8mnQR/vGy/xrFf6vXIzuaAVL54q5k2st2d5QIEwI+KDKaflMUmMvnDbPxf68bs+CF5bY3YI7qA==}
engines: {node: '>=10'}
peerDependencies:
react: '>=16 || >=17 || >= 18'
@ -8383,6 +8402,8 @@ snapshots:
dependencies:
react: 18.3.1
'@formiz/validations@2.0.1': {}
'@humanwhocodes/module-importer@1.0.1': {}
'@humanwhocodes/retry@0.3.0': {}
@ -8400,27 +8421,27 @@ snapshots:
'@jest/console@29.7.0':
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
jest-message-util: 29.7.0
jest-util: 29.7.0
slash: 3.0.0
'@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))':
'@jest/core@29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))':
dependencies:
'@jest/console': 29.7.0
'@jest/reporters': 29.7.0
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
ansi-escapes: 4.3.2
chalk: 4.1.2
ci-info: 3.9.0
exit: 0.1.2
graceful-fs: 4.2.11
jest-changed-files: 29.7.0
jest-config: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
jest-haste-map: 29.7.0
jest-message-util: 29.7.0
jest-regex-util: 29.6.3
@ -8445,7 +8466,7 @@ snapshots:
dependencies:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-mock: 29.7.0
'@jest/expect-utils@29.7.0':
@ -8463,7 +8484,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@sinonjs/fake-timers': 10.3.0
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-message-util: 29.7.0
jest-mock: 29.7.0
jest-util: 29.7.0
@ -8485,7 +8506,7 @@ snapshots:
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@jridgewell/trace-mapping': 0.3.25
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
collect-v8-coverage: 1.0.2
exit: 0.1.2
@ -8555,17 +8576,17 @@ snapshots:
'@jest/schemas': 29.6.3
'@types/istanbul-lib-coverage': 2.0.6
'@types/istanbul-reports': 3.0.4
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/yargs': 17.0.32
chalk: 4.1.2
'@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))':
'@joshwooding/vite-plugin-react-docgen-typescript@0.3.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))':
dependencies:
glob: 7.2.3
glob-promise: 4.2.2(glob@7.2.3)
magic-string: 0.27.0
react-docgen-typescript: 2.2.2(typescript@5.5.4)
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
optionalDependencies:
typescript: 5.5.4
@ -8834,7 +8855,7 @@ snapshots:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
'@storybook/builder-vite@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))':
'@storybook/builder-vite@8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))':
dependencies:
'@storybook/csf-plugin': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))
'@types/find-cache-dir': 3.2.1
@ -8846,7 +8867,7 @@ snapshots:
magic-string: 0.30.10
storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7))
ts-dedent: 2.2.0
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
optionalDependencies:
typescript: 5.5.4
transitivePeerDependencies:
@ -8928,11 +8949,11 @@ snapshots:
react-dom: 18.3.1(react@18.3.1)
storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7))
'@storybook/react-vite@8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.21.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))':
'@storybook/react-vite@8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.21.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))':
dependencies:
'@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))
'@joshwooding/vite-plugin-react-docgen-typescript': 0.3.1(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))
'@rollup/pluginutils': 5.1.0(rollup@4.21.1)
'@storybook/builder-vite': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))
'@storybook/builder-vite': 8.2.9(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))
'@storybook/react': 8.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7)))(typescript@5.5.4)
find-up: 5.0.0
magic-string: 0.30.10
@ -8942,7 +8963,7 @@ snapshots:
resolve: 1.22.8
storybook: 8.2.9(@babel/preset-env@7.24.7(@babel/core@7.24.7))
tsconfig-paths: 4.2.0
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
transitivePeerDependencies:
- '@preact/preset-vite'
- rollup
@ -9068,15 +9089,15 @@ snapshots:
'@types/body-parser@1.19.5':
dependencies:
'@types/connect': 3.4.38
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/connect@3.4.38':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/cross-spawn@6.0.6':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/debug@4.1.12':
dependencies:
@ -9094,7 +9115,7 @@ snapshots:
'@types/express-serve-static-core@4.19.5':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/qs': 6.9.15
'@types/range-parser': 1.2.7
'@types/send': 0.17.4
@ -9111,11 +9132,11 @@ snapshots:
'@types/glob@7.2.0':
dependencies:
'@types/minimatch': 5.1.2
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/graceful-fs@4.1.9':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/hast@3.0.4':
dependencies:
@ -9142,7 +9163,7 @@ snapshots:
'@types/jsdom@20.0.1':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/tough-cookie': 4.0.5
parse5: 7.1.2
@ -9172,7 +9193,7 @@ snapshots:
dependencies:
undici-types: 5.26.5
'@types/node@22.5.1':
'@types/node@22.5.2':
dependencies:
undici-types: 6.19.8
@ -9208,12 +9229,12 @@ snapshots:
'@types/send@0.17.4':
dependencies:
'@types/mime': 1.3.5
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/serve-static@1.15.7':
dependencies:
'@types/http-errors': 2.0.4
'@types/node': 22.5.1
'@types/node': 22.5.2
'@types/send': 0.17.4
'@types/stack-utils@2.0.3': {}
@ -9232,7 +9253,7 @@ snapshots:
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
optional: true
'@typescript-eslint/eslint-plugin@8.3.0(@typescript-eslint/parser@8.3.0(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4))(eslint@9.9.1(jiti@1.21.6))(typescript@5.5.4)':
@ -9359,14 +9380,14 @@ snapshots:
'@ungap/structured-clone@1.2.0': {}
'@vitejs/plugin-react@4.3.1(vite@5.4.2(@types/node@22.5.1)(terser@5.31.1))':
'@vitejs/plugin-react@4.3.1(vite@5.4.2(@types/node@22.5.2)(terser@5.31.1))':
dependencies:
'@babel/core': 7.24.7
'@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.7)
'@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.7)
'@types/babel__core': 7.20.5
react-refresh: 0.14.2
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
transitivePeerDependencies:
- supports-color
@ -9616,6 +9637,8 @@ snapshots:
asynckit@0.4.0: {}
attr-accept@2.2.2: {}
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
@ -10020,13 +10043,13 @@ snapshots:
optionalDependencies:
typescript: 5.5.4
create-jest@29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4)):
create-jest@29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/types': 29.6.3
chalk: 4.1.2
exit: 0.1.2
graceful-fs: 4.2.11
jest-config: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
jest-util: 29.7.0
prompts: 2.4.2
transitivePeerDependencies:
@ -10695,7 +10718,7 @@ snapshots:
'@nodelib/fs.walk': 1.2.8
glob-parent: 5.1.2
merge2: 1.4.1
micromatch: 4.0.5
micromatch: 4.0.7
fast-json-stable-stringify@2.1.0: {}
@ -10725,6 +10748,10 @@ snapshots:
dependencies:
flat-cache: 4.0.1
file-selector@0.6.0:
dependencies:
tslib: 2.6.3
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
@ -11299,7 +11326,7 @@ snapshots:
'@jest/expect': 29.7.0
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
co: 4.6.0
dedent: 1.5.3(babel-plugin-macros@3.1.0)
@ -11319,16 +11346,16 @@ snapshots:
- babel-plugin-macros
- supports-color
jest-cli@29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4)):
jest-cli@29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
chalk: 4.1.2
create-jest: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
create-jest: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
exit: 0.1.2
import-local: 3.1.0
jest-config: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
jest-config: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
jest-util: 29.7.0
jest-validate: 29.7.0
yargs: 17.7.2
@ -11338,7 +11365,7 @@ snapshots:
- supports-color
- ts-node
jest-config@29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4)):
jest-config@29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@babel/core': 7.24.7
'@jest/test-sequencer': 29.7.0
@ -11363,8 +11390,8 @@ snapshots:
slash: 3.0.0
strip-json-comments: 3.1.1
optionalDependencies:
'@types/node': 22.5.1
ts-node: 10.9.2(@types/node@22.5.1)(typescript@5.5.4)
'@types/node': 22.5.2
ts-node: 10.9.2(@types/node@22.5.2)(typescript@5.5.4)
transitivePeerDependencies:
- babel-plugin-macros
- supports-color
@ -11394,7 +11421,7 @@ snapshots:
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/jsdom': 20.0.1
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-mock: 29.7.0
jest-util: 29.7.0
jsdom: 20.0.3
@ -11408,7 +11435,7 @@ snapshots:
'@jest/environment': 29.7.0
'@jest/fake-timers': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-mock: 29.7.0
jest-util: 29.7.0
@ -11418,7 +11445,7 @@ snapshots:
dependencies:
'@jest/types': 29.6.3
'@types/graceful-fs': 4.1.9
'@types/node': 22.5.1
'@types/node': 22.5.2
anymatch: 3.1.3
fb-watchman: 2.0.2
graceful-fs: 4.2.11
@ -11457,7 +11484,7 @@ snapshots:
jest-mock@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-util: 29.7.0
jest-pnp-resolver@1.2.3(jest-resolve@29.7.0):
@ -11492,7 +11519,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
emittery: 0.13.1
graceful-fs: 4.2.11
@ -11520,7 +11547,7 @@ snapshots:
'@jest/test-result': 29.7.0
'@jest/transform': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
cjs-module-lexer: 1.3.1
collect-v8-coverage: 1.0.2
@ -11566,7 +11593,7 @@ snapshots:
jest-util@29.7.0:
dependencies:
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
chalk: 4.1.2
ci-info: 3.9.0
graceful-fs: 4.2.11
@ -11585,7 +11612,7 @@ snapshots:
dependencies:
'@jest/test-result': 29.7.0
'@jest/types': 29.6.3
'@types/node': 22.5.1
'@types/node': 22.5.2
ansi-escapes: 4.3.2
chalk: 4.1.2
emittery: 0.13.1
@ -11594,17 +11621,17 @@ snapshots:
jest-worker@29.7.0:
dependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
jest-util: 29.7.0
merge-stream: 2.0.0
supports-color: 8.1.1
jest@29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4)):
jest@29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4)):
dependencies:
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
'@jest/core': 29.7.0(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
'@jest/types': 29.6.3
import-local: 3.1.0
jest-cli: 29.7.0(@types/node@22.5.1)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4))
jest-cli: 29.7.0(@types/node@22.5.2)(babel-plugin-macros@3.1.0)(ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4))
transitivePeerDependencies:
- '@types/node'
- babel-plugin-macros
@ -11727,11 +11754,11 @@ snapshots:
kleur@3.0.3: {}
knip@5.27.5(@types/node@22.5.1)(typescript@5.5.4):
knip@5.29.1(@types/node@22.5.2)(typescript@5.5.4):
dependencies:
'@nodelib/fs.walk': 1.2.8
'@snyk/github-codeowners': 1.1.0
'@types/node': 22.5.1
'@types/node': 22.5.2
easy-table: 1.2.0
enhanced-resolve: 5.17.1
fast-glob: 3.3.2
@ -12188,11 +12215,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
micromatch@4.0.5:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
micromatch@4.0.7:
dependencies:
braces: 3.0.3
@ -12700,6 +12722,13 @@ snapshots:
react: 18.3.1
scheduler: 0.23.2
react-dropzone@14.2.3(react@18.3.1):
dependencies:
attr-accept: 2.2.2
file-selector: 0.6.0
prop-types: 15.8.1
react: 18.3.1
react-element-to-jsx-string@15.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@base2/pretty-print-object': 1.0.1
@ -12853,7 +12882,7 @@ snapshots:
ts-easing: 0.2.0
tslib: 2.6.3
react-virtuoso@4.10.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
react-virtuoso@4.10.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
@ -13514,14 +13543,14 @@ snapshots:
ts-easing@0.2.0: {}
ts-node@10.9.2(@types/node@22.5.1)(typescript@5.5.4):
ts-node@10.9.2(@types/node@22.5.2)(typescript@5.5.4):
dependencies:
'@cspotcode/source-map-support': 0.8.1
'@tsconfig/node10': 1.0.11
'@tsconfig/node12': 1.0.11
'@tsconfig/node14': 1.0.3
'@tsconfig/node16': 1.0.4
'@types/node': 22.5.1
'@types/node': 22.5.2
acorn: 8.12.0
acorn-walk: 8.3.3
arg: 4.1.3
@ -13773,13 +13802,13 @@ snapshots:
unist-util-stringify-position: 4.0.0
vfile-message: 4.0.2
vite-node@2.0.5(@types/node@22.5.1)(terser@5.31.1):
vite-node@2.0.5(@types/node@22.5.2)(terser@5.31.1):
dependencies:
cac: 6.7.14
debug: 4.3.6
pathe: 1.1.2
tinyrainbow: 1.2.0
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
transitivePeerDependencies:
- '@types/node'
- less
@ -13791,17 +13820,17 @@ snapshots:
- supports-color
- terser
vite@5.4.2(@types/node@22.5.1)(terser@5.31.1):
vite@5.4.2(@types/node@22.5.2)(terser@5.31.1):
dependencies:
esbuild: 0.21.5
postcss: 8.4.41
rollup: 4.21.1
optionalDependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
fsevents: 2.3.3
terser: 5.31.1
vitest@2.0.5(@types/node@22.5.1)(jsdom@20.0.3)(terser@5.31.1):
vitest@2.0.5(@types/node@22.5.2)(jsdom@20.0.3)(terser@5.31.1):
dependencies:
'@ampproject/remapping': 2.3.0
'@vitest/expect': 2.0.5
@ -13819,11 +13848,11 @@ snapshots:
tinybench: 2.8.0
tinypool: 1.0.0
tinyrainbow: 1.2.0
vite: 5.4.2(@types/node@22.5.1)(terser@5.31.1)
vite-node: 2.0.5(@types/node@22.5.1)(terser@5.31.1)
vite: 5.4.2(@types/node@22.5.2)(terser@5.31.1)
vite-node: 2.0.5(@types/node@22.5.2)(terser@5.31.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.5.1
'@types/node': 22.5.2
jsdom: 20.0.3
transitivePeerDependencies:
- less

View File

@ -231,6 +231,7 @@ export namespace AlbumResource {
},
data: {
file: File,
uri: string,
},
callbacks?: RESTCallbacks,
}): Promise<Album> {

View File

@ -162,6 +162,7 @@ export namespace ArtistResource {
},
data: {
file: File,
uri: string,
},
callbacks?: RESTCallbacks,
}): Promise<Artist> {

View File

@ -162,6 +162,7 @@ export namespace GenderResource {
},
data: {
file: File,
uri: string,
},
callbacks?: RESTCallbacks,
}): Promise<Gender> {

View File

@ -8,5 +8,6 @@ export * from "./front"
export * from "./gender-resource"
export * from "./health-check"
export * from "./playlist-resource"
export * from "./proxy-resource"
export * from "./track-resource"
export * from "./user-resource"

View File

@ -0,0 +1,30 @@
/**
* Interface of the server (auto-generated code)
*/
import {
HTTPRequestModel,
RESTConfig,
RESTRequestJson,
} from "../rest-tools";
export namespace ProxyResource {
export function getImageFromUrl({
restConfig,
queries,
}: {
restConfig: RESTConfig,
queries: {
url?: string,
},
}): Promise<object> {
return RESTRequestJson({
restModel: {
endPoint: "/proxy/",
requestType: HTTPRequestModel.GET,
},
restConfig,
queries,
});
};
}

View File

@ -204,6 +204,7 @@ export namespace TrackResource {
},
data: {
file: File,
uri: string,
},
callbacks?: RESTCallbacks,
}): Promise<Track> {

View File

@ -0,0 +1,36 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from "zod";
export const ZodInteger = zod.object({
});
export type Integer = zod.infer<typeof ZodInteger>;
export function isInteger(data: any): data is Integer {
try {
ZodInteger.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodInteger' error=${e}`);
return false;
}
}
export const ZodIntegerWrite = zod.object({
});
export type IntegerWrite = zod.infer<typeof ZodIntegerWrite>;
export function isIntegerWrite(data: any): data is IntegerWrite {
try {
ZodIntegerWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodIntegerWrite' error=${e}`);
return false;
}
}

View File

@ -1,62 +1,61 @@
/**
* Interface of the server (auto-generated code)
*/
import { z as zod } from 'zod';
import { z as zod } from "zod";
import {
ZodGenericDataSoftDelete,
ZodGenericDataSoftDeleteWrite,
} from './generic-data-soft-delete';
import { ZodLong } from './long';
import { ZodUUID } from './uuid';
import {ZodUUID} from "./uuid";
import {ZodLong} from "./long";
import {ZodGenericDataSoftDelete, ZodGenericDataSoftDeleteWrite } from "./generic-data-soft-delete";
export const ZodTrack = ZodGenericDataSoftDelete.extend({
name: zod.string().max(256).optional(),
description: zod.string().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID).optional(),
genderId: ZodLong.optional(),
albumId: ZodLong.optional(),
track: ZodLong.optional(),
dataId: ZodUUID.optional(),
artists: zod.array(ZodLong),
name: zod.string().max(256).optional(),
description: zod.string().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID).optional(),
genderId: ZodLong.optional(),
albumId: ZodLong.optional(),
track: ZodLong.optional(),
dataId: ZodUUID.optional(),
artists: zod.array(ZodLong),
});
export type Track = zod.infer<typeof ZodTrack>;
export function isTrack(data: any): data is Track {
try {
ZodTrack.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodTrack' error=${e}`);
return false;
}
try {
ZodTrack.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodTrack' error=${e}`);
return false;
}
}
export const ZodTrackWrite = ZodGenericDataSoftDeleteWrite.extend({
name: zod.string().max(256).nullable().optional(),
description: zod.string().nullable().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID).nullable().optional(),
genderId: ZodLong.nullable().optional(),
albumId: ZodLong.nullable().optional(),
track: ZodLong.nullable().optional(),
dataId: ZodUUID.nullable().optional(),
artists: zod.array(ZodLong).optional(),
name: zod.string().max(256).nullable().optional(),
description: zod.string().nullable().optional(),
/**
* List of Id of the specific covers
*/
covers: zod.array(ZodUUID).nullable().optional(),
genderId: ZodLong.nullable().optional(),
albumId: ZodLong.nullable().optional(),
track: ZodLong.nullable().optional(),
dataId: ZodUUID.nullable().optional(),
artists: zod.array(ZodLong).optional(),
});
export type TrackWrite = zod.infer<typeof ZodTrackWrite>;
export function isTrackWrite(data: any): data is TrackWrite {
try {
ZodTrackWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
return false;
}
try {
ZodTrackWrite.parse(data);
return true;
} catch (e: any) {
console.log(`Fail to parse data type='ZodTrackWrite' error=${e}`);
return false;
}
}

View File

@ -0,0 +1,185 @@
import {
DragEventHandler,
ReactNode,
RefObject,
forwardRef,
useEffect,
useState,
} from 'react';
import {
Box,
BoxProps,
Center,
Icon,
Image,
Wrap,
WrapItem,
} from '@chakra-ui/react';
import { relative } from 'path';
import {
MdCheckCircle,
MdClear,
MdHighlightOff,
MdUploadFile,
} from 'react-icons/md';
import { ProxyResource } from '@/back-api';
import { FormGroup } from '@/components/form/FormGroup';
import { UseFormidableReturn } from '@/components/form/Formidable';
import { DataUrlAccess } from '@/utils/data-url-access';
export type DragNdropProps = {
onFilesSelected?: (file: File[]) => void;
onUriSelected?: (uri: string) => void;
width?: string;
height?: string;
};
export const DragNdrop = ({
onFilesSelected = () => {},
onUriSelected = () => {},
width = '100px',
height = '100px',
}: DragNdropProps) => {
const handleFileChange = (event) => {
const selectedFiles = event.target.files;
if (selectedFiles && selectedFiles.length > 0) {
const newFiles: File[] = Array.from(selectedFiles);
onFilesSelected(newFiles);
}
};
const handleDrop = (event: DragEvent) => {
event.preventDefault();
const droppedFiles = event.dataTransfer?.files;
console.log('drop ...' + droppedFiles?.length);
if (droppedFiles && droppedFiles?.length > 0) {
const newFiles: File[] = Array.from(droppedFiles);
onFilesSelected(newFiles);
} else {
console.log(`drop types: ${event.dataTransfer?.types}`);
const listUri = event.dataTransfer?.getData('text/uri-list');
console.log(`listUri: ${listUri}`);
if (!listUri) {
return;
}
onUriSelected(listUri);
}
};
return (
<Box
width={width}
height={height}
border="2px"
borderRadius="5px"
borderStyle="dashed"
onDrop={handleDrop}
onDragOver={(event) => event.preventDefault()}
>
<label htmlFor="browse">
<Box paddingY="15%" height="100%" cursor="pointer">
<Center>
<MdUploadFile size="50%" />
</Center>
<Center>
<input
type="file"
hidden
id="browse"
onChange={handleFileChange}
//accept=".pdf,.docx,.pptx,.txt,.xlsx"
multiple
/>
Browse files
</Center>
</Box>
</label>
</Box>
);
};
export type CenterIconProps = BoxProps & {
icon: any;
sizeIcon?: string;
};
export const CenterIcon = ({
icon: IconEl,
sizeIcon = '15px',
...rest
}: CenterIconProps) => {
return (
<Box position="relative" w={sizeIcon} h={sizeIcon} flex="none" {...rest}>
<Box
as={IconEl}
w={sizeIcon}
h={sizeIcon}
position="absolute"
top="50%"
left="50%"
transform="translate(-50%, -50%)"
/>
</Box>
);
};
export type FormCoversProps = {
form: UseFormidableReturn;
variableName: string;
ref?: RefObject<any>;
label?: string;
isRequired?: boolean;
onFilesSelected?: (files: File[]) => void;
onUriSelected?: (uri: string) => void;
onRemove?: (index: number) => void;
};
export const FormCovers = ({
form,
variableName,
ref,
onFilesSelected = () => {},
onUriSelected = () => {},
onRemove = () => {},
...rest
}: FormCoversProps) => {
const urls =
DataUrlAccess.getListThumbnailUrl(form.values[variableName]) ?? [];
return (
<FormGroup
isModify={form.isModify[variableName]}
onRestore={() => form.restoreValue({ [variableName]: true })}
{...rest}
>
<Wrap width="full">
{urls.map((data, index) => (
<WrapItem key={data}>
<Box width="125px" height="125px" position="relative">
<Box width="125px" height="125px" position="absolute">
<CenterIcon
icon={MdHighlightOff}
width="125px"
sizeIcon="100%"
zIndex="+1"
color="#00000020"
_hover={{ color: 'red' }}
onClick={() => onRemove && onRemove(index)}
/>
</Box>
<Image loading="lazy" src={data} boxSize="full" />
</Box>
</WrapItem>
))}
<WrapItem key="data">
<DragNdrop
height="125px"
width="125px"
onFilesSelected={onFilesSelected}
onUriSelected={onUriSelected}
/>
</WrapItem>
</Wrap>
</FormGroup>
);
};

View File

@ -22,6 +22,7 @@ import {
import { useNavigate, useParams } from 'react-router-dom';
import { Album, AlbumResource } from '@/back-api';
import { FormCovers } from '@/components/form/FormCovers';
import { FormGroup } from '@/components/form/FormGroup';
import { FormInput } from '@/components/form/FormInput';
import { FormTextarea } from '@/components/form/FormTextarea';
@ -85,6 +86,66 @@ export const AlbumEditPopUp = ({}: AlbumEditPopUpProps) => {
})
);
};
const onUriSelected = (uri: string) => {
if (isNullOrUndefined(albumIdInt)) {
return;
}
// no real need the wrapper must be able to set optional the uri our file ...
const plop = new File([], 'emptyFile.txt', {
type: 'text/plain',
});
store.update(
AlbumResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: plop,
uri: uri,
},
params: {
id: albumIdInt,
},
})
);
};
const onFilesSelected = (files: File[]) => {
files.forEach((element) => {
console.log(`Select file: '${element.name}'`);
});
if (isNullOrUndefined(albumIdInt)) {
return;
}
store.update(
AlbumResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: files[0],
uri: null,
},
params: {
id: albumIdInt,
},
})
);
};
const onRemoveCover = (index: number) => {
if (isNullOrUndefined(dataAlbum?.covers)) {
return;
}
if (isNullOrUndefined(albumIdInt)) {
return;
}
store.update(
AlbumResource.removeCover({
restConfig: session.getRestConfig(),
params: {
id: albumIdInt,
coverId: dataAlbum.covers[index],
},
})
);
};
return (
<Modal
initialFocusRef={initialRef}
@ -151,6 +212,13 @@ export const AlbumEditPopUp = ({}: AlbumEditPopUpProps) => {
variableName="publication"
label="Publication"
/>
<FormCovers
form={form}
variableName="covers"
onFilesSelected={onFilesSelected}
onUriSelected={onUriSelected}
onRemove={onRemoveCover}
/>
</>
)}
</ModalBody>

View File

@ -22,6 +22,7 @@ import {
import { useNavigate, useParams } from 'react-router-dom';
import { Artist, ArtistResource } from '@/back-api';
import { FormCovers } from '@/components/form/FormCovers';
import { FormGroup } from '@/components/form/FormGroup';
import { FormInput } from '@/components/form/FormInput';
import { FormTextarea } from '@/components/form/FormTextarea';
@ -85,6 +86,65 @@ export const ArtistEditPopUp = ({}: ArtistEditPopUpProps) => {
})
);
};
const onUriSelected = (uri: string) => {
if (isNullOrUndefined(artistIdInt)) {
return;
}
// no real need the wrapper must be able to set optional the uri our file ...
const plop = new File([], 'emptyFile.txt', {
type: 'text/plain',
});
store.update(
ArtistResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: plop,
uri: uri,
},
params: {
id: artistIdInt,
},
})
);
};
const onFilesSelected = (files: File[]) => {
files.forEach((element) => {
console.log(`Select file: '${element.name}'`);
});
if (isNullOrUndefined(artistIdInt)) {
return;
}
store.update(
ArtistResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: files[0],
uri: null,
},
params: {
id: artistIdInt,
},
})
);
};
const onRemoveCover = (index: number) => {
if (isNullOrUndefined(dataArtist?.covers)) {
return;
}
if (isNullOrUndefined(artistIdInt)) {
return;
}
store.update(
ArtistResource.removeCover({
restConfig: session.getRestConfig(),
params: {
id: artistIdInt,
coverId: dataArtist.covers[index],
},
})
);
};
return (
<Modal
initialFocusRef={initialRef}
@ -154,6 +214,13 @@ export const ArtistEditPopUp = ({}: ArtistEditPopUpProps) => {
<FormInput form={form} variableName="surname" label="SurName" />
<FormInput form={form} variableName="birth" label="Birth date" />
<FormInput form={form} variableName="death" label="Death date" />
<FormCovers
form={form}
variableName="covers"
onFilesSelected={onFilesSelected}
onUriSelected={onUriSelected}
onRemove={onRemoveCover}
/>
</>
)}
</ModalBody>

View File

@ -0,0 +1,245 @@
import { useRef, useState } from 'react';
import {
Button,
Flex,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Text,
useDisclosure,
} from '@chakra-ui/react';
import {
MdAdminPanelSettings,
MdDeleteForever,
MdEdit,
MdWarning,
} from 'react-icons/md';
import { useNavigate, useParams } from 'react-router-dom';
import { Gender, GenderResource } from '@/back-api';
import { FormCovers } from '@/components/form/FormCovers';
import { FormGroup } from '@/components/form/FormGroup';
import { FormInput } from '@/components/form/FormInput';
import { FormTextarea } from '@/components/form/FormTextarea';
import { useFormidable } from '@/components/form/Formidable';
import { ConfirmPopUp } from '@/components/popup/ConfirmPopUp';
import { useGenderService, useSpecificGender } from '@/service/Gender';
import { useServiceContext } from '@/service/ServiceContext';
import { useCountTracksOfAGender } from '@/service/Track';
import { isNullOrUndefined } from '@/utils/validator';
export type GenderEditPopUpProps = {};
export const GenderEditPopUp = ({}: GenderEditPopUpProps) => {
const { genderId } = useParams();
const genderIdInt = isNullOrUndefined(genderId)
? undefined
: parseInt(genderId, 10);
const { session } = useServiceContext();
const { countTracksOnAGender } = useCountTracksOfAGender(genderIdInt);
const { store } = useGenderService();
const { dataGender } = useSpecificGender(genderIdInt);
const [admin, setAdmin] = useState(false);
const navigate = useNavigate();
const disclosure = useDisclosure();
const onClose = () => {
navigate('../../', { relative: 'path' });
};
const onRemove = () => {
if (isNullOrUndefined(genderIdInt)) {
return;
}
store.remove(
genderIdInt,
GenderResource.remove({
restConfig: session.getRestConfig(),
params: {
id: genderIdInt,
},
})
);
onClose();
};
const initialRef = useRef(null);
const finalRef = useRef(null);
const form = useFormidable<Gender>({
initialValues: dataGender,
});
const onSave = async () => {
if (isNullOrUndefined(genderIdInt)) {
return;
}
const dataThatNeedToBeUpdated = form.getDeltaData({ omit: ['covers'] });
console.log(`onSave = ${JSON.stringify(dataThatNeedToBeUpdated, null, 2)}`);
store.update(
GenderResource.patch({
restConfig: session.getRestConfig(),
data: dataThatNeedToBeUpdated,
params: {
id: genderIdInt,
},
})
);
};
const onUriSelected = (uri: string) => {
if (isNullOrUndefined(genderIdInt)) {
return;
}
// no real need the wrapper must be able to set optional the uri our file ...
const plop = new File([], 'emptyFile.txt', {
type: 'text/plain',
});
store.update(
GenderResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: plop,
uri: uri,
},
params: {
id: genderIdInt,
},
})
);
};
const onFilesSelected = (files: File[]) => {
files.forEach((element) => {
console.log(`Select file: '${element.name}'`);
});
if (isNullOrUndefined(genderIdInt)) {
return;
}
store.update(
GenderResource.uploadCover({
restConfig: session.getRestConfig(),
data: {
file: files[0],
uri: null,
},
params: {
id: genderIdInt,
},
})
);
};
const onRemoveCover = (index: number) => {
if (isNullOrUndefined(dataGender?.covers)) {
return;
}
if (isNullOrUndefined(genderIdInt)) {
return;
}
store.update(
GenderResource.removeCover({
restConfig: session.getRestConfig(),
params: {
id: genderIdInt,
coverId: dataGender.covers[index],
},
})
);
};
return (
<Modal
initialFocusRef={initialRef}
finalFocusRef={finalRef}
closeOnOverlayClick={false}
onClose={onClose}
isOpen={true}
>
<ModalOverlay />
<ModalContent>
<ModalHeader>Edit Gender</ModalHeader>
<ModalCloseButton ref={finalRef} />
<ModalBody pb={6} gap="0px" paddingLeft="18px">
{admin && (
<>
<FormGroup isRequired label="Id">
<Text>{dataGender?.id}</Text>
</FormGroup>
{countTracksOnAGender !== 0 && (
<Flex paddingLeft="14px">
<MdWarning color="red.600" />
<Text paddingLeft="6px" color="red.600" fontWeight="bold">
Can not remove gender {countTracksOnAGender} track(s) depend
on it.
</Text>
</Flex>
)}
<FormGroup label="Action(s):">
<Button
onClick={disclosure.onOpen}
marginRight="auto"
variant="@danger"
isDisabled={countTracksOnAGender !== 0}
>
<MdDeleteForever /> Remove gender
</Button>
</FormGroup>
<ConfirmPopUp
disclosure={disclosure}
title="Remove gender"
body={`Remove gender [${dataGender?.id}] ${dataGender?.name}`}
confirmTitle="Remove"
onConfirm={onRemove}
/>
</>
)}
{!admin && (
<>
<FormInput
form={form}
variableName="name"
isRequired
label="Gender name"
ref={initialRef}
/>
<FormTextarea
form={form}
variableName="description"
label="Description"
/>
<FormCovers
form={form}
variableName="covers"
onFilesSelected={onFilesSelected}
onUriSelected={onUriSelected}
onRemove={onRemoveCover}
/>
</>
)}
</ModalBody>
<ModalFooter>
<Button
onClick={() => setAdmin((value) => !value)}
marginRight="auto"
>
{admin ? (
<>
<MdEdit />
Edit
</>
) : (
<>
<MdAdminPanelSettings />
Admin
</>
)}
</Button>
{!admin && form.isFormModified && (
<Button colorScheme="blue" mr={3} onClick={onSave}>
Save
</Button>
)}
<Button onClick={onClose}>Cancel</Button>
</ModalFooter>
</ModalContent>
</Modal>
);
};

View File

@ -45,7 +45,7 @@ const environment_local: Environment = {
ssoSignUp: `${serverSSOAddress}/karso/signup/karusic-dev/`,
ssoSignOut: `${serverSSOAddress}/karso/signout/karusic-dev/`,
tokenStoredInPermanentStorage: false,
replaceDataToRealServer: true,
//replaceDataToRealServer: true,
};
const environment_full_local: Environment = {

View File

@ -1,12 +1,14 @@
import { Box, Flex, Text } from '@chakra-ui/react';
import { Box, Button, Flex, Text } from '@chakra-ui/react';
import { LuDisc3 } from 'react-icons/lu';
import { MdEdit } from 'react-icons/md';
import { Route, Routes, useNavigate, useParams } from 'react-router-dom';
import { Covers } from '@/components/Cover';
import { EmptyEnd } from '@/components/EmptyEnd';
import { PageLayout } from '@/components/Layout/PageLayout';
import { PageLayoutInfoCenter } from '@/components/Layout/PageLayoutInfoCenter';
import { TopBar } from '@/components/TopBar/TopBar';
import { BUTTON_TOP_BAR_PROPERTY, TopBar } from '@/components/TopBar/TopBar';
import { GenderEditPopUp } from '@/components/popup/GenderEditPopUp';
import { TrackEditPopUp } from '@/components/popup/TrackEditPopUp';
import { DisplayTrack } from '@/components/track/DisplayTrack';
import { DisplayTrackFull } from '@/components/track/DisplayTrackFull';
@ -54,7 +56,16 @@ export const GenderDetailPage = () => {
}
return (
<>
<TopBar title="Gender detail" />
<TopBar title="Gender detail">
<Button
{...BUTTON_TOP_BAR_PROPERTY}
onClick={() =>
navigate(`/gender/${genderId}/edit-gender/${genderId}`)
}
>
<MdEdit />
</Button>
</TopBar>
<PageLayout>
<Flex
direction="row"
@ -106,7 +117,7 @@ export const GenderDetailPage = () => {
{
name: 'Edit',
onClick: () => {
navigate(`/gender/${genderId}/edit/${data.id}`);
navigate(`/gender/${genderId}/edit-track/${data.id}`);
},
},
{ name: 'Add Playlist', onClick: () => {} },
@ -117,7 +128,8 @@ export const GenderDetailPage = () => {
<EmptyEnd />
</Flex>
<Routes>
<Route path="edit/:trackId" element={<TrackEditPopUp />} />
<Route path="edit-track/:trackId" element={<TrackEditPopUp />} />
<Route path="edit-gender/:genderId" element={<GenderEditPopUp />} />
</Routes>
</PageLayout>
</>

View File

@ -6,7 +6,8 @@
import { DependencyList, useCallback, useEffect, useState } from 'react';
import { RestErrorResponse } from '@/back-api';
import { isArray, isNullOrUndefined } from '@/utils/validator';
import { useToastAPIError } from '@/utils/toastHook';
import { isNullOrUndefined } from '@/utils/validator';
export type DataStoreType<TYPE> = {
isLoading: boolean;
@ -33,6 +34,7 @@ export const useDataStore = <TYPE>(
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<RestErrorResponse | undefined>(undefined);
const [data, setData] = useState<TYPE[]>([]);
const toastAPIError = useToastAPIError();
// on instantiation ==> call the request of the data...
useEffect(() => {
@ -49,6 +51,7 @@ export const useDataStore = <TYPE>(
setIsLoading(false);
})
.catch((error: RestErrorResponse) => {
toastAPIError(error);
console.log(
`[${restApiName}] catch error: ${JSON.stringify(error, null, 2)}`
);
@ -97,8 +100,8 @@ export const useDataStore = <TYPE>(
filterData.push(responseData);
setData(filterData);
})
.catch((error) => {
console.log(`catch an error: ... ${JSON.stringify(error, null, 2)}`);
.catch((error: RestErrorResponse) => {
toastAPIError(error);
});
},
[data, setData]
@ -114,6 +117,7 @@ export const useDataStore = <TYPE>(
setData(filterData);
})
.catch((error) => {
toastAPIError(error);
console.log(
`catch an error on delete: ... ${JSON.stringify(error, null, 2)}`
);

View File

@ -0,0 +1,50 @@
import { useCallback } from 'react';
import { UseToastOptions, useToast } from '@chakra-ui/react';
import { RestErrorResponse } from '@/back-api';
export const toastDefaultConfig: UseToastOptions = {
duration: 3000,
isClosable: true,
position: 'top-right',
variant: 'solid',
};
export const useToastError = () =>
useToast({
...toastDefaultConfig,
status: 'error',
duration: 5000,
});
export const useToastWarning = () =>
useToast({
...toastDefaultConfig,
status: 'warning',
});
export const useToastSuccess = () =>
useToast({
...toastDefaultConfig,
status: 'success',
});
export const useToastInfo = () =>
useToast({
...toastDefaultConfig,
status: 'info',
});
export const useToastAPIError = () => {
const toastError = useToastError();
return useCallback(
(error: RestErrorResponse) => {
toastError({
title: `[${error.status}] ${error.statusMessage}`,
description: error.message,
});
},
[toastError]
);
};