feat: Rich-Text-Editor, Bild-Skalierung, PDF-Fix & Zeichenlimit
- Bild-Skalierung in Kartenansicht gefixt (object-position: top, 300px) - Admin-Slider für Zeichenlimit (4–32.000) als Modul-Setting - Textarea durch RichTextEditor (Quill.js) ersetzt - PDF: HTML-Parsing, Einzelperson-Filter, Autorisierung für alle User
This commit is contained in:
@@ -8,47 +8,60 @@
|
||||
@inject IHallOfFameService HallOfFameService
|
||||
@inject NavigationManager NavigationManager
|
||||
@inject IStringLocalizer<Edit> Localizer
|
||||
@inject ISettingService SettingService
|
||||
|
||||
<form @ref="form" class="@(validated ? " was-validated" : "needs-validation" )" novalidate>
|
||||
<div class="container">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<Label Class="col-sm-3 col-form-label" For="name" HelpText="Gib deinen Namen ein" ResourceKey="Name">Name: </Label>
|
||||
<Label Class="col-sm-3 col-form-label" For="name" HelpText="Gib deinen Namen ein" ResourceKey="Name">Name:
|
||||
</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="name" class="form-control" @bind="@_name" required maxlength="120" />
|
||||
<div class="invalid-feedback">Bitte gib einen Namen ein (max. 120 Zeichen).</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3 align-items-center">
|
||||
<Label Class="col-sm-3 col-form-label" For="year" HelpText="Jahrgang (z.B. 2020)" ResourceKey="Year">Jahrgang: </Label>
|
||||
<Label Class="col-sm-3 col-form-label" For="year" HelpText="Jahrgang (z.B. 2020)"
|
||||
ResourceKey="Year">Jahrgang: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="year" type="number" class="form-control" @bind="@_year" required min="1900" max="@Int32.MaxValue" />
|
||||
<input id="year" type="number" class="form-control" @bind="@_year" required min="1900"
|
||||
max="@Int32.MaxValue" />
|
||||
<div class="invalid-feedback">Bitte gib einen gültigen Jahrgang ein.</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3 align-items-center">
|
||||
<Label Class="col-sm-3 col-form-label" For="description" HelpText="Kurzbeschreibung / Werdegang" ResourceKey="Description">Beschreibung: </Label>
|
||||
<div class="row mb-3">
|
||||
<Label Class="col-sm-3 col-form-label" For="description" HelpText="Kurzbeschreibung / Werdegang"
|
||||
ResourceKey="Description">Beschreibung: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="description" class="form-control" @bind="@_description" required rows="5" maxlength="500"></textarea>
|
||||
<div class="text-muted small">@(_description?.Length ?? 0) / 500 Zeichen</div>
|
||||
<div class="invalid-feedback">Bitte gib eine Beschreibung ein.</div>
|
||||
@if (_descriptionLoaded)
|
||||
{
|
||||
<RichTextEditor Content="@_description" @ref="@_richTextEditorRef"
|
||||
Placeholder="Beschreibe deinen Werdegang..."></RichTextEditor>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<Label Class="col-sm-3 col-form-label" For="image" HelpText="Porträtfoto hochladen (JPG/PNG)" ResourceKey="Image">Foto: </Label>
|
||||
<div class="row mb-3">
|
||||
<Label Class="col-sm-3 col-form-label" For="image" HelpText="Porträtfoto hochladen (JPG/PNG)"
|
||||
ResourceKey="Image">Foto: </Label>
|
||||
<div class="col-sm-9">
|
||||
<div class="mb-2">
|
||||
@if (!string.IsNullOrEmpty(_image))
|
||||
{
|
||||
<div class="position-relative d-inline-block">
|
||||
<img src="@_image" style="max-height: 150px; border-radius: 8px; margin-bottom: 10px; border: 1px solid #ddd;" alt="Vorschau" />
|
||||
<button type="button" class="btn btn-sm btn-danger" style="position: absolute; top: 5px; right: 5px;" @onclick="RemoveImage" title="Bild entfernen">
|
||||
<img src="@_image"
|
||||
style="max-height: 150px; border-radius: 8px; margin-bottom: 10px; border: 1px solid #ddd;"
|
||||
alt="Vorschau" />
|
||||
<button type="button" class="btn btn-sm btn-danger"
|
||||
style="position: absolute; top: 5px; right: 5px;" @onclick="RemoveImage"
|
||||
title="Bild entfernen">
|
||||
<i class="oi oi-x"></i>
|
||||
</button>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="bg-light d-flex align-items-center justify-content-center text-muted" style="height: 150px; width: 150px; border: 2px dashed #ddd; border-radius: 8px;">
|
||||
<div class="bg-light d-flex align-items-center justify-content-center text-muted"
|
||||
style="height: 150px; width: 150px; border: 2px dashed #ddd; border-radius: 8px;">
|
||||
Kein Bild
|
||||
</div>
|
||||
}
|
||||
@@ -65,24 +78,27 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3 align-items-center">
|
||||
<Label Class="col-sm-3 col-form-label" For="link" HelpText="Externer Link (optional)" ResourceKey="Link">Link: </Label>
|
||||
<Label Class="col-sm-3 col-form-label" For="link" HelpText="Externer Link (optional)"
|
||||
ResourceKey="Link">Link: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="link" type="url" class="form-control" @bind="@_link" placeholder="https://" />
|
||||
<div class="invalid-feedback">Bitte gib eine gültige URL ein (startet mit http:// oder https://).</div>
|
||||
<div class="invalid-feedback">Bitte gib eine gültige URL ein (startet mit http:// oder https://).</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3 align-items-center">
|
||||
<div class="row mb-3 align-items-center">
|
||||
<Label Class="col-sm-3 col-form-label" For="status" HelpText="Status" ResourceKey="Status">Status: </Label>
|
||||
<div class="col-sm-9">
|
||||
<p>Aktuell: <strong>@(_status ?? "Neu")</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="mt-4 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<button type="button" class="btn btn-secondary me-2" @onclick="@(() => Save("Draft"))" disabled="@_uploading">Als Entwurf speichern</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => Save("Published"))" disabled="@_uploading">Veröffentlichen</button>
|
||||
<button type="button" class="btn btn-secondary me-2" @onclick="@(() => Save("Draft"))"
|
||||
disabled="@_uploading">Als Entwurf speichern</button>
|
||||
<button type="button" class="btn btn-primary" @onclick="@(() => Save("Published"))"
|
||||
disabled="@_uploading">Veröffentlichen</button>
|
||||
<NavLink class="btn btn-link ms-2" href="@NavigateUrl()">Abbrechen</NavLink>
|
||||
</div>
|
||||
@if (PageState.Action == "Edit")
|
||||
@@ -96,7 +112,8 @@
|
||||
<br /><br />
|
||||
@if (PageState.Action == "Edit")
|
||||
{
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
|
||||
<AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon">
|
||||
</AuditInfo>
|
||||
}
|
||||
</form>
|
||||
|
||||
@@ -115,6 +132,8 @@
|
||||
private ElementReference form;
|
||||
private bool validated = false;
|
||||
private bool _uploading = false;
|
||||
private RichTextEditor _richTextEditorRef;
|
||||
private bool _descriptionLoaded = false;
|
||||
|
||||
private int _id;
|
||||
private string _name;
|
||||
@@ -128,22 +147,33 @@
|
||||
private DateTime _createdon;
|
||||
private string _modifiedby;
|
||||
private DateTime _modifiedon;
|
||||
private int _charLimit = 500;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Load character limit setting
|
||||
var settings = await SettingService.GetModuleSettingsAsync(ModuleState.ModuleId);
|
||||
var charLimitStr = SettingService.GetSetting(settings, "CharLimit", "500");
|
||||
if (int.TryParse(charLimitStr, out var parsed) && parsed >= 4 && parsed <= 32000)
|
||||
{
|
||||
_charLimit = parsed;
|
||||
}
|
||||
|
||||
_descriptionLoaded = false;
|
||||
|
||||
if (PageState.Action == "Edit")
|
||||
{
|
||||
_id = Int32.Parse(PageState.QueryString["id"]);
|
||||
HallOfFame HallOfFame = await HallOfFameService.GetHallOfFameAsync(_id, ModuleState.ModuleId);
|
||||
|
||||
|
||||
if (HallOfFame != null)
|
||||
{
|
||||
if (HallOfFame.UserId != PageState.User.UserId)
|
||||
if (HallOfFame.UserId != PageState.User.UserId)
|
||||
{
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
return;
|
||||
NavigationManager.NavigateTo(NavigateUrl());
|
||||
return;
|
||||
}
|
||||
|
||||
_name = HallOfFame.Name;
|
||||
@@ -152,7 +182,7 @@
|
||||
_image = HallOfFame.Image;
|
||||
_link = HallOfFame.Link;
|
||||
_status = HallOfFame.Status;
|
||||
|
||||
|
||||
_createdby = HallOfFame.CreatedBy;
|
||||
_createdon = HallOfFame.CreatedOn;
|
||||
_modifiedby = HallOfFame.ModifiedBy;
|
||||
@@ -161,12 +191,13 @@
|
||||
}
|
||||
else // Add Mode
|
||||
{
|
||||
var existing = await HallOfFameService.GetHallOfFameByUserIdAsync(PageState.User.UserId, ModuleState.ModuleId);
|
||||
if (existing != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(existing.HallOfFameId.ToString()));
|
||||
}
|
||||
var existing = await HallOfFameService.GetHallOfFameByUserIdAsync(PageState.User.UserId, ModuleState.ModuleId);
|
||||
if (existing != null)
|
||||
{
|
||||
NavigationManager.NavigateTo(EditUrl(existing.HallOfFameId.ToString()));
|
||||
}
|
||||
}
|
||||
_descriptionLoaded = true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -224,6 +255,12 @@
|
||||
try
|
||||
{
|
||||
validated = true;
|
||||
// Get the HTML content from the rich text editor
|
||||
if (_richTextEditorRef != null)
|
||||
{
|
||||
_description = await _richTextEditorRef.GetHtml();
|
||||
}
|
||||
|
||||
var interop = new Oqtane.UI.Interop(JSRuntime);
|
||||
if (await interop.FormValid(form))
|
||||
{
|
||||
@@ -247,7 +284,7 @@
|
||||
else
|
||||
{
|
||||
HallOfFame HallOfFame = await HallOfFameService.GetHallOfFameAsync(_id, ModuleState.ModuleId);
|
||||
if (HallOfFame.UserId == PageState.User.UserId)
|
||||
if (HallOfFame.UserId == PageState.User.UserId)
|
||||
{
|
||||
HallOfFame.Name = _name;
|
||||
HallOfFame.Year = _year;
|
||||
@@ -255,7 +292,7 @@
|
||||
HallOfFame.Image = _image;
|
||||
HallOfFame.Link = _link;
|
||||
HallOfFame.Status = _status;
|
||||
|
||||
|
||||
await HallOfFameService.UpdateHallOfFameAsync(HallOfFame);
|
||||
await logger.LogInformation("HallOfFame Updated {HallOfFame}", HallOfFame);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user