Added filtering and sorting to shipcalls in the list.

While doing so, I have also refactored the shipcall processing logic in the main window.
All changes now go through the filter and sorting stage before all controls are removed
and only the controls matching to the sorted list are added to the stack panel.
This commit is contained in:
Daniel Schick 2023-09-12 16:48:28 +02:00
parent 90338f9e95
commit 7660ee72f2
16 changed files with 633 additions and 245 deletions

View File

@ -1,8 +1,8 @@
//---------------------- //----------------------
// <auto-generated> // <auto-generated>
// Generated REST API Client Code Generator v1.7.17.0 on 05.09.2023 16:40:43 // Generated REST API Client Code Generator v1.8.4.0 on 11.09.2023 10:29:36
// Using the tool OpenAPI Generator v6.6.0 // Using the tool OpenAPI Generator v7.0.0
// </auto-generated> // </auto-generated>
//---------------------- //----------------------
@ -29,7 +29,8 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Http; using System.Net.Http;
using System.Net.Mime;
using System.Net.Security;
using System.Reflection; using System.Reflection;
using System.Runtime.Serialization; using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters; using System.Runtime.Serialization.Formatters;
@ -2334,7 +2335,6 @@ namespace BreCalClient.misc.Client
internal class CustomJsonCodec : IRestSerializer, ISerializer, IDeserializer internal class CustomJsonCodec : IRestSerializer, ISerializer, IDeserializer
{ {
private readonly IReadableConfiguration _configuration; private readonly IReadableConfiguration _configuration;
private static readonly string _contentType = "application/json";
private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings private readonly JsonSerializerSettings _serializerSettings = new JsonSerializerSettings
{ {
// OpenAPI generated types generally hide default constructors. // OpenAPI generated types generally hide default constructors.
@ -2435,15 +2435,11 @@ namespace BreCalClient.misc.Client
} }
public ISerializer Serializer => this; public ISerializer Serializer => this;
public IDeserializer Deserializer => this; public IDeserializer Deserializer => this;
public string[] AcceptedContentTypes => RestSharp.Serializers.ContentType.JsonAccept; public string[] AcceptedContentTypes => RestSharp.ContentType.JsonAccept;
public SupportsContentType SupportsContentType => contentType => public SupportsContentType SupportsContentType => contentType =>
contentType.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) || contentType.Value.EndsWith("json", StringComparison.InvariantCultureIgnoreCase) ||
contentType.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase); contentType.Value.EndsWith("javascript", StringComparison.InvariantCultureIgnoreCase);
public string ContentType public ContentType ContentType { get; set; } = RestSharp.ContentType.Json;
{
get { return _contentType; }
set { throw new InvalidOperationException("Not allowed to set content type."); }
}
public DataFormat DataFormat => DataFormat.Json; public DataFormat DataFormat => DataFormat.Json;
} }
/// <summary> /// <summary>
@ -2688,7 +2684,7 @@ namespace BreCalClient.misc.Client
} }
return transformed; return transformed;
} }
private ApiResponse<T> Exec<T>(RestRequest req, RequestOptions options, IReadableConfiguration configuration) private ApiResponse<T> Exec<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration)
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var cookies = new CookieContainer(); var cookies = new CookieContainer();
@ -2705,84 +2701,87 @@ namespace BreCalClient.misc.Client
CookieContainer = cookies, CookieContainer = cookies,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials,
RemoteCertificateValidationCallback = configuration.RemoteCertificateValidationCallback
}; };
RestClient client = new RestClient(clientOptions) using (RestClient client = new RestClient(clientOptions,
.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)); configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
InterceptRequest(req);
RestResponse<T> response;
if (RetryConfiguration.RetryPolicy != null)
{ {
var policy = RetryConfiguration.RetryPolicy; InterceptRequest(request);
var policyResult = policy.ExecuteAndCapture(() => client.Execute(req)); RestResponse<T> response;
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T> if (RetryConfiguration.RetryPolicy != null)
{ {
Request = req, var policy = RetryConfiguration.RetryPolicy;
ErrorException = policyResult.FinalException var policyResult = policy.ExecuteAndCapture(() => client.Execute(request));
}; response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
}
else
{
response = client.Execute<T>(req);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(BreCalClient.misc.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{
try
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
catch (Exception ex)
{
throw ex.InnerException != null ? ex.InnerException : ex;
}
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
else if (typeof(T).Name == "String") // for string response
{
response.Data = (T)(object)response.Content;
}
InterceptResponse(req, response);
var result = ToApiResponse(response);
if (response.ErrorMessage != null)
{
result.ErrorText = response.ErrorMessage;
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{
var cookie = new Cookie(
restResponseCookie.Name,
restResponseCookie.Value,
restResponseCookie.Path,
restResponseCookie.Domain
)
{ {
Comment = restResponseCookie.Comment, ErrorException = policyResult.FinalException
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
}; };
result.Cookies.Add(cookie);
} }
else
{
response = client.Execute<T>(request);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(BreCalClient.misc.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{
try
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
catch (Exception ex)
{
throw ex.InnerException != null ? ex.InnerException : ex;
}
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
else if (typeof(T).Name == "String") // for string response
{
response.Data = (T)(object)response.Content;
}
InterceptResponse(request, response);
var result = ToApiResponse(response);
if (response.ErrorMessage != null)
{
result.ErrorText = response.ErrorMessage;
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{
var cookie = new Cookie(
restResponseCookie.Name,
restResponseCookie.Value,
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
}
}
return result;
} }
return result;
} }
private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest req, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) private async Task<ApiResponse<T>> ExecAsync<T>(RestRequest request, RequestOptions options, IReadableConfiguration configuration, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken))
{ {
var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl; var baseUrl = configuration.GetOperationServerUrl(options.Operation, options.OperationIndex) ?? _baseUrl;
var clientOptions = new RestClientOptions(baseUrl) var clientOptions = new RestClientOptions(baseUrl)
@ -2790,71 +2789,73 @@ namespace BreCalClient.misc.Client
ClientCertificates = configuration.ClientCertificates, ClientCertificates = configuration.ClientCertificates,
MaxTimeout = configuration.Timeout, MaxTimeout = configuration.Timeout,
Proxy = configuration.Proxy, Proxy = configuration.Proxy,
UserAgent = configuration.UserAgent UserAgent = configuration.UserAgent,
UseDefaultCredentials = configuration.UseDefaultCredentials
}; };
RestClient client = new RestClient(clientOptions) using (RestClient client = new RestClient(clientOptions,
.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration)); configureSerialization: serializerConfig => serializerConfig.UseSerializer(() => new CustomJsonCodec(SerializerSettings, configuration))))
InterceptRequest(req);
RestResponse<T> response;
if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
var policy = RetryConfiguration.AsyncRetryPolicy; InterceptRequest(request);
var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(req, ct), cancellationToken).ConfigureAwait(false); RestResponse<T> response;
response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T> if (RetryConfiguration.AsyncRetryPolicy != null)
{ {
Request = req, var policy = RetryConfiguration.AsyncRetryPolicy;
ErrorException = policyResult.FinalException var policyResult = await policy.ExecuteAndCaptureAsync((ct) => client.ExecuteAsync(request, ct), cancellationToken).ConfigureAwait(false);
}; response = (policyResult.Outcome == OutcomeType.Successful) ? client.Deserialize<T>(policyResult.Result) : new RestResponse<T>(request)
}
else
{
response = await client.ExecuteAsync<T>(req, cancellationToken).ConfigureAwait(false);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(BreCalClient.misc.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(req, response);
var result = ToApiResponse(response);
if (response.ErrorMessage != null)
{
result.ErrorText = response.ErrorMessage;
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{
var cookie = new Cookie(
restResponseCookie.Name,
restResponseCookie.Value,
restResponseCookie.Path,
restResponseCookie.Domain
)
{ {
Comment = restResponseCookie.Comment, ErrorException = policyResult.FinalException
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
}; };
result.Cookies.Add(cookie);
} }
else
{
response = await client.ExecuteAsync<T>(request, cancellationToken).ConfigureAwait(false);
}
// if the response type is oneOf/anyOf, call FromJSON to deserialize the data
if (typeof(BreCalClient.misc.Model.AbstractOpenAPISchema).IsAssignableFrom(typeof(T)))
{
response.Data = (T) typeof(T).GetMethod("FromJson").Invoke(null, new object[] { response.Content });
}
else if (typeof(T).Name == "Stream") // for binary response
{
response.Data = (T)(object)new MemoryStream(response.RawBytes);
}
else if (typeof(T).Name == "Byte[]") // for byte response
{
response.Data = (T)(object)response.RawBytes;
}
InterceptResponse(request, response);
var result = ToApiResponse(response);
if (response.ErrorMessage != null)
{
result.ErrorText = response.ErrorMessage;
}
if (response.Cookies != null && response.Cookies.Count > 0)
{
if (result.Cookies == null) result.Cookies = new List<Cookie>();
foreach (var restResponseCookie in response.Cookies.Cast<Cookie>())
{
var cookie = new Cookie(
restResponseCookie.Name,
restResponseCookie.Value,
restResponseCookie.Path,
restResponseCookie.Domain
)
{
Comment = restResponseCookie.Comment,
CommentUri = restResponseCookie.CommentUri,
Discard = restResponseCookie.Discard,
Expired = restResponseCookie.Expired,
Expires = restResponseCookie.Expires,
HttpOnly = restResponseCookie.HttpOnly,
Port = restResponseCookie.Port,
Secure = restResponseCookie.Secure,
Version = restResponseCookie.Version
};
result.Cookies.Add(cookie);
}
}
return result;
} }
return result;
} }
#region IAsynchronousClient #region IAsynchronousClient
/// <summary> /// <summary>
@ -3521,6 +3522,7 @@ namespace BreCalClient.misc.Client
/// Example: http://localhost:3000/v1/ /// Example: http://localhost:3000/v1/
/// </summary> /// </summary>
private string _basePath; private string _basePath;
private bool _useDefaultCredentials = false;
/// <summary> /// <summary>
/// Gets or sets the API key based on the authentication name. /// Gets or sets the API key based on the authentication name.
/// This is the key and value comprising the "secret" for accessing an API. /// This is the key and value comprising the "secret" for accessing an API.
@ -3610,11 +3612,20 @@ namespace BreCalClient.misc.Client
/// <summary> /// <summary>
/// Gets or sets the base path for API access. /// Gets or sets the base path for API access.
/// </summary> /// </summary>
public virtual string BasePath { public virtual string BasePath
{
get { return _basePath; } get { return _basePath; }
set { _basePath = value; } set { _basePath = value; }
} }
/// <summary> /// <summary>
/// Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false.
/// </summary>
public virtual bool UseDefaultCredentials
{
get { return _useDefaultCredentials; }
set { _useDefaultCredentials = value; }
}
/// <summary>
/// Gets or sets the default header. /// Gets or sets the default header.
/// </summary> /// </summary>
[Obsolete("Use DefaultHeaders instead.")] [Obsolete("Use DefaultHeaders instead.")]
@ -3854,7 +3865,7 @@ namespace BreCalClient.misc.Client
/// <return>The operation server URL.</return> /// <return>The operation server URL.</return>
public string GetOperationServerUrl(string operation, int index, Dictionary<string, string> inputVariables) public string GetOperationServerUrl(string operation, int index, Dictionary<string, string> inputVariables)
{ {
if (OperationServers.TryGetValue(operation, out var operationServer)) if (operation != null && OperationServers.TryGetValue(operation, out var operationServer))
{ {
return GetServerUrl(operationServer, index, inputVariables); return GetServerUrl(operationServer, index, inputVariables);
} }
@ -3905,6 +3916,10 @@ namespace BreCalClient.misc.Client
} }
return url; return url;
} }
/// <summary>
/// Gets and Sets the RemoteCertificateValidationCallback
/// </summary>
public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
#endregion Properties #endregion Properties
#region Methods #region Methods
/// <summary> /// <summary>
@ -3970,6 +3985,8 @@ namespace BreCalClient.misc.Client
TempFolderPath = second.TempFolderPath ?? first.TempFolderPath, TempFolderPath = second.TempFolderPath ?? first.TempFolderPath,
DateTimeFormat = second.DateTimeFormat ?? first.DateTimeFormat, DateTimeFormat = second.DateTimeFormat ?? first.DateTimeFormat,
ClientCertificates = second.ClientCertificates ?? first.ClientCertificates, ClientCertificates = second.ClientCertificates ?? first.ClientCertificates,
UseDefaultCredentials = second.UseDefaultCredentials,
RemoteCertificateValidationCallback = second.RemoteCertificateValidationCallback ?? first.RemoteCertificateValidationCallback,
}; };
return config; return config;
} }
@ -4294,6 +4311,10 @@ namespace BreCalClient.misc.Client
/// <value>Password.</value> /// <value>Password.</value>
string Password { get; } string Password { get; }
/// <summary> /// <summary>
/// Determine whether or not the "default credentials" (e.g. the user account under which the current process is running) will be sent along to the server. The default is false.
/// </summary>
bool UseDefaultCredentials { get; }
/// <summary>
/// Get the servers associated with the operation. /// Get the servers associated with the operation.
/// </summary> /// </summary>
/// <value>Operation servers.</value> /// <value>Operation servers.</value>
@ -4316,6 +4337,11 @@ namespace BreCalClient.misc.Client
/// </summary> /// </summary>
/// <value>X509 Certificate collection.</value> /// <value>X509 Certificate collection.</value>
X509CertificateCollection ClientCertificates { get; } X509CertificateCollection ClientCertificates { get; }
/// <summary>
/// Callback function for handling the validation of remote certificates. Useful for certificate pinning and
/// overriding certificate errors in the scope of a request.
/// </summary>
RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; }
} }
} }
@ -4877,16 +4903,16 @@ namespace BreCalClient.misc.Model
/// <param name="id">id.</param> /// <param name="id">id.</param>
/// <param name="name">name.</param> /// <param name="name">name.</param>
/// <param name="participantId">participantId.</param> /// <param name="participantId">participantId.</param>
/// <param name="_lock">_lock.</param> /// <param name="varLock">varLock.</param>
/// <param name="created">created.</param> /// <param name="created">created.</param>
/// <param name="modified">modified.</param> /// <param name="modified">modified.</param>
/// <param name="deleted">deleted (default to false).</param> /// <param name="deleted">deleted (default to false).</param>
public Berth(int id = default(int), string name = default(string), int? participantId = default(int?), bool? _lock = default(bool?), DateTime created = default(DateTime), DateTime? modified = default(DateTime?), bool deleted = false) public Berth(int id = default(int), string name = default(string), int? participantId = default(int?), bool? varLock = default(bool?), DateTime created = default(DateTime), DateTime? modified = default(DateTime?), bool deleted = false)
{ {
this.Id = id; this.Id = id;
this.Name = name; this.Name = name;
this.ParticipantId = participantId; this.ParticipantId = participantId;
this.Lock = _lock; this.VarLock = varLock;
this.Created = created; this.Created = created;
this.Modified = modified; this.Modified = modified;
this.Deleted = deleted; this.Deleted = deleted;
@ -4907,10 +4933,10 @@ namespace BreCalClient.misc.Model
[DataMember(Name = "participant_id", EmitDefaultValue = true)] [DataMember(Name = "participant_id", EmitDefaultValue = true)]
public int? ParticipantId { get; set; } public int? ParticipantId { get; set; }
/// <summary> /// <summary>
/// Gets or Sets Lock /// Gets or Sets VarLock
/// </summary> /// </summary>
[DataMember(Name = "lock", EmitDefaultValue = true)] [DataMember(Name = "lock", EmitDefaultValue = true)]
public bool? Lock { get; set; } public bool? VarLock { get; set; }
/// <summary> /// <summary>
/// Gets or Sets Created /// Gets or Sets Created
/// </summary> /// </summary>
@ -4937,7 +4963,7 @@ namespace BreCalClient.misc.Model
sb.Append(" Id: ").Append(Id).Append("\n"); sb.Append(" Id: ").Append(Id).Append("\n");
sb.Append(" Name: ").Append(Name).Append("\n"); sb.Append(" Name: ").Append(Name).Append("\n");
sb.Append(" ParticipantId: ").Append(ParticipantId).Append("\n"); sb.Append(" ParticipantId: ").Append(ParticipantId).Append("\n");
sb.Append(" Lock: ").Append(Lock).Append("\n"); sb.Append(" VarLock: ").Append(VarLock).Append("\n");
sb.Append(" Created: ").Append(Created).Append("\n"); sb.Append(" Created: ").Append(Created).Append("\n");
sb.Append(" Modified: ").Append(Modified).Append("\n"); sb.Append(" Modified: ").Append(Modified).Append("\n");
sb.Append(" Deleted: ").Append(Deleted).Append("\n"); sb.Append(" Deleted: ").Append(Deleted).Append("\n");
@ -4988,9 +5014,9 @@ namespace BreCalClient.misc.Model
this.ParticipantId.Equals(input.ParticipantId)) this.ParticipantId.Equals(input.ParticipantId))
) && ) &&
( (
this.Lock == input.Lock || this.VarLock == input.VarLock ||
(this.Lock != null && (this.VarLock != null &&
this.Lock.Equals(input.Lock)) this.VarLock.Equals(input.VarLock))
) && ) &&
( (
this.Created == input.Created || this.Created == input.Created ||
@ -5025,9 +5051,9 @@ namespace BreCalClient.misc.Model
{ {
hashCode = (hashCode * 59) + this.ParticipantId.GetHashCode(); hashCode = (hashCode * 59) + this.ParticipantId.GetHashCode();
} }
if (this.Lock != null) if (this.VarLock != null)
{ {
hashCode = (hashCode * 59) + this.Lock.GetHashCode(); hashCode = (hashCode * 59) + this.VarLock.GetHashCode();
} }
if (this.Created != null) if (this.Created != null)
{ {
@ -6521,6 +6547,7 @@ namespace BreCalClient.misc.Model
/// <summary> /// <summary>
/// Gets or Sets Participants /// Gets or Sets Participants
/// </summary> /// </summary>
/// <example>[1,5,7]</example>
[DataMember(Name = "participants", EmitDefaultValue = true)] [DataMember(Name = "participants", EmitDefaultValue = true)]
public List<int> Participants { get; set; } public List<int> Participants { get; set; }
/// <summary> /// <summary>

View File

@ -16,6 +16,9 @@
<setting name="APP_TITLE" serializeAs="String"> <setting name="APP_TITLE" serializeAs="String">
<value>!!Bremen calling Testversion!!</value> <value>!!Bremen calling Testversion!!</value>
</setting> </setting>
<setting name="LOGO_IMAGE_URL" serializeAs="String">
<value>https://www.textbausteine.net/</value>
</setting>
</BreCalClient.Properties.Settings> </BreCalClient.Properties.Settings>
</applicationSettings> </applicationSettings>
</configuration> </configuration>

View File

@ -95,12 +95,12 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" /> <PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.1" />
<PackageReference Include="JsonSubTypes" Version="2.0.1" /> <PackageReference Include="JsonSubTypes" Version="2.0.1" />
<PackageReference Include="log4net" Version="2.0.15" /> <PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="Polly" Version="7.2.3" /> <PackageReference Include="Polly" Version="7.2.4" />
<PackageReference Include="RestSharp" Version="108.0.2" /> <PackageReference Include="RestSharp" Version="110.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -61,6 +61,13 @@ namespace BreCalClient
Shifting = 3 Shifting = 3
} }
public enum SortOrder
{
SHIP_NAME,
ETA_ETD,
MODIFIED
}
#endregion #endregion
#region public helper #region public helper

View File

@ -58,11 +58,17 @@
<ColumnDefinition Width="60" /> <ColumnDefinition Width="60" />
<ColumnDefinition Width=".1*" /> <ColumnDefinition Width=".1*" />
<ColumnDefinition Width=".2*" /> <ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".6*" /> <ColumnDefinition Width=".1*" />
<ColumnDefinition Width=".2*" />
<ColumnDefinition Width=".1*" />
<ColumnDefinition Width=".2*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Button Margin="2" Grid.Column="0" Content="{x:Static p:Resources.textNewDots}" x:Name="buttonNew" Visibility="Hidden" Click="buttonNew_Click" Background="Transparent"/> <Button Margin="2" Grid.Column="0" Content="{x:Static p:Resources.textNewDots}" x:Name="buttonNew" Visibility="Hidden" Click="buttonNew_Click" Background="Transparent"/>
<Label Content="{x:Static p:Resources.textSortOrder}" Grid.Column="1" HorizontalContentAlignment="Right"/> <Label Content="{x:Static p:Resources.textSortOrder}" Grid.Column="1" HorizontalContentAlignment="Right"/>
<ComboBox x:Name="comboBoxSortOrder" Margin="2" Grid.Column="2" /> <ComboBox x:Name="comboBoxSortOrder" Margin="2" Grid.Column="2" SelectionChanged="comboBoxSortOrder_SelectionChanged" />
<CheckBox x:Name="checkboxShowCancelledCalls" Grid.Column="3" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="2" Checked="checkboxShowCancelledCalls_Checked" Unchecked="checkboxShowCancelledCalls_Checked" />
<Label Content="{x:Static p:Resources.textShowCancelledShipcalls}" Grid.Column="4" />
<Button Margin="2" Grid.Column="6" Content="{x:Static p:Resources.textClearFilters}" x:Name="buttonClearFilter" Click="buttonClearFilter_Click" Background="Transparent" />
</Grid> </Grid>
<Grid Grid.Row="2"> <Grid Grid.Row="2">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>

View File

@ -9,10 +9,12 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
using static BreCalClient.Extensions;
namespace BreCalClient namespace BreCalClient
{ {
@ -22,22 +24,35 @@ namespace BreCalClient
public partial class MainWindow : Window public partial class MainWindow : Window
{ {
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
#region Fields #region Fields
private const int SHIPCALL_UPDATE_INTERVAL_SECONDS = 30;
private readonly DefaultApi _api;
private readonly ObservableCollection<ShipcallControlModel> _controlModels = new(); private readonly Dictionary<int, ShipcallControlModel> _allShipcallsDict = new();
private readonly Dictionary<int, ShipcallControl> _allShipCallsControlDict = new();
private readonly List<ShipcallControlModel> _visibleControlModels = new();
private List<Ship> _ships = new(); private List<Ship> _ships = new();
private readonly ConcurrentDictionary<int, Ship> _shipLookupDict = new(); private readonly ConcurrentDictionary<int, Ship> _shipLookupDict = new();
private List<Berth> _berths = new(); private List<Berth> _berths = new();
private readonly ConcurrentDictionary<int, Berth> _berthLookupDict = new(); private readonly ConcurrentDictionary<int, Berth> _berthLookupDict = new();
private List<Participant> _participants = new(); private List<Participant> _participants = new();
private readonly Dictionary<int, Participant> _participantLookupDict = new(); private readonly Dictionary<int, Participant> _participantLookupDict = new();
private readonly Dictionary<int, ShipcallControl> _shipCallControlDict = new();
private readonly DefaultApi _api;
private readonly CancellationTokenSource _tokenSource = new(); private readonly CancellationTokenSource _tokenSource = new();
private LoginResult? _loginResult; private LoginResult? _loginResult;
private bool _refreshImmediately = false; private bool _refreshImmediately = false;
private bool? _showCanceled = null;
private Extensions.SortOrder? _sortOrder;
private bool _filterChanged = false;
private bool _sequenceChanged = false;
#endregion #endregion
#region Enums #region Enums
@ -70,7 +85,13 @@ namespace BreCalClient
labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; labelVersion.Text = "V. " + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE)) if (!string.IsNullOrEmpty(Properties.Settings.Default.APP_TITLE))
this.Title = Properties.Settings.Default.APP_TITLE; this.Title = Properties.Settings.Default.APP_TITLE;
} searchFilterControl.SearchFilterChanged += SearchFilterControl_SearchFilterChanged;
searchFilterControl.LogoImageClicked += () =>
{
Process.Start("explorer", Properties.Settings.Default.LOGO_IMAGE_URL);
};
this.comboBoxSortOrder.ItemsSource = Enum.GetValues(typeof(Extensions.SortOrder));
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
@ -134,10 +155,13 @@ namespace BreCalClient
// create UI & save new dialog model // create UI & save new dialog model
if (esc.ShipcallModel.Shipcall != null) if (esc.ShipcallModel.Shipcall != null)
{ {
this.UpdateShipcallUI(esc.ShipcallModel.Shipcall, new List<Times>()); this.UpdateUI();
this._api.ShipcallsPost(esc.ShipcallModel.Shipcall);
_refreshImmediately = true; this._api.ShipcallsPost(esc.ShipcallModel.Shipcall); // save new ship call
_tokenSource.Cancel(); this.AddShipcall(esc.ShipcallModel);
_refreshImmediately = true; // set flag to avoid timer loop termination
_tokenSource.Cancel(); // force timer loop end
} }
} }
} }
@ -175,9 +199,35 @@ namespace BreCalClient
ad.ShowDialog(); ad.ShowDialog();
} }
#endregion private void buttonClearFilter_Click(object sender, RoutedEventArgs e)
{
this.searchFilterControl.ClearFilters();
this.checkboxShowCancelledCalls.IsChecked = false;
this.FilterShipcalls();
}
#region private methods private void SearchFilterControl_SearchFilterChanged()
{
this.FilterShipcalls();
this.UpdateUI();
}
private void checkboxShowCancelledCalls_Checked(object sender, RoutedEventArgs e)
{
this._showCanceled = this.checkboxShowCancelledCalls.IsChecked;
this.SearchFilterControl_SearchFilterChanged();
}
private void comboBoxSortOrder_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e)
{
_sortOrder = (Extensions.SortOrder) this.comboBoxSortOrder.SelectedIndex;
this.FilterShipcalls();
this.UpdateUI();
}
#endregion
#region network operations
private async void LoadStaticLists() private async void LoadStaticLists()
{ {
@ -205,13 +255,7 @@ namespace BreCalClient
this.searchFilterControl.SetAgencies(agencies); this.searchFilterControl.SetAgencies(agencies);
_ = Task.Run(() => RefreshShipcalls()); _ = Task.Run(() => RefreshShipcalls());
} }
private void EnableControlsForParticipant()
{
if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.buttonNew.Visibility = Visibility.Visible;
}
public async Task RefreshShipcalls() public async Task RefreshShipcalls()
{ {
@ -236,30 +280,45 @@ namespace BreCalClient
labelStatusBar.Text = ex.Message; labelStatusBar.Text = ex.Message;
})); }));
} }
if (shipcalls != null) if (shipcalls != null)
{ {
foreach (Shipcall shipcall in shipcalls) foreach (Shipcall shipcall in shipcalls)
{ {
// load times for each shipcall
List<Times> currentTimes = await _api.TimesGetAsync(shipcall.Id); List<Times> currentTimes = await _api.TimesGetAsync(shipcall.Id);
this.UpdateShipcallUI(shipcall, currentTimes); if(!_allShipcallsDict.ContainsKey(shipcall.Id))
}
List<ShipcallControl> removeList = new();
foreach (ShipcallControlModel scm in this._controlModels)
{
if (shipcalls.Find(s => s.Id == scm.Shipcall?.Id) == null) // the model is no longer in the search result
{ {
if((scm.Shipcall != null) && this._shipCallControlDict.ContainsKey(scm.Shipcall.Id)) // add entry
ShipcallControlModel scm = new()
{ {
this.Dispatcher.Invoke((Action)(() => Shipcall = shipcall,
{ Times = currentTimes
this.stackPanel.Children.Remove(this._shipCallControlDict[scm.Shipcall.Id]); };
})); this.AddShipcall(scm);
this._shipCallControlDict.Remove(scm.Shipcall.Id); }
} else
{
// update entry
_allShipcallsDict[shipcall.Id].Shipcall = shipcall;
_allShipcallsDict[shipcall.Id].Times = currentTimes;
this.UpdateShipcall(_allShipcallsDict[shipcall.Id]);
}
}
List<int> existingIds = new(this._allShipcallsDict.Keys);
foreach (int existingId in existingIds)
{
if (shipcalls.Find(s => s.Id == existingId) == null) // the model is no longer in the search result
{
this.RemoveShipcall(existingId);
} }
} }
this.FilterShipcalls();
this.UpdateUI();
} }
try try
@ -270,60 +329,167 @@ namespace BreCalClient
} }
} }
private void UpdateShipcallUI(Shipcall shipcall, List<Times> times) #endregion
{
ShipcallControlModel? selectedSCMModel = null;
foreach (ShipcallControlModel scm in this._controlModels) #region basic operations
private void AddShipcall(ShipcallControlModel scm)
{
if (scm.Shipcall == null) return;
_allShipcallsDict[scm.Shipcall.Id] = scm;
Shipcall shipcall = scm.Shipcall;
if (this._shipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = this._shipLookupDict[shipcall.ShipId];
if (this._berthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = this._berthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
scm.AssignParticipants(this._participants);
this.Dispatcher.Invoke(() =>
{ {
if (scm.Shipcall?.Id == shipcall.Id) ShipcallControl sc = new()
{ {
selectedSCMModel = scm; Height = 120,
break; ShipcallControlModel = scm,
ParticipantDict = _participantLookupDict
};
sc.EditTimesRequested += Sc_EditTimesRequested;
sc.EditRequested += Sc_EditRequested;
sc.RefreshData();
this._allShipCallsControlDict[scm.Shipcall.Id] = sc;
});
}
private void UpdateShipcall(ShipcallControlModel scm)
{
if(scm.Shipcall == null) return;
Shipcall shipcall = scm.Shipcall;
if (this._shipLookupDict.ContainsKey(shipcall.ShipId))
scm.Ship = this._shipLookupDict[shipcall.ShipId];
if (this._berthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
scm.Berth = this._berthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
scm.AssignParticipants(this._participants);
}
private void RemoveShipcall(int shipcallId)
{
this.Dispatcher.Invoke(() =>
{
this.stackPanel.Children.Remove(this._allShipCallsControlDict[shipcallId]);
});
ShipcallControlModel removeModel = this._allShipcallsDict[shipcallId];
_visibleControlModels.Remove(removeModel);
this._allShipCallsControlDict.Remove(shipcallId);
this._allShipcallsDict.Remove(shipcallId);
}
private void FilterShipcalls()
{
SearchFilterModel sfm = this.searchFilterControl.SearchFilter;
this._visibleControlModels.Clear();
// first add everything
this._visibleControlModels.AddRange(_allShipcallsDict.Values);
// now remove elements whose filter criteria are met
if(sfm.Berths.Count > 0 )
{
this._visibleControlModels.RemoveAll(x => !sfm.Berths.Contains((x.Shipcall?.ArrivalBerthId) ?? -1));
}
if(sfm.Agencies.Count > 0 )
{
this._visibleControlModels.RemoveAll(x => !sfm.Agencies.Contains((x.GetParticipantIdForType(Extensions.ParticipantType.AGENCY)) ?? -1));
}
if(sfm.Categories.Count > 0 )
{
this._visibleControlModels.RemoveAll(x => !sfm.Categories.Contains((x.Shipcall?.Type) ?? -1));
}
if(!string.IsNullOrEmpty(sfm.SearchString))
{
this._visibleControlModels.RemoveAll(x => !x.ContainsRemarkText(sfm.SearchString));
}
if(sfm.ShipLengthTo != null)
{
this._visibleControlModels.RemoveAll(x => x.Ship?.Length > sfm.ShipLengthTo);
}
if(sfm.ShipLengthFrom != null)
{
this._visibleControlModels.RemoveAll(x => x.Ship?.Length < sfm.ShipLengthFrom);
}
if(sfm.EtaFrom != null)
{
this._visibleControlModels.RemoveAll(x => x.Shipcall?.Eta < sfm.EtaFrom);
}
if(sfm.EtaTo != null)
{
this._visibleControlModels.RemoveAll(x => x.Shipcall?.Eta > sfm.EtaTo);
}
if(!_showCanceled ?? true) // canceled calls are filtered by default
{
this._visibleControlModels.RemoveAll(x => x.Shipcall?.Canceled ?? true);
}
if (this._sortOrder != null)
{
switch(this._sortOrder)
{
case Extensions.SortOrder.SHIP_NAME:
this._visibleControlModels.Sort((x, y) => { if (x.Ship == null) return 0; if (y.Ship == null) return 0; return x.Ship.Name.CompareTo(y.Ship.Name); });
break;
case Extensions.SortOrder.MODIFIED:
this._visibleControlModels.Sort((x, y) => { if (x.Shipcall == null) return 0; if (y.Shipcall == null) return 0; return DateTime.Compare(x.Shipcall.Modified ?? x.Shipcall.Created, y.Shipcall.Modified ?? x.Shipcall.Created); });
break;
case Extensions.SortOrder.ETA_ETD:
this._visibleControlModels.Sort((x, y) =>
{
if (x.Shipcall == null) return 0;
if (y.Shipcall == null) return 0;
DateTime xDate = (x.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? x.Shipcall.Eta : x.Shipcall.Etd ?? x.Shipcall.Eta;
DateTime yDate = (y.Shipcall.Type == (int) Extensions.TypeEnum.Incoming) ? y.Shipcall.Eta : y.Shipcall.Etd ?? y.Shipcall.Eta;
return DateTime.Compare(xDate, yDate);
});
break;
default:
break;
} }
} }
}
if (selectedSCMModel != null)
{
selectedSCMModel.Shipcall = shipcall;
selectedSCMModel.Times = times;
}
else
{
// no: create new entry
selectedSCMModel = new()
{
Shipcall = shipcall,
Times = times
};
if (this._shipLookupDict.ContainsKey(shipcall.ShipId))
selectedSCMModel.Ship = this._shipLookupDict[shipcall.ShipId];
if (this._berthLookupDict.ContainsKey(shipcall.ArrivalBerthId ?? 0))
selectedSCMModel.Berth = this._berthLookupDict[shipcall.ArrivalBerthId ?? 0].Name;
_controlModels.Add(selectedSCMModel); #endregion
this.Dispatcher.Invoke(new Action(() =>
private void UpdateUI()
{
this.Dispatcher.Invoke(new Action(() =>
{
this.stackPanel.Children.Clear();
foreach(ShipcallControlModel visibleModel in this._visibleControlModels)
{ {
ShipcallControl sc = new() if (visibleModel.Shipcall == null) continue; // should not happen
if(this._allShipCallsControlDict.ContainsKey(visibleModel.Shipcall.Id))
{ {
Height = 120, this._allShipCallsControlDict[visibleModel.Shipcall.Id].RefreshData();
ShipcallControlModel = selectedSCMModel, this.stackPanel.Children.Add(this._allShipCallsControlDict[visibleModel.Shipcall.Id]);
ParticipantDict = _participantLookupDict }
}; }
sc.EditTimesRequested += Sc_EditTimesRequested;
sc.EditRequested += Sc_EditRequested;
this.stackPanel.Children.Add(sc);
this._shipCallControlDict[shipcall.Id] = sc;
}));
}
selectedSCMModel.AssignParticipants(this._participants);
this.Dispatcher.Invoke((Action)(() =>
{
this._shipCallControlDict[shipcall.Id].RefreshData();
})); }));
} }
#region control event handler
private async void Sc_EditRequested(ShipcallControl obj) private async void Sc_EditRequested(ShipcallControl obj)
{ {
if (obj.ShipcallControlModel != null) if (obj.ShipcallControlModel != null)
@ -391,6 +557,10 @@ namespace BreCalClient
} }
} }
#endregion
#region helper
private void ShowErrorDialog(string message, string caption) private void ShowErrorDialog(string message, string caption)
{ {
Dispatcher.Invoke(new Action(() => Dispatcher.Invoke(new Action(() =>
@ -399,6 +569,12 @@ namespace BreCalClient
})); }));
} }
private void EnableControlsForParticipant()
{
if (App.Participant.IsTypeFlagSet(Extensions.ParticipantType.BSMD))
this.buttonNew.Visibility = Visibility.Visible;
}
#endregion #endregion
} }

View File

@ -49,5 +49,14 @@ namespace BreCalClient.Properties {
return ((string)(this["APP_TITLE"])); return ((string)(this["APP_TITLE"]));
} }
} }
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("https://www.textbausteine.net/")]
public string LOGO_IMAGE_URL {
get {
return ((string)(this["LOGO_IMAGE_URL"]));
}
}
} }
} }

View File

@ -11,5 +11,8 @@
<Setting Name="APP_TITLE" Type="System.String" Scope="Application"> <Setting Name="APP_TITLE" Type="System.String" Scope="Application">
<Value Profile="(Default)">!!Bremen calling Testversion!!</Value> <Value Profile="(Default)">!!Bremen calling Testversion!!</Value>
</Setting> </Setting>
<Setting Name="LOGO_IMAGE_URL" Type="System.String" Scope="Application">
<Value Profile="(Default)">https://www.textbausteine.net/</Value>
</Setting>
</Settings> </Settings>
</SettingsFile> </SettingsFile>

View File

@ -0,0 +1,18 @@
# Bremen calling WPF client
## Introduction
## API / code generation
The Rest API client is generated from the OpenAPI specification [BreCalApi.yaml](../../misc/BreCalApiyaml) into the C# file [BreCalApi.cs](../../misc/BreCalApi.cs).
In order to do so an extension for Visual Studio needs to be installed: REST API Client Code Generator for VS 2022.
https://marketplace.visualstudio.com/items?itemName=ChristianResmaHelle.ApiClientCodeGenerator2022
This extension has multiple generators, for this project OpenApiCodeGenerator is used (must be set on the yaml file in the project settings).
Internally this uses Java, currently > 55 which translates into Java JDK 17 LTS.
If code generation is not working please have a look in the output pane and select appropriate output source.
## Installation
The client is deployed via ClickOnce.

View File

@ -316,6 +316,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Clear filters.
/// </summary>
public static string textClearFilters {
get {
return ResourceManager.GetString("textClearFilters", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Clear value. /// Looks up a localized string similar to Clear value.
/// </summary> /// </summary>
@ -694,6 +703,15 @@ namespace BreCalClient.Resources {
} }
} }
/// <summary>
/// Looks up a localized string similar to Show cancelled calls.
/// </summary>
public static string textShowCancelledShipcalls {
get {
return ResourceManager.GetString("textShowCancelledShipcalls", resourceCulture);
}
}
/// <summary> /// <summary>
/// Looks up a localized string similar to Sort order. /// Looks up a localized string similar to Sort order.
/// </summary> /// </summary>

View File

@ -370,4 +370,10 @@
<data name="textPasswordChanged" xml:space="preserve"> <data name="textPasswordChanged" xml:space="preserve">
<value>Passwort geändert.</value> <value>Passwort geändert.</value>
</data> </data>
<data name="textClearFilters" xml:space="preserve">
<value>Filter löschen</value>
</data>
<data name="textShowCancelledShipcalls" xml:space="preserve">
<value>Stornierte anzeigen</value>
</data>
</root> </root>

View File

@ -199,6 +199,9 @@
<data name="textClearAssignment" xml:space="preserve"> <data name="textClearAssignment" xml:space="preserve">
<value>Clear assignment</value> <value>Clear assignment</value>
</data> </data>
<data name="textClearFilters" xml:space="preserve">
<value>Clear filters</value>
</data>
<data name="textClearValue" xml:space="preserve"> <data name="textClearValue" xml:space="preserve">
<value>Clear value</value> <value>Clear value</value>
</data> </data>
@ -325,6 +328,9 @@
<data name="textShipLength" xml:space="preserve"> <data name="textShipLength" xml:space="preserve">
<value>Ship length</value> <value>Ship length</value>
</data> </data>
<data name="textShowCancelledShipcalls" xml:space="preserve">
<value>Show cancelled calls</value>
</data>
<data name="textSortOrder" xml:space="preserve"> <data name="textSortOrder" xml:space="preserve">
<value>Sort order</value> <value>Sort order</value>
</data> </data>

View File

@ -59,23 +59,23 @@
<ColumnDefinition Width="30" /> <ColumnDefinition Width="30" />
<ColumnDefinition Width=".5*" /> <ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<DatePicker x:Name="datePickerETAFrom" Grid.Column="0" Margin="2" /> <DatePicker x:Name="datePickerETAFrom" Grid.Column="0" Margin="2" SelectedDateChanged="datePickerETAFrom_SelectedDateChanged" SelectedDate="{Binding Path=EtaFrom}"/>
<Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" /> <Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" />
<DatePicker x:Name="datePickerETATo" Grid.Column="2" Margin="2" /> <DatePicker x:Name="datePickerETATo" Grid.Column="2" Margin="2" SelectedDateChanged="datePickerETATo_SelectedDateChanged" SelectedDate="{Binding Path=EtaTo}"/>
</Grid> </Grid>
<xctk:CheckComboBox x:Name="comboBoxCategories" Grid.Column="4" Margin="2" /> <xctk:CheckComboBox x:Name="comboBoxCategories" Grid.Column="4" Margin="2" ItemSelectionChanged="comboBoxCategories_ItemSelectionChanged" />
<Grid Grid.Column="6" Grid.Row="0"> <Grid Grid.Column="6" Grid.Row="0">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*" /> <ColumnDefinition Width=".5*" />
<ColumnDefinition Width="30" /> <ColumnDefinition Width="30" />
<ColumnDefinition Width=".5*" /> <ColumnDefinition Width=".5*" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<xctk:DoubleUpDown x:Name="upDownShiplengthFrom" Grid.Column="0" Margin="2" Minimum="0" Maximum="1000" /> <xctk:DoubleUpDown x:Name="upDownShiplengthFrom" Grid.Column="0" Margin="2" Minimum="0" Maximum="1000" ValueChanged="upDownShiplengthFrom_ValueChanged" Value="{Binding Path=ShipLengthFrom}" />
<Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" /> <Label Grid.Column="1" Content="{x:Static p:Resources.textTo}" />
<xctk:DoubleUpDown x:Name="upDownShiplengthTo" Grid.Column="2" Margin="2" Minimum="0" Maximum="1000"/> <xctk:DoubleUpDown x:Name="upDownShiplengthTo" Grid.Column="2" Margin="2" Minimum="0" Maximum="1000" ValueChanged="upDownShiplengthTo_ValueChanged" Value="{Binding Path=ShipLengthTo}"/>
</Grid> </Grid>
<xctk:WatermarkTextBox x:Name="textBoxSearch" Grid.Column="2" Grid.Row="1" Margin="2" Watermark="{x:Static p:Resources.textEnterKeyword}" /> <xctk:WatermarkTextBox x:Name="textBoxSearch" Grid.Column="2" Grid.Row="1" Margin="2" Watermark="{x:Static p:Resources.textEnterKeyword}" PreviewTextInput="textBoxSearch_PreviewTextInput" DataObject.Pasting="textBoxSearch_Pasting" TextChanged="textBoxSearch_TextChanged" />
<xctk:CheckComboBox x:Name="comboBoxBerths" DisplayMemberPath="Name" Grid.Column="4" Grid.Row="1" Margin="2" /> <xctk:CheckComboBox x:Name="comboBoxBerths" DisplayMemberPath="Name" Grid.Column="4" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxBerths_ItemSelectionChanged" />
<xctk:CheckComboBox x:Name="comboBoxAgencies" DisplayMemberPath="Name" Grid.Column="6" Grid.Row="1" Margin="2" /> <xctk:CheckComboBox x:Name="comboBoxAgencies" DisplayMemberPath="Name" Grid.Column="6" Grid.Row="1" Margin="2" ItemSelectionChanged="comboBoxAgencies_ItemSelectionChanged" />
</Grid> </Grid>
</UserControl> </UserControl>

View File

@ -16,23 +16,41 @@ namespace BreCalClient
public partial class SearchFilterControl : UserControl public partial class SearchFilterControl : UserControl
{ {
#region private fields
private SearchFilterModel _model = new();
#endregion
#region Construction #region Construction
public SearchFilterControl() public SearchFilterControl()
{ {
InitializeComponent(); InitializeComponent();
this.DataContext = this._model;
} }
#endregion #endregion
#region events #region events
/// <summary>
/// historically we love a clickable logo and see what will happen
/// </summary>
public event Action? LogoImageClicked; public event Action? LogoImageClicked;
/// <summary>
/// if the user somewhat changes the filters trigger this
/// </summary>
internal event Action? SearchFilterChanged;
#endregion #endregion
#region Properties #region Properties
public string FilterAsJson { get; set; } = "";
internal SearchFilterModel SearchFilter { get { return _model; } }
#endregion #endregion
@ -48,6 +66,19 @@ namespace BreCalClient
this.comboBoxAgencies.ItemsSource = agencies; this.comboBoxAgencies.ItemsSource = agencies;
} }
public void ClearFilters()
{
this._model = new SearchFilterModel();
this.comboBoxAgencies.UnSelectAll();
this.comboBoxBerths.UnSelectAll();
this.comboBoxCategories.UnSelectAll();
this.datePickerETAFrom.SelectedDate = null;
this.datePickerETATo.SelectedDate = null;
this.textBoxSearch.Clear();
this.upDownShiplengthFrom.Value = null;
this.upDownShiplengthTo.Value = null;
}
#endregion #endregion
#region event handler #region event handler
@ -60,6 +91,69 @@ namespace BreCalClient
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e) private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{ {
this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(Extensions.TypeEnum)); this.comboBoxCategories.ItemsSource = Enum.GetValues(typeof(Extensions.TypeEnum));
}
private void datePickerETAFrom_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
SearchFilterChanged?.Invoke();
}
private void datePickerETATo_SelectedDateChanged(object sender, SelectionChangedEventArgs e)
{
//_model.EtaTo = datePickerETATo.SelectedDate;
SearchFilterChanged?.Invoke();
}
private void comboBoxCategories_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{
_model.Categories.Clear();
foreach(int category in comboBoxCategories.SelectedItems)
_model.Categories.Add(category);
SearchFilterChanged?.Invoke();
}
private void upDownShiplengthFrom_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<object> e)
{
SearchFilterChanged?.Invoke();
}
private void upDownShiplengthTo_ValueChanged(object sender, System.Windows.RoutedPropertyChangedEventArgs<object> e)
{
SearchFilterChanged?.Invoke();
}
private void comboBoxBerths_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{
_model.Berths.Clear();
foreach(Berth berth in comboBoxBerths.SelectedItems)
_model.Berths.Add(berth.Id);
SearchFilterChanged?.Invoke();
}
private void comboBoxAgencies_ItemSelectionChanged(object sender, Xceed.Wpf.Toolkit.Primitives.ItemSelectionChangedEventArgs e)
{
_model.Agencies.Clear();
foreach (Participant agency in comboBoxAgencies.SelectedItems)
_model.Agencies.Add(agency.Id);
SearchFilterChanged?.Invoke();
}
private void textBoxSearch_Pasting(object sender, System.Windows.DataObjectPastingEventArgs e)
{
this.SearchFilter.SearchString = e.SourceDataObject.ToString();
SearchFilterChanged?.Invoke();
}
private void textBoxSearch_PreviewTextInput(object sender, TextCompositionEventArgs e)
{
this.SearchFilter.SearchString = e.Text;
SearchFilterChanged?.Invoke();
}
private void textBoxSearch_TextChanged(object sender, TextChangedEventArgs e)
{
SearchFilterChanged?.Invoke();
} }
#endregion #endregion

View File

@ -18,11 +18,11 @@ namespace BreCalClient
public DateTime? EtaTo { get; set; } public DateTime? EtaTo { get; set; }
public IEnumerable<int>? Categories { get; set; } public List<int> Categories { get; set; } = new();
public IEnumerable<Participant>? Agencies { get; set; } public List<int> Agencies { get; set; } = new();
public IEnumerable<Berth>? Berths { get; set; } public List<int> Berths { get; set; } = new();
public string? SearchString { get; set; } public string? SearchString { get; set; }
@ -30,7 +30,7 @@ namespace BreCalClient
public double? ShipLengthTo { get; set; } public double? ShipLengthTo { get; set; }
#endregion #endregion
} }
} }

View File

@ -42,6 +42,7 @@ namespace BreCalClient
#region Properties #region Properties
public Shipcall? Shipcall { get; set; } public Shipcall? Shipcall { get; set; }
public Ship? Ship { get; set; } public Ship? Ship { get; set; }
public string? Berth { get; set; } public string? Berth { get; set; }
@ -108,6 +109,20 @@ namespace BreCalClient
return null; return null;
} }
public bool ContainsRemarkText(string text)
{
if(Shipcall != null)
{
foreach(Times times in this.Times)
{
if (times.Remarks == null) continue;
if(times.Remarks.Contains(text)) return true;
}
}
return false;
}
#endregion #endregion
#region helper #region helper