Enhance app management with 'IsRecommended' feature
CI / Backend (build + test) (push) Successful in 1m15s
CI / Frontend (lint + typecheck + build) (push) Successful in 31s

- Added 'IsRecommended' property to app-related data models, allowing apps to be marked as recommended.
- Updated API endpoints for creating and updating apps to include 'IsRecommended' in request bodies.
- Modified database schema to accommodate the new 'IsRecommended' field.
- Enhanced frontend components to display recommended apps with a star icon and updated forms to manage this property.
- Improved sorting logic in app listings to prioritize recommended apps.
- Updated documentation to reflect changes in API and data models.
This commit is contained in:
Leonid Pershin
2026-07-14 19:19:43 +03:00
parent 701c3a1d51
commit 8c53fcded2
24 changed files with 1154 additions and 31 deletions
@@ -0,0 +1,100 @@
using PnvPanel.Application.Apps;
using PnvPanel.Application.Tests.TestSupport;
using PnvPanel.Domain.Apps;
using Xunit;
namespace PnvPanel.Application.Tests.Apps;
public class ListAppsQueryHandlerTests
{
[Fact]
public async Task Handle_SortsRecommendedFirstWithinEachOsGroup()
{
using var dbContext = InMemoryDbContextFactory.Create();
var regular1 = ClientApp.Create(
"Regular 1",
new Uri("https://a.example.com"),
OsPlatform.Android,
null,
null,
10,
isRecommended: false
);
var recommended = ClientApp.Create(
"Recommended",
new Uri("https://b.example.com"),
OsPlatform.Android,
null,
null,
20,
isRecommended: true
);
var regular2 = ClientApp.Create(
"Regular 2",
new Uri("https://c.example.com"),
OsPlatform.Android,
null,
null,
30,
isRecommended: false
);
dbContext.ClientApps.AddRange(regular1, recommended, regular2);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new ListAppsQueryHandler(dbContext);
var result = await handler.Handle(new ListAppsQuery(), CancellationToken.None);
Assert.True(result.IsSuccess);
var androidApps = result.Value[OsPlatform.Android];
Assert.Equal(["Recommended", "Regular 1", "Regular 2"], androidApps.Select(a => a.Name));
}
[Fact]
public async Task Handle_ExcludesDisabledApps()
{
using var dbContext = InMemoryDbContextFactory.Create();
var enabled = ClientApp.Create(
"Enabled",
new Uri("https://a.example.com"),
OsPlatform.IOS,
null,
null,
10,
isRecommended: false
);
var disabled = ClientApp.Create(
"Disabled",
new Uri("https://b.example.com"),
OsPlatform.IOS,
null,
null,
20,
isRecommended: false
);
disabled.Update(
disabled.Name,
disabled.DownloadUrl,
disabled.OperatingSystem,
null,
null,
disabled.SortOrder,
isEnabled: false,
isRecommended: false
);
dbContext.ClientApps.AddRange(enabled, disabled);
await dbContext.SaveChangesAsync(CancellationToken.None);
var handler = new ListAppsQueryHandler(dbContext);
var result = await handler.Handle(new ListAppsQuery(), CancellationToken.None);
Assert.True(result.IsSuccess);
var iosApps = result.Value[OsPlatform.IOS];
Assert.Equal(["Enabled"], iosApps.Select(a => a.Name));
}
}