Co-authored-by: Andre Smith <Hobogrammer@users.noreply.github.com>
This commit is contained in:
Joe Milazzo 2023-11-28 16:00:04 -06:00 committed by GitHub
parent 565a93f2d2
commit 915bf13a7c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 606 additions and 342 deletions

View File

@ -72,7 +72,6 @@
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" /> <PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" /> <PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" /> <PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" /> <PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" /> <PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />

View File

@ -101,8 +101,8 @@ public class BookController : BaseApiController
if (chapterId <= 0) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist")); if (chapterId <= 0) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist"));
var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId); var chapter = await _unitOfWork.ChapterRepository.GetChapterAsync(chapterId);
if (chapter == null) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist")); if (chapter == null) return BadRequest(await _localizationService.Get("en", "chapter-doesnt-exist"));
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath, BookService.BookReaderOptions);
using var book = await EpubReader.OpenBookAsync(chapter.Files.ElementAt(0).FilePath, BookService.BookReaderOptions);
var key = BookService.CoalesceKeyForAnyFile(book, file); var key = BookService.CoalesceKeyForAnyFile(book, file);
if (!book.Content.AllFiles.ContainsLocalFileRefWithKey(key)) return BadRequest(await _localizationService.Get("en", "file-missing")); if (!book.Content.AllFiles.ContainsLocalFileRefWithKey(key)) return BadRequest(await _localizationService.Get("en", "file-missing"));

View File

@ -40,13 +40,15 @@ public class LibraryController : BaseApiController
private readonly IEventHub _eventHub; private readonly IEventHub _eventHub;
private readonly ILibraryWatcher _libraryWatcher; private readonly ILibraryWatcher _libraryWatcher;
private readonly ILocalizationService _localizationService; private readonly ILocalizationService _localizationService;
private readonly IStreamService _streamService;
private readonly IEasyCachingProvider _libraryCacheProvider; private readonly IEasyCachingProvider _libraryCacheProvider;
private const string CacheKey = "library_"; private const string CacheKey = "library_";
public LibraryController(IDirectoryService directoryService, public LibraryController(IDirectoryService directoryService,
ILogger<LibraryController> logger, IMapper mapper, ITaskScheduler taskScheduler, ILogger<LibraryController> logger, IMapper mapper, ITaskScheduler taskScheduler,
IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher, IUnitOfWork unitOfWork, IEventHub eventHub, ILibraryWatcher libraryWatcher,
IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService) IEasyCachingProviderFactory cachingProviderFactory, ILocalizationService localizationService,
IStreamService streamService)
{ {
_directoryService = directoryService; _directoryService = directoryService;
_logger = logger; _logger = logger;
@ -56,6 +58,7 @@ public class LibraryController : BaseApiController
_eventHub = eventHub; _eventHub = eventHub;
_libraryWatcher = libraryWatcher; _libraryWatcher = libraryWatcher;
_localizationService = localizationService; _localizationService = localizationService;
_streamService = streamService;
_libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library); _libraryCacheProvider = cachingProviderFactory.GetCachingProvider(EasyCacheProfiles.Library);
} }
@ -240,8 +243,6 @@ public class LibraryController : BaseApiController
// Bust cache // Bust cache
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey); await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
// TODO: Update a user's SideNav based on library access
_unitOfWork.UserRepository.Update(user); _unitOfWork.UserRepository.Update(user);
return Ok(_mapper.Map<MemberDto>(user)); return Ok(_mapper.Map<MemberDto>(user));

View File

@ -834,10 +834,10 @@ public class OpdsController : BaseApiController
foreach (var chapter in chapters) foreach (var chapter in chapters)
{ {
var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id); var files = await _unitOfWork.ChapterRepository.GetFilesForChapterAsync(chapter.Id);
var chapterTest = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id); var chapterDto = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapter.Id);
foreach (var mangaFile in files) foreach (var mangaFile in files)
{ {
feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volumeId, chapter.Id, mangaFile, series, chapterTest, apiKey, prefix, baseUrl)); feed.Entries.Add(await CreateChapterWithFile(userId, seriesId, volumeId, chapter.Id, mangaFile, series, chapterDto!, apiKey, prefix, baseUrl));
} }
} }

View File

@ -231,6 +231,7 @@ public class ReaderController : BaseApiController
var mangaFile = chapter.Files.First(); var mangaFile = chapter.Files.First();
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(dto.SeriesId, User.GetUserId()); var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(dto.SeriesId, User.GetUserId());
if (series == null) return Unauthorized();
var info = new ChapterInfoDto() var info = new ChapterInfoDto()
{ {
@ -278,6 +279,7 @@ public class ReaderController : BaseApiController
} }
} }
return Ok(info); return Ok(info);
} }

View File

@ -2,10 +2,8 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using API.Data.Repositories; using API.Data.Repositories;
using API.Entities; using API.Entities;
using API.Services;
using AutoMapper; using AutoMapper;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
namespace API.Data; namespace API.Data;
@ -42,7 +40,6 @@ public class UnitOfWork : IUnitOfWork
private readonly DataContext _context; private readonly DataContext _context;
private readonly IMapper _mapper; private readonly IMapper _mapper;
private readonly UserManager<AppUser> _userManager; private readonly UserManager<AppUser> _userManager;
private readonly ILocalizationService _localizationService;
public UnitOfWork(DataContext context, IMapper mapper, UserManager<AppUser> userManager) public UnitOfWork(DataContext context, IMapper mapper, UserManager<AppUser> userManager)
{ {

View File

@ -189,12 +189,6 @@
"user-no-access-library-from-series": "User does not have access to the library this series belongs to", "user-no-access-library-from-series": "User does not have access to the library this series belongs to",
"series-restricted-age-restriction": "User is not allowed to view this series due to age restrictions", "series-restricted-age-restriction": "User is not allowed to view this series due to age restrictions",
"next-volume-num": "Upcoming Volume: {0}",
"next-book-num": "Upcoming Book: {0}",
"next-issue-num": "Upcoming Issue: {0}{1}",
"next-chapter-num": "Upcoming Chapter: {0}",
"volume-num": "Volume {0}", "volume-num": "Volume {0}",
"book-num": "Book {0}", "book-num": "Book {0}",
"issue-num": "Issue {0}{1}", "issue-num": "Issue {0}{1}",

View File

@ -261,7 +261,8 @@ public class SeriesService : ISeriesService
HandleAddPerson, () => series.Metadata.CoverArtistLocked = true); HandleAddPerson, () => series.Metadata.CoverArtistLocked = true);
} }
if (updateSeriesMetadataDto.SeriesMetadata != null)
{
series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked; series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked;
series.Metadata.PublicationStatusLocked = updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked; series.Metadata.PublicationStatusLocked = updateSeriesMetadataDto.SeriesMetadata.PublicationStatusLocked;
series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked; series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked;
@ -279,6 +280,7 @@ public class SeriesService : ISeriesService
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked; series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked;
series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked; series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked;
series.Metadata.ReleaseYearLocked = updateSeriesMetadataDto.SeriesMetadata.ReleaseYearLocked; series.Metadata.ReleaseYearLocked = updateSeriesMetadataDto.SeriesMetadata.ReleaseYearLocked;
}
if (!_unitOfWork.HasChanges()) if (!_unitOfWork.HasChanges())
{ {
@ -721,6 +723,7 @@ public class SeriesService : ISeriesService
return _emptyExpectedChapter; return _emptyExpectedChapter;
} }
const int minimumTimeDeltas = 3;
var chapters = _unitOfWork.ChapterRepository.GetChaptersForSeries(seriesId) var chapters = _unitOfWork.ChapterRepository.GetChaptersForSeries(seriesId)
.Where(c => !c.IsSpecial) .Where(c => !c.IsSpecial)
.OrderBy(c => c.CreatedUtc) .OrderBy(c => c.CreatedUtc)
@ -746,15 +749,14 @@ public class SeriesService : ISeriesService
previousChapterTime = chapter.CreatedUtc; previousChapterTime = chapter.CreatedUtc;
} }
if (timeDifferences.Count < minimumTimeDeltas)
if (timeDifferences.Count < 3)
{ {
return _emptyExpectedChapter; return _emptyExpectedChapter;
} }
var historicalTimeDifferences = timeDifferences.Select(td => td.TotalDays).ToList(); var historicalTimeDifferences = timeDifferences.Select(td => td.TotalDays).ToList();
if (historicalTimeDifferences.Count < 3) if (historicalTimeDifferences.Count < minimumTimeDeltas)
{ {
return _emptyExpectedChapter; return _emptyExpectedChapter;
} }
@ -793,10 +795,10 @@ public class SeriesService : ISeriesService
result.VolumeNumber = lastChapter.Volume.Number; result.VolumeNumber = lastChapter.Volume.Number;
result.Title = series.Library.Type switch result.Title = series.Library.Type switch
{ {
LibraryType.Manga => await _localizationService.Translate(userId, "next-chapter-num", result.ChapterNumber), LibraryType.Manga => await _localizationService.Translate(userId, "chapter-num", result.ChapterNumber),
LibraryType.Comic => await _localizationService.Translate(userId, "next-issue-num", "#", result.ChapterNumber), LibraryType.Comic => await _localizationService.Translate(userId, "issue-num", "#", result.ChapterNumber),
LibraryType.Book => await _localizationService.Translate(userId, "next-book-num", result.ChapterNumber), LibraryType.Book => await _localizationService.Translate(userId, "book-num", result.ChapterNumber),
_ => await _localizationService.Translate(userId, "next-chapter-num", result.ChapterNumber) _ => await _localizationService.Translate(userId, "chapter-num", result.ChapterNumber)
}; };
} }
else else

View File

@ -33,7 +33,6 @@ public interface IStreamService
Task<ExternalSourceDto> CreateExternalSource(int userId, ExternalSourceDto dto); Task<ExternalSourceDto> CreateExternalSource(int userId, ExternalSourceDto dto);
Task<ExternalSourceDto> UpdateExternalSource(int userId, ExternalSourceDto dto); Task<ExternalSourceDto> UpdateExternalSource(int userId, ExternalSourceDto dto);
Task DeleteExternalSource(int userId, int externalSourceId); Task DeleteExternalSource(int userId, int externalSourceId);
} }
public class StreamService : IStreamService public class StreamService : IStreamService
@ -342,6 +341,4 @@ public class StreamService : IStreamService
await _unitOfWork.CommitAsync(); await _unitOfWork.CommitAsync();
} }
} }

View File

@ -295,14 +295,17 @@ public class ProcessSeries : IProcessSeries
if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1) if (series.Format == MangaFormat.Epub || series.Format == MangaFormat.Pdf && chapters.Count == 1)
{ {
series.Metadata.MaxCount = 1; series.Metadata.MaxCount = 1;
} else if (series.Metadata.TotalCount == 1 && chapters.Count == 1 && chapters.First().IsSpecial)
{
// If a series has a TotalCount of 1 and there is only a Special, mark it as Complete
series.Metadata.MaxCount = series.Metadata.TotalCount;
} else if ((maxChapter == 0 || maxChapter > series.Metadata.TotalCount) && maxVolume <= series.Metadata.TotalCount) } else if ((maxChapter == 0 || maxChapter > series.Metadata.TotalCount) && maxVolume <= series.Metadata.TotalCount)
{ {
series.Metadata.MaxCount = maxVolume; series.Metadata.MaxCount = maxVolume;
} else if (maxVolume == series.Metadata.TotalCount) } else if (maxVolume == series.Metadata.TotalCount)
{ {
series.Metadata.MaxCount = maxVolume; series.Metadata.MaxCount = maxVolume;
} } else
else
{ {
series.Metadata.MaxCount = maxChapter; series.Metadata.MaxCount = maxChapter;
} }

View File

@ -9,7 +9,7 @@ your reading collection with your friends and family!
[![Release](https://img.shields.io/github/release/Kareadita/Kavita.svg?style=flat&maxAge=3600)](https://github.com/Kareadita/Kavita/releases) [![Release](https://img.shields.io/github/release/Kareadita/Kavita.svg?style=flat&maxAge=3600)](https://github.com/Kareadita/Kavita/releases)
[![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://github.com/Kareadita/Kavita/blob/master/LICENSE) [![License](https://img.shields.io/badge/license-GPLv3-blue.svg?style=flat)](https://github.com/Kareadita/Kavita/blob/master/LICENSE)
[![Downloads](https://img.shields.io/github/downloads/Kareadita/Kavita/total.svg?style=flat)](https://github.com/Kareadita/Kavita/releases) [![Downloads](https://img.shields.io/github/downloads/Kareadita/Kavita/total.svg?style=flat)](https://github.com/Kareadita/Kavita/releases)
[![Docker Pulls](https://img.shields.io/docker/pulls/kizaing/kavita.svg)](https://hub.docker.com/r/kizaing/kavita/) [![Docker Pulls](https://img.shields.io/docker/pulls/kizaing/kavita.svg)](https://hub.docker.com/r/jvmilazz0/kavita)
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita)
[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=security_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=Kareadita_Kavita&metric=security_rating)](https://sonarcloud.io/dashboard?id=Kareadita_Kavita)
[![Backers on Open Collective](https://opencollective.com/kavita/backers/badge.svg)](#backers) [![Backers on Open Collective](https://opencollective.com/kavita/backers/badge.svg)](#backers)

555
UI/Web/package-lock.json generated
View File

@ -8,21 +8,21 @@
"name": "kavita-webui", "name": "kavita-webui",
"version": "0.4.2", "version": "0.4.2",
"dependencies": { "dependencies": {
"@angular/animations": "^17.0.3", "@angular/animations": "^17.0.4",
"@angular/cdk": "^17.0.1", "@angular/cdk": "^17.0.1",
"@angular/common": "^17.0.3", "@angular/common": "^17.0.4",
"@angular/compiler": "^17.0.3", "@angular/compiler": "^17.0.4",
"@angular/core": "^17.0.3", "@angular/core": "^17.0.4",
"@angular/forms": "^17.0.3", "@angular/forms": "^17.0.4",
"@angular/localize": "^17.0.3", "@angular/localize": "^17.0.4",
"@angular/platform-browser": "^17.0.3", "@angular/platform-browser": "^17.0.4",
"@angular/platform-browser-dynamic": "^17.0.3", "@angular/platform-browser-dynamic": "^17.0.4",
"@angular/router": "^17.0.3", "@angular/router": "^17.0.4",
"@fortawesome/fontawesome-free": "^6.4.2", "@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^17.0.0", "@iharbeck/ngx-virtual-scroller": "^17.0.0",
"@iplab/ngx-file-upload": "^17.0.0", "@iplab/ngx-file-upload": "^17.0.0",
"@microsoft/signalr": "^7.0.12", "@microsoft/signalr": "^7.0.12",
"@ng-bootstrap/ng-bootstrap": "^15.1.2", "@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@ngneat/transloco": "^6.0.0", "@ngneat/transloco": "^6.0.0",
"@ngneat/transloco-locale": "^5.1.1", "@ngneat/transloco-locale": "^5.1.1",
"@ngneat/transloco-persist-lang": "^5.0.0", "@ngneat/transloco-persist-lang": "^5.0.0",
@ -38,12 +38,13 @@
"ng-circle-progress": "^1.7.1", "ng-circle-progress": "^1.7.1",
"ng-lazyload-image": "^9.1.3", "ng-lazyload-image": "^9.1.3",
"ng-select2-component": "^13.0.9", "ng-select2-component": "^13.0.9",
"ngx-color-picker": "^15.0.0", "ngx-color-picker": "^16.0.0",
"ngx-extended-pdf-viewer": "^18.1.6", "ngx-extended-pdf-viewer": "^18.1.9",
"ngx-file-drop": "^16.0.0", "ngx-file-drop": "^16.0.0",
"ngx-slider-v2": "^17.0.0", "ngx-slider-v2": "^17.0.0",
"ngx-stars": "^1.6.5", "ngx-stars": "^1.6.5",
"ngx-toastr": "^17.0.2", "ngx-toaster": "^1.0.1",
"ngx-toastr": "^18.0.0",
"nosleep.js": "^0.12.0", "nosleep.js": "^0.12.0",
"rxjs": "^7.8.0", "rxjs": "^7.8.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
@ -52,20 +53,20 @@
"zone.js": "^0.14.2" "zone.js": "^0.14.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.0.1", "@angular-devkit/build-angular": "^17.0.3",
"@angular-eslint/builder": "^17.1.0", "@angular-eslint/builder": "^17.1.0",
"@angular-eslint/eslint-plugin": "^17.1.0", "@angular-eslint/eslint-plugin": "^17.1.0",
"@angular-eslint/eslint-plugin-template": "^17.1.0", "@angular-eslint/eslint-plugin-template": "^17.1.0",
"@angular-eslint/schematics": "^17.1.0", "@angular-eslint/schematics": "^17.1.0",
"@angular-eslint/template-parser": "^17.1.0", "@angular-eslint/template-parser": "^17.1.0",
"@angular/cli": "^17.0.1", "@angular/cli": "^17.0.3",
"@angular/compiler-cli": "^17.0.3", "@angular/compiler-cli": "^17.0.4",
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/luxon": "^3.3.4", "@types/luxon": "^3.3.5",
"@types/node": "^20.9.2", "@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.11.0", "@typescript-eslint/parser": "^6.13.0",
"eslint": "^8.54.0", "eslint": "^8.54.0",
"jsonminify": "^0.4.2", "jsonminify": "^0.4.2",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",
@ -96,12 +97,12 @@
} }
}, },
"node_modules/@angular-devkit/architect": { "node_modules/@angular-devkit/architect": {
"version": "0.1700.1", "version": "0.1700.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.1.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1700.3.tgz",
"integrity": "sha512-w84luzQNRjlt7XxX3+jyzcwBBv3gAjjvFWTjN1E5mlpDCUXgYmQ3CMowFHeu0U06HD5Sapap9p2l6GoajuZK5Q==", "integrity": "sha512-HUjx7vD16paWXHKHYc2LsSn/kaYbFr2YNnlzuSr9C0kauKS1e7sRpRvtGwQzXfohzgyKi81AAU5uA2KLRGq83w==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@angular-devkit/core": "17.0.1", "@angular-devkit/core": "17.0.3",
"rxjs": "7.8.1" "rxjs": "7.8.1"
}, },
"engines": { "engines": {
@ -111,15 +112,15 @@
} }
}, },
"node_modules/@angular-devkit/build-angular": { "node_modules/@angular-devkit/build-angular": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-17.0.3.tgz",
"integrity": "sha512-OomGAeBg/OOxzPpoU7EkdD3WwhKip+0Giy/cGtkalSgQ5vWTuZhf8UnxwTf7xEXW5LtvfoTtv7sKmb1dJT7FzA==", "integrity": "sha512-1lx0mERC1eTHX4vf8q7kUHZNHS0jwZxbwYHAISOplwHjkzRociX0W6rx04yMXn2NCSNhK+w3xbWyAIgyYbP9nA==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@ampproject/remapping": "2.2.1", "@ampproject/remapping": "2.2.1",
"@angular-devkit/architect": "0.1700.1", "@angular-devkit/architect": "0.1700.3",
"@angular-devkit/build-webpack": "0.1700.1", "@angular-devkit/build-webpack": "0.1700.3",
"@angular-devkit/core": "17.0.1", "@angular-devkit/core": "17.0.3",
"@babel/core": "7.23.2", "@babel/core": "7.23.2",
"@babel/generator": "7.23.0", "@babel/generator": "7.23.0",
"@babel/helper-annotate-as-pure": "7.22.5", "@babel/helper-annotate-as-pure": "7.22.5",
@ -130,7 +131,7 @@
"@babel/preset-env": "7.23.2", "@babel/preset-env": "7.23.2",
"@babel/runtime": "7.23.2", "@babel/runtime": "7.23.2",
"@discoveryjs/json-ext": "0.5.7", "@discoveryjs/json-ext": "0.5.7",
"@ngtools/webpack": "17.0.1", "@ngtools/webpack": "17.0.3",
"@vitejs/plugin-basic-ssl": "1.0.1", "@vitejs/plugin-basic-ssl": "1.0.1",
"ansi-colors": "4.1.3", "ansi-colors": "4.1.3",
"autoprefixer": "10.4.16", "autoprefixer": "10.4.16",
@ -669,12 +670,12 @@
"dev": true "dev": true
}, },
"node_modules/@angular-devkit/build-webpack": { "node_modules/@angular-devkit/build-webpack": {
"version": "0.1700.1", "version": "0.1700.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.1.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1700.3.tgz",
"integrity": "sha512-u9LTcG9Kg2J6WkF1WSoBLdDabhbKxcuHY24SouAJTwg33j6YksglL7qnofOsNxny3Gdnze2BhCjQ1GS9Y8ovXw==", "integrity": "sha512-r8nVakAnwV5Yy2AjWDpdcGUjHRQBcPljZDhX5tX2H7M3bxD6zG5owXDy8XmG64A7U1jd6D7dQv7zoW/tZwpYvw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@angular-devkit/architect": "0.1700.1", "@angular-devkit/architect": "0.1700.3",
"rxjs": "7.8.1" "rxjs": "7.8.1"
}, },
"engines": { "engines": {
@ -688,9 +689,9 @@
} }
}, },
"node_modules/@angular-devkit/core": { "node_modules/@angular-devkit/core": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.0.3.tgz",
"integrity": "sha512-UjNx9fZW0oU7UaeoB0HblYz/Nm8MWtinAe39XkY+zjECLWqKAcHPotfYjucXiky1UlBUOScIKbwjMDdEY8xkuw==", "integrity": "sha512-SOngD3rKnwZWhhUV68AYlH8M3LRGvF69jnDrYKwtRy1ESqSH7tt+1vexGC290gKvqH7bNMgYv8f5BS1AASRfzw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"ajv": "8.12.0", "ajv": "8.12.0",
@ -727,12 +728,12 @@
} }
}, },
"node_modules/@angular-devkit/schematics": { "node_modules/@angular-devkit/schematics": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.0.3.tgz",
"integrity": "sha512-bwgdGviRZC5X8Tl4QcjtIJAcC0p8yIhOyYVFrq4PWYvI+DfV9P6w3OFuoS6rwEoiIQR90+12iKBYMt1MfL/c0Q==", "integrity": "sha512-gNocyYuNJRd24+JSM5kpO7g9Vg4THcoH5It8nJmS3muelLHDzegvDzXB7iPBjVR8Lxts6sbifYdIkKencUc4vg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@angular-devkit/core": "17.0.1", "@angular-devkit/core": "17.0.3",
"jsonc-parser": "3.2.0", "jsonc-parser": "3.2.0",
"magic-string": "0.30.5", "magic-string": "0.30.5",
"ora": "5.4.1", "ora": "5.4.1",
@ -843,9 +844,9 @@
} }
}, },
"node_modules/@angular/animations": { "node_modules/@angular/animations": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.4.tgz",
"integrity": "sha512-aBLVJ0HHYdIZCAXymQDP6UGuz/5oM/3uLCFVHx32vhibLByjw0jNCvy2lzmPLU5gUU6wEWX2b3ZtnzFqhmo4+A==", "integrity": "sha512-XHkTBZAoYf1t4Hb06RkYa6cgtjEA5JGq1ArXu/DckOS6G/ZuY+dwWULEmaf9ejJem8O78ol223ZQ5d7sXqpixQ==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -853,7 +854,7 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "17.0.3" "@angular/core": "17.0.4"
} }
}, },
"node_modules/@angular/cdk": { "node_modules/@angular/cdk": {
@ -873,15 +874,15 @@
} }
}, },
"node_modules/@angular/cli": { "node_modules/@angular/cli": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-17.0.3.tgz",
"integrity": "sha512-3iJWw+bpr/8y1ZY1m0wGfukffQVmD6DJUNubB297NCq1bJyUj+uwBuDnpIH+vidJvPBEEY+9XPJr0Jnd6+i7rg==", "integrity": "sha512-pRGXms87aEqmB4yPdcPI/VM7JegjDcBIeLadms0wrBkoyQiv+jL5LesxODhid6ujXZOj1xqMCYbCnX7HY+mLcQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@angular-devkit/architect": "0.1700.1", "@angular-devkit/architect": "0.1700.3",
"@angular-devkit/core": "17.0.1", "@angular-devkit/core": "17.0.3",
"@angular-devkit/schematics": "17.0.1", "@angular-devkit/schematics": "17.0.3",
"@schematics/angular": "17.0.1", "@schematics/angular": "17.0.3",
"@yarnpkg/lockfile": "1.1.0", "@yarnpkg/lockfile": "1.1.0",
"ansi-colors": "4.1.3", "ansi-colors": "4.1.3",
"ini": "4.1.1", "ini": "4.1.1",
@ -940,9 +941,9 @@
"dev": true "dev": true
}, },
"node_modules/@angular/common": { "node_modules/@angular/common": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.4.tgz",
"integrity": "sha512-AD/d1n0hNisHDhIeBsW2ERZI9ChjiOuZ3IiGwcYKmlcOHTrZTJPAh/ZMgahv24rArlNVax7bT+Ik8+sJedGcEQ==", "integrity": "sha512-/y38PbuiaWOuOmP5ZELTlJSjZGijc6Nq2XQloT5pKsaH935prxPjyWazwlY6cUnJMQgSRU644/ULosDJec7Zxw==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -950,14 +951,14 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "17.0.3", "@angular/core": "17.0.4",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/compiler": { "node_modules/@angular/compiler": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.4.tgz",
"integrity": "sha512-ryUcj8Vc+Q4jMrjrmsEIsGLXeWSmNE/KoTyURPCH+NWq9GBMbjv4oe0/oFSBMN2ZtRMVCvqv2Nq+Z2KRDRGB0A==", "integrity": "sha512-OweJui9EWCa1ZcZjkJHS5z1gqICqyryR1Gdmyr8vIa6HD8wU/5BaeBJPCDgYgt+qJkvcT/sPxgZQsc2pVeUwbQ==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -965,7 +966,7 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/core": "17.0.3" "@angular/core": "17.0.4"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@angular/core": { "@angular/core": {
@ -974,9 +975,9 @@
} }
}, },
"node_modules/@angular/compiler-cli": { "node_modules/@angular/compiler-cli": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.4.tgz",
"integrity": "sha512-oj7KJBFgs6ulT1/A/xkkDHBOB0c7o9HV2Mn5pUosXBo2VgcGYeuJeXffC+mFr5FyiRO1sUanw4vSWnLzK1U0pQ==", "integrity": "sha512-ywj8XNI+hvHHYGcNWvXaVHSRtcd3S7MqJNgXWfnb0JjAb282oGSvjEc7wnH4ERqkvnSrpk1kQ2Fj3uJ2P5zfmQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@babel/core": "7.23.2", "@babel/core": "7.23.2",
@ -997,14 +998,14 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "17.0.3", "@angular/compiler": "17.0.4",
"typescript": ">=5.2 <5.3" "typescript": ">=5.2 <5.3"
} }
}, },
"node_modules/@angular/core": { "node_modules/@angular/core": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.4.tgz",
"integrity": "sha512-zY4yhPiphuktrodaM+GiP8G07qnUlmwKElLjYazeIR8A+kF51RQRpSf/pWe5M0uJIn5Oja+RdO9kzhDI9QvOcA==", "integrity": "sha512-zk+z5sYPZd87pLxECx27quB5FvSmoi9PjJlcSlaBwwqaGnh/tPJI14u3q1dRY/CoZgP9egEiwc428+DzvOzJew==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -1017,9 +1018,9 @@
} }
}, },
"node_modules/@angular/forms": { "node_modules/@angular/forms": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.4.tgz",
"integrity": "sha512-slCUGe5nVOrA0Su9pkmgPXBVzkgh4stvVFBb6ic9/+GlmtRi8h1v5jAFhR4B0R4iaaIoF+TTpRKhZShwtFSqSg==", "integrity": "sha512-R5J87dfJNWwi5SESD7tRkZnqG4u8KNAT4vImX4oG70/6vWioKUSWpLoSp1gpzy9UW51E85AKb8DNvIex7LclSg==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -1027,16 +1028,16 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "17.0.3", "@angular/common": "17.0.4",
"@angular/core": "17.0.3", "@angular/core": "17.0.4",
"@angular/platform-browser": "17.0.3", "@angular/platform-browser": "17.0.4",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
"node_modules/@angular/localize": { "node_modules/@angular/localize": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.4.tgz",
"integrity": "sha512-AxMqZwClWNiYjoaHYu2Y499yM30DJDCgrV9k8mOb92SU3IUaQ52loPb91Cb2Xp4h0b6A3TVAmnYApcLYaUf40g==", "integrity": "sha512-4kLvkwAI9lVaxKzMVjF/0do/Xfn4r3W1bk9Xzb7fYLS21wz11ouL0aV7umtHT5DOZiwkY/F8rOYaJm5Fkz8ubw==",
"dependencies": { "dependencies": {
"@babel/core": "7.23.2", "@babel/core": "7.23.2",
"fast-glob": "3.3.1", "fast-glob": "3.3.1",
@ -1051,14 +1052,14 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/compiler": "17.0.3", "@angular/compiler": "17.0.4",
"@angular/compiler-cli": "17.0.3" "@angular/compiler-cli": "17.0.4"
} }
}, },
"node_modules/@angular/platform-browser": { "node_modules/@angular/platform-browser": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.4.tgz",
"integrity": "sha512-4SoW0yeAxgfcLIekKsvZVg/WgI5aQZyz9HGOoyBcVQ8coYoZmM9bAYQi+9zvyweqoWc+jgw72X1E8wtmMXt7Aw==", "integrity": "sha512-lApUzVPfCEz/4aot77qzWUNg7yQgT0JSzy3BrBm95+2TbgH894J9Fswhig0sEN9jxGSkc3A5Yp5fs1HJcPqUiw==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -1066,9 +1067,9 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/animations": "17.0.3", "@angular/animations": "17.0.4",
"@angular/common": "17.0.3", "@angular/common": "17.0.4",
"@angular/core": "17.0.3" "@angular/core": "17.0.4"
}, },
"peerDependenciesMeta": { "peerDependenciesMeta": {
"@angular/animations": { "@angular/animations": {
@ -1077,9 +1078,9 @@
} }
}, },
"node_modules/@angular/platform-browser-dynamic": { "node_modules/@angular/platform-browser-dynamic": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.4.tgz",
"integrity": "sha512-Ab6ZeGG63z9Ilv8r4lHcmSirVaw8quRrPjDbT8cgIteHbj0SbwgDzxX0ve+fjjubFUluNSNtc6OYglWMHJ/g7Q==", "integrity": "sha512-mZZNH+iFzFug0z7rBQKdFz375sR6Y4iBbHu2aJD2BpgA2/SJaZ0WHGlF4bHbtpCYkZi3f4wKF2+Cwe4G5ebPOQ==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -1087,16 +1088,16 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "17.0.3", "@angular/common": "17.0.4",
"@angular/compiler": "17.0.3", "@angular/compiler": "17.0.4",
"@angular/core": "17.0.3", "@angular/core": "17.0.4",
"@angular/platform-browser": "17.0.3" "@angular/platform-browser": "17.0.4"
} }
}, },
"node_modules/@angular/router": { "node_modules/@angular/router": {
"version": "17.0.3", "version": "17.0.4",
"resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.3.tgz", "resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.4.tgz",
"integrity": "sha512-zw31XXMqLJ1CcHxDtEl2/FTJXeRbbnLM8oHtCPzbbxTkhAlnXxSYxjds0+1IMmpzz/v9qGBhYvUt8ZfZhqDBHQ==", "integrity": "sha512-hQ+T+h6YE9NqyOmjqAIHe/k8xtW+yh0Mp8FCcl8REBezNyLXmOdsScCIOOc7GeFtbjRnQyJrBo4QxZ81acHP7Q==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -1104,9 +1105,9 @@
"node": "^18.13.0 || >=20.9.0" "node": "^18.13.0 || >=20.9.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "17.0.3", "@angular/common": "17.0.4",
"@angular/core": "17.0.3", "@angular/core": "17.0.4",
"@angular/platform-browser": "17.0.3", "@angular/platform-browser": "17.0.4",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
@ -3608,18 +3609,18 @@
} }
}, },
"node_modules/@ng-bootstrap/ng-bootstrap": { "node_modules/@ng-bootstrap/ng-bootstrap": {
"version": "15.1.2", "version": "16.0.0",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-15.1.2.tgz", "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-16.0.0.tgz",
"integrity": "sha512-mM2yiGnt9o7KZLIFp8K1vjfmVfu7HR3d8dhH5SszfArbgn9DvvQ4P5D5TDGygzyBSzeyZe18p7I8rX8vgA6DKw==", "integrity": "sha512-+FJ3e6cX9DW2t7021Ji3oz433rk3+4jLfqzU+Jyx6/vJz1dIOaML3EAY6lYuW4TLiXgMPOMvs6KzPFALGh4Lag==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": "^16.0.0", "@angular/common": "^17.0.0",
"@angular/core": "^16.0.0", "@angular/core": "^17.0.0",
"@angular/forms": "^16.0.0", "@angular/forms": "^17.0.0",
"@angular/localize": "^16.0.0", "@angular/localize": "^17.0.0",
"@popperjs/core": "^2.11.6", "@popperjs/core": "^2.11.8",
"rxjs": "^6.5.3 || ^7.4.0" "rxjs": "^6.5.3 || ^7.4.0"
} }
}, },
@ -3714,9 +3715,9 @@
} }
}, },
"node_modules/@ngtools/webpack": { "node_modules/@ngtools/webpack": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.0.3.tgz",
"integrity": "sha512-IfiWIBY1GntfJFV/U1CSOHZ7zF5p0zFMFzux7/iGXUXit299LTdJ5mZTe9++lFcH6dPHgEPWlinuYAfzorY4ng==", "integrity": "sha512-H39WQ/tM6kOErfiyU6QkPasMtuOZHbm6INkirSR3ol4e93o6gLJ0ptwg3IQlyGtZK2QexWagPC6jzsdGIaN3iw==",
"dev": true, "dev": true,
"engines": { "engines": {
"node": "^18.13.0 || >=20.9.0", "node": "^18.13.0 || >=20.9.0",
@ -4176,13 +4177,13 @@
} }
}, },
"node_modules/@schematics/angular": { "node_modules/@schematics/angular": {
"version": "17.0.1", "version": "17.0.3",
"resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.1.tgz", "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-17.0.3.tgz",
"integrity": "sha512-BacI1fQsEXNYkfJzDJn3CsUSc9A4M7nhXtvt3XjceUhOqUp2AR4uIeUwDOrpLnkRwv5+rZLafUnRN3k01WUJOw==", "integrity": "sha512-pFHAqHMNm2WLoquJD4osSA/OAgH+wsFayPuqQnKjDEzeVW/YfJSbUksJ2iFt+uSfrhc/VxPf6pmGBMzi+9d0ng==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@angular-devkit/core": "17.0.1", "@angular-devkit/core": "17.0.3",
"@angular-devkit/schematics": "17.0.1", "@angular-devkit/schematics": "17.0.3",
"jsonc-parser": "3.2.0" "jsonc-parser": "3.2.0"
}, },
"engines": { "engines": {
@ -4387,9 +4388,9 @@
} }
}, },
"node_modules/@types/connect-history-api-fallback": { "node_modules/@types/connect-history-api-fallback": {
"version": "1.5.3", "version": "1.5.4",
"resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.3.tgz", "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz",
"integrity": "sha512-6mfQ6iNvhSKCZJoY6sIG3m0pKkdUcweVNOLuBBKvoWGzl2yRxOJcYOTRyLKt3nxXvBLJWa6QkW//tgbIwJehmA==", "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/express-serve-static-core": "*", "@types/express-serve-static-core": "*",
@ -4748,9 +4749,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/luxon": { "node_modules/@types/luxon": {
"version": "3.3.4", "version": "3.3.5",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.4.tgz", "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.3.5.tgz",
"integrity": "sha512-H9OXxv4EzJwE75aTPKpiGXJq+y4LFxjpsdgKwSmr503P5DkWc3AG7VAFYrFNVvqemT5DfgZJV9itYhqBHSGujA==", "integrity": "sha512-1cyf6Ge/94zlaWIZA2ei1pE6SZ8xpad2hXaYa5JEFiaUH0YS494CZwyi4MXNpXD9oEuv6ZH0Bmh0e7F9sPhmZA==",
"dev": true "dev": true
}, },
"node_modules/@types/mime": { "node_modules/@types/mime": {
@ -4760,18 +4761,18 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.9.2", "version": "20.10.0",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.2.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.0.tgz",
"integrity": "sha512-WHZXKFCEyIUJzAwh3NyyTHYSR35SevJ6mZ1nWwJafKtiQbqRTIKSRcw3Ma3acqgsent3RRDqeVwpHntMk+9irg==", "integrity": "sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"undici-types": "~5.26.4" "undici-types": "~5.26.4"
} }
}, },
"node_modules/@types/node-forge": { "node_modules/@types/node-forge": {
"version": "1.3.9", "version": "1.3.10",
"resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.9.tgz", "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.10.tgz",
"integrity": "sha512-meK88cx/sTalPSLSoCzkiUB4VPIFHmxtXm5FaaqRDqBX2i/Sy8bJ4odsan0b20RBjPh06dAQ+OTTdnyQyhJZyQ==", "integrity": "sha512-y6PJDYN4xYBxwd22l+OVH35N+1fCYWiuC3aiP2SlXVE6Lo7SS+rSx9r89hLxrP4pn6n1lBGhHJ12pj3F3Mpttw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
@ -4841,25 +4842,25 @@
} }
}, },
"node_modules/@types/ws": { "node_modules/@types/ws": {
"version": "8.5.9", "version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.9.tgz", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-jbdrY0a8lxfdTp/+r7Z4CkycbOFN8WX+IOchLJr3juT/xzbJ8URyTVSJ/hvNdadTgM1mnedb47n+Y31GsFnQlg==", "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "6.11.0", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.13.0.tgz",
"integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", "integrity": "sha512-HTvbSd0JceI2GW5DHS3R9zbarOqjkM9XDR7zL8eCsBUO/eSiHcoNE7kSL5sjGXmVa9fjH5LCfHDXNnH4QLp7tQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@eslint-community/regexpp": "^4.5.1", "@eslint-community/regexpp": "^4.5.1",
"@typescript-eslint/scope-manager": "6.11.0", "@typescript-eslint/scope-manager": "6.13.0",
"@typescript-eslint/type-utils": "6.11.0", "@typescript-eslint/type-utils": "6.13.0",
"@typescript-eslint/utils": "6.11.0", "@typescript-eslint/utils": "6.13.0",
"@typescript-eslint/visitor-keys": "6.11.0", "@typescript-eslint/visitor-keys": "6.13.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"graphemer": "^1.4.0", "graphemer": "^1.4.0",
"ignore": "^5.2.4", "ignore": "^5.2.4",
@ -4884,6 +4885,132 @@
} }
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.0.tgz",
"integrity": "sha512-2x0K2/CujsokIv+LN2T0l5FVDMtsCjkUyYtlcY4xxnxLAW+x41LXr16duoicHpGtLhmtN7kqvuFJ3zbz00Ikhw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"@typescript-eslint/visitor-keys": "6.13.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.0.tgz",
"integrity": "sha512-YHufAmZd/yP2XdoD3YeFEjq+/Tl+myhzv+GJHSOz+ro/NFGS84mIIuLU3pVwUcauSmwlCrVXbBclkn1HfjY0qQ==",
"dev": true,
"dependencies": {
"@typescript-eslint/typescript-estree": "6.13.0",
"@typescript-eslint/utils": "6.13.0",
"debug": "^4.3.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.0.tgz",
"integrity": "sha512-oXg7DFxx/GmTrKXKKLSoR2rwiutOC7jCQ5nDH5p5VS6cmHE1TcPTaYQ0VPSSUvj7BnNqCgQ/NXcTBxn59pfPTQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.0.tgz",
"integrity": "sha512-IT4O/YKJDoiy/mPEDsfOfp+473A9GVqXlBKckfrAOuVbTqM8xbc0LuqyFCcgeFWpqu3WjQexolgqN2CuWBYbog==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"@typescript-eslint/visitor-keys": "6.13.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.0.tgz",
"integrity": "sha512-V+txaxARI8yznDkcQ6FNRXxG+T37qT3+2NsDTZ/nKLxv6VfGrRhTnuvxPUxpVuWWr+eVeIxU53PioOXbz8ratQ==",
"dev": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.4.0",
"@types/json-schema": "^7.0.12",
"@types/semver": "^7.5.0",
"@typescript-eslint/scope-manager": "6.13.0",
"@typescript-eslint/types": "6.13.0",
"@typescript-eslint/typescript-estree": "6.13.0",
"semver": "^7.5.4"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
"eslint": "^7.0.0 || ^8.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.0.tgz",
"integrity": "sha512-UQklteCEMCRoq/1UhKFZsHv5E4dN1wQSzJoxTfABasWk1HgJRdg1xNUve/Kv/Sdymt4x+iEzpESOqRFlQr/9Aw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
@ -4918,15 +5045,15 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "6.11.0", "version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.13.0.tgz",
"integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", "integrity": "sha512-VpG+M7GNhHLI/aTDctqAV0XbzB16vf+qDX9DXuMZSe/0bahzDA9AKZB15NDbd+D9M4cDsJvfkbGOA7qiZ/bWJw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "6.11.0", "@typescript-eslint/scope-manager": "6.13.0",
"@typescript-eslint/types": "6.11.0", "@typescript-eslint/types": "6.13.0",
"@typescript-eslint/typescript-estree": "6.11.0", "@typescript-eslint/typescript-estree": "6.13.0",
"@typescript-eslint/visitor-keys": "6.11.0", "@typescript-eslint/visitor-keys": "6.13.0",
"debug": "^4.3.4" "debug": "^4.3.4"
}, },
"engines": { "engines": {
@ -4945,6 +5072,113 @@
} }
} }
}, },
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.0.tgz",
"integrity": "sha512-2x0K2/CujsokIv+LN2T0l5FVDMtsCjkUyYtlcY4xxnxLAW+x41LXr16duoicHpGtLhmtN7kqvuFJ3zbz00Ikhw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"@typescript-eslint/visitor-keys": "6.13.0"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.0.tgz",
"integrity": "sha512-oXg7DFxx/GmTrKXKKLSoR2rwiutOC7jCQ5nDH5p5VS6cmHE1TcPTaYQ0VPSSUvj7BnNqCgQ/NXcTBxn59pfPTQ==",
"dev": true,
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.0.tgz",
"integrity": "sha512-IT4O/YKJDoiy/mPEDsfOfp+473A9GVqXlBKckfrAOuVbTqM8xbc0LuqyFCcgeFWpqu3WjQexolgqN2CuWBYbog==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"@typescript-eslint/visitor-keys": "6.13.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.5.4",
"ts-api-utils": "^1.0.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.0.tgz",
"integrity": "sha512-UQklteCEMCRoq/1UhKFZsHv5E4dN1wQSzJoxTfABasWk1HgJRdg1xNUve/Kv/Sdymt4x+iEzpESOqRFlQr/9Aw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "6.13.0",
"eslint-visitor-keys": "^3.4.1"
},
"engines": {
"node": "^16.0.0 || >=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser/node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/parser/node_modules/semver": {
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
"bin": {
"semver": "bin/semver.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/@typescript-eslint/parser/node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
"node_modules/@typescript-eslint/scope-manager": { "node_modules/@typescript-eslint/scope-manager": {
"version": "6.11.0", "version": "6.11.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz",
@ -11489,9 +11723,9 @@
} }
}, },
"node_modules/ngx-color-picker": { "node_modules/ngx-color-picker": {
"version": "15.0.0", "version": "16.0.0",
"resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-15.0.0.tgz", "resolved": "https://registry.npmjs.org/ngx-color-picker/-/ngx-color-picker-16.0.0.tgz",
"integrity": "sha512-+7wK8Pz9pm7ywJQOWELRcLYO9J0q4giF4b5QFxq8J3kEcHsUBn0hKOpBbGud+UmNnOwbJVgU2rhyRpGIDUCDJw==", "integrity": "sha512-Dk2FvcbebD6STZSVzkI5oFHOlTrrNC5bOHh+YVaFgaWuWrVUdVIJm68ocUvTgr/qxTEJjrfcnRnS4wi7BJ2hKg==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
@ -11502,16 +11736,16 @@
} }
}, },
"node_modules/ngx-extended-pdf-viewer": { "node_modules/ngx-extended-pdf-viewer": {
"version": "18.1.6", "version": "18.1.9",
"resolved": "https://registry.npmjs.org/ngx-extended-pdf-viewer/-/ngx-extended-pdf-viewer-18.1.6.tgz", "resolved": "https://registry.npmjs.org/ngx-extended-pdf-viewer/-/ngx-extended-pdf-viewer-18.1.9.tgz",
"integrity": "sha512-MZi1bVgMtEqNGTW5QvjEAq7+x2Fje7yejxopBB+1yysevETt8cs9XYtiwiHWBpS7VhmOc0+vPgeK9FvZ4/27Tg==", "integrity": "sha512-puISS6h1JoHObo0BZK68EhlWlI215AWP5RJ5D86yuWiBgVYeNUa8JrEVnaJtQ/bI6WbfvleaBe8NBwKnM0Bqsw==",
"dependencies": { "dependencies": {
"lodash.deburr": "^4.1.0", "lodash.deburr": "^4.1.0",
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },
"peerDependencies": { "peerDependencies": {
"@angular/common": ">=12.0.0 <17.0.0", "@angular/common": ">=12.0.0 <18.0.0",
"@angular/core": ">=12.0.0 <17.0.0" "@angular/core": ">=12.0.0 <18.0.0"
} }
}, },
"node_modules/ngx-file-drop": { "node_modules/ngx-file-drop": {
@ -11557,10 +11791,23 @@
"@angular/core": ">=2.0.0" "@angular/core": ">=2.0.0"
} }
}, },
"node_modules/ngx-toaster": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ngx-toaster/-/ngx-toaster-1.0.1.tgz",
"integrity": "sha512-2XxTCT7+EWffb8wDpMLiFhwwZJ4B36Y1RM4m3rgG2cCt/8edsj3UzvqZjapF5wKwB+Jz8lVuVYJ94Hztcj83Cg==",
"peerDependencies": {
"@angular/common": ">=4.3.0 || >5.0.0",
"@angular/compiler": ">=4.3.0 || >5.0.0",
"@angular/core": ">=4.3.0 || >5.0.0",
"@angular/forms": ">=4.3.0 || >5.0.0",
"rxjs": ">=5.4.3",
"typescript": ">=2.3.0"
}
},
"node_modules/ngx-toastr": { "node_modules/ngx-toastr": {
"version": "17.0.2", "version": "18.0.0",
"resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-17.0.2.tgz", "resolved": "https://registry.npmjs.org/ngx-toastr/-/ngx-toastr-18.0.0.tgz",
"integrity": "sha512-KehiPx6bkbiUyJbabf0ZA04+ASumS8r/y4wPsUOMI9OrBvBcfq27UQmWuQKoVR8E+9y4Pq7eZkSg2kvxNvpxTA==", "integrity": "sha512-jZ3rOG6kygl8ittY8OltIMSo47P1VStuS01igm3MZXK6InJwHVvxU7wDHI/HGMlXSyNvWncyOuFHnnMEAifsew==",
"dependencies": { "dependencies": {
"tslib": "^2.3.0" "tslib": "^2.3.0"
}, },

View File

@ -13,21 +13,21 @@
}, },
"private": true, "private": true,
"dependencies": { "dependencies": {
"@angular/animations": "^17.0.3", "@angular/animations": "^17.0.4",
"@angular/cdk": "^17.0.1", "@angular/cdk": "^17.0.1",
"@angular/common": "^17.0.3", "@angular/common": "^17.0.4",
"@angular/compiler": "^17.0.3", "@angular/compiler": "^17.0.4",
"@angular/core": "^17.0.3", "@angular/core": "^17.0.4",
"@angular/forms": "^17.0.3", "@angular/forms": "^17.0.4",
"@angular/localize": "^17.0.3", "@angular/localize": "^17.0.4",
"@angular/platform-browser": "^17.0.3", "@angular/platform-browser": "^17.0.4",
"@angular/platform-browser-dynamic": "^17.0.3", "@angular/platform-browser-dynamic": "^17.0.4",
"@angular/router": "^17.0.3", "@angular/router": "^17.0.4",
"@fortawesome/fontawesome-free": "^6.4.2", "@fortawesome/fontawesome-free": "^6.4.2",
"@iharbeck/ngx-virtual-scroller": "^17.0.0", "@iharbeck/ngx-virtual-scroller": "^17.0.0",
"@iplab/ngx-file-upload": "^17.0.0", "@iplab/ngx-file-upload": "^17.0.0",
"@microsoft/signalr": "^7.0.12", "@microsoft/signalr": "^7.0.12",
"@ng-bootstrap/ng-bootstrap": "^15.1.2", "@ng-bootstrap/ng-bootstrap": "^16.0.0",
"@ngneat/transloco": "^6.0.0", "@ngneat/transloco": "^6.0.0",
"@ngneat/transloco-locale": "^5.1.1", "@ngneat/transloco-locale": "^5.1.1",
"@ngneat/transloco-persist-lang": "^5.0.0", "@ngneat/transloco-persist-lang": "^5.0.0",
@ -43,12 +43,13 @@
"ng-circle-progress": "^1.7.1", "ng-circle-progress": "^1.7.1",
"ng-lazyload-image": "^9.1.3", "ng-lazyload-image": "^9.1.3",
"ng-select2-component": "^13.0.9", "ng-select2-component": "^13.0.9",
"ngx-color-picker": "^15.0.0", "ngx-color-picker": "^16.0.0",
"ngx-extended-pdf-viewer": "^18.1.6", "ngx-extended-pdf-viewer": "^18.1.9",
"ngx-file-drop": "^16.0.0", "ngx-file-drop": "^16.0.0",
"ngx-slider-v2": "^17.0.0", "ngx-slider-v2": "^17.0.0",
"ngx-stars": "^1.6.5", "ngx-stars": "^1.6.5",
"ngx-toastr": "^17.0.2", "ngx-toaster": "^1.0.1",
"ngx-toastr": "^18.0.0",
"nosleep.js": "^0.12.0", "nosleep.js": "^0.12.0",
"rxjs": "^7.8.0", "rxjs": "^7.8.0",
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
@ -57,20 +58,20 @@
"zone.js": "^0.14.2" "zone.js": "^0.14.2"
}, },
"devDependencies": { "devDependencies": {
"@angular-devkit/build-angular": "^17.0.1", "@angular-devkit/build-angular": "^17.0.3",
"@angular-eslint/builder": "^17.1.0", "@angular-eslint/builder": "^17.1.0",
"@angular-eslint/eslint-plugin": "^17.1.0", "@angular-eslint/eslint-plugin": "^17.1.0",
"@angular-eslint/eslint-plugin-template": "^17.1.0", "@angular-eslint/eslint-plugin-template": "^17.1.0",
"@angular-eslint/schematics": "^17.1.0", "@angular-eslint/schematics": "^17.1.0",
"@angular-eslint/template-parser": "^17.1.0", "@angular-eslint/template-parser": "^17.1.0",
"@angular/cli": "^17.0.1", "@angular/cli": "^17.0.3",
"@angular/compiler-cli": "^17.0.3", "@angular/compiler-cli": "^17.0.4",
"@types/d3": "^7.4.3", "@types/d3": "^7.4.3",
"@types/file-saver": "^2.0.7", "@types/file-saver": "^2.0.7",
"@types/luxon": "^3.3.4", "@types/luxon": "^3.3.5",
"@types/node": "^20.9.2", "@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.11.0", "@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.11.0", "@typescript-eslint/parser": "^6.13.0",
"eslint": "^8.54.0", "eslint": "^8.54.0",
"jsonminify": "^0.4.2", "jsonminify": "^0.4.2",
"karma-coverage": "~2.2.0", "karma-coverage": "~2.2.0",

View File

@ -160,7 +160,7 @@
<button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i></button> <button class="btn btn-secondary col-2 col-xs-1" (click)="closeReader()"><i class="fa fa-times-circle" aria-hidden="true"></i></button>
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1" <button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled" [disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)" title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page"> title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i> <i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
</button> </button>
</div> </div>

View File

@ -102,10 +102,29 @@ const elementLevelStyles = ['line-height', 'font-family'];
]) ])
], ],
standalone: true, standalone: true,
imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink, NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip, BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective] imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink,
NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip,
BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective]
}) })
export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
private readonly route = inject(ActivatedRoute);
private readonly router = inject(Router);
private readonly accountService = inject(AccountService);
private readonly seriesService = inject(SeriesService);
private readonly readerService = inject(ReaderService);
private readonly renderer = inject(Renderer2);
private readonly navService = inject(NavService);
private readonly toastr = inject(ToastrService);
private readonly domSanitizer = inject(DomSanitizer);
private readonly bookService = inject(BookService);
private readonly memberService = inject(MemberService);
private readonly scrollService = inject(ScrollService);
private readonly utilityService = inject(UtilityService);
private readonly libraryService = inject(LibraryService);
private readonly themeService = inject(ThemeService);
private readonly cdRef = inject(ChangeDetectorRef);
libraryId!: number; libraryId!: number;
seriesId!: number; seriesId!: number;
volumeId!: number; volumeId!: number;
@ -194,11 +213,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
*/ */
page: SafeHtml | undefined = undefined; page: SafeHtml | undefined = undefined;
/** /**
* Next Chapter Id. This is not garunteed to be a valid ChapterId. Prefetched on page load (non-blocking). * Next Chapter Id. This is not guaranteed to be a valid ChapterId. Prefetched on page load (non-blocking).
*/ */
nextChapterId: number = CHAPTER_ID_NOT_FETCHED; nextChapterId: number = CHAPTER_ID_NOT_FETCHED;
/** /**
* Previous Chapter Id. This is not garunteed to be a valid ChapterId. Prefetched on page load (non-blocking). * Previous Chapter Id. This is not guaranteed to be a valid ChapterId. Prefetched on page load (non-blocking).
*/ */
prevChapterId: number = CHAPTER_ID_NOT_FETCHED; prevChapterId: number = CHAPTER_ID_NOT_FETCHED;
/** /**
@ -480,13 +499,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
return (this.windowHeight) - (this.topOffset * 2) + 'px'; return (this.windowHeight) - (this.topOffset * 2) + 'px';
} }
constructor(@Inject(DOCUMENT) private document: Document) {
constructor(private route: ActivatedRoute, private router: Router, private accountService: AccountService,
private seriesService: SeriesService, private readerService: ReaderService, private location: Location,
private renderer: Renderer2, private navService: NavService, private toastr: ToastrService,
private domSanitizer: DomSanitizer, private bookService: BookService, private memberService: MemberService,
private scrollService: ScrollService, private utilityService: UtilityService, private libraryService: LibraryService,
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService, private readonly cdRef: ChangeDetectorRef) {
this.navService.hideNavBar(); this.navService.hideNavBar();
this.themeService.clearThemes(); this.themeService.clearThemes();
this.navService.hideSideNav(); this.navService.hideSideNav();
@ -1620,7 +1633,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Responsible for handling pagination only // Responsible for handling pagination only
handleContainerClick(event: MouseEvent) { handleContainerClick(event: MouseEvent) {
if (this.drawerOpen || ['action-bar'].some(className => (event.target as Element).classList.contains(className))) { if (this.drawerOpen || ['action-bar', 'offcanvas-backdrop'].some(className => (event.target as Element).classList.contains(className))) {
return; return;
} }

View File

@ -3,11 +3,12 @@
<app-image borderRadius=".25rem .25rem 0 0" height="230px" width="158px" classes="extreme-blur" [imageUrl]="imageUrl"></app-image> <app-image borderRadius=".25rem .25rem 0 0" height="230px" width="158px" classes="extreme-blur" [imageUrl]="imageUrl"></app-image>
<div class="card-overlay"></div> <div class="card-overlay"></div>
<ng-container *ngIf="overlayInformation | safeHtml as info"> <ng-container *ngIf="entity.title | safeHtml as info">
<div class="overlay-information overlay-information--centered" *ngIf="info !== '' || info !== undefined"> <div class="overlay-information overlay-information--centered" *ngIf="info !== ''">
<div class="position-relative"> <div class="position-relative">
<span class="card-title library mx-auto" style="width: auto;"> <span class="card-title library mx-auto" style="width: auto;">
<i class="fa-regular fa-clock mb-2" style="font-size: 26px" aria-hidden="true"></i> <i class="fa-regular fa-clock mb-2" style="font-size: 26px" aria-hidden="true"></i>
<div class="upcoming-header">Upcoming</div>
<span [innerHTML]="info"></span> <span [innerHTML]="info"></span>
</span> </span>
</div> </div>

View File

@ -6,6 +6,10 @@
background-color: transparent; background-color: transparent;
} }
.upcoming-header {
font-size: 16px;
}
.card-title { .card-title {
width: 146px; width: 146px;
} }

View File

@ -1,4 +1,4 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input} from '@angular/core'; import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {ImageComponent} from "../../shared/image/image.component"; import {ImageComponent} from "../../shared/image/image.component";
import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter"; import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter";
@ -14,7 +14,7 @@ import {translate} from "@ngneat/transloco";
styleUrl: './next-expected-card.component.scss', styleUrl: './next-expected-card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush
}) })
export class NextExpectedCardComponent { export class NextExpectedCardComponent implements OnInit {
private readonly cdRef = inject(ChangeDetectorRef); private readonly cdRef = inject(ChangeDetectorRef);
/** /**
@ -25,19 +25,9 @@ export class NextExpectedCardComponent {
* This is the entity we are representing. It will be returned if an action is executed. * This is the entity we are representing. It will be returned if an action is executed.
*/ */
@Input({required: true}) entity!: NextExpectedChapter; @Input({required: true}) entity!: NextExpectedChapter;
/**
* Additional information to show on the overlay area. Will always render.
*/
@Input() overlayInformation: string = '';
title: string = ''; title: string = '';
ngOnInit(): void { ngOnInit(): void {
const tokens = this.entity.title.split(':');
this.overlayInformation = `<div>${tokens[0]}</div><div>${tokens[1]}</div>`;
if (this.entity.expectedDate) { if (this.entity.expectedDate) {
const utcPipe = new UtcToLocalTimePipe(); const utcPipe = new UtcToLocalTimePipe();
this.title = translate('next-expected-card.title', {date: utcPipe.transform(this.entity.expectedDate, 'shortDate')}); this.title = translate('next-expected-card.title', {date: utcPipe.transform(this.entity.expectedDate, 'shortDate')});

View File

@ -67,7 +67,7 @@ import { FittingIconPipe } from '../../../_pipes/fitting-icon.pipe';
import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scroller.component'; import { InfiniteScrollerComponent } from '../infinite-scroller/infinite-scroller.component';
import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive'; import { SwipeDirective } from '../../../ng-swipe/ng-swipe.directive';
import { LoadingComponent } from '../../../shared/loading/loading.component'; import { LoadingComponent } from '../../../shared/loading/loading.component';
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco"; import {translate, TranslocoDirective} from "@ngneat/transloco";
const PREFETCH_PAGES = 10; const PREFETCH_PAGES = 10;

View File

@ -22,23 +22,24 @@
<p>{{t('validate-description')}}</p> <p>{{t('validate-description')}}</p>
<div class="row g-0"> <div class="row g-0">
<ngb-accordion #a="ngbAccordion"> <div ngbAccordion #accordion="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess"> @for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<ng-container *ngIf="fileToProcess.validateSummary as summary"> <div ngbAccordionItem *ngIf="fileToProcess.validateSummary as summary">
<ng-template ngbPanelTitle> <h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container> <ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template> </button>
</h5>
<ng-template ngbPanelContent> <div ngbAccordionCollapse>
<ng-container *ngIf="summary.results.length > 0; else noValidateIssues"> <div ngbAccordionBody>
@if(summary.results.length > 0) {
<h5>{{t('validate-warning')}}</h5> <h5>{{t('validate-warning')}}</h5>
<ol class="list-group list-group-numbered list-group-flush" > <ol class="list-group list-group-numbered list-group-flush" >
<li class="list-group-item no-hover" *ngFor="let result of summary.results" <li class="list-group-item no-hover" *ngFor="let result of summary.results"
[innerHTML]="result | cblConflictReason | safeHtml"> [innerHTML]="result | cblConflictReason | safeHtml">
</li> </li>
</ol> </ol>
</ng-container> } @else {
<ng-template #noValidateIssues>
<div class="justify-content-center col"> <div class="justify-content-center col">
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="flex-shrink-0"> <div class="flex-shrink-0">
@ -50,11 +51,12 @@
</div> </div>
{{t('validate-no-issue-description')}} {{t('validate-no-issue-description')}}
</div> </div>
</ng-template> }
</ng-template> </div>
</ng-container> </div>
</ngb-panel> </div>
</ngb-accordion> }
</div>
</div> </div>
</ng-container> </ng-container>
@ -62,45 +64,53 @@
<div class="row g-0"> <div class="row g-0">
<p>{{t('dry-run-description')}}</p> <p>{{t('dry-run-description')}}</p>
<ngb-accordion #a="ngbAccordion"> <div ngbAccordion #a="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess"> @for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<ng-container *ngIf="fileToProcess.dryRunSummary as summary"> <div ngbAccordionItem *ngIf="fileToProcess.dryRunSummary as summary">
<ng-template ngbPanelTitle> <h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container> <ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template> </button>
</h5>
<ng-template ngbPanelContent> <div ngbAccordionCollapse>
<div ngbAccordionBody>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container> <ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</ng-template> </div>
</ng-container> </div>
</ngb-panel> </div>
</ngb-accordion> }
</div>
</div> </div>
</ng-container> </ng-container>
<ng-container *ngIf="currentStepIndex === Step.Finalize"> <ng-container *ngIf="currentStepIndex === Step.Finalize">
<div class="row g-0"> <div class="row g-0">
<ngb-accordion #a="ngbAccordion"> <div ngbAccordion #a="ngbAccordion">
<ngb-panel *ngFor="let fileToProcess of filesToProcess"> @for(fileToProcess of filesToProcess; track fileToProcess.fileName) {
<ng-container *ngIf="fileToProcess.finalizeSummary as summary"> <div ngbAccordionItem *ngIf="fileToProcess.finalizeSummary as summary">
<ng-template ngbPanelTitle> <h5 ngbAccordionHeader>
<button ngbAccordionButton>
<ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container> <ng-container [ngTemplateOutlet]="heading" [ngTemplateOutletContext]="{ summary: summary, filename: fileToProcess.fileName }"></ng-container>
</ng-template> </button>
</h5>
<ng-template ngbPanelContent> <div ngbAccordionCollapse>
<div ngbAccordionBody>
<ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container> <ng-container [ngTemplateOutlet]="resultsList" [ngTemplateOutletContext]="{ summary: summary }"></ng-container>
</ng-template> </div>
</ng-container> </div>
</ngb-panel> </div>
</ngb-accordion> }
</div>
</div> </div>
</ng-container> </ng-container>
</div> </div>
<ng-template #resultsList let-summary="summary"> <ng-template #resultsList let-summary="summary">
<ul class="list-group list-group-flush"> <ul class="list-group list-group-flush">
<li class="list-group-item no-hover" *ngFor="let result of summary.results" @for(result of summary.results; track result.order) {
<li class="list-group-item no-hover"
innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li> innerHTML="{{result.order + 1}}. {{result | cblConflictReason | safeHtml}}"></li>
}
</ul> </ul>
</ng-template> </ng-template>

View File

@ -16,7 +16,7 @@ import {CommonModule} from "@angular/common";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe"; import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
import {CblConflictReasonPipe} from "../../../_pipes/cbl-conflict-reason.pipe"; import {CblConflictReasonPipe} from "../../../_pipes/cbl-conflict-reason.pipe";
import {CblImportResultPipe} from "../../../_pipes/cbl-import-result.pipe"; import {CblImportResultPipe} from "../../../_pipes/cbl-import-result.pipe";
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
interface FileStep { interface FileStep {
fileName: string; fileName: string;
@ -48,8 +48,6 @@ export class ImportCblModalComponent {
@ViewChild('fileUpload') fileUpload!: ElementRef<HTMLInputElement>; @ViewChild('fileUpload') fileUpload!: ElementRef<HTMLInputElement>;
translocoService = inject(TranslocoService);
fileUploadControl = new FormControl<undefined | Array<File>>(undefined, [ fileUploadControl = new FormControl<undefined | Array<File>>(undefined, [
FileUploadValidators.accept(['.cbl']), FileUploadValidators.accept(['.cbl']),
]); ]);
@ -61,10 +59,10 @@ export class ImportCblModalComponent {
isLoading: boolean = false; isLoading: boolean = false;
steps: Array<TimelineStep> = [ steps: Array<TimelineStep> = [
{title: this.translocoService.translate('import-cbl-modal.import-step'), index: Step.Import, active: true, icon: 'fa-solid fa-file-arrow-up'}, {title: translate('import-cbl-modal.import-step'), index: Step.Import, active: true, icon: 'fa-solid fa-file-arrow-up'},
{title: this.translocoService.translate('import-cbl-modal.validate-cbl-step'), index: Step.Validate, active: false, icon: 'fa-solid fa-spell-check'}, {title: translate('import-cbl-modal.validate-cbl-step'), index: Step.Validate, active: false, icon: 'fa-solid fa-spell-check'},
{title: this.translocoService.translate('import-cbl-modal.dry-run-step'), index: Step.DryRun, active: false, icon: 'fa-solid fa-gears'}, {title: translate('import-cbl-modal.dry-run-step'), index: Step.DryRun, active: false, icon: 'fa-solid fa-gears'},
{title: this.translocoService.translate('import-cbl-modal.final-import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'}, {title: translate('import-cbl-modal.final-import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'},
]; ];
currentStepIndex = this.steps[0].index; currentStepIndex = this.steps[0].index;
@ -103,7 +101,7 @@ export class ImportCblModalComponent {
case Step.Import: case Step.Import:
const files = this.uploadForm.get('files')?.value; const files = this.uploadForm.get('files')?.value;
if (!files) { if (!files) {
this.toastr.error(this.translocoService.translate('toasts.select-files-warning')); this.toastr.error(translate('toasts.select-files-warning'));
return; return;
} }
// Load each file into filesToProcess and group their data // Load each file into filesToProcess and group their data
@ -236,7 +234,7 @@ export class ImportCblModalComponent {
this.isLoading = false; this.isLoading = false;
this.currentStepIndex++; this.currentStepIndex++;
this.toastr.success(this.translocoService.translate('toasts.reading-list-imported')); this.toastr.success(translate('toasts.reading-list-imported'));
this.cdRef.markForCheck(); this.cdRef.markForCheck();
}); });
} }

View File

@ -5,7 +5,7 @@
<div class="col-lg-10 col-md-12 pe-2"> <div class="col-lg-10 col-md-12 pe-2">
<div class="mb-3"> <div class="mb-3">
<label for="item--{{i}}" class="visually-hidden">{{label}}</label> <label for="item--{{i}}" class="visually-hidden">{{label}}</label>
<input type="text" class="form-control" formControlName="link{{i}}" attr.id="item--{{i}}"> <input type="text" class="form-control" formControlName="link{{i}}" id="item--{{i}}">
</div> </div>
</div> </div>
<div class="col-lg-2"> <div class="col-lg-2">

View File

@ -27,7 +27,8 @@ enum TabID {
selector: 'app-customize-dashboard-modal', selector: 'app-customize-dashboard-modal',
standalone: true, standalone: true,
imports: [CommonModule, SafeHtmlPipe, TranslocoDirective, DraggableOrderedListComponent, ReadingListItemComponent, DashboardStreamListItemComponent, imports: [CommonModule, SafeHtmlPipe, TranslocoDirective, DraggableOrderedListComponent, ReadingListItemComponent, DashboardStreamListItemComponent,
NgbNav, NgbNavContent, NgbNavLink, NgbNavItem, NgbNavOutlet, CustomizeDashboardStreamsComponent, CustomizeSidenavStreamsComponent, ManageExternalSourcesComponent, ManageSmartFiltersComponent], NgbNav, NgbNavContent, NgbNavLink, NgbNavItem, NgbNavOutlet, CustomizeDashboardStreamsComponent, CustomizeSidenavStreamsComponent,
ManageExternalSourcesComponent, ManageSmartFiltersComponent],
templateUrl: './customize-dashboard-modal.component.html', templateUrl: './customize-dashboard-modal.component.html',
styleUrls: ['./customize-dashboard-modal.component.scss'], styleUrls: ['./customize-dashboard-modal.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -13,6 +13,7 @@ import {forkJoin} from "rxjs";
import {TranslocoDirective} from "@ngneat/transloco"; import {TranslocoDirective} from "@ngneat/transloco";
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms"; import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
import {FilterPipe} from "../../../_pipes/filter.pipe"; import {FilterPipe} from "../../../_pipes/filter.pipe";
import {Breakpoint, UtilityService} from "../../../shared/_services/utility.service";
@Component({ @Component({
selector: 'app-customize-dashboard-streams', selector: 'app-customize-dashboard-streams',
@ -31,6 +32,7 @@ export class CustomizeDashboardStreamsComponent {
private readonly dashboardService = inject(DashboardService); private readonly dashboardService = inject(DashboardService);
private readonly filterService = inject(FilterService); private readonly filterService = inject(FilterService);
private readonly cdRef = inject(ChangeDetectorRef); private readonly cdRef = inject(ChangeDetectorRef);
private readonly utilityService = inject(UtilityService);
listForm: FormGroup = new FormGroup({ listForm: FormGroup = new FormGroup({
'filterQuery': new FormControl('', []) 'filterQuery': new FormControl('', [])
@ -50,7 +52,7 @@ export class CustomizeDashboardStreamsComponent {
this.items = results[0]; this.items = results[0];
// After 100 items, drag and drop is disabled to use virtualization // After 100 items, drag and drop is disabled to use virtualization
if (this.items.length > 100) { if (this.items.length > 100 || this.utilityService.getActiveBreakpoint() <= Breakpoint.Tablet) {
this.accessibilityMode = true; this.accessibilityMode = true;
} }

View File

@ -2,7 +2,7 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
DestroyRef, EventEmitter, DestroyRef,
HostListener, HostListener,
inject, inject,
OnDestroy OnDestroy
@ -29,9 +29,9 @@ import {FilterPipe} from "../../../_pipes/filter.pipe";
import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component"; import {BulkOperationsComponent} from "../../../cards/bulk-operations/bulk-operations.component";
import {Action, ActionItem} from "../../../_services/action-factory.service"; import {Action, ActionItem} from "../../../_services/action-factory.service";
import {BulkSelectionService} from "../../../cards/bulk-selection.service"; import {BulkSelectionService} from "../../../cards/bulk-selection.service";
import {filter, tap} from "rxjs/operators"; import {tap} from "rxjs/operators";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {KEY_CODES} from "../../../shared/_services/utility.service"; import {Breakpoint, KEY_CODES, UtilityService} from "../../../shared/_services/utility.service";
@Component({ @Component({
selector: 'app-customize-sidenav-streams', selector: 'app-customize-sidenav-streams',
@ -43,7 +43,6 @@ import {KEY_CODES} from "../../../shared/_services/utility.service";
}) })
export class CustomizeSidenavStreamsComponent implements OnDestroy { export class CustomizeSidenavStreamsComponent implements OnDestroy {
//@Input({required: true}) parentScrollElem!: Element | Window;
items: SideNavStream[] = []; items: SideNavStream[] = [];
smartFilters: SmartFilter[] = []; smartFilters: SmartFilter[] = [];
externalSources: ExternalSource[] = []; externalSources: ExternalSource[] = [];
@ -108,6 +107,7 @@ export class CustomizeSidenavStreamsComponent implements OnDestroy {
private readonly cdRef = inject(ChangeDetectorRef); private readonly cdRef = inject(ChangeDetectorRef);
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
public readonly bulkSelectionService = inject(BulkSelectionService); public readonly bulkSelectionService = inject(BulkSelectionService);
public readonly utilityService = inject(UtilityService);
@HostListener('document:keydown.shift', ['$event']) @HostListener('document:keydown.shift', ['$event'])
handleKeypress(event: KeyboardEvent) { handleKeypress(event: KeyboardEvent) {
@ -172,7 +172,7 @@ export class CustomizeSidenavStreamsComponent implements OnDestroy {
this.items = results[0]; this.items = results[0];
// After X items, drag and drop is disabled to use virtualization // After X items, drag and drop is disabled to use virtualization
if (this.items.length > this.virtualizeAfter) { if (this.items.length > this.virtualizeAfter || this.utilityService.getActiveBreakpoint() <= Breakpoint.Tablet) {
this.pageOperationsForm.get('accessibilityMode')?.setValue(true); this.pageOperationsForm.get('accessibilityMode')?.setValue(true);
} }

View File

@ -66,10 +66,10 @@
</button> </button>
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
<p>{{t('help-us-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files" rel="noopener noreferrer" target="_blank" referrerpolicy="no-refer">{{t('help-us-part-2')}}</a> {{t('help-us-part-3')}}</p> <p>{{t('help-us-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">{{t('help-us-part-2')}}</a> {{t('help-us-part-3')}}</p>
</div> </div>
<div class="row mt-2"> <div class="row mt-2">
<p>{{t('naming-conventions-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files/scanner#introduction" rel="noopener noreferrer" target="_blank" referrerpolicy="no-refer">{{t('naming-conventions-part-2')}}</a> {{t('naming-conventions-part-3')}}</p> <p>{{t('naming-conventions-part-1')}}<a href="https://wiki.kavitareader.com/en/guides/managing-your-files/scanner#introduction" rel="noopener noreferrer" target="_blank" referrerpolicy="no-referrer">{{t('naming-conventions-part-2')}}</a> {{t('naming-conventions-part-3')}}</p>
</div> </div>
</ng-template> </ng-template>
</li> </li>

View File

@ -11,7 +11,6 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import { LineChartModule } from '@swimlane/ngx-charts'; import { LineChartModule } from '@swimlane/ngx-charts';
import { NgIf, NgFor, AsyncPipe } from '@angular/common'; import { NgIf, NgFor, AsyncPipe } from '@angular/common';
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco"; import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe";
const options: Intl.DateTimeFormatOptions = { month: "short", day: "numeric" }; const options: Intl.DateTimeFormatOptions = { month: "short", day: "numeric" };
@ -32,10 +31,10 @@ export class ReadingActivityComponent implements OnInit {
@Input() isAdmin: boolean = true; @Input() isAdmin: boolean = true;
@Input() individualUserMode: boolean = false; @Input() individualUserMode: boolean = false;
private readonly utcDatePipe = new UtcToLocalTimePipe();
private readonly destroyRef = inject(DestroyRef); private readonly destroyRef = inject(DestroyRef);
private readonly translocoService = inject(TranslocoService); //private readonly translocoService = inject(TranslocoService);
private readonly cdRef = inject(ChangeDetectorRef); private readonly statService = inject(StatisticsService);
private readonly memberService = inject(MemberService);
view: [number, number] = [0, 400]; view: [number, number] = [0, 400];
formGroup: FormGroup = new FormGroup({ formGroup: FormGroup = new FormGroup({
@ -45,14 +44,14 @@ export class ReadingActivityComponent implements OnInit {
users$: Observable<Member[]> | undefined; users$: Observable<Member[]> | undefined;
data$: Observable<Array<PieDataItem>>; data$: Observable<Array<PieDataItem>>;
timePeriods = TimePeriods; timePeriods = TimePeriods;
mangaFormatPipe = new MangaFormatPipe(this.translocoService); //mangaFormatPipe = new MangaFormatPipe(this.translocoService);
constructor(private statService: StatisticsService, private memberService: MemberService) { constructor() {
this.data$ = this.formGroup.valueChanges.pipe( this.data$ = this.formGroup.valueChanges.pipe(
switchMap(_ => this.statService.getReadCountByDay(this.formGroup.get('users')!.value, this.formGroup.get('days')!.value)), switchMap(_ => this.statService.getReadCountByDay(this.formGroup.get('users')!.value, this.formGroup.get('days')!.value)),
map(data => { map(data => {
const gList = data.reduce((formats, entry) => { const gList = data.reduce((formats, entry) => {
const formatTranslated = this.mangaFormatPipe.transform(entry.format); const formatTranslated = this.statService.mangaFormatPipe.transform(entry.format);
if (!formats[formatTranslated]) { if (!formats[formatTranslated]) {
formats[formatTranslated] = { formats[formatTranslated] = {
name: formatTranslated, name: formatTranslated,
@ -76,7 +75,10 @@ export class ReadingActivityComponent implements OnInit {
} }
ngOnInit(): void { ngOnInit(): void {
this.users$ = (this.isAdmin ? this.memberService.getMembers() : of([])).pipe(filter(_ => this.isAdmin), takeUntilDestroyed(this.destroyRef), shareReplay()); this.users$ = (this.isAdmin ? this.memberService.getMembers() : of([])).pipe(
filter(_ => this.isAdmin),
takeUntilDestroyed(this.destroyRef),
shareReplay());
this.formGroup.get('users')?.setValue(this.userId, {emitValue: true}); this.formGroup.get('users')?.setValue(this.userId, {emitValue: true});
if (!this.isAdmin) { if (!this.isAdmin) {

View File

@ -7,7 +7,7 @@
"name": "GPL-3.0", "name": "GPL-3.0",
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
}, },
"version": "0.7.10.16" "version": "0.7.10.17"
}, },
"servers": [ "servers": [
{ {