Enhance Smartphones API with pagination and count endpoints

- Updated SmartphonesRequestBuilder to support pagination with skip and take query parameters.
- Added new CountRequestBuilder for counting smartphones by letter and year.
- Modified SmartphonesController to handle pagination in GetSmartphonesByLetterAsync and GetSmartphonesByYearAsync methods.
- Updated Search.razor and Search.razor.cs to implement infinite scrolling and display total smartphone count.
- Enhanced SmartphonesService to support pagination and count retrieval for smartphones.
- Added new Count endpoints for prototypes and by year in the API client.
This commit is contained in:
2026-05-09 14:16:41 +01:00
parent 4e469dcf31
commit a44b0cc87f
12 changed files with 702 additions and 124 deletions

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.Smartphones.ByLetter.Item.Count
{
/// <summary>
/// Builds and executes requests for operations under \smartphones\by-letter\{c}\count
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}/count", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}/count", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -1,6 +1,7 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.Smartphones.ByLetter.Item.Count;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
@@ -17,12 +18,17 @@ namespace Marechai.ApiClient.Smartphones.ByLetter.Item
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithCItemRequestBuilder : BaseRequestBuilder
{
/// <summary>The count property</summary>
public global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder Count
{
get => new global::Marechai.ApiClient.Smartphones.ByLetter.Item.Count.CountRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithCItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}", pathParameters)
public WithCItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}{?skip*,take*}", pathParameters)
{
}
/// <summary>
@@ -30,7 +36,7 @@ namespace Marechai.ApiClient.Smartphones.ByLetter.Item
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithCItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}", rawUrl)
public WithCItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-letter/{c}{?skip*,take*}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.MachineDto&gt;</returns>
@@ -39,11 +45,11 @@ namespace Marechai.ApiClient.Smartphones.ByLetter.Item
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder.WithCItemRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder.WithCItemRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
@@ -58,11 +64,11 @@ namespace Marechai.ApiClient.Smartphones.ByLetter.Item
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder.WithCItemRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder.WithCItemRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
@@ -79,12 +85,22 @@ namespace Marechai.ApiClient.Smartphones.ByLetter.Item
{
return new global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class WithCItemRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
[QueryParameter("skip")]
public int? Skip { get; set; }
[QueryParameter("take")]
public int? Take { get; set; }
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithCItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
public partial class WithCItemRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByLetter.Item.WithCItemRequestBuilder.WithCItemRequestBuilderGetQueryParameters>
{
}
}

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.Smartphones.ByYear.Item.Count
{
/// <summary>
/// Builds and executes requests for operations under \smartphones\by-year\{year}\count
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}/count", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}/count", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -1,6 +1,7 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.Smartphones.ByYear.Item.Count;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
@@ -17,12 +18,17 @@ namespace Marechai.ApiClient.Smartphones.ByYear.Item
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithYearItemRequestBuilder : BaseRequestBuilder
{
/// <summary>The count property</summary>
public global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder Count
{
get => new global::Marechai.ApiClient.Smartphones.ByYear.Item.Count.CountRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithYearItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}", pathParameters)
public WithYearItemRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}{?skip*,take*}", pathParameters)
{
}
/// <summary>
@@ -30,7 +36,7 @@ namespace Marechai.ApiClient.Smartphones.ByYear.Item
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public WithYearItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}", rawUrl)
public WithYearItemRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/by-year/{year}{?skip*,take*}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.MachineDto&gt;</returns>
@@ -39,11 +45,11 @@ namespace Marechai.ApiClient.Smartphones.ByYear.Item
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder.WithYearItemRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder.WithYearItemRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
@@ -58,11 +64,11 @@ namespace Marechai.ApiClient.Smartphones.ByYear.Item
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder.WithYearItemRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder.WithYearItemRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
@@ -79,12 +85,22 @@ namespace Marechai.ApiClient.Smartphones.ByYear.Item
{
return new global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class WithYearItemRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
[QueryParameter("skip")]
public int? Skip { get; set; }
[QueryParameter("take")]
public int? Take { get; set; }
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class WithYearItemRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
public partial class WithYearItemRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.Smartphones.ByYear.Item.WithYearItemRequestBuilder.WithYearItemRequestBuilderGetQueryParameters>
{
}
}

View File

@@ -0,0 +1,91 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System;
namespace Marechai.ApiClient.Smartphones.Prototypes.Count
{
/// <summary>
/// Builds and executes requests for operations under \smartphones\prototypes\count
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilder : BaseRequestBuilder
{
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes/count", pathParameters)
{
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public CountRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes/count", rawUrl)
{
}
/// <returns>A <see cref="int"/></returns>
/// <param name="cancellationToken">Cancellation token to use when cancelling requests</param>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<int?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
var errorMapping = new Dictionary<string, ParsableFactory<IParsable>>
{
{ "400", global::Marechai.ApiClient.Models.ProblemDetails.CreateFromDiscriminatorValue },
};
return await RequestAdapter.SendPrimitiveAsync<int?>(requestInfo, errorMapping, cancellationToken).ConfigureAwait(false);
}
/// <returns>A <see cref="RequestInformation"/></returns>
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
requestInfo.Configure(requestConfiguration);
requestInfo.Headers.TryAdd("Accept", "text/plain;q=0.9");
return requestInfo;
}
/// <summary>
/// Returns a request builder with the provided arbitrary URL. Using this method means any other path or query parameters are ignored.
/// </summary>
/// <returns>A <see cref="global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder"/></returns>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
public global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder WithUrl(string rawUrl)
{
return new global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder(rawUrl, RequestAdapter);
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class CountRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
{
}
}
}
#pragma warning restore CS0618

View File

@@ -1,6 +1,7 @@
// <auto-generated/>
#pragma warning disable CS0618
using Marechai.ApiClient.Models;
using Marechai.ApiClient.Smartphones.Prototypes.Count;
using Microsoft.Kiota.Abstractions.Extensions;
using Microsoft.Kiota.Abstractions.Serialization;
using Microsoft.Kiota.Abstractions;
@@ -17,12 +18,17 @@ namespace Marechai.ApiClient.Smartphones.Prototypes
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class PrototypesRequestBuilder : BaseRequestBuilder
{
/// <summary>The count property</summary>
public global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder Count
{
get => new global::Marechai.ApiClient.Smartphones.Prototypes.Count.CountRequestBuilder(PathParameters, RequestAdapter);
}
/// <summary>
/// Instantiates a new <see cref="global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder"/> and sets the default values.
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public PrototypesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes", pathParameters)
public PrototypesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes{?skip*,take*}", pathParameters)
{
}
/// <summary>
@@ -30,7 +36,7 @@ namespace Marechai.ApiClient.Smartphones.Prototypes
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public PrototypesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes", rawUrl)
public PrototypesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones/prototypes{?skip*,take*}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.MachineDto&gt;</returns>
@@ -39,11 +45,11 @@ namespace Marechai.ApiClient.Smartphones.Prototypes
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder.PrototypesRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder.PrototypesRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
@@ -58,11 +64,11 @@ namespace Marechai.ApiClient.Smartphones.Prototypes
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder.PrototypesRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder.PrototypesRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
@@ -79,12 +85,22 @@ namespace Marechai.ApiClient.Smartphones.Prototypes
{
return new global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class PrototypesRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
[QueryParameter("skip")]
public int? Skip { get; set; }
[QueryParameter("take")]
public int? Take { get; set; }
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class PrototypesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
public partial class PrototypesRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.Smartphones.Prototypes.PrototypesRequestBuilder.PrototypesRequestBuilderGetQueryParameters>
{
}
}

View File

@@ -64,7 +64,7 @@ namespace Marechai.ApiClient.Smartphones
/// </summary>
/// <param name="pathParameters">Path parameters for the request</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SmartphonesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones", pathParameters)
public SmartphonesRequestBuilder(Dictionary<string, object> pathParameters, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones{?skip*,take*}", pathParameters)
{
}
/// <summary>
@@ -72,7 +72,7 @@ namespace Marechai.ApiClient.Smartphones
/// </summary>
/// <param name="rawUrl">The raw URL to use for the request builder.</param>
/// <param name="requestAdapter">The request adapter to use to execute the requests.</param>
public SmartphonesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones", rawUrl)
public SmartphonesRequestBuilder(string rawUrl, IRequestAdapter requestAdapter) : base(requestAdapter, "{+baseurl}/smartphones{?skip*,take*}", rawUrl)
{
}
/// <returns>A List&lt;global::Marechai.ApiClient.Models.MachineDto&gt;</returns>
@@ -81,11 +81,11 @@ namespace Marechai.ApiClient.Smartphones
/// <exception cref="global::Marechai.ApiClient.Models.ProblemDetails">When receiving a 400 status code</exception>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>?> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder.SmartphonesRequestBuilderGetQueryParameters>>? requestConfiguration = default, CancellationToken cancellationToken = default)
{
#nullable restore
#else
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
public async Task<List<global::Marechai.ApiClient.Models.MachineDto>> GetAsync(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder.SmartphonesRequestBuilderGetQueryParameters>> requestConfiguration = default, CancellationToken cancellationToken = default)
{
#endif
var requestInfo = ToGetRequestInformation(requestConfiguration);
@@ -100,11 +100,11 @@ namespace Marechai.ApiClient.Smartphones
/// <param name="requestConfiguration">Configuration for the request such as headers, query parameters, and middleware options.</param>
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
#nullable enable
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>>? requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder.SmartphonesRequestBuilderGetQueryParameters>>? requestConfiguration = default)
{
#nullable restore
#else
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<DefaultQueryParameters>> requestConfiguration = default)
public RequestInformation ToGetRequestInformation(Action<RequestConfiguration<global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder.SmartphonesRequestBuilderGetQueryParameters>> requestConfiguration = default)
{
#endif
var requestInfo = new RequestInformation(Method.GET, UrlTemplate, PathParameters);
@@ -121,12 +121,22 @@ namespace Marechai.ApiClient.Smartphones
{
return new global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder(rawUrl, RequestAdapter);
}
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
#pragma warning disable CS1591
public partial class SmartphonesRequestBuilderGetQueryParameters
#pragma warning restore CS1591
{
[QueryParameter("skip")]
public int? Skip { get; set; }
[QueryParameter("take")]
public int? Take { get; set; }
}
/// <summary>
/// Configuration for the request such as headers, query parameters, and middleware options.
/// </summary>
[Obsolete("This class is deprecated. Please use the generic RequestConfiguration class generated by the generator.")]
[global::System.CodeDom.Compiler.GeneratedCode("Kiota", "1.0.0")]
public partial class SmartphonesRequestBuilderGetRequestConfiguration : RequestConfiguration<DefaultQueryParameters>
public partial class SmartphonesRequestBuilderGetRequestConfiguration : RequestConfiguration<global::Marechai.ApiClient.Smartphones.SmartphonesRequestBuilder.SmartphonesRequestBuilderGetQueryParameters>
{
}
}

View File

@@ -1,5 +1,5 @@
{
"descriptionHash": "4E08D950A0D9E3D626B689AD691DC24BCA2478D82B6D0A73A6A1D80383CB7897EE7428C2FDC1AA1C8BEB56CB97EAA53C97F2EB535A39CAC7A257024ECBC52AB7",
"descriptionHash": "DE8F8DE43202CD0088961BD423206A3DD0A796459AA0CBC25F38B876E309CA69E68277197810ABC0C9EE80930AC13381BB614334CF9677C87DCB8241BE192BA8",
"descriptionLocation": "../../../../../tmp/openapi.json",
"lockFileVersion": "1.0.0",
"kiotaVersion": "1.31.1",

View File

@@ -25,6 +25,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Marechai.Data;
using Marechai.Data.Dtos;
@@ -70,75 +71,135 @@ public class SmartphonesController(MarechaiContext context) : ControllerBase
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<MachineDto>> GetSmartphonesByLetterAsync(char c) => context.Machines.Include(m => m.Company)
.Where(m =>
m.Type == MachineType.Smartphone &&
EF.Functions.Like(m.Name, $"{c}%"))
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name))
.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync();
public Task<List<MachineDto>> GetSmartphonesByLetterAsync(char c, [FromQuery] int? skip = null,
[FromQuery] int? take = null,
CancellationToken cancellationToken = default)
{
IQueryable<Machine> ordered = context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone &&
EF.Functions.Like(m.Name, $"{c}%"))
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name));
if(skip.HasValue) ordered = ordered.Skip(skip.Value);
if(take.HasValue) ordered = ordered.Take(take.Value);
return ordered.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync(cancellationToken);
}
[HttpGet("by-letter/{c}/count")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetSmartphonesByLetterCountAsync(char c, CancellationToken cancellationToken = default) =>
context.Machines
.Where(m => m.Type == MachineType.Smartphone && EF.Functions.Like(m.Name, $"{c}%"))
.CountAsync(cancellationToken);
[HttpGet("by-year/{year:int}")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<MachineDto>> GetSmartphonesByYearAsync(int year) => context.Machines.Include(m => m.Company)
.Where(m =>
m.Type == MachineType.Smartphone &&
m.Introduced != null &&
m.Introduced.Value.Year == year)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name))
.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync();
public Task<List<MachineDto>> GetSmartphonesByYearAsync(int year, [FromQuery] int? skip = null,
[FromQuery] int? take = null,
CancellationToken cancellationToken = default)
{
IQueryable<Machine> ordered = context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone &&
m.Introduced != null &&
m.Introduced.Value.Year == year)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name));
if(skip.HasValue) ordered = ordered.Skip(skip.Value);
if(take.HasValue) ordered = ordered.Take(take.Value);
return ordered.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync(cancellationToken);
}
[HttpGet("by-year/{year:int}/count")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetSmartphonesByYearCountAsync(int year, CancellationToken cancellationToken = default) =>
context.Machines
.Where(m => m.Type == MachineType.Smartphone &&
m.Introduced != null &&
m.Introduced.Value.Year == year)
.CountAsync(cancellationToken);
[HttpGet]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<MachineDto>> GetSmartphonesAsync() => context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name))
.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync();
public Task<List<MachineDto>> GetSmartphonesAsync([FromQuery] int? skip = null, [FromQuery] int? take = null,
CancellationToken cancellationToken = default)
{
IQueryable<Machine> ordered = context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name));
if(skip.HasValue) ordered = ordered.Skip(skip.Value);
if(take.HasValue) ordered = ordered.Take(take.Value);
return ordered.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Introduced = m.Introduced
})
.ToListAsync(cancellationToken);
}
[HttpGet("prototypes")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<List<MachineDto>> GetPrototypesAsync() => context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone &&
m.Prototype)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name))
.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Prototype = m.Prototype
})
.ToListAsync();
public Task<List<MachineDto>> GetPrototypesAsync([FromQuery] int? skip = null, [FromQuery] int? take = null,
CancellationToken cancellationToken = default)
{
IQueryable<Machine> ordered = context.Machines.Include(m => m.Company)
.Where(m => m.Type == MachineType.Smartphone && m.Prototype)
.OrderBy(m => MarechaiContext.NaturalSortKey(m.Company.Name))
.ThenBy(m => MarechaiContext.NaturalSortKey(m.Name));
if(skip.HasValue) ordered = ordered.Skip(skip.Value);
if(take.HasValue) ordered = ordered.Take(take.Value);
return ordered.Select(m => new MachineDto
{
Id = m.Id,
Name = m.Name,
Company = m.Company.Name,
Prototype = m.Prototype
})
.ToListAsync(cancellationToken);
}
[HttpGet("prototypes/count")]
[AllowAnonymous]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public Task<int> GetPrototypesCountAsync(CancellationToken cancellationToken = default) =>
context.Machines
.Where(m => m.Type == MachineType.Smartphone && m.Prototype)
.CountAsync(cancellationToken);
[HttpGet("companies")]
[AllowAnonymous]

View File

@@ -32,7 +32,7 @@
@inherits OwningComponentBase<SmartphonesService>
@inject IStringLocalizer<SmartphonesService> L
@if(_smartphones is null)
@if(!_initialized)
{
<div class="d-flex justify-center pa-8">
<MudProgressCircular Color="Color.Primary" Indeterminate="true"/>
@@ -56,17 +56,25 @@
}
</MudPaper>
@if(_smartphones?.Count > 0)
@if(_total > 0)
{
<MudText Typo="Typo.body2" Class="mb-2">@string.Format(L["{0} smartphones found in the database."], _smartphones.Count)</MudText>
<MudList T="MachineDto" Dense="true">
@foreach(MachineDto smartphone in _smartphones)
<MudText Typo="Typo.body2" Class="mb-2">@string.Format(L["{0} smartphones found in the database."], _total)</MudText>
<div id="@_scrollerId" style="height: calc(100dvh - 220px); min-height: 360px; overflow-y: auto;">
<MudList T="MachineDto" Dense="true">
<Virtualize Items="_smartphones" Context="smartphone" ItemSize="64">
<MudListItem T="MachineDto" Href="@($"/machine/{smartphone.Id}")" Icon="@Icons.Material.Filled.Smartphone">
@smartphone.Company @smartphone.Name
</MudListItem>
</Virtualize>
</MudList>
<div id="@_sentinelId" style="height: 1px;"></div>
@if(_loadingMore)
{
<MudListItem T="MachineDto" Href="@($"/machine/{smartphone.Id}")" Icon="@Icons.Material.Filled.Smartphone">
@smartphone.Company @smartphone.Name
</MudListItem>
<div class="d-flex justify-center pa-3">
<MudProgressCircular Color="Color.Primary" Size="Size.Small" Indeterminate="true"/>
</div>
}
</MudList>
</div>
}
else
{

View File

@@ -23,21 +23,33 @@
// Copyright © 2003-2026 Natalia Portillo
*******************************************************************************/
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Marechai.ApiClient.Models;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
namespace Marechai.Pages.Smartphones;
public partial class Search
public partial class Search : IAsyncDisposable
{
char? _character;
List<MachineDto> _smartphones;
bool _loaded;
bool _showPrototypes;
string _lastStartingCharacter;
int? _lastYear;
const int _pageSize = 100;
readonly string _sentinelId = $"smartphones-search-sentinel-{Guid.NewGuid():N}";
readonly string _scrollerId = $"smartphones-search-scroll-{Guid.NewGuid():N}";
readonly List<MachineDto> _smartphones = [];
char? _character;
bool _initialized;
bool _loadingMore;
bool _observerRegistered;
bool _showPrototypes;
string _lastStartingCharacter;
int? _lastYear;
int _total;
DotNetObjectReference<Search> _selfRef;
[Inject]
IJSRuntime JS { get; set; }
[Parameter]
public int? Year { get; set; }
@@ -45,30 +57,29 @@ public partial class Search
[Parameter]
public string StartingCharacter { get; set; }
bool HasMore => _smartphones.Count < _total;
protected override void OnParametersSet()
{
if(Year == _lastYear && StartingCharacter == _lastStartingCharacter) return;
_lastYear = Year;
_lastStartingCharacter = StartingCharacter;
_loaded = false;
_initialized = false;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(_loaded) return;
_character = null;
_showPrototypes = false;
if(StartingCharacter == "prototypes")
if(!_initialized)
{
_showPrototypes = true;
_smartphones = await Service.GetPrototypesAsync();
}
else
{
if(!string.IsNullOrWhiteSpace(StartingCharacter) && StartingCharacter.Length == 1)
_character = null;
_showPrototypes = false;
_total = 0;
_smartphones.Clear();
if(StartingCharacter == "prototypes")
_showPrototypes = true;
else if(!string.IsNullOrWhiteSpace(StartingCharacter) && StartingCharacter.Length == 1)
{
_character = StartingCharacter[0];
@@ -79,14 +90,117 @@ public partial class Search
if(_character < '0' || _character > '9' && _character < 'A' || _character > 'Z') _character = null;
}
if(_character.HasValue) _smartphones = await Service.GetSmartphonesByLetterAsync(_character.Value);
// Stop the previous observer (if any) before swapping in a new
// batch — the sentinel/scroller IDs are stable per page instance,
// but a route-parameter change forces a fresh data load and the
// observer must be re-armed once the new sentinel has rendered.
await UnobserveAsync();
if(Year.HasValue && _smartphones is null) _smartphones = await Service.GetSmartphonesByYearAsync(Year.Value);
// Fetch the total count + first page in parallel so the page is
// fully painted in one round-trip pair.
Task<int> countTask = GetCurrentCountAsync();
Task<List<MachineDto>> firstPageTask = FetchBatchAsync(0, _pageSize);
_smartphones ??= await Service.GetSmartphonesAsync();
await Task.WhenAll(countTask, firstPageTask);
_total = countTask.Result;
_smartphones.AddRange(firstPageTask.Result);
_initialized = true;
StateHasChanged();
return;
}
_loaded = true;
// Register the IntersectionObserver once the sentinel has been
// rendered. The observer keeps the same target reference, so this is
// a one-shot until UnobserveAsync clears the flag.
if(!_observerRegistered && HasMore)
{
_selfRef ??= DotNetObjectReference.Create(this);
try
{
await JS.InvokeVoidAsync("marechaiInfiniteScroll.observe", _sentinelId, _selfRef, $"#{_scrollerId}",
"200px");
_observerRegistered = true;
}
catch(JSDisconnectedException)
{
// Circuit gone — nothing to do.
}
}
}
/// <summary>
/// Invoked by the IntersectionObserver in <c>infinite-scroll.js</c> when the
/// sentinel scrolls into view. Loads the next page, appends it to the
/// in-memory list, and re-renders.
/// </summary>
[JSInvokable]
public async Task OnSentinelVisibleAsync()
{
if(_loadingMore || !HasMore) return;
_loadingMore = true;
StateHasChanged();
try
{
List<MachineDto> next = await FetchBatchAsync(_smartphones.Count, _pageSize);
_smartphones.AddRange(next);
// If the very last page just arrived, stop the observer so it
// doesn't keep firing as the user scrolls past the new tail.
if(!HasMore) await UnobserveAsync();
}
finally
{
_loadingMore = false;
StateHasChanged();
}
}
Task<int> GetCurrentCountAsync()
{
if(_showPrototypes) return Service.GetPrototypesCountAsync();
if(_character.HasValue) return Service.GetSmartphonesByLetterCountAsync(_character.Value);
if(Year.HasValue) return Service.GetSmartphonesByYearCountAsync(Year.Value);
return Service.GetSmartphonesCountAsync();
}
Task<List<MachineDto>> FetchBatchAsync(int skip, int take)
{
if(_showPrototypes) return Service.GetPrototypesAsync(skip, take);
if(_character.HasValue) return Service.GetSmartphonesByLetterAsync(_character.Value, skip, take);
if(Year.HasValue) return Service.GetSmartphonesByYearAsync(Year.Value, skip, take);
return Service.GetSmartphonesAsync(skip, take);
}
async Task UnobserveAsync()
{
if(!_observerRegistered) return;
try
{
await JS.InvokeVoidAsync("marechaiInfiniteScroll.unobserve", _sentinelId);
}
catch(JSDisconnectedException)
{
// Circuit gone; nothing to do.
}
_observerRegistered = false;
}
public async ValueTask DisposeAsync()
{
await UnobserveAsync();
_selfRef?.Dispose();
}
}

View File

@@ -25,6 +25,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Marechai.ApiClient.Models;
@@ -74,11 +75,16 @@ public class SmartphonesService(Marechai.ApiClient.Client client)
}
}
public async Task<List<MachineDto>> GetSmartphonesByLetterAsync(char c)
public async Task<List<MachineDto>> GetSmartphonesByLetterAsync(char c, int? skip = null, int? take = null,
CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.ByLetter[c.ToString()].GetAsync();
List<MachineDto> machines = await client.Smartphones.ByLetter[c.ToString()].GetAsync(config =>
{
if(skip.HasValue) config.QueryParameters.Skip = skip.Value;
if(take.HasValue) config.QueryParameters.Take = take.Value;
}, cancellationToken);
return machines ?? [];
}
@@ -88,11 +94,31 @@ public class SmartphonesService(Marechai.ApiClient.Client client)
}
}
public async Task<List<MachineDto>> GetSmartphonesByYearAsync(int year)
public async Task<int> GetSmartphonesByLetterCountAsync(char c, CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.ByYear[year].GetAsync();
int? count = await client.Smartphones.ByLetter[c.ToString()].Count
.GetAsync(cancellationToken: cancellationToken);
return count ?? 0;
}
catch
{
return 0;
}
}
public async Task<List<MachineDto>> GetSmartphonesByYearAsync(int year, int? skip = null, int? take = null,
CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.ByYear[year].GetAsync(config =>
{
if(skip.HasValue) config.QueryParameters.Skip = skip.Value;
if(take.HasValue) config.QueryParameters.Take = take.Value;
}, cancellationToken);
return machines ?? [];
}
@@ -102,11 +128,30 @@ public class SmartphonesService(Marechai.ApiClient.Client client)
}
}
public async Task<List<MachineDto>> GetSmartphonesAsync()
public async Task<int> GetSmartphonesByYearCountAsync(int year, CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.GetAsync();
int? count = await client.Smartphones.ByYear[year].Count.GetAsync(cancellationToken: cancellationToken);
return count ?? 0;
}
catch
{
return 0;
}
}
public async Task<List<MachineDto>> GetSmartphonesAsync(int? skip = null, int? take = null,
CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.GetAsync(config =>
{
if(skip.HasValue) config.QueryParameters.Skip = skip.Value;
if(take.HasValue) config.QueryParameters.Take = take.Value;
}, cancellationToken);
return machines ?? [];
}
@@ -116,11 +161,16 @@ public class SmartphonesService(Marechai.ApiClient.Client client)
}
}
public async Task<List<MachineDto>> GetPrototypesAsync()
public async Task<List<MachineDto>> GetPrototypesAsync(int? skip = null, int? take = null,
CancellationToken cancellationToken = default)
{
try
{
List<MachineDto> machines = await client.Smartphones.Prototypes.GetAsync();
List<MachineDto> machines = await client.Smartphones.Prototypes.GetAsync(config =>
{
if(skip.HasValue) config.QueryParameters.Skip = skip.Value;
if(take.HasValue) config.QueryParameters.Take = take.Value;
}, cancellationToken);
return machines ?? [];
}
@@ -130,6 +180,20 @@ public class SmartphonesService(Marechai.ApiClient.Client client)
}
}
public async Task<int> GetPrototypesCountAsync(CancellationToken cancellationToken = default)
{
try
{
int? count = await client.Smartphones.Prototypes.Count.GetAsync(cancellationToken: cancellationToken);
return count ?? 0;
}
catch
{
return 0;
}
}
public async Task<List<CompanyDto>> GetCompaniesAsync()
{
try