Skip to content

Commit 5e2df13

Browse files
committed
feat: general improvements
1 parent 962742b commit 5e2df13

10 files changed

Lines changed: 218 additions & 40 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* German translation for WebExpress
3+
*/
4+
var webexpress = webexpress || {}
5+
webexpress.webui = {}
6+
webexpress.webui.i18n_data = webexpress.webui.i18n_data || {};
7+
8+
webexpress.webui.i18n_data["de"] = {
9+
"webexpress.webapp": {
10+
"form.edit_row": "Eintrag bearbeiten"
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* English translations for WebExpress
3+
*/
4+
var webexpress = webexpress || {}
5+
webexpress.webui = {}
6+
webexpress.webui.i18n_data = webexpress.webui.i18n_data || {};
7+
8+
webexpress.webui.i18n_data["en"] = {
9+
"webexpress.webapp": {
10+
"form.edit_row": "Edit Row"
11+
}
12+
}

src/WebExpress.WebApp/Assets/js/webexpress.webapp.table.js

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,15 +202,37 @@ webexpress.webapp.TableCtrl = class extends webexpress.webui.TableCtrl {
202202
const editModal = new webexpress.webapp.ModalFormCtrl();
203203
editModal._uri = uri;
204204
editModal._selector = uri?.includes("#") ? "#" + uri.split("#")[1] : "form";
205-
205+
editModal._titleH1.textContent = webexpress.webui.I18N.translate("webexpress.webapp:form.edit_row");
206+
editModal._dialogDiv.className = "modal-dialog modal-dialog-scrollable modal-xl";
207+
206208
// Bind form submission logic
207-
editModal._form.addEventListener("submit", (event) => {
208-
fetch(`${this._restUri}?id=${rowId}`, { method: "PUT" })
209-
.then(response => {
210-
if (!response.ok) throw new Error("Failed to edit row");
209+
editModal._form.addEventListener("submit", async (event) => {
210+
event.preventDefault();
211+
212+
const formData = new FormData(editModal._form);
213+
214+
try {
215+
const response = await fetch(`${this._restUri}?id=${rowId}`, {
216+
method: "PUT",
217+
body: formData
218+
});
219+
220+
if (response.ok) {
211221
this._receiveData();
212-
})
213-
.catch(error => console.error(`Failed to edit row with ID ${rowId}.`, error));
222+
editModal.hide();
223+
return;
224+
}
225+
226+
if (response.status === 400) {
227+
const errors = await response.json();
228+
editModal.showValidationErrors(errors);
229+
return;
230+
}
231+
232+
throw new Error(`HTTP ${response.status}`);
233+
} catch (error) {
234+
console.error(`Failed to edit row with ID ${rowId}.`, error);
235+
}
214236
});
215237

216238
// Fill the form fields with the row's values after the form is rendered

src/WebExpress.WebApp/WebExpress.WebApp.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
</PropertyGroup>
2626

2727
<ItemGroup>
28+
<None Remove="Assets\js\i18n\de.js" />
29+
<None Remove="Assets\js\i18n\en.js" />
2830
<None Remove="Assets\js\webexpress.webapp.modalform.js" />
2931
</ItemGroup>
3032

@@ -34,6 +36,8 @@
3436
<EmbeddedResource Include="Assets\css\webexpress.webapp.table.css" />
3537
<EmbeddedResource Include="Assets\css\webexpress.webapp.taskprogressbar.css" />
3638
<EmbeddedResource Include="Assets\css\webexpress.webapp.theme.css" />
39+
<EmbeddedResource Include="Assets\js\i18n\de.js" />
40+
<EmbeddedResource Include="Assets\js\i18n\en.js" />
3741
<EmbeddedResource Include="Assets\js\webexpress.webapp.modalform.js" />
3842
<EmbeddedResource Include="Assets\js\webexpress.webapp.popupnotification.js" />
3943
<EmbeddedResource Include="Assets\js\webexpress.webapp.table.js" />

src/WebExpress.WebApp/WebPage/VisualTreeWebApp.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System.Collections.Generic;
2-
using System.Linq;
1+
using System.Linq;
32
using WebExpress.WebApp.WebControl;
43
using WebExpress.WebCore.Internationalization;
54
using WebExpress.WebCore.WebComponent;
@@ -77,25 +76,28 @@ public VisualTreeWebApp(IComponentHub componentHub, IPageContext pageContext)
7776
: base(componentHub, pageContext)
7877
{
7978
var applicationContext = pageContext?.ApplicationContext;
79+
var baseUri = RouteEndpoint.Combine(applicationContext?.Route, "assets");
8080

8181
Header.Fixed = TypeFixed.Top;
82-
Header.Styles = new List<string>(["position: sticky; top: 0; z-index: 99;"]);
82+
Header.Styles = ["position: sticky; top: 0; z-index: 99;"];
8383

8484
Breadcrumb.Uri = pageContext?.Route.ToUri();
8585
Breadcrumb.Margin = new PropertySpacingMargin(PropertySpacing.Space.Null);
8686
Content.Margin = new PropertySpacingMargin(PropertySpacing.Space.Two, PropertySpacing.Space.None, PropertySpacing.Space.None, PropertySpacing.Space.None);
8787

88-
AddCssLink(RouteEndpoint.Combine(applicationContext?.Route, "/assets/css/webexpress.webapp.css"));
89-
AddCssLink(RouteEndpoint.Combine(applicationContext?.Route, "/assets/css/webexpress.webapp.popupnotification.css"));
90-
AddCssLink(RouteEndpoint.Combine(applicationContext?.Route, "/assets/css/webexpress.webapp.table.css"));
91-
AddCssLink(RouteEndpoint.Combine(applicationContext?.Route, "/assets/css/webexpress.webapp.taskprogressbar.css"));
92-
AddCssLink(Theme?.ThemeStyle.ToString() ?? RouteEndpoint.Combine(applicationContext?.Route, "/assets/css/webexpress.webapp.theme.css"));
93-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.js"));
94-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.popupnotification.js"));
95-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.selection.js"));
96-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.modalform.js"));
97-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.table.js"));
98-
AddHeaderScriptLink(RouteEndpoint.Combine(applicationContext?.Route, "assets/js/webexpress.webapp.taskprogressbar.js"));
88+
AddCssLink(RouteEndpoint.Combine(baseUri, "css/webexpress.webapp.css"));
89+
AddCssLink(RouteEndpoint.Combine(baseUri, "css/webexpress.webapp.popupnotification.css"));
90+
AddCssLink(RouteEndpoint.Combine(baseUri, "css/webexpress.webapp.table.css"));
91+
AddCssLink(RouteEndpoint.Combine(baseUri, "css/webexpress.webapp.taskprogressbar.css"));
92+
AddCssLink(Theme?.ThemeStyle.ToString() ?? RouteEndpoint.Combine(baseUri, "css/webexpress.webapp.theme.css"));
93+
AddHeaderLanguageLink(RouteEndpoint.Combine(baseUri, "js/i18n/en.js"));
94+
AddHeaderLanguageLink(RouteEndpoint.Combine(baseUri, "js/i18n/de.js"));
95+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "js/webexpress.webapp.js"));
96+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "js/webexpress.webapp.popupnotification.js"));
97+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "assets/js/webexpress.webapp.selection.js"));
98+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "js/webexpress.webapp.modalform.js"));
99+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "js/webexpress.webapp.table.js"));
100+
AddHeaderScriptLink(RouteEndpoint.Combine(baseUri, "js/webexpress.webapp.taskprogressbar.js"));
99101
}
100102

101103
/// <summary>
@@ -115,7 +117,8 @@ public override IHtmlNode Render(IVisualTreeContext context)
115117
html.Head.Meta = Meta;
116118
html.Head.Scripts = HeaderScripts;
117119
html.Head.CssLinks = CssLinks.Where(x => x != null).Select(x => x.ToString());
118-
html.Head.ScriptLinks = HeaderScriptLinks?.Where(x => x != null).Select(x => x.ToString());
120+
html.Head.ScriptLinks = HeaderLanguageLinks?.Where(x => x != null).Select(x => x.ToString())
121+
.Union(HeaderScriptLinks?.Where(x => x != null).Select(x => x.ToString()));
119122

120123
// header
121124
Header.AppTitle.Text = html.Head.Title;

src/WebExpress.WebApp/WebRestApi/RestApiCrud.cs

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ namespace WebExpress.WebApp.WebRestApi
1717
public abstract class RestApiCrud<TIndexItem> : IRestApi
1818
where TIndexItem : IIndexItem
1919
{
20+
/// <summary>
21+
/// Returns the collection of indexed items.
22+
/// </summary>
23+
public IEnumerable<TIndexItem> Data { get; protected set; } = [];
24+
2025
/// <summary>
2126
/// Processing of the resource that was called via the get request.
2227
/// </summary>
@@ -46,7 +51,7 @@ public virtual Response GetData(Request request)
4651
data = GetData(filter, request);
4752
}
4853

49-
var result = new RestApiResult()
54+
var result = new RestApiCrudResult<TIndexItem>()
5055
{
5156
Data = data.Skip(pageSize * pageNumber).Take(pageSize),
5257
Pagination = new RestApiPaginationInfo()
@@ -105,24 +110,46 @@ public virtual Response CreateData(Request request)
105110
public virtual Response UpdateData(Request request)
106111
{
107112
var id = request.GetParameter("id")?.Value;
113+
var item = Data.FirstOrDefault(x => x.Id.ToString() == id);
108114

109-
var errors = UpdateData(id, request);
115+
var validationRsult = ValidateUpdateData(item, request);
110116

111-
var result = new RestApiResult()
112-
.AddError(errors);
117+
if (!validationRsult.IsValid)
118+
{
119+
return new ResponseBadRequest()
120+
{
121+
Content = validationRsult.ToJson(),
122+
}
123+
.AddHeaderContentType("application/json");
124+
}
113125

114-
return result.ToResponse();
126+
UpdateData(item, request);
127+
128+
return new ResponseOK();
115129
}
116130

117131
/// <summary>
118-
/// Updates data.
132+
/// Performs validation before updating data.
119133
/// </summary>
120-
/// <param name="id">The id of the data to delete.</param>
121-
/// <param name="request">The request.</param>
122-
/// <returns>An enumeration of validation errors or null.</returns>
123-
public virtual IEnumerable<RestApiError> UpdateData(string id, Request request)
134+
/// <param name="item"> The item containing the updated data.</param>
135+
/// <param name="request">The HTTP request containing input data and parameters.</param>
136+
/// <returns>
137+
/// A <see cref="RestApiValidationResult"/> containing any validation errors
138+
/// encountered during the update process. If the operation completes successfully,
139+
/// the result will contain no errors.
140+
/// </returns>
141+
public virtual RestApiValidationResult ValidateUpdateData(TIndexItem item, Request request)
142+
{
143+
return new RestApiValidationResult();
144+
}
145+
146+
/// <summary>
147+
/// Updates the data record identified by the specified ID.
148+
/// </summary>
149+
/// <param name="item"> The item containing the updated data.</param>
150+
/// <param name="request">The HTTP request containing the update parameters.</param>
151+
public virtual void UpdateData(TIndexItem item, Request request)
124152
{
125-
return [];
126153
}
127154

128155
/// <summary>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Collections.Generic;
2+
using System.Text;
3+
using System.Text.Json;
4+
using WebExpress.WebCore.WebMessage;
5+
using WebExpress.WebCore.WebRestApi;
6+
using WebExpress.WebIndex;
7+
8+
namespace WebExpress.WebApp.WebRestApi
9+
{
10+
/// <summary>
11+
/// Represents the result of a REST API CRUD operation, including data and pagination information.
12+
/// </summary>
13+
/// <remarks>
14+
/// This class provides a standardized structure for returning data and pagination
15+
/// details from a REST API endpoint. It also includes functionality to convert
16+
/// the result into a <see cref="Response"/> object for further processing or transmission.
17+
/// </remarks>
18+
/// <typeparam name="TIndexItem">
19+
/// The type of items contained in the result. Must implement <see cref="IIndexItem"/>.
20+
/// </typeparam>
21+
public class RestApiCrudResult<TIndexItem> : IRestApiResult
22+
where TIndexItem : IIndexItem
23+
{
24+
private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true };
25+
26+
/// <summary>
27+
/// Returns or sets the collection of items.
28+
/// </summary>
29+
public IEnumerable<TIndexItem> Data { get; set; }
30+
31+
/// <summary>
32+
/// Returns or sets the pagination information for the current API request.
33+
/// </summary>
34+
public RestApiPaginationInfo Pagination { get; set; }
35+
36+
/// <summary>
37+
/// Converts the current instance into a <see cref="Response"/> object.
38+
/// </summary>
39+
/// <returns>A Response object representing the result of the conversion.</returns>
40+
public virtual Response ToResponse()
41+
{
42+
var data = new
43+
{
44+
data = Data,
45+
pagination = Pagination
46+
};
47+
48+
var jsonData = JsonSerializer.Serialize(data, _jsonOptions);
49+
var content = Encoding.UTF8.GetBytes(jsonData);
50+
51+
return new ResponseOK
52+
{
53+
Content = content
54+
}
55+
.AddHeaderContentType("application/json");
56+
}
57+
}
58+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System.Text;
2+
using System.Text.Json;
3+
using WebExpress.WebCore.WebMessage;
4+
using WebExpress.WebCore.WebRestApi;
5+
6+
namespace WebExpress.WebApp.WebRestApi
7+
{
8+
/// <summary>
9+
/// Represents the result of a CRUD operation in a REST API, with validation details.
10+
/// </summary>
11+
/// <remarks>
12+
/// This class provides functionality to validate the result of a REST API
13+
/// operation and convert it into a standardized response format.
14+
/// </remarks>
15+
public class RestApiCrudResultValidation : IRestApiResult
16+
{
17+
private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true };
18+
19+
/// <summary>
20+
/// Returns or sets the collection of items.
21+
/// </summary>
22+
public RestApiValidator Validator { get; set; }
23+
24+
/// <summary>
25+
/// Converts the current instance into a <see cref="Response"/> object.
26+
/// </summary>
27+
/// <returns>A Response object representing the result of the conversion.</returns>
28+
public virtual Response ToResponse()
29+
{
30+
var data = new
31+
{
32+
errors = Validator?.Result?.Errors
33+
};
34+
35+
var jsonData = JsonSerializer.Serialize(data, _jsonOptions);
36+
var content = Encoding.UTF8.GetBytes(jsonData);
37+
38+
return new ResponseOK
39+
{
40+
Content = content
41+
}
42+
.AddHeaderContentType("application/json");
43+
}
44+
}
45+
}

src/WebExpress.WebApp/WebRestApi/RestApiCrudTable.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public override Response GetData(Request request)
101101
.Where(x => x.Value.Visible)
102102
.Select(x => x.Value);
103103

104-
var result = new RestApiTableResult()
104+
var result = new RestApiCrudTableResult()
105105
{
106106
Title = I18N.Translate(request, Title),
107107
Columns = columns,

src/WebExpress.WebApp/WebRestApi/RestApiTableResult.cs renamed to src/WebExpress.WebApp/WebRestApi/RestApiCrudTableResult.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using System.Collections.Generic;
22
using System.Text;
33
using System.Text.Json;
4-
using System.Text.Json.Serialization;
54
using WebExpress.WebCore.WebMessage;
65
using WebExpress.WebCore.WebRestApi;
76

@@ -11,32 +10,28 @@ namespace WebExpress.WebApp.WebRestApi
1110
/// Represents the result of a REST API call that returns a table structure,
1211
/// including metadata, columns, rows, and pagination information.
1312
/// </summary>
14-
public class RestApiTableResult : IRestApiResult
13+
public class RestApiCrudTableResult : IRestApiResult
1514
{
1615
private readonly JsonSerializerOptions _jsonOptions = new() { WriteIndented = true };
1716

1817
/// <summary>
1918
/// Returns or sets the title associated with the object.
2019
/// </summary>
21-
[JsonPropertyName("title")]
2220
public string Title { get; set; }
2321

2422
/// <summary>
2523
/// Returns or sets the collection of columns associated with the table.
2624
/// </summary>
27-
[JsonPropertyName("columns")]
2825
public IEnumerable<RestApiCrudTableColumn> Columns { get; set; }
2926

3027
/// <summary>
3128
/// Returns or sets the collection of rows in the table.
3229
/// </summary>
33-
[JsonPropertyName("rows")]
3430
public IEnumerable<RestApiCrudTableRow> Rows { get; set; }
3531

3632
/// <summary>
3733
/// Returns or sets the pagination information for the current API request.
3834
/// </summary>
39-
[JsonPropertyName("pagination")]
4035
public RestApiPaginationInfo Pagination { get; set; }
4136

4237
/// <summary>

0 commit comments

Comments
 (0)