mirror of
https://github.com/claunia/marechai.git
synced 2026-07-08 17:57:08 +00:00
Add Internet Archive URL to Book model and UI
- Added a new column `InternetArchiveUrl` to the `Books` table in the database. - Updated the `Book` model to include the `InternetArchiveUrl` property with validation attributes. - Modified the `BooksController` to handle the new `InternetArchiveUrl` property in various endpoints. - Enhanced the admin UI to allow input and display of the Internet Archive link in `BookDialog`, `BookImportDialog`, and `Books` pages. - Updated localization files to include translations for "Internet Archive link" and "View on Internet Archive".
This commit is contained in:
@@ -30,6 +30,14 @@ namespace Marechai.ApiClient.Models
|
||||
public int? Edition { get; set; }
|
||||
/// <summary>The id property</summary>
|
||||
public long? Id { get; set; }
|
||||
/// <summary>The internet_archive_url property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
public string? InternetArchiveUrl { get; set; }
|
||||
#nullable restore
|
||||
#else
|
||||
public string InternetArchiveUrl { get; set; }
|
||||
#endif
|
||||
/// <summary>The isbn property</summary>
|
||||
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
||||
#nullable enable
|
||||
@@ -110,6 +118,7 @@ namespace Marechai.ApiClient.Models
|
||||
{ "cover_guid", n => { CoverGuid = n.GetGuidValue(); } },
|
||||
{ "edition", n => { Edition = n.GetIntValue(); } },
|
||||
{ "id", n => { Id = n.GetLongValue(); } },
|
||||
{ "internet_archive_url", n => { InternetArchiveUrl = n.GetStringValue(); } },
|
||||
{ "isbn", n => { Isbn = n.GetStringValue(); } },
|
||||
{ "native_title", n => { NativeTitle = n.GetStringValue(); } },
|
||||
{ "original_cover_extension", n => { OriginalCoverExtension = n.GetStringValue(); } },
|
||||
@@ -134,6 +143,7 @@ namespace Marechai.ApiClient.Models
|
||||
writer.WriteGuidValue("cover_guid", CoverGuid);
|
||||
writer.WriteIntValue("edition", Edition);
|
||||
writer.WriteLongValue("id", Id);
|
||||
writer.WriteStringValue("internet_archive_url", InternetArchiveUrl);
|
||||
writer.WriteStringValue("isbn", Isbn);
|
||||
writer.WriteStringValue("native_title", NativeTitle);
|
||||
writer.WriteStringValue("original_cover_extension", OriginalCoverExtension);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"descriptionHash": "EF8A471973B5B2FC244AF87CD9FEC3CBCD1EAB14311EC2A99398CC11EBC9DDBED82790ACDCB38998C4559BC5F28D358034B1377F0070988F3D34E623FF99B385",
|
||||
"descriptionHash": "354E86D017E9237894C1A6384EC2610C9287C8448F403FF3891932735D4B7068F18D65BAC8ADAEE624D7CBEFDDB36B22670384F9176CFE5F1C35CF172CDA6942",
|
||||
"descriptionLocation": "../../../../../tmp/openapi.json",
|
||||
"lockFileVersion": "1.0.0",
|
||||
"kiotaVersion": "1.31.1",
|
||||
|
||||
@@ -44,4 +44,6 @@ public class BookDto : DocumentBaseDto
|
||||
public Guid? CoverGuid { get; set; }
|
||||
[JsonPropertyName("original_cover_extension")]
|
||||
public string? OriginalCoverExtension { get; set; }
|
||||
[JsonPropertyName("internet_archive_url")]
|
||||
public string? InternetArchiveUrl { get; set; }
|
||||
}
|
||||
10810
Marechai.Database/Migrations/20260510003737_AddBookInternetArchiveUrl.Designer.cs
generated
Normal file
10810
Marechai.Database/Migrations/20260510003737_AddBookInternetArchiveUrl.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Marechai.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class AddBookInternetArchiveUrl : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "InternetArchiveUrl",
|
||||
table: "Books",
|
||||
type: "varchar(2048)",
|
||||
maxLength: 2048,
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "InternetArchiveUrl",
|
||||
table: "Books");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,10 @@ namespace Marechai.Database.Migrations
|
||||
b.Property<int?>("Edition")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("InternetArchiveUrl")
|
||||
.HasMaxLength(2048)
|
||||
.HasColumnType("varchar(2048)");
|
||||
|
||||
b.Property<string>("Isbn")
|
||||
.HasMaxLength(13)
|
||||
.HasColumnType("varchar(13)");
|
||||
|
||||
@@ -39,6 +39,9 @@ public class Book : DocumentBase
|
||||
public long? SourceId { get; set; }
|
||||
public Guid? CoverGuid { get; set; }
|
||||
public string OriginalCoverExtension { get; set; }
|
||||
[Url]
|
||||
[StringLength(2048)]
|
||||
public string InternetArchiveUrl { get; set; }
|
||||
|
||||
public virtual Book Previous { get; set; }
|
||||
public virtual Book Source { get; set; }
|
||||
|
||||
@@ -154,7 +154,8 @@ public class BooksController(
|
||||
Pages = b.Pages,
|
||||
Country = b.Country.Name,
|
||||
CoverGuid = b.CoverGuid,
|
||||
OriginalCoverExtension = b.OriginalCoverExtension
|
||||
OriginalCoverExtension = b.OriginalCoverExtension,
|
||||
InternetArchiveUrl = b.InternetArchiveUrl
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
@@ -200,7 +201,8 @@ public class BooksController(
|
||||
Pages = b.Pages,
|
||||
Country = b.Country.Name,
|
||||
CoverGuid = b.CoverGuid,
|
||||
OriginalCoverExtension = b.OriginalCoverExtension
|
||||
OriginalCoverExtension = b.OriginalCoverExtension,
|
||||
InternetArchiveUrl = b.InternetArchiveUrl
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
@@ -245,7 +247,8 @@ public class BooksController(
|
||||
SourceId = b.SourceId,
|
||||
Country = b.Country.Name,
|
||||
CoverGuid = b.CoverGuid,
|
||||
OriginalCoverExtension = b.OriginalCoverExtension
|
||||
OriginalCoverExtension = b.OriginalCoverExtension,
|
||||
InternetArchiveUrl = b.InternetArchiveUrl
|
||||
})
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
@@ -271,7 +274,8 @@ public class BooksController(
|
||||
SourceId = b.SourceId,
|
||||
Country = b.Country.Name,
|
||||
CoverGuid = b.CoverGuid,
|
||||
OriginalCoverExtension = b.OriginalCoverExtension
|
||||
OriginalCoverExtension = b.OriginalCoverExtension,
|
||||
InternetArchiveUrl = b.InternetArchiveUrl
|
||||
})
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
@@ -326,6 +330,7 @@ public class BooksController(
|
||||
CountryName = b.Country.Name,
|
||||
b.CoverGuid,
|
||||
b.OriginalCoverExtension,
|
||||
b.InternetArchiveUrl,
|
||||
PreviousTitle = b.PreviousId.HasValue ? b.Previous.Title : null,
|
||||
SourceTitle = b.SourceId.HasValue ? b.Source.Title : null
|
||||
})
|
||||
@@ -430,7 +435,8 @@ public class BooksController(
|
||||
SourceId = head.SourceId,
|
||||
Country = head.CountryName,
|
||||
CoverGuid = head.CoverGuid,
|
||||
OriginalCoverExtension = head.OriginalCoverExtension
|
||||
OriginalCoverExtension = head.OriginalCoverExtension,
|
||||
InternetArchiveUrl = head.InternetArchiveUrl
|
||||
};
|
||||
|
||||
List<PersonByBookDto> people = peopleTask.Result.OrderBy(p => p.FullName).ThenBy(p => p.Role).ToList();
|
||||
@@ -475,6 +481,7 @@ public class BooksController(
|
||||
model.Edition = dto.Edition;
|
||||
model.PreviousId = dto.PreviousId;
|
||||
model.SourceId = dto.SourceId;
|
||||
model.InternetArchiveUrl = dto.InternetArchiveUrl;
|
||||
|
||||
await context.News.AddAsync(new News
|
||||
{
|
||||
@@ -512,7 +519,8 @@ public class BooksController(
|
||||
Pages = dto.Pages,
|
||||
Edition = dto.Edition,
|
||||
PreviousId = dto.PreviousId,
|
||||
SourceId = dto.SourceId
|
||||
SourceId = dto.SourceId,
|
||||
InternetArchiveUrl = dto.InternetArchiveUrl
|
||||
};
|
||||
|
||||
await context.Books.AddAsync(model);
|
||||
|
||||
@@ -85,6 +85,14 @@
|
||||
Variant="Variant.Outlined"
|
||||
Clearable="true"
|
||||
Class="mb-4"/>
|
||||
|
||||
<MudTextField @bind-Value="InternetArchiveUrl"
|
||||
Label="@L["Internet Archive link"]"
|
||||
Variant="Variant.Outlined"
|
||||
MaxLength="2048"
|
||||
InputType="InputType.Url"
|
||||
Placeholder="https://archive.org/details/..."
|
||||
Class="mb-4"/>
|
||||
</MudTabPanel>
|
||||
|
||||
@* Tab 2 - Synopsis (only for existing books) *@
|
||||
@@ -469,6 +477,7 @@
|
||||
[Parameter] public long? PreviousId { get; set; }
|
||||
[Parameter] public long? SourceId { get; set; }
|
||||
[Parameter] public bool HasCover { get; set; }
|
||||
[Parameter] public string InternetArchiveUrl { get; set; }
|
||||
|
||||
[Inject] BooksService BooksService { get; set; } = null!;
|
||||
[Inject] TranslationService TranslationService { get; set; } = null!;
|
||||
@@ -988,7 +997,8 @@
|
||||
Published = Published,
|
||||
PublishedPrecision = PublishedPrecision,
|
||||
PreviousId = _selectedPreviousBook?.Id,
|
||||
SourceId = _selectedSourceBook?.Id
|
||||
SourceId = _selectedSourceBook?.Id,
|
||||
InternetArchiveUrl = string.IsNullOrWhiteSpace(InternetArchiveUrl) ? null : InternetArchiveUrl.Trim()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,4 +15,5 @@ public sealed class BookDialogResult
|
||||
public int PublishedPrecision { get; set; }
|
||||
public long? PreviousId { get; set; }
|
||||
public long? SourceId { get; set; }
|
||||
public string InternetArchiveUrl { get; set; }
|
||||
}
|
||||
|
||||
@@ -168,6 +168,20 @@
|
||||
</div>
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn Title="@L["Internet Archive link"]">
|
||||
<CellTemplate>
|
||||
@if(!string.IsNullOrWhiteSpace(context.Item.InternetArchiveUrl))
|
||||
{
|
||||
<MudTooltip Text="@context.Item.InternetArchiveUrl">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.OpenInNew"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary"
|
||||
Href="@context.Item.InternetArchiveUrl"
|
||||
Target="_blank"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
</Columns>
|
||||
</MudDataGrid>
|
||||
</MudStack>
|
||||
@@ -305,7 +319,7 @@
|
||||
static readonly string[] CsvHeaders =
|
||||
[
|
||||
"Title", "NativeTitle", "SortTitle", "ISBN", "Edition", "Pages",
|
||||
"PublishedYear", "PublishedMonth", "PublishedDay", "Country"
|
||||
"PublishedYear", "PublishedMonth", "PublishedDay", "Country", "InternetArchiveUrl"
|
||||
];
|
||||
|
||||
static readonly Dictionary<string, string> CountryAliases = new(StringComparer.OrdinalIgnoreCase)
|
||||
@@ -430,7 +444,8 @@
|
||||
NativeTitle = GetValueOrNull(row, "NativeTitle"),
|
||||
SortTitle = GetValueOrNull(row, "SortTitle"),
|
||||
Isbn = GetValueOrNull(row, "ISBN"),
|
||||
CountryInput = GetValueOrNull(row, "Country")
|
||||
CountryInput = GetValueOrNull(row, "Country"),
|
||||
InternetArchiveUrl = GetValueOrNull(row, "InternetArchiveUrl")
|
||||
};
|
||||
|
||||
if(TryParseInt(row, "Edition", out int edition))
|
||||
@@ -562,7 +577,8 @@
|
||||
Pages = row.Pages,
|
||||
CountryId = row.SelectedCountry?.Id,
|
||||
Published = row.PublishedDate.HasValue ? new DateTimeOffset(row.PublishedDate.Value) : null,
|
||||
PublishedPrecision = row.PublishedPrecision
|
||||
PublishedPrecision = row.PublishedPrecision,
|
||||
InternetArchiveUrl = row.InternetArchiveUrl
|
||||
};
|
||||
|
||||
(long? id, string error) = await BooksService.CreateAsync(dto);
|
||||
@@ -604,7 +620,8 @@
|
||||
["PublishedYear"] = row.PublishedYear?.ToString() ?? "",
|
||||
["PublishedMonth"] = row.PublishedMonth?.ToString() ?? "",
|
||||
["PublishedDay"] = row.PublishedDay?.ToString() ?? "",
|
||||
["Country"] = row.CountryInput ?? ""
|
||||
["Country"] = row.CountryInput ?? "",
|
||||
["InternetArchiveUrl"] = row.InternetArchiveUrl ?? ""
|
||||
};
|
||||
|
||||
rows.Add(dict);
|
||||
|
||||
@@ -41,6 +41,7 @@ public sealed class BookImportRow
|
||||
public int? PublishedMonth { get; set; }
|
||||
public int? PublishedDay { get; set; }
|
||||
public string CountryInput { get; set; }
|
||||
public string InternetArchiveUrl { get; set; }
|
||||
|
||||
public List<Iso31661NumericDto> MatchedCountries { get; set; } = [];
|
||||
public Iso31661NumericDto SelectedCountry { get; set; }
|
||||
|
||||
@@ -52,6 +52,20 @@
|
||||
</CellTemplate>
|
||||
</PropertyColumn>
|
||||
<PropertyColumn Property="x => x.Country" Title="@L["Country"]"/>
|
||||
<TemplateColumn Title="@L["Internet Archive link"]" Sortable="false" Filterable="false">
|
||||
<CellTemplate>
|
||||
@if(!string.IsNullOrWhiteSpace(context.Item.InternetArchiveUrl))
|
||||
{
|
||||
<MudTooltip Text="@context.Item.InternetArchiveUrl">
|
||||
<MudIconButton Icon="@Icons.Material.Filled.OpenInNew"
|
||||
Size="Size.Small"
|
||||
Color="Color.Primary"
|
||||
Href="@context.Item.InternetArchiveUrl"
|
||||
Target="_blank"/>
|
||||
</MudTooltip>
|
||||
}
|
||||
</CellTemplate>
|
||||
</TemplateColumn>
|
||||
<TemplateColumn Title="@L["Actions"]" Sortable="false" Filterable="false">
|
||||
<CellTemplate>
|
||||
<MudStack Row="true" Spacing="1">
|
||||
|
||||
@@ -61,7 +61,8 @@ public partial class Books
|
||||
PublishedPrecision = data.PublishedPrecision,
|
||||
Published = data.Published.HasValue ? new DateTimeOffset(data.Published.Value, TimeSpan.Zero) : null,
|
||||
PreviousId = data.PreviousId,
|
||||
SourceId = data.SourceId
|
||||
SourceId = data.SourceId,
|
||||
InternetArchiveUrl = data.InternetArchiveUrl
|
||||
};
|
||||
|
||||
(long? id, string errorMessage) = await BooksService.CreateAsync(dto);
|
||||
@@ -99,7 +100,8 @@ public partial class Books
|
||||
{ x => x.PublishedPrecision, fullBook.PublishedPrecision ?? 0 },
|
||||
{ x => x.PreviousId, fullBook.PreviousId },
|
||||
{ x => x.SourceId, fullBook.SourceId },
|
||||
{ x => x.HasCover, fullBook.CoverGuid is not null }
|
||||
{ x => x.HasCover, fullBook.CoverGuid is not null },
|
||||
{ x => x.InternetArchiveUrl, fullBook.InternetArchiveUrl }
|
||||
};
|
||||
|
||||
IDialogReference dialog = await DialogService.ShowAsync<BookDialog>(L["Edit Book"], parameters,
|
||||
@@ -127,7 +129,8 @@ public partial class Books
|
||||
CountryId = data.CountryId,
|
||||
Published = data.Published.HasValue ? new DateTimeOffset(data.Published.Value, TimeSpan.Zero) : null,
|
||||
PreviousId = data.PreviousId,
|
||||
SourceId = data.SourceId
|
||||
SourceId = data.SourceId,
|
||||
InternetArchiveUrl = data.InternetArchiveUrl
|
||||
};
|
||||
|
||||
(bool succeeded, string errorMessage) = await BooksService.UpdateAsync(book.Id ?? 0, dto);
|
||||
|
||||
@@ -150,6 +150,20 @@
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
@if(!string.IsNullOrEmpty(_book.InternetArchiveUrl))
|
||||
{
|
||||
<tr>
|
||||
<td style="width: 33%; text-align: right;"><strong>@L["Internet Archive link"]</strong></td>
|
||||
<td>
|
||||
<MudLink Href="@_book.InternetArchiveUrl" Target="_blank" Underline="Underline.Always">
|
||||
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
|
||||
<MudIcon Icon="@Icons.Material.Filled.OpenInNew" Size="Size.Small"/>
|
||||
<span>@L["View on Internet Archive"]</span>
|
||||
</MudStack>
|
||||
</MudLink>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
</MudCardContent>
|
||||
|
||||
@@ -445,4 +445,10 @@
|
||||
<data name="There are no companies in the database." xml:space="preserve">
|
||||
<value>Es gibt keine Unternehmen in der Datenbank.</value>
|
||||
</data>
|
||||
<data name="Internet Archive link" xml:space="preserve">
|
||||
<value>Internet Archive Link</value>
|
||||
</data>
|
||||
<data name="View on Internet Archive" xml:space="preserve">
|
||||
<value>Auf Internet Archive ansehen</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -408,4 +408,10 @@
|
||||
<data name="There are no companies in the database." xml:space="preserve">
|
||||
<value>There are no companies in the database.</value>
|
||||
</data>
|
||||
<data name="Internet Archive link" xml:space="preserve">
|
||||
<value>Internet Archive link</value>
|
||||
</data>
|
||||
<data name="View on Internet Archive" xml:space="preserve">
|
||||
<value>View on Internet Archive</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -354,4 +354,10 @@
|
||||
<data name="There are no companies in the database." xml:space="preserve">
|
||||
<value>No hay compañías en la base de datos.</value>
|
||||
</data>
|
||||
<data name="Internet Archive link" xml:space="preserve">
|
||||
<value>Enlace a Internet Archive</value>
|
||||
</data>
|
||||
<data name="View on Internet Archive" xml:space="preserve">
|
||||
<value>Ver en Internet Archive</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -445,4 +445,10 @@
|
||||
<data name="There are no companies in the database." xml:space="preserve">
|
||||
<value>Il n'y a pas d'entreprises dans la base de données.</value>
|
||||
</data>
|
||||
<data name="Internet Archive link" xml:space="preserve">
|
||||
<value>Lien Internet Archive</value>
|
||||
</data>
|
||||
<data name="View on Internet Archive" xml:space="preserve">
|
||||
<value>Voir sur Internet Archive</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -445,4 +445,10 @@
|
||||
<data name="There are no companies in the database." xml:space="preserve">
|
||||
<value>Non ci sono aziende nel database.</value>
|
||||
</data>
|
||||
<data name="Internet Archive link" xml:space="preserve">
|
||||
<value>Collegamento Internet Archive</value>
|
||||
</data>
|
||||
<data name="View on Internet Archive" xml:space="preserve">
|
||||
<value>Visualizza su Internet Archive</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
Reference in New Issue
Block a user