Refactor Api-Client
Add Exceptionhandler, AlertService JSON-Converter AppSettings via DI Reformat Code
@@ -1,4 +1,5 @@
|
||||
<?xml version = "1.0" encoding = "UTF-8" ?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Application xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:Jugenddienst_Stunden"
|
||||
@@ -11,4 +12,4 @@
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
</Application>
|
||||
@@ -4,12 +4,11 @@
|
||||
/// Die Hauptanwendungsklasse.
|
||||
/// </summary>
|
||||
public partial class App : Application {
|
||||
|
||||
/// <summary>
|
||||
/// Initialisiert eine neue Instanz der <see cref="App"/>-Klasse.
|
||||
/// </summary>
|
||||
public App() {
|
||||
InitializeComponent();
|
||||
MainPage = new AppShell();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Initialisiert eine neue Instanz der <see cref="App"/>-Klasse.
|
||||
/// </summary>
|
||||
public App() {
|
||||
InitializeComponent();
|
||||
MainPage = new AppShell();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<Shell
|
||||
x:Class="Jugenddienst_Stunden.AppShell"
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
@@ -10,8 +11,9 @@
|
||||
<ShellContent
|
||||
Title="Stunden"
|
||||
ContentTemplate="{DataTemplate views:StundenPage}"
|
||||
Icon="{OnPlatform 'icon_watch.png', iOS='icon_watch_ios.png', MacCatalyst='icon_watch_ios.png'}" Route="StundenPage" />
|
||||
|
||||
Icon="{OnPlatform 'icon_watch.png', iOS='icon_watch_ios.png', MacCatalyst='icon_watch_ios.png'}"
|
||||
Route="StundenPage" />
|
||||
|
||||
<ShellContent
|
||||
Title="Notizen"
|
||||
ContentTemplate="{DataTemplate views:AllNotesPage}"
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
namespace Jugenddienst_Stunden;
|
||||
|
||||
/// <summary>
|
||||
/// AppShell.xaml.cs
|
||||
/// </summary>
|
||||
@@ -12,8 +13,8 @@ public partial class AppShell : Shell {
|
||||
//Seiten, die nicht in der Appshell sichtbar sind, aber trotzdem aufgerufen werden können
|
||||
Routing.RegisterRoute(nameof(Views.NotePage), typeof(Views.NotePage));
|
||||
Routing.RegisterRoute(nameof(Views.StundePage), typeof(Views.StundePage));
|
||||
|
||||
|
||||
//Muss ich die registrieren?
|
||||
Routing.RegisterRoute(nameof(Views.LoginPage), typeof(Views.LoginPage));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,21 @@
|
||||
|
||||
/// Gib true zurück, wenn die Collection Werte enthält
|
||||
namespace Jugenddienst_Stunden.Converter {
|
||||
internal class CollectionVisibilityConverter : IValueConverter {
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is IEnumerable<object> collection) {
|
||||
if ((string)parameter == "Invert")
|
||||
return !collection.Any();
|
||||
return collection.Any();
|
||||
}
|
||||
if ((string)parameter == "Invert")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
internal class CollectionVisibilityConverter : IValueConverter {
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is IEnumerable<object> collection) {
|
||||
if ((string)parameter == "Invert")
|
||||
return !collection.Any();
|
||||
return collection.Any();
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((string)parameter == "Invert")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jugenddienst_Stunden.Converter;
|
||||
|
||||
/// <summary>
|
||||
/// Falls ein int als bool dargestellt werden soll
|
||||
/// </summary>
|
||||
public class IntBoolConverter : IValueConverter {
|
||||
|
||||
/// <summary>
|
||||
/// Konvertiert einen int in einen bool
|
||||
/// </summary>
|
||||
@@ -18,6 +18,7 @@ public class IntBoolConverter : IValueConverter {
|
||||
if (value is int) {
|
||||
return (int)value != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,6 +34,7 @@ public class IntBoolConverter : IValueConverter {
|
||||
if (value is bool) {
|
||||
return (bool)value ? 1 : 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +1,37 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jugenddienst_Stunden.Converter;
|
||||
|
||||
internal class SecondsTimeConverter : IValueConverter {
|
||||
private int seconds;
|
||||
|
||||
private int seconds;
|
||||
/// <summary>
|
||||
/// Konvertiert eine Sekundenangabe nach Stunden:Minuten, auch bei mehr als 24 Stunden
|
||||
/// </summary>
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is null)
|
||||
return "0:0";
|
||||
if (value is int) {
|
||||
seconds = (int)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Konvertiert eine Sekundenangabe nach Stunden:Minuten, auch bei mehr als 24 Stunden
|
||||
/// </summary>
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is null)
|
||||
return "0:0";
|
||||
if (value is int) {
|
||||
seconds = (int)value;
|
||||
}
|
||||
if (value is double) {
|
||||
seconds = (int)Math.Round((double)value);
|
||||
} else {
|
||||
int.TryParse((string?)value, out seconds);
|
||||
}
|
||||
if (value is double) {
|
||||
seconds = (int)Math.Round((double)value);
|
||||
} else {
|
||||
int.TryParse((string?)value, out seconds);
|
||||
}
|
||||
|
||||
TimeSpan time = TimeSpan.FromSeconds(seconds);
|
||||
TimeSpan time = TimeSpan.FromSeconds(seconds);
|
||||
|
||||
return (int)time.TotalHours + ":" + Math.Abs(time.Minutes);
|
||||
return (int)time.TotalHours + ":" + Math.Abs(time.Minutes);
|
||||
|
||||
//return time.ToString(@"hh\:mm");
|
||||
//return time.ToString(@"hh\:mm\:ss");
|
||||
//return time.ToString(@"hh\:mm");
|
||||
//return time.ToString(@"hh\:mm\:ss");
|
||||
|
||||
//return "00:00";
|
||||
}
|
||||
//return "00:00";
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace Jugenddienst_Stunden.Converter;
|
||||
internal class StringVisibilityConverter : IValueConverter {
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is string strValue) {
|
||||
return !string.IsNullOrEmpty(strValue.Replace("Server: ",""));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
internal class StringVisibilityConverter : IValueConverter {
|
||||
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
if (value is string strValue) {
|
||||
return !string.IsNullOrEmpty(strValue.Replace("Server: ", ""));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,15 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jugenddienst_Stunden.Exceptions;
|
||||
namespace Jugenddienst_Stunden.Exceptions;
|
||||
|
||||
public class NoDataException : Exception {
|
||||
public NoDataException() : base("Keine Daten gefunden") { }
|
||||
public NoDataException(string message) : base(message) { }
|
||||
public NoDataException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
public NoDataException() : base("Keine Daten gefunden") {
|
||||
}
|
||||
|
||||
public NoDataException(string message) : base(message) {
|
||||
}
|
||||
|
||||
public NoDataException(string message, Exception inner) : base(message, inner) {
|
||||
}
|
||||
}
|
||||
15
Jugenddienst Stunden/Infrastructure/AlertService.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class AlertService : IAlertService {
|
||||
public event EventHandler<string>? AlertRaised;
|
||||
public void Raise(string message) {
|
||||
AlertRaised?.Invoke(this, message);
|
||||
}
|
||||
}
|
||||
111
Jugenddienst Stunden/Infrastructure/ApiClient.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Net.Http.Json;
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class ApiClient : IApiClient {
|
||||
private readonly HttpClient _http;
|
||||
private readonly JsonSerializerOptions _json;
|
||||
private readonly ApiOptions _options;
|
||||
private readonly IAppSettings _settings;
|
||||
|
||||
public ApiClient(HttpClient http, ApiOptions options, ITokenProvider tokenProvider, IAppSettings settings) {
|
||||
_http = http;
|
||||
_options = options;
|
||||
_settings = settings;
|
||||
|
||||
_http.Timeout = options.Timeout;
|
||||
if (!_http.DefaultRequestHeaders.Accept.Any())
|
||||
_http.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
|
||||
var token = tokenProvider.GetToken();
|
||||
if (!string.IsNullOrWhiteSpace(token))
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
_json = new JsonSerializerOptions {
|
||||
PropertyNameCaseInsensitive = true,
|
||||
WriteIndented = false,
|
||||
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
// Globale Converter: erlauben numerische Felder auch als Strings (z.B. user.id)
|
||||
_json.Converters.Add(new Jugenddienst_Stunden.Models.JsonFlexibleInt32Converter());
|
||||
_json.Converters.Add(new Jugenddienst_Stunden.Models.JsonFlexibleNullableInt32Converter());
|
||||
|
||||
// Stelle sicher, dass die BaseAddress sofort aus den aktuellen Settings (Preferences) gesetzt wird
|
||||
// und nicht erst beim ersten Request. Dadurch steht die ApiUrl ab Initialisierung zur Verfügung.
|
||||
EnsureBaseAddress();
|
||||
}
|
||||
|
||||
public Task<T> GetAsync<T>(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default)
|
||||
=> SendAsync<T>(HttpMethod.Get, path, null, query, ct);
|
||||
|
||||
public async Task<T> SendAsync<T>(HttpMethod method, string path, object? body = null,
|
||||
IDictionary<string, string?>? query = null, CancellationToken ct = default) {
|
||||
// Vor jedem Request sicherstellen, dass die (ggf. geänderte) BaseAddress gesetzt ist
|
||||
EnsureBaseAddress();
|
||||
var uri = BuildUri(path, query);
|
||||
using var req = new HttpRequestMessage(method, uri) {
|
||||
Content = body is null ? null : JsonContent.Create(body, options: _json)
|
||||
};
|
||||
|
||||
using var res = await _http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, ct).ConfigureAwait(false);
|
||||
var text = await res.Content.ReadAsStringAsync(ct).ConfigureAwait(false);
|
||||
if (!res.IsSuccessStatusCode)
|
||||
throw ApiException.From(res.StatusCode, TryGetMessage(text), text);
|
||||
|
||||
if (typeof(T) == typeof(void) || typeof(T) == typeof(object) || string.IsNullOrWhiteSpace(text))
|
||||
return default!;
|
||||
|
||||
var obj = System.Text.Json.JsonSerializer.Deserialize<T>(text, _json);
|
||||
if (obj is null)
|
||||
throw new ApiException("Fehler beim Deserialisieren der Daten.");
|
||||
return obj;
|
||||
}
|
||||
|
||||
public Task DeleteAsync(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default)
|
||||
=> SendAsync<object>(HttpMethod.Delete, path, null, query, ct);
|
||||
|
||||
private void EnsureBaseAddress() {
|
||||
var baseUrl = _settings.ApiUrl;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(baseUrl)) {
|
||||
throw new InvalidOperationException(
|
||||
"ApiUrl ist leer. Bitte zuerst eine gültige Server-URL setzen (Preferences key 'apiUrl'), " +
|
||||
"z.B. im Login/Setup, bevor API-Aufrufe stattfinden."
|
||||
);
|
||||
}
|
||||
|
||||
// nur setzen, wenn nötig (damit spätere Änderungen nach Login greifen)
|
||||
if (_http.BaseAddress is null || !Uri.Equals(_http.BaseAddress, new Uri(baseUrl, UriKind.Absolute))) {
|
||||
_http.BaseAddress = new Uri(baseUrl, UriKind.Absolute);
|
||||
_http.Timeout = _options.Timeout;
|
||||
}
|
||||
}
|
||||
|
||||
private static string TryGetMessage(string text) {
|
||||
try {
|
||||
using var doc = JsonDocument.Parse(text);
|
||||
if (doc.RootElement.TryGetProperty("message", out var m) && m.ValueKind == JsonValueKind.String)
|
||||
return m.GetString() ?? text;
|
||||
} catch {
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
private static Uri BuildUri(string path, IDictionary<string, string?>? query) {
|
||||
if (query is null || query.Count == 0)
|
||||
return new Uri(path, UriKind.Relative);
|
||||
|
||||
var sb = new StringBuilder(path);
|
||||
sb.Append(path.Contains('?') ? '&' : '?');
|
||||
sb.Append(string.Join('&', query
|
||||
.Where(kv => kv.Value is not null)
|
||||
.Select(kv => $"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value!)}")));
|
||||
return new Uri(sb.ToString(), UriKind.Relative);
|
||||
}
|
||||
}
|
||||
6
Jugenddienst Stunden/Infrastructure/ApiOptions.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class ApiOptions {
|
||||
public required string BaseUrl { get; init; }
|
||||
public TimeSpan Timeout { get; init; } = TimeSpan.FromSeconds(15);
|
||||
}
|
||||
23
Jugenddienst Stunden/Infrastructure/Exceptions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System.Net;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal class ApiException : Exception {
|
||||
public HttpStatusCode StatusCode { get; }
|
||||
public string? ResponseBody { get; }
|
||||
|
||||
public ApiException(string message, HttpStatusCode statusCode = 0, string? responseBody = null,
|
||||
Exception? inner = null)
|
||||
: base(message, inner) {
|
||||
StatusCode = statusCode;
|
||||
ResponseBody = responseBody;
|
||||
}
|
||||
|
||||
public static ApiException From(HttpStatusCode statusCode, string message, string? responseBody = null)
|
||||
=> new ApiException(message, statusCode, responseBody);
|
||||
}
|
||||
|
||||
internal class ValidationException : Exception {
|
||||
public ValidationException(string message) : base(message) {
|
||||
}
|
||||
}
|
||||
23
Jugenddienst Stunden/Infrastructure/NullApiClient.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class NullApiClient : IApiClient {
|
||||
private readonly string _message;
|
||||
public NullApiClient(string message) => _message = message ?? "API nicht konfiguriert.";
|
||||
|
||||
public Task<T> GetAsync<T>(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default)
|
||||
=> Task.FromException<T>(new InvalidOperationException(_message));
|
||||
|
||||
public Task<T> SendAsync<T>(HttpMethod method, string path, object? body = null,
|
||||
IDictionary<string, string?>? query = null, CancellationToken ct = default)
|
||||
=> Task.FromException<T>(new InvalidOperationException(_message));
|
||||
|
||||
public Task DeleteAsync(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default)
|
||||
=> Task.FromException(new InvalidOperationException(_message));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class PreferencesAppSettings : IAppSettings {
|
||||
public string ApiUrl {
|
||||
get => Preferences.Default.Get("apiUrl", "");
|
||||
set => Preferences.Default.Set("apiUrl", value);
|
||||
}
|
||||
|
||||
public string ApiKey {
|
||||
get => Preferences.Default.Get("apiKey", "");
|
||||
set => Preferences.Default.Set("apiKey", value);
|
||||
}
|
||||
|
||||
public int EmployeeId {
|
||||
get => Preferences.Default.Get("EmployeeId", 0);
|
||||
set => Preferences.Default.Set("EmployeeId", value);
|
||||
}
|
||||
|
||||
public string Name {
|
||||
get => Preferences.Default.Get("name", "Nicht");
|
||||
set => Preferences.Default.Set("name", value);
|
||||
}
|
||||
|
||||
public string Surname {
|
||||
get => Preferences.Default.Get("surname", "Eingeloggt");
|
||||
set => Preferences.Default.Set("surname", value);
|
||||
}
|
||||
}
|
||||
10
Jugenddienst Stunden/Infrastructure/SettingsTokenProvider.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class SettingsTokenProvider : ITokenProvider {
|
||||
private readonly IAppSettings _settings;
|
||||
public SettingsTokenProvider(IAppSettings settings) => _settings = settings;
|
||||
|
||||
public string GetToken() => _settings.ApiKey;
|
||||
}
|
||||
7
Jugenddienst Stunden/Infrastructure/TokenProvider.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
namespace Jugenddienst_Stunden.Infrastructure;
|
||||
|
||||
internal sealed class GlobalVarTokenProvider : ITokenProvider {
|
||||
public string? GetToken() => Models.GlobalVar.ApiKey;
|
||||
}
|
||||
11
Jugenddienst Stunden/Interfaces/IAlertService.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Jugenddienst_Stunden.Interfaces;
|
||||
internal interface IAlertService {
|
||||
event EventHandler<string> AlertRaised;
|
||||
void Raise(string message);
|
||||
}
|
||||
10
Jugenddienst Stunden/Interfaces/IApiClient.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
internal interface IApiClient {
|
||||
Task<T> GetAsync<T>(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default);
|
||||
|
||||
Task<T> SendAsync<T>(HttpMethod method, string path, object? body = null,
|
||||
IDictionary<string, string?>? query = null, CancellationToken ct = default);
|
||||
|
||||
Task DeleteAsync(string path, IDictionary<string, string?>? query = null, CancellationToken ct = default);
|
||||
}
|
||||
10
Jugenddienst Stunden/Interfaces/IAppSettings.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
public interface IAppSettings {
|
||||
string ApiUrl { get; set; }
|
||||
string ApiKey { get; set; }
|
||||
|
||||
int EmployeeId { get; set; }
|
||||
string Name { get; set; }
|
||||
string Surname { get; set; }
|
||||
}
|
||||
@@ -6,12 +6,12 @@ namespace Jugenddienst_Stunden.Interfaces;
|
||||
/// Repository‑Schnittstelle für Datenzugriff (API/Storage) rund um Stunden.
|
||||
/// </summary>
|
||||
internal interface IHoursRepository {
|
||||
Task<BaseResponse> LoadBase(string query);
|
||||
Task<Settings> LoadSettings();
|
||||
Task<Hours> LoadData();
|
||||
Task<User> LoadUser(string apiKey);
|
||||
Task<List<DayTime>> LoadDay(DateTime date);
|
||||
Task<DayTime> LoadEntry(int id);
|
||||
Task<DayTime> SaveEntry(DayTime stunde);
|
||||
Task DeleteEntry(DayTime stunde);
|
||||
}
|
||||
Task<BaseResponse> LoadBase(string query);
|
||||
Task<Settings> LoadSettings();
|
||||
Task<Hours> LoadData();
|
||||
Task<User> LoadUser(string apiKey);
|
||||
Task<List<DayTime>> LoadDay(DateTime date);
|
||||
Task<DayTime> LoadEntry(int id);
|
||||
Task<DayTime> SaveEntry(DayTime stunde);
|
||||
Task DeleteEntry(DayTime stunde);
|
||||
}
|
||||
@@ -5,12 +5,12 @@ namespace Jugenddienst_Stunden.Interfaces;
|
||||
/// <summary>
|
||||
/// Fachlicher Service für Stunden – konsumiert Repository und stellt VM‑freundliche Methoden bereit.
|
||||
/// </summary>
|
||||
internal interface IHoursService {
|
||||
Task<(Hours hours, Settings settings)> GetMonthSummaryAsync(DateTime monthDate);
|
||||
Task<(List<DayTime> dayTimes, Settings settings)> GetDayWithSettingsAsync(DateTime date);
|
||||
Task<List<DayTime>> GetDayRangeAsync(DateTime from, DateTime to);
|
||||
Task<Settings> GetSettingsAsync();
|
||||
Task<DayTime> GetEntryAsync(int id);
|
||||
Task<DayTime> SaveEntryAsync(DayTime stunde);
|
||||
Task DeleteEntryAsync(DayTime stunde);
|
||||
}
|
||||
public interface IHoursService {
|
||||
Task<(Hours hours, Settings settings)> GetMonthSummaryAsync(DateTime monthDate);
|
||||
Task<(List<DayTime> dayTimes, Settings settings)> GetDayWithSettingsAsync(DateTime date);
|
||||
Task<List<DayTime>> GetDayRangeAsync(DateTime from, DateTime to);
|
||||
Task<Settings> GetSettingsAsync();
|
||||
Task<DayTime> GetEntryAsync(int id);
|
||||
Task<DayTime> SaveEntryAsync(DayTime stunde);
|
||||
Task DeleteEntryAsync(DayTime stunde);
|
||||
}
|
||||
5
Jugenddienst Stunden/Interfaces/ITokenProvider.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Jugenddienst_Stunden.Interfaces;
|
||||
|
||||
internal interface ITokenProvider {
|
||||
string? GetToken();
|
||||
}
|
||||
@@ -1,312 +1,312 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<!-- <TargetFrameworks>net8.0-maccatalyst;net9.0-android35.0</TargetFrameworks> -->
|
||||
<TargetFrameworks>net9.0-android35.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||
<PropertyGroup>
|
||||
<!-- <TargetFrameworks>net8.0-maccatalyst;net9.0-android35.0</TargetFrameworks> -->
|
||||
<TargetFrameworks>net9.0-android35.0</TargetFrameworks>
|
||||
<!-- Uncomment to also build the tizen app. You will need to install tizen by following this: https://github.com/Samsung/Tizen.NET -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net8.0-tizen</TargetFrameworks> -->
|
||||
|
||||
<!-- Note for MacCatalyst:
|
||||
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
|
||||
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
|
||||
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
|
||||
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
|
||||
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
|
||||
<!-- Note for MacCatalyst:
|
||||
The default runtime is maccatalyst-x64, except in Release config, in which case the default is maccatalyst-x64;maccatalyst-arm64.
|
||||
When specifying both architectures, use the plural <RuntimeIdentifiers> instead of the singular <RuntimeIdentifier>.
|
||||
The Mac App Store will NOT accept apps with ONLY maccatalyst-arm64 indicated;
|
||||
either BOTH runtimes must be indicated or ONLY macatalyst-x64. -->
|
||||
<!-- For example: <RuntimeIdentifiers>maccatalyst-x64;maccatalyst-arm64</RuntimeIdentifiers> -->
|
||||
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Jugenddienst_Stunden</RootNamespace>
|
||||
<UseMaui>true</UseMaui>
|
||||
<SingleProject>true</SingleProject>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>Jugenddienst_Stunden</RootNamespace>
|
||||
<UseMaui>true</UseMaui>
|
||||
<SingleProject>true</SingleProject>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<!-- Display name -->
|
||||
<ApplicationTitle>Jugenddienst Stunden</ApplicationTitle>
|
||||
<!-- Display name -->
|
||||
<ApplicationTitle>Jugenddienst Stunden</ApplicationTitle>
|
||||
|
||||
<!-- App Identifier -->
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<!-- App Identifier -->
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">29.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<PackageIcon>paket_icon.png</PackageIcon>
|
||||
<NeutralLanguage>de</NeutralLanguage>
|
||||
<PackageVersion>1.0.9</PackageVersion>
|
||||
</PropertyGroup>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">11.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">13.1</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">29.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'tizen'">6.5</SupportedOSPlatformVersion>
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
<PackageIcon>paket_icon.png</PackageIcon>
|
||||
<NeutralLanguage>de</NeutralLanguage>
|
||||
<PackageVersion>1.0.9</PackageVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-maccatalyst|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Optimize>False</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-maccatalyst|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Optimize>False</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-maccatalyst|AnyCPU'">
|
||||
<Optimize>True</Optimize>
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-maccatalyst|AnyCPU'">
|
||||
<Optimize>True</Optimize>
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android34.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<Debugger>Xamarin</Debugger>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<Optimize>False</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<AndroidKeyStore>False</AndroidKeyStore>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android34.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<Debugger>Xamarin</Debugger>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
<Optimize>False</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<AndroidKeyStore>False</AndroidKeyStore>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<DefaultLanguage>de-de</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<DefaultLanguage>de-de</DefaultLanguage>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.26100.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>
|
||||
<Deterministic>True</Deterministic>
|
||||
<Optimize>False</Optimize>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.26100.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>
|
||||
<Deterministic>True</Deterministic>
|
||||
<Optimize>False</Optimize>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-android34.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<Debugger>Xamarin</Debugger>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<Optimize>True</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<RunAOTCompilation>False</RunAOTCompilation>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
<AndroidKeyStore>False</AndroidKeyStore>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-android34.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<Debugger>Xamarin</Debugger>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<Optimize>True</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<RunAOTCompilation>False</RunAOTCompilation>
|
||||
<PublishTrimmed>True</PublishTrimmed>
|
||||
<AndroidKeyStore>False</AndroidKeyStore>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.26100.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Optimize>True</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.26100.0|AnyCPU'">
|
||||
<ApplicationId>com.companyname.jugenddienststunden</ApplicationId>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<Optimize>True</Optimize>
|
||||
<Deterministic>True</Deterministic>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<IncludeSymbols>True</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
|
||||
<AssemblyVersion>1.0.9</AssemblyVersion>
|
||||
<FileVersion>1.0.9</FileVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<IncludeSymbols>True</IncludeSymbols>
|
||||
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<ProduceReferenceAssembly>False</ProduceReferenceAssembly>
|
||||
<AssemblyVersion>1.0.9</AssemblyVersion>
|
||||
<FileVersion>1.0.9</FileVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android|AnyCPU'">
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-android|AnyCPU'">
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-android|AnyCPU'">
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-android|AnyCPU'">
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DebugSymbols>True</DebugSymbols>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION</DefineConstants>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-windows10.0.19041.0|AnyCPU'">
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-android35.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<EnableLLVM>True</EnableLLVM>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-android35.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<EnableLLVM>True</EnableLLVM>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-android35.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<EnableLLVM>True</EnableLLVM>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-android35.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
<EnableLLVM>True</EnableLLVM>
|
||||
<DebugSymbols>False</DebugSymbols>
|
||||
<AndroidEnableProfiledAot>False</AndroidEnableProfiledAot>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-ios|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-ios|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-ios|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-ios|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-windows10.0.26100.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
<WarningsAsErrors>$(WarningsAsErrors);NU1605</WarningsAsErrors>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net9.0-windows10.0.26100.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
<WarningsAsErrors>$(WarningsAsErrors);NU1605</WarningsAsErrors>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-windows10.0.26100.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
<WarningsAsErrors>$(WarningsAsErrors);NU1605</WarningsAsErrors>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net9.0-windows10.0.26100.0|AnyCPU'">
|
||||
<WarningLevel>8</WarningLevel>
|
||||
<NoWarn>1701;1702</NoWarn>
|
||||
<WarningsAsErrors>$(WarningsAsErrors);NU1605</WarningsAsErrors>
|
||||
<ApplicationDisplayVersion>1.0.9</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>10</ApplicationVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.26100.0</TargetFrameworks>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<!-- <TargetFrameworks>;net9.0-android35.0</TargetFrameworks> -->
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="$([MSBuild]::IsOSPlatform('windows'))">$(TargetFrameworks);net9.0-windows10.0.26100.0</TargetFrameworks>
|
||||
<WindowsPackageType>None</WindowsPackageType>
|
||||
<!-- <TargetFrameworks>;net9.0-android35.0</TargetFrameworks> -->
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- App Icon -->
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
|
||||
<ItemGroup>
|
||||
<!-- App Icon -->
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4"/>
|
||||
|
||||
<!-- Splash Screen -->
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#F7931D" BaseSize="128,128" />
|
||||
|
||||
<!-- Splash Screen (Windows fix) -->
|
||||
<!--<MauiImage Include="Resources\Images\logo_splash_win.svg" Color="#F7931D" BaseSize="208,208" />-->
|
||||
<!-- Splash Screen -->
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#F7931D" BaseSize="128,128"/>
|
||||
|
||||
<!-- Images -->
|
||||
<MauiImage Include="Resources\Images\*" />
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185" />
|
||||
<!-- Splash Screen (Windows fix) -->
|
||||
<!--<MauiImage Include="Resources\Images\logo_splash_win.svg" Color="#F7931D" BaseSize="208,208" />-->
|
||||
|
||||
<!-- Custom Fonts -->
|
||||
<MauiFont Include="Resources\Fonts\*" />
|
||||
<!-- Images -->
|
||||
<MauiImage Include="Resources\Images\*"/>
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185"/>
|
||||
|
||||
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
|
||||
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</ItemGroup>
|
||||
<!-- Custom Fonts -->
|
||||
<MauiFont Include="Resources\Fonts\*"/>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-100.png" />
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-125.png" />
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-150.png" />
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-200.png" />
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-400.png" />
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-100.png" />
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-125.png" />
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-150.png" />
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-200.png" />
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-400.png" />
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-100.png" />
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-125.png" />
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-150.png" />
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-200.png" />
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-400.png" />
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-100.png" />
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-125.png" />
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-150.png" />
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-200.png" />
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-400.png" />
|
||||
</ItemGroup>
|
||||
<!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
|
||||
<MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-100.png" />
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-125.png" />
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-150.png" />
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-200.png" />
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-400.png" />
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-100.png" />
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-125.png" />
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-150.png" />
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-200.png" />
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-400.png" />
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-100.png" />
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-125.png" />
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-150.png" />
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-200.png" />
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-400.png" />
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-100.png" />
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-125.png" />
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-150.png" />
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-200.png" />
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-400.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-100.png"/>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-125.png"/>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-150.png"/>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-200.png"/>
|
||||
<None Remove="Resources\Windows\%24placeholder%24.scale-400.png"/>
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-100.png"/>
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-125.png"/>
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-150.png"/>
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-200.png"/>
|
||||
<None Remove="Resources\Windows\Small\%24placeholder%24.scale-400.png"/>
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-100.png"/>
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-125.png"/>
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-150.png"/>
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-200.png"/>
|
||||
<None Remove="Resources\Windows\Splash\%24placeholder%24.scale-400.png"/>
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-100.png"/>
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-125.png"/>
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-150.png"/>
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-200.png"/>
|
||||
<None Remove="Resources\Windows\Wide\%24placeholder%24.scale-400.png"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="..\paket_icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-100.png"/>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-125.png"/>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-150.png"/>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-200.png"/>
|
||||
<Content Include="Resources\Windows\$placeholder$.scale-400.png"/>
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-100.png"/>
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-125.png"/>
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-150.png"/>
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-200.png"/>
|
||||
<Content Include="Resources\Windows\Small\$placeholder$.scale-400.png"/>
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-100.png"/>
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-125.png"/>
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-150.png"/>
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-200.png"/>
|
||||
<Content Include="Resources\Windows\Splash\$placeholder$.scale-400.png"/>
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-100.png"/>
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-125.png"/>
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-150.png"/>
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-200.png"/>
|
||||
<Content Include="Resources\Windows\Wide\$placeholder$.scale-400.png"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Maui" Version="12.2.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.110">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.Maui.Graphics" Version="9.0.110" />
|
||||
<PackageReference Include="Microsoft.NET.Runtime.MonoAOTCompiler.Task" Version="9.0.9" />
|
||||
<PackageReference Include="Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk" Version="9.0.9" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="ZXing.Net.Maui.Controls" Version="0.5.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\paket_icon.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\LoginPage.xaml.cs">
|
||||
<DependentUpon>LoginPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\StundePage.xaml.cs">
|
||||
<DependentUpon>StundePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommunityToolkit.Maui" Version="12.2.0"/>
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0"/>
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.110">
|
||||
<TreatAsUsed>true</TreatAsUsed>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.110"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="9.0.9"/>
|
||||
<PackageReference Include="Microsoft.Maui.Graphics" Version="9.0.110"/>
|
||||
<PackageReference Include="Microsoft.NET.Runtime.MonoAOTCompiler.Task" Version="9.0.9"/>
|
||||
<PackageReference Include="Microsoft.NET.Runtime.WebAssembly.Wasi.Sdk" Version="9.0.9"/>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
<PackageReference Include="ZXing.Net.Maui.Controls" Version="0.5.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Update="Properties\Resources.Designer.cs">
|
||||
<DesignTime>True</DesignTime>
|
||||
<AutoGen>True</AutoGen>
|
||||
<DependentUpon>Resources.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\LoginPage.xaml.cs">
|
||||
<DependentUpon>LoginPage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Update="Views\StundePage.xaml.cs">
|
||||
<DependentUpon>StundePage.xaml</DependentUpon>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MauiXaml Update="Views\LoginPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\AllNotesPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\StundePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\NotePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\StundenPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Update="Properties\Resources.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<MauiXaml Update="Views\LoginPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\AllNotesPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\StundePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\NotePage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
<MauiXaml Update="Views\StundenPage.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</MauiXaml>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -3,8 +3,11 @@ using Jugenddienst_Stunden.Models;
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using Jugenddienst_Stunden.Repositories;
|
||||
using Jugenddienst_Stunden.Services;
|
||||
using Jugenddienst_Stunden.Infrastructure;
|
||||
using Jugenddienst_Stunden.Validators;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using ZXing.Net.Maui.Controls;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Jugenddienst_Stunden;
|
||||
|
||||
@@ -12,39 +15,84 @@ namespace Jugenddienst_Stunden;
|
||||
/// Das Hauptprogramm.
|
||||
/// </summary>
|
||||
public static class MauiProgram {
|
||||
public static MauiApp CreateMauiApp() {
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
.UseMauiApp<App>()
|
||||
// Initialize the .NET MAUI Community Toolkit by adding the below line of code
|
||||
.UseMauiCommunityToolkit(options => { options.SetShouldEnableSnackbarOnWindows(true); })
|
||||
.ConfigureFonts(fonts => {
|
||||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||
})
|
||||
//.UseBarcodeScanning();
|
||||
.UseBarcodeReader();
|
||||
|
||||
public static MauiApp CreateMauiApp() {
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
.UseMauiApp<App>()
|
||||
// Initialize the .NET MAUI Community Toolkit by adding the below line of code
|
||||
.UseMauiCommunityToolkit(options => {
|
||||
options.SetShouldEnableSnackbarOnWindows(true);
|
||||
})
|
||||
.ConfigureFonts(fonts => {
|
||||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||
})
|
||||
//.UseBarcodeScanning();
|
||||
.UseBarcodeReader();
|
||||
//#if DEBUG
|
||||
// if (string.IsNullOrWhiteSpace(GlobalVar.ApiKey)) {
|
||||
// GlobalVar.ApiKey = Preferences.Default.Get("apiKey",
|
||||
// "MTQxfHNkdFptQkNZTXlPT3ZyMHNBZDl0UnVxNExMRXxodHRwOi8vaG91cnMuZGF1bmkubWluZS5udTo4MS9hcHBhcGk=");
|
||||
// GlobalVar.Name = Preferences.Default.Get("name", "Testserver: Isabell");
|
||||
// GlobalVar.Surname = Preferences.Default.Get("surname", "Biasi");
|
||||
// GlobalVar.EmployeeId = Preferences.Default.Get("EmployeeId", 141);
|
||||
// GlobalVar.ApiUrl = Preferences.Default.Get("apiUrl", "https://hours.dauni.mine.nu/appapi");
|
||||
// }
|
||||
|
||||
#if DEBUG
|
||||
if (GlobalVar.ApiKey == null) {
|
||||
GlobalVar.ApiKey = Preferences.Default.Get("apiKey", "MTQxfHNkdFptQkNZTXlPT3ZyMHNBZDl0UnVxNExMRXxodHRwOi8vaG91cnMuZGF1bmkubWluZS5udTo4MS9hcHBhcGk=");
|
||||
GlobalVar.Name = Preferences.Default.Get("name", "Testserver: Isabell");
|
||||
GlobalVar.Surname = Preferences.Default.Get("surname", "Biasi");
|
||||
GlobalVar.EmployeeId = Preferences.Default.Get("EmployeeId", 141);
|
||||
GlobalVar.ApiUrl = Preferences.Default.Get("apiUrl", "https://hours.dauni.mine.nu/appapi");
|
||||
}
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
// builder.Logging.AddDebug();
|
||||
//#endif
|
||||
|
||||
// DI: Services & Repositories
|
||||
builder.Services.AddSingleton<IHoursRepository, HoursRepository>();
|
||||
builder.Services.AddSingleton<IHoursService, HoursService>();
|
||||
// DI: AlertService für globale Alerts (z. B. leere ApiUrl)
|
||||
builder.Services.AddSingleton<IAlertService, AlertService>();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
// DI: Settings aus Preferences (Single Source of Truth bleibt Preferences)
|
||||
builder.Services.AddSingleton<IAppSettings, PreferencesAppSettings>();
|
||||
|
||||
// DI: ApiOptions IMMER aus aktuellen Settings erzeugen (nicht beim Start einfrieren)
|
||||
builder.Services.AddTransient(sp => new ApiOptions {
|
||||
BaseUrl = sp.GetRequiredService<IAppSettings>().ApiUrl, Timeout = TimeSpan.FromSeconds(15)
|
||||
});
|
||||
|
||||
}
|
||||
// Token Provider soll ebenfalls aus Settings/Preferences lesen
|
||||
builder.Services.AddSingleton<ITokenProvider, SettingsTokenProvider>();
|
||||
|
||||
// HttpClient + ApiClient
|
||||
builder.Services.AddSingleton<HttpClient>(_ => new HttpClient());
|
||||
builder.Services.AddSingleton<IApiClient>(sp => {
|
||||
var alert = sp.GetRequiredService<IAlertService>();
|
||||
try {
|
||||
return new ApiClient(
|
||||
sp.GetRequiredService<HttpClient>(),
|
||||
sp.GetRequiredService<ApiOptions>(),
|
||||
sp.GetRequiredService<ITokenProvider>(),
|
||||
sp.GetRequiredService<IAppSettings>());
|
||||
} catch (Exception e) {
|
||||
// Alert an UI/VM weiterreichen
|
||||
alert.Raise(e.Message);
|
||||
// Fallback: NullApiClient liefert beim Aufruf aussagekräftige Exception
|
||||
return new NullApiClient(e.Message);
|
||||
}
|
||||
});
|
||||
|
||||
// DI: Infrastruktur
|
||||
//builder.Services.AddSingleton(new ApiOptions { BaseUrl = GlobalVar.ApiUrl, Timeout = TimeSpan.FromSeconds(15) });
|
||||
//builder.Services.AddSingleton<ITokenProvider, GlobalVarTokenProvider>();
|
||||
//builder.Services.AddSingleton<HttpClient>(_ => new HttpClient());
|
||||
//builder.Services.AddSingleton<IApiClient>(sp => new ApiClient(
|
||||
// sp.GetRequiredService<HttpClient>(),
|
||||
// sp.GetRequiredService<ApiOptions>(),
|
||||
// sp.GetRequiredService<ITokenProvider>()));
|
||||
|
||||
// DI: Validatoren
|
||||
builder.Services.AddSingleton<IHoursValidator, HoursValidator>();
|
||||
|
||||
// DI: Services & Repositories
|
||||
builder.Services.AddSingleton<IHoursRepository, HoursRepository>();
|
||||
builder.Services.AddSingleton<IHoursService, HoursService>();
|
||||
|
||||
// DI: Views/ViewModels
|
||||
builder.Services.AddTransient<ViewModels.StundenViewModel>();
|
||||
builder.Services.AddTransient<Views.StundenPage>();
|
||||
|
||||
return builder.Build();
|
||||
}
|
||||
}
|
||||
@@ -7,186 +7,175 @@ using System.Text.Json;
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal static class BaseFunc {
|
||||
internal static async Task<string> GetApiDataWithAuthAsync(string url, string token) {
|
||||
if (Connectivity.Current.NetworkAccess == NetworkAccess.None)
|
||||
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
|
||||
|
||||
if (string.IsNullOrEmpty(token))
|
||||
throw new Exception("Kein APIKEY, bitte zuerst Login durchführen");
|
||||
|
||||
// Erstellen eines HttpClient-Objekts
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
|
||||
// Hinzufügen des Bearer-Tokens zum Authorization-Header
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
// Senden der Anfrage und Abrufen der Antwort
|
||||
using (HttpResponseMessage HttpResponseMessage = await client.GetAsync(url).ConfigureAwait(false)) {
|
||||
var byteArray = await HttpResponseMessage.Content.ReadAsByteArrayAsync();
|
||||
string responseData = Encoding.UTF8.GetString(byteArray);
|
||||
//using (HttpContent HttpContent = HttpResponseMessage.Content) {
|
||||
// //responseData = await HttpContent.ReadAsStringAsync();
|
||||
//}
|
||||
if (HttpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||
return responseData;
|
||||
} else {
|
||||
var options = new JsonDocumentOptions { AllowTrailingCommas = true };
|
||||
using (JsonDocument doc = JsonDocument.Parse(responseData, options)) {
|
||||
JsonElement root = doc.RootElement;
|
||||
string message = root.GetProperty("message").GetString() ??
|
||||
throw new Exception("Fehler: 'message' ist null.");
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal static async Task<string> GetApiDataWithAuthAsync(string url, string token) {
|
||||
internal static async Task<User> AuthUserPass(string user, string pass, string url) {
|
||||
var values = new Dictionary<string, string> { { "user", user }, { "pass", pass } };
|
||||
|
||||
if (Connectivity.Current.NetworkAccess == NetworkAccess.None)
|
||||
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
|
||||
var content = new FormUrlEncodedContent(values);
|
||||
|
||||
if (string.IsNullOrEmpty(token))
|
||||
throw new Exception("Kein APIKEY, bitte zuerst Login durchführen");
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
|
||||
// Erstellen eines HttpClient-Objekts
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
// Senden der Anfrage und Abrufen der Antwort
|
||||
using (HttpResponseMessage HttpResponseMessage =
|
||||
await client.PostAsync(url, content).ConfigureAwait(false)) {
|
||||
if (!HttpResponseMessage.IsSuccessStatusCode) {
|
||||
//throw new Exception("Fehler beim Einloggen " + HttpResponseMessage.Content);
|
||||
var byteArray = await HttpResponseMessage.Content.ReadAsByteArrayAsync();
|
||||
string responseData = Encoding.UTF8.GetString(byteArray);
|
||||
var options = new JsonDocumentOptions { AllowTrailingCommas = true };
|
||||
using (JsonDocument doc = JsonDocument.Parse(responseData, options)) {
|
||||
JsonElement root = doc.RootElement;
|
||||
string message = root.GetProperty("message").GetString() ??
|
||||
throw new Exception("Fehler: 'message' ist null.");
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
// Überprüfen, ob die Anfrage erfolgreich war
|
||||
if (HttpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||
using (HttpContent HttpContent = HttpResponseMessage.Content) {
|
||||
// Lesen und Rückgabe der Antwort als String
|
||||
|
||||
// Hinzufügen des Bearer-Tokens zum Authorization-Header
|
||||
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
string responseData = await HttpContent.ReadAsStringAsync();
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(responseData) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
//User userData = System.Text.Json.JsonSerializer.Deserialize<User>(responseData) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.user;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Senden der Anfrage und Abrufen der Antwort
|
||||
using (HttpResponseMessage HttpResponseMessage = await client.GetAsync(url).ConfigureAwait(false)) {
|
||||
var byteArray = await HttpResponseMessage.Content.ReadAsByteArrayAsync();
|
||||
string responseData = Encoding.UTF8.GetString(byteArray);
|
||||
//using (HttpContent HttpContent = HttpResponseMessage.Content) {
|
||||
// //responseData = await HttpContent.ReadAsStringAsync();
|
||||
//}
|
||||
if (HttpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||
return responseData;
|
||||
} else {
|
||||
var options = new JsonDocumentOptions {
|
||||
AllowTrailingCommas = true
|
||||
};
|
||||
using (JsonDocument doc = JsonDocument.Parse(responseData, options)) {
|
||||
JsonElement root = doc.RootElement;
|
||||
string message = root.GetProperty("message").GetString() ?? throw new Exception("Fehler: 'message' ist null.");
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notiz laden
|
||||
/// </summary>
|
||||
internal static Note Load(string filename) {
|
||||
filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
|
||||
|
||||
if (!File.Exists(filename))
|
||||
throw new FileNotFoundException("Unable to find file on local storage.", filename);
|
||||
|
||||
internal static async Task<User> AuthUserPass(string user, string pass, string url) {
|
||||
return
|
||||
new() { Date = File.GetLastWriteTime(filename) };
|
||||
}
|
||||
|
||||
var values = new Dictionary<string, string>
|
||||
{
|
||||
{ "user", user },
|
||||
{ "pass", pass }
|
||||
};
|
||||
/// <summary>
|
||||
/// Stundeneintrag speichern
|
||||
/// </summary>
|
||||
internal static async Task SaveItemAsync(string url, string token, DayTime item, bool isNewItem = false) {
|
||||
//Uhrzeiten sollten sinnvolle Werte haben - außer bei Freistellungen, da wäre eigentlich null
|
||||
if (item.TimeSpanVon == item.TimeSpanBis && item.FreistellungAktiv == null) {
|
||||
throw new Exception("Beginn und Ende sind gleich");
|
||||
}
|
||||
|
||||
var content = new FormUrlEncodedContent(values);
|
||||
if (item.TimeSpanBis < item.TimeSpanVon) {
|
||||
throw new Exception("Ende ist vor Beginn");
|
||||
}
|
||||
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
TimeSpan span = TimeSpan.Zero;
|
||||
span += item.TimeSpanBis - item.TimeSpanVon;
|
||||
if (span.Hours > 10) {
|
||||
//Hier vielleicht eine Abfrage, ob mehr als 10 Stunden gesund sind?
|
||||
//Das müsste aber das ViewModel machen
|
||||
}
|
||||
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
//Gemeinde ist ein Pflichtfeld
|
||||
if (item.GemeindeAktiv == null && GlobalVar.Settings.GemeindeAktivSet) {
|
||||
throw new Exception("Gemeinde nicht gewählt");
|
||||
}
|
||||
|
||||
// Senden der Anfrage und Abrufen der Antwort
|
||||
using (HttpResponseMessage HttpResponseMessage = await client.PostAsync(url, content).ConfigureAwait(false)) {
|
||||
if (!HttpResponseMessage.IsSuccessStatusCode)
|
||||
{
|
||||
//throw new Exception("Fehler beim Einloggen " + HttpResponseMessage.Content);
|
||||
var byteArray = await HttpResponseMessage.Content.ReadAsByteArrayAsync();
|
||||
string responseData = Encoding.UTF8.GetString(byteArray);
|
||||
var options = new JsonDocumentOptions {
|
||||
AllowTrailingCommas = true
|
||||
};
|
||||
using (JsonDocument doc = JsonDocument.Parse(responseData, options)) {
|
||||
JsonElement root = doc.RootElement;
|
||||
string message = root.GetProperty("message").GetString() ?? throw new Exception("Fehler: 'message' ist null.");
|
||||
throw new Exception(message);
|
||||
}
|
||||
}
|
||||
//Projekt ist ein Pflichtfeld
|
||||
if (item.ProjektAktiv == null && GlobalVar.Settings.ProjektAktivSet) {
|
||||
throw new Exception("Projekt nicht gewählt");
|
||||
}
|
||||
|
||||
// Überprüfen, ob die Anfrage erfolgreich war
|
||||
if (HttpResponseMessage.StatusCode == System.Net.HttpStatusCode.OK) {
|
||||
using (HttpContent HttpContent = HttpResponseMessage.Content) {
|
||||
// Lesen und Rückgabe der Antwort als String
|
||||
//Keine Beschreibung
|
||||
if (string.IsNullOrEmpty(item.Description) && item.FreistellungAktiv == null) {
|
||||
throw new Exception("Keine Beschreibung");
|
||||
}
|
||||
|
||||
string responseData = await HttpContent.ReadAsStringAsync();
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(responseData) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
//User userData = System.Text.Json.JsonSerializer.Deserialize<User>(responseData) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.user;
|
||||
}
|
||||
}
|
||||
}
|
||||
//Keine Beschreibung
|
||||
if (string.IsNullOrEmpty(item.Description)) {
|
||||
item.Description = item.FreistellungAktiv.Name;
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
//HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
/// <summary>
|
||||
/// Notiz laden
|
||||
/// </summary>
|
||||
internal static Note Load(string filename) {
|
||||
filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
|
||||
//string json = JsonSerializer.Serialize<DayTime>(item);
|
||||
string json = JsonConvert.SerializeObject(item);
|
||||
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
if (!File.Exists(filename))
|
||||
throw new FileNotFoundException("Unable to find file on local storage.", filename);
|
||||
HttpResponseMessage? response = null;
|
||||
if (isNewItem)
|
||||
response = await client.PostAsync(url, content);
|
||||
else
|
||||
response = await client.PutAsync(url, content);
|
||||
|
||||
return
|
||||
new() {
|
||||
Date = File.GetLastWriteTime(filename)
|
||||
};
|
||||
}
|
||||
if (!response.IsSuccessStatusCode) {
|
||||
throw new Exception("Fehler beim Speichern " + response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stundeneintrag speichern
|
||||
/// </summary>
|
||||
internal static async Task SaveItemAsync(string url, string token, DayTime item, bool isNewItem = false) {
|
||||
/// <summary>
|
||||
/// Stundeneintrag löschen
|
||||
/// </summary>
|
||||
internal static async Task DeleteItemAsync(string url, string token) {
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
//HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
client.DefaultRequestHeaders.Authorization =
|
||||
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
//Uhrzeiten sollten sinnvolle Werte haben - außer bei Freistellungen, da wäre eigentlich null
|
||||
if (item.TimeSpanVon == item.TimeSpanBis && item.FreistellungAktiv == null) {
|
||||
throw new Exception("Beginn und Ende sind gleich");
|
||||
}
|
||||
HttpResponseMessage response = await client.DeleteAsync(url);
|
||||
|
||||
if (item.TimeSpanBis < item.TimeSpanVon) {
|
||||
throw new Exception("Ende ist vor Beginn");
|
||||
}
|
||||
|
||||
TimeSpan span = TimeSpan.Zero;
|
||||
span += item.TimeSpanBis - item.TimeSpanVon;
|
||||
if (span.Hours > 10) {
|
||||
//Hier vielleicht eine Abfrage, ob mehr als 10 Stunden gesund sind?
|
||||
//Das müsste aber das ViewModel machen
|
||||
}
|
||||
|
||||
//Gemeinde ist ein Pflichtfeld
|
||||
if (item.GemeindeAktiv == null && GlobalVar.Settings.GemeindeAktivSet) {
|
||||
throw new Exception("Gemeinde nicht gewählt");
|
||||
}
|
||||
//Projekt ist ein Pflichtfeld
|
||||
if (item.ProjektAktiv == null && GlobalVar.Settings.ProjektAktivSet) {
|
||||
throw new Exception("Projekt nicht gewählt");
|
||||
}
|
||||
//Keine Beschreibung
|
||||
if (string.IsNullOrEmpty(item.Description) && item.FreistellungAktiv == null) {
|
||||
throw new Exception("Keine Beschreibung");
|
||||
}
|
||||
//Keine Beschreibung
|
||||
if (string.IsNullOrEmpty(item.Description)) {
|
||||
item.Description = item.FreistellungAktiv.Name;
|
||||
}
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
//HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
//string json = JsonSerializer.Serialize<DayTime>(item);
|
||||
string json = JsonConvert.SerializeObject(item);
|
||||
StringContent content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
|
||||
HttpResponseMessage? response = null;
|
||||
if (isNewItem)
|
||||
response = await client.PostAsync(url, content);
|
||||
else
|
||||
response = await client.PutAsync(url, content);
|
||||
|
||||
if (!response.IsSuccessStatusCode) {
|
||||
throw new Exception("Fehler beim Speichern " + response.Content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stundeneintrag löschen
|
||||
/// </summary>
|
||||
internal static async Task DeleteItemAsync(string url, string token) {
|
||||
using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {
|
||||
|
||||
//HttpClient client = new HttpClient();
|
||||
client.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
|
||||
|
||||
HttpResponseMessage response = await client.DeleteAsync(url);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception("Fehler beim Löschen " + response.Content);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (!response.IsSuccessStatusCode)
|
||||
throw new Exception("Fehler beim Löschen " + response.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,32 @@
|
||||
using Jugenddienst_Stunden.Types;
|
||||
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal static class GlobalVar {
|
||||
public static string ApiKey {
|
||||
get => Preferences.Default.Get("apiKey", "");
|
||||
set => Preferences.Default.Set("apiKey", value);
|
||||
}
|
||||
public static int EmployeeId {
|
||||
get => Preferences.Default.Get("EmployeeId", 0);
|
||||
set => Preferences.Default.Set("EmployeeId", value);
|
||||
}
|
||||
public static string Name {
|
||||
get => Preferences.Default.Get("name", "Nicht");
|
||||
set => Preferences.Default.Set("name", value);
|
||||
}
|
||||
public static string Surname {
|
||||
get => Preferences.Default.Get("surname", "Eingeloggt");
|
||||
set => Preferences.Default.Set("surname", value);
|
||||
}
|
||||
public static string ApiUrl {
|
||||
get => Preferences.Default.Get("apiUrl", "");
|
||||
set => Preferences.Default.Set("apiUrl", value);
|
||||
}
|
||||
public static Settings Settings { get; set; }
|
||||
}
|
||||
public static string ApiKey {
|
||||
get => Preferences.Default.Get("apiKey", "");
|
||||
set => Preferences.Default.Set("apiKey", value);
|
||||
}
|
||||
|
||||
public static int EmployeeId {
|
||||
get => Preferences.Default.Get("EmployeeId", 0);
|
||||
set => Preferences.Default.Set("EmployeeId", value);
|
||||
}
|
||||
|
||||
public static string Name {
|
||||
get => Preferences.Default.Get("name", "Nicht");
|
||||
set => Preferences.Default.Set("name", value);
|
||||
}
|
||||
|
||||
public static string Surname {
|
||||
get => Preferences.Default.Get("surname", "Eingeloggt");
|
||||
set => Preferences.Default.Set("surname", value);
|
||||
}
|
||||
|
||||
public static string ApiUrl {
|
||||
get => Preferences.Default.Get("apiUrl", "");
|
||||
set => Preferences.Default.Set("apiUrl", value);
|
||||
}
|
||||
|
||||
public static Settings Settings { get; set; }
|
||||
}
|
||||
@@ -4,83 +4,90 @@ using Newtonsoft.Json;
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal static class HoursBase {
|
||||
/// <summary>
|
||||
/// Laden ... what can be: "settings", "hours", date="YYYY-MM-DD", id=<int/>
|
||||
/// </summary>
|
||||
/// <returns>Entire response</returns>
|
||||
internal static async Task<BaseResponse> LoadBase(string what) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?" + what, GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Laden ... what can be: "settings", "hours", date="YYYY-MM-DD", id=<int/>
|
||||
/// </summary>
|
||||
/// <returns>Entire response</returns>
|
||||
internal static async Task<BaseResponse> LoadBase(string what) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?"+what, GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res;
|
||||
}
|
||||
/// <summary>
|
||||
/// Einstellungen laden
|
||||
/// </summary>
|
||||
/// <returns>Settings only</returns>
|
||||
internal static async Task<Settings> LoadSettings() {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?settings", GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.settings;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Einstellungen laden
|
||||
/// </summary>
|
||||
/// <returns>Settings only</returns>
|
||||
internal static async Task<Settings> LoadSettings() {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?settings", GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.settings;
|
||||
}
|
||||
/// <summary>
|
||||
/// Daten laden
|
||||
/// </summary>
|
||||
/// <returns>Hours only</returns>
|
||||
internal static async Task<Hours> LoadData() {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?hours", GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.hour;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Daten laden
|
||||
/// </summary>
|
||||
/// <returns>Hours only</returns>
|
||||
internal static async Task<Hours> LoadData() {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?hours", GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.hour;
|
||||
}
|
||||
/// <summary>
|
||||
/// Benutzerdaten laden
|
||||
/// </summary>
|
||||
/// <returns>User-Object</returns>
|
||||
public static async Task<User> LoadUser(string apiKey) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl, apiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.user;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Benutzerdaten laden
|
||||
/// </summary>
|
||||
/// <returns>User-Object</returns>
|
||||
public static async Task<User> LoadUser(string apiKey) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl, apiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.user;
|
||||
}
|
||||
/// <summary>
|
||||
/// Zeiten eines Tages holen
|
||||
/// </summary>
|
||||
internal static async Task<List<DayTime>> LoadDay(DateTime date) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?date=" + date.ToString("yyyy-MM-dd"),
|
||||
GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.daytimes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Zeiten eines Tages holen
|
||||
/// </summary>
|
||||
internal static async Task<List<DayTime>> LoadDay(DateTime date) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?date=" + date.ToString("yyyy-MM-dd"), GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
return res.daytimes;
|
||||
}
|
||||
/// <summary>
|
||||
/// Einzelnen Stundeneintrag holen
|
||||
/// </summary>
|
||||
internal static async Task<DayTime> LoadEntry(int id) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?id=" + id, GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ??
|
||||
throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
res.daytime.TimeSpanVon = res.daytime.Begin.ToTimeSpan();
|
||||
res.daytime.TimeSpanBis = res.daytime.End.ToTimeSpan();
|
||||
return res.daytime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Einzelnen Stundeneintrag holen
|
||||
/// </summary>
|
||||
internal static async Task<DayTime> LoadEntry(int id) {
|
||||
string data = await BaseFunc.GetApiDataWithAuthAsync(GlobalVar.ApiUrl + "?id=" + id, GlobalVar.ApiKey);
|
||||
BaseResponse res = JsonConvert.DeserializeObject<BaseResponse>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
|
||||
res.daytime.TimeSpanVon = res.daytime.Begin.ToTimeSpan();
|
||||
res.daytime.TimeSpanBis = res.daytime.End.ToTimeSpan();
|
||||
return res.daytime;
|
||||
}
|
||||
/// <summary>
|
||||
/// Eintrag speichern
|
||||
/// </summary>
|
||||
internal static async Task<DayTime> SaveEntry(DayTime stunde) {
|
||||
//, string begin, string end, string freistellung, string bemerkung) {
|
||||
bool isNew = false;
|
||||
if (stunde.Id == null)
|
||||
isNew = true;
|
||||
await BaseFunc.SaveItemAsync(GlobalVar.ApiUrl, GlobalVar.ApiKey, stunde, isNew);
|
||||
|
||||
/// <summary>
|
||||
/// Eintrag speichern
|
||||
/// </summary>
|
||||
internal static async Task<DayTime> SaveEntry(DayTime stunde) { //, string begin, string end, string freistellung, string bemerkung) {
|
||||
bool isNew = false;
|
||||
if (stunde.Id == null)
|
||||
isNew = true;
|
||||
await BaseFunc.SaveItemAsync(GlobalVar.ApiUrl, GlobalVar.ApiKey, stunde, isNew);
|
||||
return stunde;
|
||||
}
|
||||
|
||||
return stunde;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eintrag löschen
|
||||
/// </summary>
|
||||
internal static async Task DeleteEntry(DayTime stunde) {
|
||||
await BaseFunc.DeleteItemAsync(GlobalVar.ApiUrl + "/entry/" + stunde.Id, GlobalVar.ApiKey);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Eintrag löschen
|
||||
/// </summary>
|
||||
internal static async Task DeleteEntry(DayTime stunde) {
|
||||
await BaseFunc.DeleteItemAsync(GlobalVar.ApiUrl + "/entry/" + stunde.Id, GlobalVar.ApiKey);
|
||||
}
|
||||
}
|
||||
56
Jugenddienst Stunden/Models/JsonFlexibleIntConverters.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal sealed class JsonFlexibleInt32Converter : JsonConverter<int> {
|
||||
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
||||
switch (reader.TokenType) {
|
||||
case JsonTokenType.Number:
|
||||
if (reader.TryGetInt32(out var n)) return n;
|
||||
// Fallback via double to cover edge cases
|
||||
var d = reader.GetDouble();
|
||||
return (int)d;
|
||||
case JsonTokenType.String:
|
||||
var s = reader.GetString();
|
||||
if (string.IsNullOrWhiteSpace(s)) return 0;
|
||||
s = s.Trim();
|
||||
// Some APIs embed id like "141|..." -> take leading numeric part
|
||||
int i = 0;
|
||||
int sign = 1;
|
||||
int idx = 0;
|
||||
if (s.StartsWith("-")) { sign = -1; idx = 1; }
|
||||
for (; idx < s.Length; idx++) {
|
||||
char c = s[idx];
|
||||
if (c < '0' || c > '9') break;
|
||||
i = i * 10 + (c - '0');
|
||||
}
|
||||
if (idx > 0 && (idx > 1 || sign == 1)) return i * sign;
|
||||
if (int.TryParse(s, out var parsed)) return parsed;
|
||||
if (long.TryParse(s, out var l)) return (int)l;
|
||||
throw new JsonException($"Cannot convert string '{s}' to Int32.");
|
||||
case JsonTokenType.Null:
|
||||
return 0;
|
||||
default:
|
||||
throw new JsonException($"Token {reader.TokenType} is not valid for Int32.");
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
|
||||
=> writer.WriteNumberValue(value);
|
||||
}
|
||||
|
||||
internal sealed class JsonFlexibleNullableInt32Converter : JsonConverter<int?> {
|
||||
public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) {
|
||||
if (reader.TokenType == JsonTokenType.Null) return null;
|
||||
// Reuse non-nullable converter
|
||||
var conv = new JsonFlexibleInt32Converter();
|
||||
return conv.Read(ref reader, typeof(int), options);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) {
|
||||
if (value.HasValue) writer.WriteNumberValue(value.Value);
|
||||
else writer.WriteNullValue();
|
||||
}
|
||||
}
|
||||
@@ -11,37 +11,44 @@ namespace Jugenddienst_Stunden.Models {
|
||||
return typeof(T).IsAssignableFrom(objectType);
|
||||
}
|
||||
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override bool CanWrite {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
|
||||
JsonSerializer serializer) {
|
||||
var contract = serializer.ContractResolver.ResolveContract(objectType);
|
||||
if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract || contract is Newtonsoft.Json.Serialization.JsonDictionaryContract)) {
|
||||
throw new JsonSerializationException(string.Format("Unsupported objectType {0} at {1}.", objectType, reader.Path));
|
||||
if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract ||
|
||||
contract is Newtonsoft.Json.Serialization.JsonDictionaryContract)) {
|
||||
throw new JsonSerializationException(string.Format("Unsupported objectType {0} at {1}.", objectType,
|
||||
reader.Path));
|
||||
}
|
||||
|
||||
switch (reader.SkipComments().TokenType) {
|
||||
case JsonToken.StartArray: {
|
||||
int count = 0;
|
||||
while (reader.Read()) {
|
||||
switch (reader.TokenType) {
|
||||
case JsonToken.Comment:
|
||||
break;
|
||||
case JsonToken.EndArray:
|
||||
return existingValue;
|
||||
default: {
|
||||
count++;
|
||||
if (count > 1)
|
||||
throw new JsonSerializationException(string.Format("Too many objects at path {0}.", reader.Path));
|
||||
existingValue = existingValue ?? contract.DefaultCreator();
|
||||
serializer.Populate(reader, existingValue);
|
||||
}
|
||||
break;
|
||||
int count = 0;
|
||||
while (reader.Read()) {
|
||||
switch (reader.TokenType) {
|
||||
case JsonToken.Comment:
|
||||
break;
|
||||
case JsonToken.EndArray:
|
||||
return existingValue;
|
||||
default: {
|
||||
count++;
|
||||
if (count > 1)
|
||||
throw new JsonSerializationException(string.Format("Too many objects at path {0}.",
|
||||
reader.Path));
|
||||
existingValue = existingValue ?? contract.DefaultCreator();
|
||||
serializer.Populate(reader, existingValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Should not come here.
|
||||
throw new JsonSerializationException(string.Format("Unclosed array at path {0}.", reader.Path));
|
||||
}
|
||||
|
||||
// Should not come here.
|
||||
throw new JsonSerializationException(string.Format("Unclosed array at path {0}.", reader.Path));
|
||||
}
|
||||
|
||||
case JsonToken.Null:
|
||||
return null;
|
||||
|
||||
@@ -67,4 +74,4 @@ namespace Jugenddienst_Stunden.Models {
|
||||
return reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,50 @@
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal class Note {
|
||||
public string Filename { get; set; }
|
||||
public string Text { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
public string Filename { get; set; }
|
||||
public string Text { get; set; }
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public void Save() =>
|
||||
File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text);
|
||||
public void Save() =>
|
||||
File.WriteAllText(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename), Text);
|
||||
|
||||
public void Delete() =>
|
||||
File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));
|
||||
public void Delete() =>
|
||||
File.Delete(System.IO.Path.Combine(FileSystem.AppDataDirectory, Filename));
|
||||
|
||||
public static Note Load(string filename) {
|
||||
filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
|
||||
public static Note Load(string filename) {
|
||||
filename = System.IO.Path.Combine(FileSystem.AppDataDirectory, filename);
|
||||
|
||||
if (!File.Exists(filename))
|
||||
throw new FileNotFoundException("Unable to find file on local storage.", filename);
|
||||
if (!File.Exists(filename))
|
||||
throw new FileNotFoundException("Unable to find file on local storage.", filename);
|
||||
|
||||
return
|
||||
new() {
|
||||
Filename = Path.GetFileName(filename),
|
||||
Text = File.ReadAllText(filename),
|
||||
Date = File.GetLastWriteTime(filename)
|
||||
};
|
||||
}
|
||||
return
|
||||
new() {
|
||||
Filename = Path.GetFileName(filename),
|
||||
Text = File.ReadAllText(filename),
|
||||
Date = File.GetLastWriteTime(filename)
|
||||
};
|
||||
}
|
||||
|
||||
public static IEnumerable<Note> LoadAll() {
|
||||
// Get the folder where the notes are stored.
|
||||
string appDataPath = FileSystem.AppDataDirectory;
|
||||
public static IEnumerable<Note> LoadAll() {
|
||||
// Get the folder where the notes are stored.
|
||||
string appDataPath = FileSystem.AppDataDirectory;
|
||||
|
||||
// Use Linq extensions to load the *.notes.txt files.
|
||||
return Directory
|
||||
// Use Linq extensions to load the *.notes.txt files.
|
||||
return Directory
|
||||
|
||||
// Select the file names from the directory
|
||||
.EnumerateFiles(appDataPath, "*.notes.txt")
|
||||
// Select the file names from the directory
|
||||
.EnumerateFiles(appDataPath, "*.notes.txt")
|
||||
|
||||
// Each file name is used to load a note
|
||||
.Select(filename => Note.Load(Path.GetFileName(filename)))
|
||||
// Each file name is used to load a note
|
||||
.Select(filename => Note.Load(Path.GetFileName(filename)))
|
||||
|
||||
// With the final collection of notes, order them by date
|
||||
.OrderByDescending(note => note.Date);
|
||||
}
|
||||
// With the final collection of notes, order them by date
|
||||
.OrderByDescending(note => note.Date);
|
||||
}
|
||||
|
||||
public Note() {
|
||||
Filename = $"{Path.GetRandomFileName()}.notes.txt";
|
||||
Date = DateTime.Now;
|
||||
Text = "";
|
||||
}
|
||||
}
|
||||
public Note() {
|
||||
Filename = $"{Path.GetRandomFileName()}.notes.txt";
|
||||
Date = DateTime.Now;
|
||||
Text = "";
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,20 @@
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Jugenddienst_Stunden.Models;
|
||||
|
||||
internal class Operator {
|
||||
public string? id;
|
||||
public string? name;
|
||||
public string? surname;
|
||||
public string? email;
|
||||
public string? password;
|
||||
public string? lang;
|
||||
public string? admin;
|
||||
public string? aktiv;
|
||||
public string? department;
|
||||
public string? department_name;
|
||||
public string? num;
|
||||
public string? year;
|
||||
public string? id;
|
||||
public string? name;
|
||||
public string? surname;
|
||||
public string? email;
|
||||
public string? password;
|
||||
public string? lang;
|
||||
public string? admin;
|
||||
public string? aktiv;
|
||||
public string? department;
|
||||
public string? department_name;
|
||||
public string? num;
|
||||
public string? year;
|
||||
|
||||
public event EventHandler<string>? AlertEvent;
|
||||
|
||||
}
|
||||
public event EventHandler<string>? AlertEvent;
|
||||
}
|
||||
@@ -10,19 +10,23 @@ internal class TokenData {
|
||||
public string Operator_id { get; set; }
|
||||
|
||||
public TokenData(string ak) {
|
||||
if (string.IsNullOrEmpty(ak)) {
|
||||
throw new ArgumentException("API key cannot be null or empty", nameof(ak));
|
||||
}
|
||||
string dat = Encoding.UTF8.GetString(Convert.FromBase64String(ak));
|
||||
|
||||
string[] parts = dat.Split('|');
|
||||
if (parts.Length < 3) {
|
||||
throw new FormatException("API key format is invalid");
|
||||
}
|
||||
if (string.IsNullOrEmpty(ak)) {
|
||||
throw new ArgumentException("API key cannot be null or empty", nameof(ak));
|
||||
}
|
||||
|
||||
Token = dat.Split('|')[1]; ;
|
||||
Url = dat.Split('|')[2]; ;
|
||||
Operator_id = dat.Split('|')[0]; ;
|
||||
string dat = Encoding.UTF8.GetString(Convert.FromBase64String(ak));
|
||||
|
||||
string[] parts = dat.Split('|');
|
||||
if (parts.Length < 3) {
|
||||
throw new FormatException("API key format is invalid");
|
||||
}
|
||||
|
||||
Token = dat.Split('|')[1];
|
||||
;
|
||||
Url = dat.Split('|')[2];
|
||||
;
|
||||
Operator_id = dat.Split('|')[0];
|
||||
;
|
||||
ApiKey = ak;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/appicon"
|
||||
android:supportsRtl="true"
|
||||
android:label="Stunden"
|
||||
/>
|
||||
<!-- android:usesCleartextTraffic="true" -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/appicon"
|
||||
android:supportsRtl="true"
|
||||
android:label="Stunden"
|
||||
/>
|
||||
<!-- android:usesCleartextTraffic="true" -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
|
||||
</manifest>
|
||||
@@ -2,7 +2,10 @@
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
|
||||
namespace Jugenddienst_Stunden;
|
||||
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
||||
namespace Jugenddienst_Stunden;
|
||||
|
||||
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true, LaunchMode = LaunchMode.SingleTop,
|
||||
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode |
|
||||
ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
|
||||
public class MainActivity : MauiAppCompatActivity {
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using Android.App;
|
||||
using Android.Runtime;
|
||||
|
||||
namespace Jugenddienst_Stunden;
|
||||
namespace Jugenddienst_Stunden;
|
||||
#if DEBUG
|
||||
[Application(UsesCleartextTraffic = true)]
|
||||
#else
|
||||
@@ -13,4 +13,4 @@ public class MainApplication : MauiApplication {
|
||||
}
|
||||
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- The Mac App Store requires you specify if the app uses encryption. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
|
||||
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
|
||||
<!-- Please indicate <true/> or <false/> here. -->
|
||||
<dict>
|
||||
<!-- The Mac App Store requires you specify if the app uses encryption. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption -->
|
||||
<!-- <key>ITSAppUsesNonExemptEncryption</key> -->
|
||||
<!-- Please indicate <true/> or <false/> here. -->
|
||||
|
||||
<!-- Specify the category for your app here. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
|
||||
<!-- <key>LSApplicationCategoryType</key> -->
|
||||
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
<!-- Specify the category for your app here. -->
|
||||
<!-- Please consult https://developer.apple.com/documentation/bundleresources/information_property_list/lsapplicationcategorytype -->
|
||||
<!-- <key>LSApplicationCategoryType</key> -->
|
||||
<!-- <string>public.app-category.YOUR-CATEGORY-HERE</string> -->
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="8" xmlns="http://tizen.org/ns/packages">
|
||||
<profile name="common" />
|
||||
<ui-application appid="maui-application-id-placeholder" exec="Jugenddienst Stunden.dll" multiple="false" nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
|
||||
<label>maui-application-title-placeholder</label>
|
||||
<icon>maui-appicon-placeholder</icon>
|
||||
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true" />
|
||||
</ui-application>
|
||||
<shortcut-list />
|
||||
<privileges>
|
||||
<privilege>http://tizen.org/privilege/internet</privilege>
|
||||
</privileges>
|
||||
<dependencies />
|
||||
<provides-appdefined-privileges />
|
||||
<manifest package="maui-application-id-placeholder" version="0.0.0" api-version="8"
|
||||
xmlns="http://tizen.org/ns/packages">
|
||||
<profile name="common"/>
|
||||
<ui-application appid="maui-application-id-placeholder" exec="Jugenddienst Stunden.dll" multiple="false"
|
||||
nodisplay="false" taskmanage="true" type="dotnet" launch_mode="single">
|
||||
<label>maui-application-title-placeholder</label>
|
||||
<icon>maui-appicon-placeholder</icon>
|
||||
<metadata key="http://tizen.org/metadata/prefer_dotnet_aot" value="true"/>
|
||||
</ui-application>
|
||||
<shortcut-list/>
|
||||
<privileges>
|
||||
<privilege>http://tizen.org/privilege/internet</privilege>
|
||||
</privileges>
|
||||
<dependencies/>
|
||||
<provides-appdefined-privileges/>
|
||||
</manifest>
|
||||
@@ -5,4 +5,4 @@
|
||||
xmlns:maui="using:Microsoft.Maui"
|
||||
xmlns:local="using:Jugenddienst_Stunden.WinUI">
|
||||
|
||||
</maui:MauiWinUIApplication>
|
||||
</maui:MauiWinUIApplication>
|
||||
@@ -19,9 +19,5 @@ namespace Jugenddienst_Stunden.WinUI {
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,70 +1,74 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
IgnorableNamespaces="uap rescap com desktop">
|
||||
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:mp="http://schemas.microsoft.com/appx/2014/phone/manifest"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
IgnorableNamespaces="uap rescap com desktop">
|
||||
|
||||
<Identity Name="JugenddienstStunden" Publisher="CN=User Name" Version="0.0.0.0" />
|
||||
<Identity Name="JugenddienstStunden" Publisher="CN=User Name" Version="0.0.0.0"/>
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="4BA4D7D7-E3C2-4BBF-92EF-0EDB5DB5CDB4" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
<mp:PhoneIdentity PhoneProductId="4BA4D7D7-E3C2-4BBF-92EF-0EDB5DB5CDB4"
|
||||
PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
<Properties>
|
||||
<DisplayName>$placeholder$</DisplayName>
|
||||
<PublisherDisplayName>Daniel Pichler</PublisherDisplayName>
|
||||
<Logo>Resources\Windows\$placeholder$.png</Logo>
|
||||
</Properties>
|
||||
<Properties>
|
||||
<DisplayName>$placeholder$</DisplayName>
|
||||
<PublisherDisplayName>Daniel Pichler</PublisherDisplayName>
|
||||
<Logo>Resources\Windows\$placeholder$.png</Logo>
|
||||
</Properties>
|
||||
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" />
|
||||
</Dependencies>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Universal" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0"/>
|
||||
</Dependencies>
|
||||
|
||||
<Resources>
|
||||
<Resource Language="x-generate" />
|
||||
</Resources>
|
||||
<Resources>
|
||||
<Resource Language="x-generate"/>
|
||||
</Resources>
|
||||
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="$placeholder$"
|
||||
Description="$placeholder$"
|
||||
Square150x150Logo="Resources\Windows\$placeholder$.png"
|
||||
Square44x44Logo="Resources\Windows\$placeholder$.png"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Square71x71Logo="Resources\Windows\Small\$placeholder$.png" Wide310x150Logo="Resources\Windows\Wide\$placeholder$.png" Square310x310Logo="Resources\Windows\$placeholder$.png" ShortName="Stunden"/>
|
||||
<uap:SplashScreen Image="Resources\Windows\Splash\$placeholder$.png" BackgroundColor="#F7931D"/>
|
||||
</uap:VisualElements>
|
||||
<Applications>
|
||||
<Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
|
||||
<uap:VisualElements
|
||||
DisplayName="$placeholder$"
|
||||
Description="$placeholder$"
|
||||
Square150x150Logo="Resources\Windows\$placeholder$.png"
|
||||
Square44x44Logo="Resources\Windows\$placeholder$.png"
|
||||
BackgroundColor="transparent">
|
||||
<uap:DefaultTile Square71x71Logo="Resources\Windows\Small\$placeholder$.png"
|
||||
Wide310x150Logo="Resources\Windows\Wide\$placeholder$.png"
|
||||
Square310x310Logo="Resources\Windows\$placeholder$.png" ShortName="Stunden"/>
|
||||
<uap:SplashScreen Image="Resources\Windows\Splash\$placeholder$.png" BackgroundColor="#F7931D"/>
|
||||
</uap:VisualElements>
|
||||
|
||||
<Extensions>
|
||||
<Extensions>
|
||||
|
||||
<!-- Specify which CLSID to activate when notification is clicked -->
|
||||
<desktop:Extension Category="windows.toastNotificationActivation">
|
||||
<desktop:ToastNotificationActivation ToastActivatorCLSID="6e919706-2634-4d97-a93c-2213b2acc334" />
|
||||
</desktop:Extension>
|
||||
<!-- Specify which CLSID to activate when notification is clicked -->
|
||||
<desktop:Extension Category="windows.toastNotificationActivation">
|
||||
<desktop:ToastNotificationActivation ToastActivatorCLSID="6e919706-2634-4d97-a93c-2213b2acc334"/>
|
||||
</desktop:Extension>
|
||||
|
||||
<!-- Register COM CLSID -->
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Jugenddienst Stunden.exe" DisplayName="$targetnametoken$" Arguments="----AppNotificationActivated:">
|
||||
<!-- Example path to executable: CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.exe -->
|
||||
<com:Class Id="6e919706-2634-4d97-a93c-2213b2acc334" />
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
<!-- Register COM CLSID -->
|
||||
<com:Extension Category="windows.comServer">
|
||||
<com:ComServer>
|
||||
<com:ExeServer Executable="Jugenddienst Stunden.exe" DisplayName="$targetnametoken$"
|
||||
Arguments="----AppNotificationActivated:">
|
||||
<!-- Example path to executable: CommunityToolkit.Maui.Sample\CommunityToolkit.Maui.Sample.exe -->
|
||||
<com:Class Id="6e919706-2634-4d97-a93c-2213b2acc334"/>
|
||||
</com:ExeServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
|
||||
</Extensions>
|
||||
</Extensions>
|
||||
|
||||
</Application>
|
||||
</Applications>
|
||||
</Application>
|
||||
</Applications>
|
||||
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<Capability Name="internetClient"/>
|
||||
<DeviceCapability Name="webcam"/>
|
||||
</Capabilities>
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust"/>
|
||||
<Capability Name="internetClient"/>
|
||||
<DeviceCapability Name="webcam"/>
|
||||
</Capabilities>
|
||||
|
||||
</Package>
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<assemblyIdentity version="1.0.0.0" name="Jugenddienst Stunden.WinUI.app"/>
|
||||
<assemblyIdentity version="1.0.0.0" name="Jugenddienst Stunden.WinUI.app"/>
|
||||
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
<application xmlns="urn:schemas-microsoft-com:asm.v3">
|
||||
<windowsSettings>
|
||||
<!-- The combination of below two tags have the following effect:
|
||||
1) Per-Monitor for >= Windows 10 Anniversary Update
|
||||
2) System < Windows 10 Anniversary Update
|
||||
-->
|
||||
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/PM</dpiAware>
|
||||
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2, PerMonitor
|
||||
</dpiAwareness>
|
||||
</windowsSettings>
|
||||
</application>
|
||||
</assembly>
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app uses barcode scanning to...</string>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIDeviceFamily</key>
|
||||
<array>
|
||||
<integer>1</integer>
|
||||
<integer>2</integer>
|
||||
</array>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app uses barcode scanning to...</string>
|
||||
<key>XSAppIconAssets</key>
|
||||
<string>Assets.xcassets/appicon.appiconset</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// Dieser Code wurde von einem Tool generiert.
|
||||
// Laufzeitversion:4.0.30319.42000
|
||||
// This code was generated by a tool.
|
||||
//
|
||||
// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
|
||||
// der Code erneut generiert wird.
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -13,12 +12,12 @@ namespace Jugenddienst_Stunden.Properties {
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
|
||||
// -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert.
|
||||
// Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen
|
||||
// mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
@@ -33,7 +32,7 @@ namespace Jugenddienst_Stunden.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
@@ -47,8 +46,8 @@ namespace Jugenddienst_Stunden.Properties {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
|
||||
/// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
|
||||
@@ -1,101 +1,106 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 1.3
|
||||
Version 1.3
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">1.3</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1">this is my long string</data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
[base64 mime encoded serialized .NET Framework object]
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
[base64 mime encoded string representing a byte array form of the .NET Framework object]
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1"/>
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>1.3</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral,
|
||||
PublicKeyToken=b77a5c561934e089
|
||||
</value>
|
||||
</resheader>
|
||||
</root>
|
||||
@@ -1,4 +1,5 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using Jugenddienst_Stunden.Infrastructure;
|
||||
using Jugenddienst_Stunden.Models;
|
||||
using Jugenddienst_Stunden.Types;
|
||||
|
||||
@@ -8,12 +9,72 @@ namespace Jugenddienst_Stunden.Repositories;
|
||||
/// Standard-Repository, das die bestehende API-/Model-Logik kapselt.
|
||||
/// </summary>
|
||||
internal class HoursRepository : IHoursRepository {
|
||||
public async Task<BaseResponse> LoadBase(string query) => await HoursBase.LoadBase(query);
|
||||
public async Task<Settings> LoadSettings() => await HoursBase.LoadSettings();
|
||||
public async Task<Hours> LoadData() => await HoursBase.LoadData();
|
||||
public async Task<User> LoadUser(string apiKey) => await HoursBase.LoadUser(apiKey);
|
||||
public async Task<List<DayTime>> LoadDay(DateTime date) => await HoursBase.LoadDay(date);
|
||||
public async Task<DayTime> LoadEntry(int id) => await HoursBase.LoadEntry(id);
|
||||
public async Task<DayTime> SaveEntry(DayTime stunde) => await HoursBase.SaveEntry(stunde);
|
||||
public async Task DeleteEntry(DayTime stunde) => await HoursBase.DeleteEntry(stunde);
|
||||
}
|
||||
private readonly IApiClient _api;
|
||||
|
||||
public HoursRepository(IApiClient api) {
|
||||
_api = api;
|
||||
}
|
||||
|
||||
public async Task<BaseResponse> LoadBase(string query) {
|
||||
// Der bestehende Code übergab eine Query ohne führendes '?'
|
||||
var dict = QueryToDictionary(query);
|
||||
var res= await _api.GetAsync<BaseResponse>("", dict).ConfigureAwait(false);
|
||||
return res;
|
||||
}
|
||||
|
||||
public async Task<Settings> LoadSettings() {
|
||||
var res = await _api.GetAsync<BaseResponse>("", new Dictionary<string, string?> { ["settings"] = "1" })
|
||||
.ConfigureAwait(false);
|
||||
return res.settings;
|
||||
}
|
||||
|
||||
public async Task<Hours> LoadData() {
|
||||
var res = await _api.GetAsync<BaseResponse>("", new Dictionary<string, string?> { ["hours"] = "1" })
|
||||
.ConfigureAwait(false);
|
||||
return res.hour;
|
||||
}
|
||||
|
||||
public Task<User> LoadUser(string apiKey) {
|
||||
// Für die erste Iteration bleibt das Token global; der Endpoint ohne Query liefert user
|
||||
return _api.GetAsync<BaseResponse>("", null).ContinueWith(t => t.Result.user);
|
||||
}
|
||||
|
||||
public async Task<List<DayTime>> LoadDay(DateTime date) {
|
||||
var res = await _api
|
||||
.GetAsync<BaseResponse>("", new Dictionary<string, string?> { ["date"] = date.ToString("yyyy-MM-dd") })
|
||||
.ConfigureAwait(false);
|
||||
return res.daytimes ?? new List<DayTime>();
|
||||
}
|
||||
|
||||
public async Task<DayTime> LoadEntry(int id) {
|
||||
var res = await _api.GetAsync<BaseResponse>("", new Dictionary<string, string?> { ["id"] = id.ToString() })
|
||||
.ConfigureAwait(false);
|
||||
res.daytime.TimeSpanVon = res.daytime.Begin.ToTimeSpan();
|
||||
res.daytime.TimeSpanBis = res.daytime.End.ToTimeSpan();
|
||||
return res.daytime;
|
||||
}
|
||||
|
||||
public async Task<DayTime> SaveEntry(DayTime stunde) {
|
||||
bool isNew = stunde.Id is null;
|
||||
var method = isNew ? HttpMethod.Post : HttpMethod.Put;
|
||||
await _api.SendAsync<object>(method, "", stunde).ConfigureAwait(false);
|
||||
return stunde;
|
||||
}
|
||||
|
||||
public Task DeleteEntry(DayTime stunde)
|
||||
=> _api.DeleteAsync($"/entry/{stunde.Id}");
|
||||
|
||||
private static Dictionary<string, string?> QueryToDictionary(string query) {
|
||||
var dict = new Dictionary<string, string?>();
|
||||
if (string.IsNullOrWhiteSpace(query)) return dict;
|
||||
var q = query.TrimStart('?');
|
||||
foreach (var part in q.Split('&', StringSplitOptions.RemoveEmptyEntries)) {
|
||||
var kv = part.Split('=', 2);
|
||||
var key = Uri.UnescapeDataString(kv[0]);
|
||||
var val = kv.Length > 1 ? Uri.UnescapeDataString(kv[1]) : null;
|
||||
dict[key] = val;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<rect x="0" y="0" width="456" height="456" style="fill:white;"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 521 B After Width: | Height: | Size: 531 B |
@@ -1,41 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g id="JD" transform="matrix(0.96485,0,0,0.96485,104.499,104.499)">
|
||||
<g transform="matrix(12.0607,0,0,12.0607,101.169,256)">
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0,12.0607,12.0607,0,87.6884,17.5193)">
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246" style="fill-rule:nonzero;"/>
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,32.4324,105.748)">
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018" style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018"
|
||||
style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,109.437,86.7078)">
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
<g id="Uhr" transform="matrix(0.0220493,0,0,0.0220493,267.879,222.51)">
|
||||
<g transform="matrix(4.16667,0,0,4.16667,-6599.04,0)">
|
||||
<path d="M1638.49,981.86C1405.19,981.86 1216.06,792.734 1216.06,559.433C1216.06,326.132 1405.19,137.006 1638.49,137.006C1871.79,137.006 2060.92,326.132 2060.92,559.433C2060.92,792.734 1871.79,981.86 1638.49,981.86ZM1638.49,70.646C1368.54,70.646 1149.7,289.481 1149.7,559.433C1149.7,829.385 1368.54,1048.22 1638.49,1048.22C1908.44,1048.22 2127.28,829.385 2127.28,559.433C2127.28,289.481 1908.44,70.646 1638.49,70.646Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1638.49,981.86C1405.19,981.86 1216.06,792.734 1216.06,559.433C1216.06,326.132 1405.19,137.006 1638.49,137.006C1871.79,137.006 2060.92,326.132 2060.92,559.433C2060.92,792.734 1871.79,981.86 1638.49,981.86ZM1638.49,70.646C1368.54,70.646 1149.7,289.481 1149.7,559.433C1149.7,829.385 1368.54,1048.22 1638.49,1048.22C1908.44,1048.22 2127.28,829.385 2127.28,559.433C2127.28,289.481 1908.44,70.646 1638.49,70.646Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(4.14211,-0.451661,0.451661,4.14211,-6813.16,744.211)">
|
||||
<path d="M1677.2,559.433C1677.2,580.825 1659.85,598.166 1638.45,598.145C1617.12,598.124 1599.75,580.168 1599.78,558.84C1599.88,474.921 1621.18,308.102 1632.14,254.433C1633.54,247.532 1643.41,247.537 1644.81,254.44C1655.78,308.457 1677.2,476.326 1677.2,559.433Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1677.2,559.433C1677.2,580.825 1659.85,598.166 1638.45,598.145C1617.12,598.124 1599.75,580.168 1599.78,558.84C1599.88,474.921 1621.18,308.102 1632.14,254.433C1633.54,247.532 1643.41,247.537 1644.81,254.44C1655.78,308.457 1677.2,476.326 1677.2,559.433Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(-4.09852,-0.750473,-0.750473,4.09852,7333.98,1287.55)">
|
||||
<path d="M1595.39,590.238C1532.86,586.449 1465.21,572.938 1428.29,565.401C1422.75,564.267 1422.76,556.353 1428.29,555.229C1463.02,548.172 1526.22,535.741 1585.66,531.077C1585.22,540.595 1584.97,549.88 1584.96,558.834C1584.95,570.561 1587.38,581.586 1595.39,590.238Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1595.39,590.238C1532.86,586.449 1465.21,572.938 1428.29,565.401C1422.75,564.267 1422.76,556.353 1428.29,555.229C1463.02,548.172 1526.22,535.741 1585.66,531.077C1585.22,540.595 1584.97,549.88 1584.96,558.834C1584.95,570.561 1587.38,581.586 1595.39,590.238Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(4.16667,0,0,4.16667,-6599.04,0)">
|
||||
<path d="M1656.19,176.808C1656.19,186.583 1648.27,194.507 1638.49,194.507C1628.72,194.507 1620.79,186.583 1620.79,176.808C1620.79,167.033 1628.72,159.108 1638.49,159.108C1648.27,159.108 1656.19,167.033 1656.19,176.808Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1656.19,176.808C1656.19,186.583 1648.27,194.507 1638.49,194.507C1628.72,194.507 1620.79,186.583 1620.79,176.808C1620.79,167.033 1628.72,159.108 1638.49,159.108C1648.27,159.108 1656.19,167.033 1656.19,176.808Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(4.16667,0,0,4.16667,-6599.04,0)">
|
||||
<path d="M1255.87,541.734C1265.64,541.734 1273.57,549.658 1273.57,559.433C1273.57,569.208 1265.64,577.132 1255.87,577.132C1246.09,577.132 1238.17,569.208 1238.17,559.433C1238.17,549.658 1246.09,541.734 1255.87,541.734Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1255.87,541.734C1265.64,541.734 1273.57,549.658 1273.57,559.433C1273.57,569.208 1265.64,577.132 1255.87,577.132C1246.09,577.132 1238.17,569.208 1238.17,559.433C1238.17,549.658 1246.09,541.734 1255.87,541.734Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(4.16667,0,0,4.16667,-6599.04,0)">
|
||||
<path d="M1620.79,942.059C1620.79,932.283 1628.72,924.359 1638.49,924.359C1648.27,924.359 1656.19,932.283 1656.19,942.059C1656.19,951.834 1648.27,959.758 1638.49,959.758C1628.72,959.758 1620.79,951.834 1620.79,942.059Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M1620.79,942.059C1620.79,932.283 1628.72,924.359 1638.49,924.359C1648.27,924.359 1656.19,932.283 1656.19,942.059C1656.19,951.834 1648.27,959.758 1638.49,959.758C1628.72,959.758 1620.79,951.834 1620.79,942.059Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(4.16667,0,0,4.16667,-6599.04,0)">
|
||||
<path d="M2021.12,577.132C2011.34,577.132 2003.42,569.208 2003.42,559.433C2003.42,549.658 2011.34,541.734 2021.12,541.734C2030.89,541.734 2038.82,549.658 2038.82,559.433C2038.82,569.208 2030.89,577.132 2021.12,577.132Z" style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
<path d="M2021.12,577.132C2011.34,577.132 2003.42,569.208 2003.42,559.433C2003.42,549.658 2011.34,541.734 2021.12,541.734C2030.89,541.734 2038.82,549.658 2038.82,559.433C2038.82,569.208 2030.89,577.132 2021.12,577.132Z"
|
||||
style="fill:rgb(0,0,6);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 5.4 KiB |
@@ -1,21 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,201.169,356)">
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0,12.0607,12.0607,0,187.688,117.519)">
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246" style="fill-rule:nonzero;"/>
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,132.432,205.748)">
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018" style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018"
|
||||
style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,132.432,205.748)">
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018"
|
||||
style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,209.437,186.708)">
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -1,21 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<svg width="100%" height="100%" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/"
|
||||
style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,201.169,356)">
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,-11.938L0,-4.95C0,-3.918 -0.071,-3.151 -0.214,-2.644C-0.359,-2.069 -0.604,-1.583 -0.95,-1.184C-1.624,-0.397 -2.515,0 -3.626,0C-4.117,0 -4.588,-0.075 -5.04,-0.226L-5.04,-2.305L-4.999,-2.375C-4.509,-2.048 -4.052,-1.884 -3.626,-1.884C-3.026,-1.884 -2.612,-2.112 -2.382,-2.565C-2.141,-3.019 -2.02,-3.817 -2.02,-4.95L-2.02,-11.938L0,-11.938"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0,12.0607,12.0607,0,187.688,117.519)">
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246" style="fill-rule:nonzero;"/>
|
||||
<path d="M1.249,-1.246C0.559,-1.246 -0.002,-0.687 -0.002,0.002C-0.002,0.692 0.559,1.249 1.249,1.249C1.936,1.249 2.497,0.692 2.497,0.002C2.497,-0.687 1.936,-1.246 1.249,-1.246"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,132.432,205.748)">
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018" style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018"
|
||||
style="fill:rgb(247,147,29);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,132.432,205.748)">
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018" style="fill:white;fill-rule:nonzero;"/>
|
||||
<path d="M0,-5.018C0.88,-4.169 2.079,-3.647 3.398,-3.647C6.103,-3.647 8.293,-5.838 8.293,-8.539L8.288,-8.768L10.103,-8.768L10.107,-8.539C10.107,-5.953 8.64,-3.705 6.49,-2.586C7.743,-2.48 8.98,-2.02 10.042,-1.19L10.221,-1.044L9.103,0.384L8.926,0.242C6.796,-1.425 3.719,-1.044 2.055,1.084C1.43,1.878 1.096,2.811 1.03,3.75L-0.788,3.75C-0.72,2.42 -0.256,1.095 0.626,-0.033C1.239,-0.816 1.992,-1.427 2.82,-1.857C1.229,-1.993 -0.204,-2.685 -1.283,-3.737L0,-5.018"
|
||||
style="fill:white;fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(12.0607,0,0,12.0607,209.437,186.708)">
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401" style="fill-rule:nonzero;"/>
|
||||
<path d="M0,7.464C0,4.526 2.384,2.142 5.325,2.142C6.633,2.142 7.832,2.61 8.758,3.395L8.758,-5.324L10.746,-5.324L10.746,12.598L8.758,12.598L8.758,11.534C7.832,12.318 6.633,12.788 5.325,12.788C2.384,12.788 0,10.404 0,7.464M2.113,7.401C2.113,9.234 3.598,10.719 5.432,10.719C7.263,10.719 8.747,9.234 8.747,7.401C8.747,5.569 7.263,4.083 5.432,4.083C3.598,4.083 2.113,5.569 2.113,7.401"
|
||||
style="fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.0 KiB |
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xaml-comp compile="true" ?>
|
||||
<ResourceDictionary
|
||||
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
|
||||
|
||||
@@ -30,18 +31,18 @@
|
||||
<Color x:Key="Gray900">#212121</Color>
|
||||
<Color x:Key="Gray950">#141414</Color>
|
||||
|
||||
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}"/>
|
||||
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}"/>
|
||||
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}"/>
|
||||
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}"/>
|
||||
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}"/>
|
||||
<SolidColorBrush x:Key="PrimaryBrush" Color="{StaticResource Primary}" />
|
||||
<SolidColorBrush x:Key="SecondaryBrush" Color="{StaticResource Secondary}" />
|
||||
<SolidColorBrush x:Key="TertiaryBrush" Color="{StaticResource Tertiary}" />
|
||||
<SolidColorBrush x:Key="WhiteBrush" Color="{StaticResource White}" />
|
||||
<SolidColorBrush x:Key="BlackBrush" Color="{StaticResource Black}" />
|
||||
|
||||
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}"/>
|
||||
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}"/>
|
||||
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}"/>
|
||||
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}"/>
|
||||
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}"/>
|
||||
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}"/>
|
||||
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}"/>
|
||||
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}"/>
|
||||
<SolidColorBrush x:Key="Gray100Brush" Color="{StaticResource Gray100}" />
|
||||
<SolidColorBrush x:Key="Gray200Brush" Color="{StaticResource Gray200}" />
|
||||
<SolidColorBrush x:Key="Gray300Brush" Color="{StaticResource Gray300}" />
|
||||
<SolidColorBrush x:Key="Gray400Brush" Color="{StaticResource Gray400}" />
|
||||
<SolidColorBrush x:Key="Gray500Brush" Color="{StaticResource Gray500}" />
|
||||
<SolidColorBrush x:Key="Gray600Brush" Color="{StaticResource Gray600}" />
|
||||
<SolidColorBrush x:Key="Gray900Brush" Color="{StaticResource Gray900}" />
|
||||
<SolidColorBrush x:Key="Gray950Brush" Color="{StaticResource Gray950}" />
|
||||
</ResourceDictionary>
|
||||
@@ -1,54 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xaml-comp compile="true" ?>
|
||||
<ResourceDictionary
|
||||
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
|
||||
|
||||
<Style TargetType="ToolbarItem">
|
||||
<Style.Triggers>
|
||||
<Trigger TargetType="ToolbarItem" Property="VisualElement.BackgroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}">
|
||||
<Trigger TargetType="ToolbarItem" Property="VisualElement.BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}">
|
||||
</Trigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
|
||||
|
||||
<Style TargetType="ActivityIndicator">
|
||||
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="IndicatorView">
|
||||
<Setter Property="IndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}"/>
|
||||
<Setter Property="SelectedIndicatorColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}"/>
|
||||
<Setter Property="IndicatorColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="SelectedIndicatorColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray100}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Border">
|
||||
<Setter Property="Stroke" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="StrokeShape" Value="Rectangle"/>
|
||||
<Setter Property="StrokeThickness" Value="1"/>
|
||||
<Setter Property="Stroke"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="StrokeShape" Value="Rectangle" />
|
||||
<Setter Property="StrokeThickness" Value="1" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="BoxView">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="Color"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Button">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource PrimaryDarkText}}" />
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="BorderWidth" Value="0"/>
|
||||
<Setter Property="CornerRadius" Value="8"/>
|
||||
<Setter Property="Padding" Value="14,10"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource PrimaryDarkText}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="BorderWidth" Value="0" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="Padding" Value="14,10" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="PointerOver" />
|
||||
@@ -59,15 +70,16 @@
|
||||
|
||||
<Style TargetType="CheckBox">
|
||||
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="Color" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="Color"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -76,19 +88,21 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="DatePicker">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -97,20 +111,23 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Editor">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="PlaceholderColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -119,20 +136,23 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Entry">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="PlaceholderColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -142,18 +162,20 @@
|
||||
|
||||
<Style TargetType="Frame">
|
||||
<Setter Property="HasShadow" Value="False" />
|
||||
<Setter Property="BorderColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="BorderColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="CornerRadius" Value="8" />
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="ImageButton">
|
||||
<Setter Property="Opacity" Value="1" />
|
||||
<Setter Property="BorderColor" Value="Transparent"/>
|
||||
<Setter Property="BorderWidth" Value="0"/>
|
||||
<Setter Property="CornerRadius" Value="0"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="BorderColor" Value="Transparent" />
|
||||
<Setter Property="BorderWidth" Value="0" />
|
||||
<Setter Property="CornerRadius" Value="0" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
@@ -170,7 +192,8 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Label">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
@@ -180,7 +203,8 @@
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -189,44 +213,53 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Span">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Label" x:Key="Headline">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
|
||||
<Setter Property="FontSize" Value="32" />
|
||||
<Setter Property="HorizontalOptions" Value="Center" />
|
||||
<Setter Property="HorizontalTextAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Label" x:Key="SubHeadline">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource MidnightBlue}, Dark={StaticResource White}}" />
|
||||
<Setter Property="FontSize" Value="24" />
|
||||
<Setter Property="HorizontalOptions" Value="Center" />
|
||||
<Setter Property="HorizontalTextAlignment" Value="Center" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="ListView">
|
||||
<Setter Property="SeparatorColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="RefreshControlColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="SeparatorColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="RefreshControlColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Picker">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TitleColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TitleColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TitleColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -235,14 +268,16 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="ProgressBar">
|
||||
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="ProgressColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="ProgressColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ProgressColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -251,19 +286,21 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="RadioButton">
|
||||
<Setter Property="BackgroundColor" Value="Transparent"/>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -272,26 +309,30 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="RefreshView">
|
||||
<Setter Property="RefreshColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="RefreshColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="SearchBar">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
|
||||
<Setter Property="CancelButtonColor" Value="{StaticResource Gray500}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="PlaceholderColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -300,7 +341,8 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="SearchHandler">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{StaticResource Gray500}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
@@ -311,8 +353,10 @@
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="PlaceholderColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="PlaceholderColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -328,18 +372,24 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Slider">
|
||||
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="MinimumTrackColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="MaximumTrackColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ThumbColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="MinimumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
|
||||
<Setter Property="MaximumTrackColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
|
||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}"/>
|
||||
<Setter Property="MinimumTrackColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="MaximumTrackColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ThumbColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -348,11 +398,13 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="SwipeItem">
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Switch">
|
||||
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="OnColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="ThumbColor" Value="{StaticResource White}" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
@@ -360,19 +412,24 @@
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="OnColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="ThumbColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="On">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="OnColor" Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
<Setter Property="OnColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Secondary}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="ThumbColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource White}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState x:Name="Off">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="ThumbColor" Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
|
||||
<Setter Property="ThumbColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray400}, Dark={StaticResource Gray500}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -381,19 +438,21 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TimePicker">
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent"/>
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular"/>
|
||||
<Setter Property="FontSize" Value="14"/>
|
||||
<Setter Property="MinimumHeightRequest" Value="44"/>
|
||||
<Setter Property="MinimumWidthRequest" Value="44"/>
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BackgroundColor" Value="Transparent" />
|
||||
<Setter Property="FontFamily" Value="OpenSansRegular" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="MinimumHeightRequest" Value="44" />
|
||||
<Setter Property="MinimumWidthRequest" Value="44" />
|
||||
<Setter Property="VisualStateManager.VisualStateGroups">
|
||||
<VisualStateGroupList>
|
||||
<VisualStateGroup x:Name="CommonStates">
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="Disabled">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="TextColor" Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
<Setter Property="TextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray300}, Dark={StaticResource Gray600}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
@@ -402,34 +461,51 @@
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Page" ApplyToDerivedTypes="True">
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="Shell" ApplyToDerivedTypes="True">
|
||||
<Setter Property="Shell.BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
<Setter Property="Shell.ForegroundColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
|
||||
<Setter Property="Shell.TitleColor" Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
|
||||
<Setter Property="Shell.DisabledColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="Shell.UnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="Shell.BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
<Setter Property="Shell.ForegroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
|
||||
<Setter Property="Shell.TitleColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource SecondaryDarkText}}" />
|
||||
<Setter Property="Shell.DisabledColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="Shell.UnselectedColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="Shell.NavBarHasShadow" Value="False" />
|
||||
<Setter Property="Shell.TabBarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
<Setter Property="Shell.TabBarForegroundColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="Shell.TabBarTitleColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="Shell.TabBarUnselectedColor" Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="Shell.TabBarBackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Black}}" />
|
||||
<Setter Property="Shell.TabBarForegroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="Shell.TabBarTitleColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="Shell.TabBarUnselectedColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray900}, Dark={StaticResource Gray200}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="NavigationPage">
|
||||
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
|
||||
<Setter Property="IconColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
|
||||
<Setter Property="BarBackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource OffBlack}}" />
|
||||
<Setter Property="BarTextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
|
||||
<Setter Property="IconColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource White}}" />
|
||||
</Style>
|
||||
|
||||
<Style TargetType="TabbedPage">
|
||||
<Setter Property="BarBackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="BarTextColor" Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="UnselectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="SelectedTabColor" Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
<Setter Property="BarBackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="BarTextColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Magenta}, Dark={StaticResource White}}" />
|
||||
<Setter Property="UnselectedTabColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray200}, Dark={StaticResource Gray950}}" />
|
||||
<Setter Property="SelectedTabColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Gray950}, Dark={StaticResource Gray200}}" />
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
</ResourceDictionary>
|
||||
@@ -1,38 +1,58 @@
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using Jugenddienst_Stunden.Types;
|
||||
using Jugenddienst_Stunden.Validators;
|
||||
|
||||
namespace Jugenddienst_Stunden.Services;
|
||||
|
||||
internal class HoursService : IHoursService {
|
||||
private readonly IHoursRepository _repo;
|
||||
private readonly IHoursRepository _repo;
|
||||
private readonly IHoursValidator _validator;
|
||||
|
||||
public HoursService(IHoursRepository repo) {
|
||||
_repo = repo;
|
||||
}
|
||||
public HoursService(IHoursRepository repo, IHoursValidator validator) {
|
||||
_repo = repo;
|
||||
_validator = validator;
|
||||
}
|
||||
|
||||
public async Task<(Hours hours, Settings settings)> GetMonthSummaryAsync(DateTime monthDate) {
|
||||
string q = $"hours&year={monthDate:yyyy}&month={monthDate:MM}";
|
||||
public async Task<(Hours hours, Settings settings)> GetMonthSummaryAsync(DateTime monthDate) {
|
||||
string q = $"hours=&year={monthDate:yyyy}&month={monthDate:MM}";
|
||||
var baseRes = await _repo.LoadBase(q);
|
||||
return (baseRes.hour, baseRes.settings);
|
||||
|
||||
// Fallbacks, da einige Endpoints 'settings' nicht mitsenden bzw. 'hour' auslassen können
|
||||
var settings = baseRes.settings ?? await _repo.LoadSettings();
|
||||
var hours = baseRes.hour ?? new Hours {
|
||||
daytime = new List<DayTime>(),
|
||||
Nominal_day_api = new List<NominalDay>(),
|
||||
Nominal_week_api = new List<NominalWeek>(),
|
||||
zeit_total_daily_api = new List<TimeDay>(),
|
||||
Projekte = new System.Collections.ObjectModel.Collection<Projekt>(),
|
||||
Gemeinden = new System.Collections.ObjectModel.Collection<Gemeinde>(),
|
||||
Freistellungen = new System.Collections.ObjectModel.Collection<Freistellung>()
|
||||
};
|
||||
|
||||
return (hours, settings);
|
||||
}
|
||||
|
||||
public async Task<(List<DayTime> dayTimes, Settings settings)> GetDayWithSettingsAsync(DateTime date) {
|
||||
string q = $"date={date:yyyy-MM-dd}";
|
||||
var baseRes = await _repo.LoadBase(q);
|
||||
return (baseRes.daytimes ?? new List<DayTime>(), baseRes.settings);
|
||||
}
|
||||
public async Task<(List<DayTime> dayTimes, Settings settings)> GetDayWithSettingsAsync(DateTime date) {
|
||||
string q = $"date={date:yyyy-MM-dd}";
|
||||
var baseRes = await _repo.LoadBase(q);
|
||||
return (baseRes.daytimes ?? new List<DayTime>(), baseRes.settings);
|
||||
}
|
||||
|
||||
public async Task<List<DayTime>> GetDayRangeAsync(DateTime from, DateTime to) {
|
||||
string q = $"date={from:yyyy-MM-dd}&tilldate={to:yyyy-MM-dd}";
|
||||
var baseRes = await _repo.LoadBase(q);
|
||||
return baseRes.daytimes ?? new List<DayTime>();
|
||||
}
|
||||
public async Task<List<DayTime>> GetDayRangeAsync(DateTime from, DateTime to) {
|
||||
string q = $"date={from:yyyy-MM-dd}&tilldate={to:yyyy-MM-dd}";
|
||||
var baseRes = await _repo.LoadBase(q);
|
||||
return baseRes.daytimes ?? new List<DayTime>();
|
||||
}
|
||||
|
||||
public async Task<Settings> GetSettingsAsync() => await _repo.LoadSettings();
|
||||
public async Task<Settings> GetSettingsAsync() => await _repo.LoadSettings();
|
||||
|
||||
public async Task<DayTime> GetEntryAsync(int id) => await _repo.LoadEntry(id);
|
||||
public async Task<DayTime> GetEntryAsync(int id) => await _repo.LoadEntry(id);
|
||||
|
||||
public async Task<DayTime> SaveEntryAsync(DayTime stunde) => await _repo.SaveEntry(stunde);
|
||||
public async Task<DayTime> SaveEntryAsync(DayTime stunde) {
|
||||
var settings = await _repo.LoadSettings();
|
||||
_validator.Validate(stunde, settings);
|
||||
return await _repo.SaveEntry(stunde);
|
||||
}
|
||||
|
||||
public async Task DeleteEntryAsync(DayTime stunde) => await _repo.DeleteEntry(stunde);
|
||||
}
|
||||
public async Task DeleteEntryAsync(DayTime stunde) => await _repo.DeleteEntry(stunde);
|
||||
}
|
||||
@@ -1,36 +1,37 @@
|
||||
using Jugenddienst_Stunden.Models;
|
||||
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal class BaseResponse {
|
||||
|
||||
public Settings settings { get; set; }
|
||||
public Settings settings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht
|
||||
/// </summary>
|
||||
public Hours hour { get; set; }
|
||||
/// <summary>
|
||||
/// Monatsübersicht
|
||||
/// </summary>
|
||||
public Hours hour { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Stundenliste ... für die Katz?
|
||||
/// </summary>
|
||||
public List<Hours> hours { get; set; }
|
||||
/// <summary>
|
||||
/// Stundenliste ... für die Katz?
|
||||
/// </summary>
|
||||
public List<Hours> hours { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste der Stundeneinträge
|
||||
/// </summary>
|
||||
public List<DayTime> daytimes { get; set; }
|
||||
/// <summary>
|
||||
/// Liste der Stundeneinträge
|
||||
/// </summary>
|
||||
public List<DayTime> daytimes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Einzelner Stundeneintrag
|
||||
/// </summary>
|
||||
public DayTime daytime { get; set; }
|
||||
/// <summary>
|
||||
/// Einzelner Stundeneintrag
|
||||
/// </summary>
|
||||
public DayTime daytime { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Auch irgendwie doppelt ...
|
||||
/// </summary>
|
||||
public Operator operatorVar { get; set; }
|
||||
public User user { get; set; }
|
||||
/// <summary>
|
||||
/// Auch irgendwie doppelt ...
|
||||
/// </summary>
|
||||
public Operator operatorVar { get; set; }
|
||||
|
||||
public int error { get; set; }
|
||||
public string message { get; set; }
|
||||
}
|
||||
public User user { get; set; }
|
||||
|
||||
public int error { get; set; }
|
||||
public string message { get; set; }
|
||||
}
|
||||
@@ -2,95 +2,95 @@
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a day time entry for an employee.
|
||||
/// </summary>
|
||||
public class DayTime {
|
||||
/// <summary>
|
||||
/// ID des Stundeneintrages
|
||||
/// </summary>
|
||||
public int? Id { get; set; }
|
||||
/// <summary>
|
||||
/// ID des Stundeneintrages
|
||||
/// </summary>
|
||||
public int? Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Mitarbeiter-ID
|
||||
/// </summary>
|
||||
public int EmployeeId { get; set; }
|
||||
/// <summary>
|
||||
/// Mitarbeiter-ID
|
||||
/// </summary>
|
||||
public int EmployeeId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Der betreffende Tag
|
||||
/// </summary>
|
||||
public DateTime Day { get; set; }
|
||||
/// <summary>
|
||||
/// Der betreffende Tag
|
||||
/// </summary>
|
||||
public DateTime Day { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Der Wochentag
|
||||
/// </summary>
|
||||
public int Wday { get; set; }
|
||||
/// <summary>
|
||||
/// Der Wochentag
|
||||
/// </summary>
|
||||
public int Wday { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arbeitsbeginn
|
||||
/// </summary>
|
||||
public TimeOnly Begin { get; set; }
|
||||
/// <summary>
|
||||
/// Arbeitsbeginn
|
||||
/// </summary>
|
||||
public TimeOnly Begin { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arbeitsende
|
||||
/// </summary>
|
||||
public TimeOnly End { get; set; }
|
||||
/// <summary>
|
||||
/// Arbeitsende
|
||||
/// </summary>
|
||||
public TimeOnly End { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Beschreibung der Tätigkeit
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
/// <summary>
|
||||
/// Beschreibung der Tätigkeit
|
||||
/// </summary>
|
||||
public string? Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Freistellung
|
||||
/// </summary>
|
||||
public string? Free { get; set; }
|
||||
/// <summary>
|
||||
/// Freistellung
|
||||
/// </summary>
|
||||
public string? Free { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Freistellung genehmigt?
|
||||
/// </summary>
|
||||
public bool Approved { get; set; }
|
||||
/// <summary>
|
||||
/// Freistellung genehmigt?
|
||||
/// </summary>
|
||||
public bool Approved { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Das gewählte Projekt
|
||||
/// </summary>
|
||||
public int? Projekt { get; set; }
|
||||
/// <summary>
|
||||
/// Das gewählte Projekt
|
||||
/// </summary>
|
||||
public int? Projekt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Die gewählte Gemeinde
|
||||
/// </summary>
|
||||
public int? Gemeinde { get; set; }
|
||||
/// <summary>
|
||||
/// Die gewählte Gemeinde
|
||||
/// </summary>
|
||||
public int? Gemeinde { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nachtstunden
|
||||
/// </summary>
|
||||
public TimeOnly Night { get; set; }
|
||||
/// <summary>
|
||||
/// Nachtstunden
|
||||
/// </summary>
|
||||
public TimeOnly Night { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Summe Arbeitszeit (inklusive Nachstunden mit Faktor)
|
||||
/// </summary>
|
||||
public Dictionary<string, TimeOnly> Total { get; set; }
|
||||
/// <summary>
|
||||
/// Summe Arbeitszeit (inklusive Nachstunden mit Faktor)
|
||||
/// </summary>
|
||||
public Dictionary<string, TimeOnly> Total { get; set; }
|
||||
|
||||
public TimeOnly End_print { get; set; }
|
||||
public TimeSpan TimeSpanVon { get; set; }
|
||||
public TimeSpan TimeSpanBis { get; set; }
|
||||
public TimeOnly End_print { get; set; }
|
||||
public TimeSpan TimeSpanVon { get; set; }
|
||||
public TimeSpan TimeSpanBis { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active Gemeinde based on the gemeinde ID.
|
||||
/// </summary>
|
||||
public Gemeinde? GemeindeAktiv { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the active Gemeinde based on the gemeinde ID.
|
||||
/// </summary>
|
||||
public Gemeinde? GemeindeAktiv { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active Projekt based on the projekt ID.
|
||||
/// </summary>
|
||||
public Projekt? ProjektAktiv { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the active Projekt based on the projekt ID.
|
||||
/// </summary>
|
||||
public Projekt? ProjektAktiv { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active Freistellung based on the Freistellung ID
|
||||
/// </summary>
|
||||
public Freistellung? FreistellungAktiv { get; set; }
|
||||
/// <summary>
|
||||
/// Gets the active Freistellung based on the Freistellung ID
|
||||
/// </summary>
|
||||
public Freistellung? FreistellungAktiv { get; set; }
|
||||
|
||||
public int TimeTable { get; set; }
|
||||
|
||||
}
|
||||
public int TimeTable { get; set; }
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Freistellungen: Urlaub, Zeitausgleich, Krankheit, ...
|
||||
/// </summary>
|
||||
public class Freistellung {
|
||||
public string? Id { get; set; }
|
||||
public string? Id { get; set; }
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -13,4 +13,4 @@ public class Gemeinde {
|
||||
/// Name der Gemeinde.
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,45 @@
|
||||
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal partial class Hours : ObservableObject {
|
||||
public partial class Hours : ObservableObject {
|
||||
public double? Zeit;
|
||||
|
||||
public double? Nominal;
|
||||
|
||||
//public Dictionary<DateOnly,NominalDay> nominal_day_api;
|
||||
public List<NominalDay>? Nominal_day_api;
|
||||
|
||||
//public Dictionary<int,NominalWeek> nominal_week_api;
|
||||
public List<NominalWeek>? Nominal_week_api;
|
||||
//public List<string> time_line;
|
||||
public double? Zeit_total;
|
||||
|
||||
//https://stackoverflow.com/questions/29449641/deserialize-json-when-a-value-can-be-an-object-or-an-empty-array/29450279#29450279
|
||||
//[JsonConverter(typeof(JsonSingleOrEmptyArrayConverter<Hours>))]
|
||||
//public Dictionary<int,decimal> zeit_total_daily;
|
||||
//public List<string> time_line;
|
||||
public double? Zeit_total;
|
||||
|
||||
public List<TimeDay> zeit_total_daily_api;
|
||||
//https://stackoverflow.com/questions/29449641/deserialize-json-when-a-value-can-be-an-object-or-an-empty-array/29450279#29450279
|
||||
//[JsonConverter(typeof(JsonSingleOrEmptyArrayConverter<Hours>))]
|
||||
//public Dictionary<int,decimal> zeit_total_daily;
|
||||
|
||||
public List<TimeDay> zeit_total_daily_api;
|
||||
public List<DayTime>? daytime;
|
||||
//public List<string> wochensumme;
|
||||
//public List<string> wochensumme;
|
||||
|
||||
[ObservableProperty]
|
||||
public double overtime_month;
|
||||
[ObservableProperty] public double overtime_month;
|
||||
|
||||
[ObservableProperty]
|
||||
public double overtime;
|
||||
//public List<string> overtime_day;
|
||||
[ObservableProperty] public double overtime;
|
||||
//public List<string> overtime_day;
|
||||
|
||||
[ObservableProperty]
|
||||
public double zeitausgleich;
|
||||
[ObservableProperty] public double zeitausgleich;
|
||||
|
||||
public double zeitausgleich_month;
|
||||
public double holiday;
|
||||
public double krankheit;
|
||||
public double weiterbildung;
|
||||
public double bereitschaft;
|
||||
|
||||
public double bereitschaft_month;
|
||||
|
||||
//public Operator operator_api;
|
||||
public DateTime Today;
|
||||
public DateTime Date;
|
||||
@@ -47,5 +49,4 @@ internal partial class Hours : ObservableObject {
|
||||
public Collection<Gemeinde> Gemeinden { get; set; }
|
||||
public Collection<Freistellung> Freistellungen { get; set; }
|
||||
public int EmployeeId { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
internal class NominalDay {
|
||||
|
||||
public class NominalDay {
|
||||
public int day_number;
|
||||
public int month_number;
|
||||
public double hours;
|
||||
public DateOnly date;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal class NominalWeek {
|
||||
public int Week_number;
|
||||
public double Hours;
|
||||
}
|
||||
public class NominalWeek {
|
||||
public int Week_number;
|
||||
public double Hours;
|
||||
}
|
||||
@@ -13,4 +13,4 @@ public class Projekt {
|
||||
/// Holt oder setzt den Namen des Projekts.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -4,35 +4,35 @@
|
||||
/// Einstellungen
|
||||
/// </summary>
|
||||
public class Settings {
|
||||
/// <summary>
|
||||
/// Sind Projekte aktiv?
|
||||
/// </summary>
|
||||
public bool ProjektAktivSet { get; set; }
|
||||
/// <summary>
|
||||
/// Sind Projekte aktiv?
|
||||
/// </summary>
|
||||
public bool ProjektAktivSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Sind Gemeinden aktiv?
|
||||
/// </summary>
|
||||
public bool GemeindeAktivSet { get; set; }
|
||||
/// <summary>
|
||||
/// Sind Gemeinden aktiv?
|
||||
/// </summary>
|
||||
public bool GemeindeAktivSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste der Projekte
|
||||
/// </summary>
|
||||
public List<Projekt>? Projekte { get; set; }
|
||||
/// <summary>
|
||||
/// Liste der Projekte
|
||||
/// </summary>
|
||||
public List<Projekt>? Projekte { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste der Gemeinden
|
||||
/// </summary>
|
||||
public List<Gemeinde>? Gemeinden { get; set; }
|
||||
/// <summary>
|
||||
/// Liste der Gemeinden
|
||||
/// </summary>
|
||||
public List<Gemeinde>? Gemeinden { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Liste der Freistellungen
|
||||
/// </summary>
|
||||
public List<Freistellung>? Freistellungen { get; set; }
|
||||
/// <summary>
|
||||
/// Liste der Freistellungen
|
||||
/// </summary>
|
||||
public List<Freistellung>? Freistellungen { get; set; }
|
||||
|
||||
public List<Sollstunden> Nominal { get; set; }
|
||||
public List<Sollstunden>? Nominal { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Version der API
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
/// <summary>
|
||||
/// Version der API
|
||||
/// </summary>
|
||||
public string? Version { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
public class Sollstunden {
|
||||
public int Timetable { get; set; }
|
||||
public int Wochentag { get; set; }
|
||||
public double Zeit { get; set; }
|
||||
}
|
||||
public int Timetable { get; set; }
|
||||
public int Wochentag { get; set; }
|
||||
public double Zeit { get; set; }
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
/// <summary>
|
||||
/// Summe der geleisteten Stunden.
|
||||
/// </summary>
|
||||
internal struct TimeDay {
|
||||
public int Day { get; set; }
|
||||
public double Hours { get; set; }
|
||||
}
|
||||
public struct TimeDay {
|
||||
public int Day { get; set; }
|
||||
public double Hours { get; set; }
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal class Timetable {
|
||||
public List<TimetableEntry> timetable;
|
||||
public decimal wochensumme;
|
||||
}
|
||||
public List<TimetableEntry> timetable;
|
||||
public decimal wochensumme;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal class TimetableEntry {
|
||||
public List<TimeOnly>? Von;
|
||||
public List<TimeOnly>? Bis;
|
||||
public decimal Summe { get; set; }
|
||||
}
|
||||
public List<TimeOnly>? Von;
|
||||
public List<TimeOnly>? Bis;
|
||||
public decimal Summe { get; set; }
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
namespace Jugenddienst_Stunden.Types;
|
||||
|
||||
internal class User {
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Surname { get; set; }
|
||||
public string Token { get; set; }
|
||||
}
|
||||
}
|
||||
31
Jugenddienst Stunden/Validators/HoursValidator.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Jugenddienst_Stunden.Infrastructure;
|
||||
using Jugenddienst_Stunden.Types;
|
||||
|
||||
namespace Jugenddienst_Stunden.Validators;
|
||||
|
||||
internal interface IHoursValidator {
|
||||
void Validate(DayTime item, Settings settings);
|
||||
}
|
||||
|
||||
internal sealed class HoursValidator : IHoursValidator {
|
||||
public void Validate(DayTime item, Settings settings) {
|
||||
if (item.FreistellungAktiv is null && item.TimeSpanVon == item.TimeSpanBis)
|
||||
throw new ValidationException("Beginn und Ende sind gleich");
|
||||
|
||||
if (item.TimeSpanBis < item.TimeSpanVon)
|
||||
throw new ValidationException("Ende ist vor Beginn");
|
||||
|
||||
if (settings.GemeindeAktivSet && item.GemeindeAktiv is null)
|
||||
throw new ValidationException("Gemeinde nicht gewählt");
|
||||
|
||||
if (settings.ProjektAktivSet && item.ProjektAktiv is null)
|
||||
throw new ValidationException("Projekt nicht gewählt");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(item.Description)) {
|
||||
if (item.FreistellungAktiv?.Name is string name && !string.IsNullOrWhiteSpace(name))
|
||||
item.Description = name;
|
||||
else
|
||||
throw new ValidationException("Keine Beschreibung");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,28 +4,30 @@
|
||||
/// Die Loginseite
|
||||
/// </summary>
|
||||
public class LoginViewModel {
|
||||
/// <summary>
|
||||
/// Name der Anwendung
|
||||
/// </summary>
|
||||
public string AppTitle => AppInfo.Name;
|
||||
/// <summary>
|
||||
/// Name der Anwendung
|
||||
/// </summary>
|
||||
public string AppTitle => AppInfo.Name;
|
||||
|
||||
/// <summary>
|
||||
/// Programmversion
|
||||
/// </summary>
|
||||
public string Version => AppInfo.VersionString;
|
||||
/// <summary>
|
||||
/// Programmversion
|
||||
/// </summary>
|
||||
public string Version => AppInfo.VersionString;
|
||||
|
||||
/// <summary>
|
||||
/// Kurze Mitteilung für den Anwender
|
||||
/// </summary>
|
||||
public string Message { get; set; } = "Scanne den QR-Code von deinem Benutzerprofil auf der Stundenseite.";
|
||||
/// <summary>
|
||||
/// Kurze Mitteilung für den Anwender
|
||||
/// </summary>
|
||||
public string Message { get; set; } = "Scanne den QR-Code von deinem Benutzerprofil auf der Stundenseite.";
|
||||
|
||||
/// <summary>
|
||||
/// Genutzer Server für die API
|
||||
/// </summary>
|
||||
public string Server { get; set; } = "Server: " + Preferences.Default.Get("apiUrl", "").Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
|
||||
/// <summary>
|
||||
/// Genutzer Server für die API
|
||||
/// </summary>
|
||||
public string Server { get; set; } = "Server: " + Preferences.Default.Get("apiUrl", "").Replace("/appapi", "")
|
||||
.Replace("https://", "").Replace("http://", "");
|
||||
|
||||
/// <summary>
|
||||
/// Titel der Seite - im Moment der aktuelle Anwender
|
||||
/// </summary>
|
||||
public string Title { get; set; } = Preferences.Default.Get("name", "Nicht") + " " + Preferences.Default.Get("surname", "eingeloggt");
|
||||
}
|
||||
/// <summary>
|
||||
/// Titel der Seite - im Moment der aktuelle Anwender
|
||||
/// </summary>
|
||||
public string Title { get; set; } = Preferences.Default.Get("name", "Nicht") + " " +
|
||||
Preferences.Default.Get("surname", "eingeloggt");
|
||||
}
|
||||
@@ -2,9 +2,11 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Jugenddienst_Stunden.ViewModels;
|
||||
namespace Jugenddienst_Stunden.ViewModels;
|
||||
|
||||
internal class NoteViewModel : ObservableObject, IQueryAttributable {
|
||||
private Models.Note _note;
|
||||
|
||||
public string Text {
|
||||
get => _note.Text;
|
||||
set {
|
||||
@@ -61,4 +63,4 @@ internal class NoteViewModel : ObservableObject, IQueryAttributable {
|
||||
OnPropertyChanged(nameof(Text));
|
||||
OnPropertyChanged(nameof(Date));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,8 +47,10 @@ internal class NotesViewModel : IQueryAttributable {
|
||||
}
|
||||
|
||||
// If note isn't found, it's new; add it.
|
||||
else { AllNotes.Insert(0, new NoteViewModel(Note.Load(noteId))); }
|
||||
else {
|
||||
AllNotes.Insert(0, new NoteViewModel(Note.Load(noteId)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,243 +8,257 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using Jugenddienst_Stunden.Repositories;
|
||||
using Jugenddienst_Stunden.Services;
|
||||
using Jugenddienst_Stunden.Infrastructure;
|
||||
using Jugenddienst_Stunden.Validators;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace Jugenddienst_Stunden.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// Viewmodel für die einzelnen Stundeneinträge / Bearbeitung
|
||||
/// </summary>
|
||||
public partial class StundeViewModel : ObservableObject, IQueryAttributable {
|
||||
private readonly IHoursService _hoursService;
|
||||
private readonly IHoursService _hoursService;
|
||||
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = "Eintrag bearbeiten";
|
||||
public string SubTitle { get; set; } = DateTime.Today.ToString("dddd, d. MMMM yyyy");
|
||||
public int Id { get; set; }
|
||||
public string Title { get; set; } = "Eintrag bearbeiten";
|
||||
public string SubTitle { get; set; } = DateTime.Today.ToString("dddd, d. MMMM yyyy");
|
||||
|
||||
//private HoursBase HoursBase = new HoursBase();
|
||||
internal Settings Settings = new Settings();
|
||||
//private HoursBase HoursBase = new HoursBase();
|
||||
internal Settings Settings = new Settings();
|
||||
|
||||
public event EventHandler<string> AlertEvent;
|
||||
public event EventHandler<string> InfoEvent;
|
||||
public event Func<string, string, Task<bool>> ConfirmEvent;
|
||||
//public event Func<string, string, string?, string?, Task<bool>> ConfirmEvent;
|
||||
//public event EventHandler<ConfirmEventArgs> ConfirmEvent;
|
||||
public event EventHandler<string> AlertEvent;
|
||||
public event EventHandler<string> InfoEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Gemeinden für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private List<Gemeinde> optionsGemeinde;
|
||||
public event Func<string, string, Task<bool>> ConfirmEvent;
|
||||
//public event Func<string, string, string?, string?, Task<bool>> ConfirmEvent;
|
||||
//public event EventHandler<ConfirmEventArgs> ConfirmEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Projekte für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private List<Projekt> optionsProjekt;
|
||||
/// <summary>
|
||||
/// Gemeinden für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty] private List<Gemeinde> optionsGemeinde;
|
||||
|
||||
/// <summary>
|
||||
/// Freistellungen für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private List<Freistellung> optionsFreistellung;
|
||||
/// <summary>
|
||||
/// Projekte für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty] private List<Projekt> optionsProjekt;
|
||||
|
||||
/// <summary>
|
||||
/// Vorhandene Zeiten anzeigen, wenn neuer Eintrag erstellt wird
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private List<DayTime> dayTimes;
|
||||
/// <summary>
|
||||
/// Freistellungen für die Auswahlliste
|
||||
/// </summary>
|
||||
[ObservableProperty] private List<Freistellung> optionsFreistellung;
|
||||
|
||||
/// <summary>
|
||||
/// Aktueller Stundeneintrag
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private DayTime dayTime;
|
||||
/// <summary>
|
||||
/// Vorhandene Zeiten anzeigen, wenn neuer Eintrag erstellt wird
|
||||
/// </summary>
|
||||
[ObservableProperty] private List<DayTime> dayTimes;
|
||||
|
||||
/// <summary>
|
||||
/// Dürfen Gemeinden verwendet werden?
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool gemeindeAktivSet;
|
||||
/// <summary>
|
||||
/// Aktueller Stundeneintrag
|
||||
/// </summary>
|
||||
[ObservableProperty] private DayTime dayTime;
|
||||
|
||||
/// <summary>
|
||||
/// Dürfen Projekte verwendet werden?
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool projektAktivSet;
|
||||
/// <summary>
|
||||
/// Dürfen Gemeinden verwendet werden?
|
||||
/// </summary>
|
||||
[ObservableProperty] private bool gemeindeAktivSet;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool freistellungEnabled;
|
||||
/// <summary>
|
||||
/// Dürfen Projekte verwendet werden?
|
||||
/// </summary>
|
||||
[ObservableProperty] private bool projektAktivSet;
|
||||
|
||||
public ICommand SaveCommand { get; private set; }
|
||||
public ICommand DeleteCommand { get; private set; }
|
||||
public ICommand DeleteConfirmCommand { get; private set; }
|
||||
//public ICommand LoadDataCommand { get; private set; }
|
||||
[ObservableProperty] private bool freistellungEnabled;
|
||||
|
||||
public ICommand SaveCommand { get; private set; }
|
||||
public ICommand DeleteCommand { get; private set; }
|
||||
public ICommand DeleteConfirmCommand { get; private set; }
|
||||
//public ICommand LoadDataCommand { get; private set; }
|
||||
|
||||
|
||||
public StundeViewModel() : this(GetServiceOrCreate()) { }
|
||||
public StundeViewModel() : this(GetServiceOrCreate()) {
|
||||
}
|
||||
|
||||
private static IHoursService GetServiceOrCreate() => new HoursService(new HoursRepository());
|
||||
private static IHoursService GetServiceOrCreate() {
|
||||
// Fallback-Konstruktion, falls DI nicht injiziert wurde (z. B. im Designer)
|
||||
var http = new HttpClient();
|
||||
var options = new Infrastructure.ApiOptions { BaseUrl = GlobalVar.ApiUrl, Timeout = TimeSpan.FromSeconds(15) };
|
||||
var tokenProvider = new GlobalVarTokenProvider();
|
||||
var api = new ApiClient(http, options, tokenProvider, new PreferencesAppSettings());
|
||||
var repo = new HoursRepository(api);
|
||||
var validator = new HoursValidator();
|
||||
return new HoursService(repo, validator);
|
||||
}
|
||||
|
||||
internal StundeViewModel(IHoursService hoursService) {
|
||||
_hoursService = hoursService;
|
||||
SaveCommand = new AsyncRelayCommand(Save);
|
||||
//DeleteCommand = new AsyncRelayCommand(Delete);
|
||||
DeleteConfirmCommand = new Command(async () => await DeleteConfirm());
|
||||
}
|
||||
internal StundeViewModel(IHoursService hoursService) {
|
||||
_hoursService = hoursService;
|
||||
SaveCommand = new AsyncRelayCommand(Save);
|
||||
//DeleteCommand = new AsyncRelayCommand(Delete);
|
||||
DeleteConfirmCommand = new Command(async () => await DeleteConfirm());
|
||||
}
|
||||
|
||||
private async void LoadSettingsAsync() {
|
||||
try {
|
||||
Settings = await _hoursService.GetSettingsAsync();
|
||||
GlobalVar.Settings = Settings;
|
||||
// DI-Konstruktor, der den globalen Alert-Service abonniert und Alerts an das ViewModel weiterreicht.
|
||||
internal StundeViewModel(IHoursService hoursService, IAlertService alertService) : this(hoursService) {
|
||||
if (alertService is not null) {
|
||||
alertService.AlertRaised += (s, msg) => AlertEvent?.Invoke(this, msg);
|
||||
}
|
||||
}
|
||||
|
||||
OptionsGemeinde = Settings.Gemeinden;
|
||||
OptionsProjekt = Settings.Projekte;
|
||||
OptionsFreistellung = Settings.Freistellungen;
|
||||
private async void LoadSettingsAsync() {
|
||||
try {
|
||||
Settings = await _hoursService.GetSettingsAsync();
|
||||
GlobalVar.Settings = Settings;
|
||||
|
||||
GemeindeAktivSet = Settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = Settings.ProjektAktivSet;
|
||||
OptionsGemeinde = Settings.Gemeinden;
|
||||
OptionsProjekt = Settings.Projekte;
|
||||
OptionsFreistellung = Settings.Freistellungen;
|
||||
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
}
|
||||
}
|
||||
GemeindeAktivSet = Settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = Settings.ProjektAktivSet;
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
async Task Save() {
|
||||
bool exceptionOccurred = false;
|
||||
bool proceed = true;
|
||||
if (DayTime.TimeSpanVon == DayTime.TimeSpanBis && DayTime.FreistellungAktiv.Name == null) {
|
||||
proceed = false;
|
||||
AlertEvent?.Invoke(this, "Uhrzeiten sollten unterschiedlich sein");
|
||||
}
|
||||
|
||||
if (proceed) {
|
||||
try {
|
||||
await _hoursService.SaveEntryAsync(DayTime);
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
exceptionOccurred = true;
|
||||
}
|
||||
if (!exceptionOccurred) {
|
||||
if (DayTime.Id != null) {
|
||||
await Shell.Current.GoToAsync($"..?saved={DayTime.Id}");
|
||||
} else {
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async Task Save() {
|
||||
bool exceptionOccurred = false;
|
||||
bool proceed = true;
|
||||
if (DayTime.TimeSpanVon == DayTime.TimeSpanBis && DayTime.FreistellungAktiv.Name == null) {
|
||||
proceed = false;
|
||||
AlertEvent?.Invoke(this, "Uhrzeiten sollten unterschiedlich sein");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Löschen ohne Bestätigung
|
||||
/// </summary>
|
||||
private async Task Delete() {
|
||||
await _hoursService.DeleteEntryAsync(DayTime);
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
}
|
||||
if (proceed) {
|
||||
try {
|
||||
await _hoursService.SaveEntryAsync(DayTime);
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
exceptionOccurred = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Löschen mit Bestätigung
|
||||
/// </summary>
|
||||
private async Task DeleteConfirm() {
|
||||
if (ConfirmEvent != null) {
|
||||
bool answer = await ConfirmEvent.Invoke("Achtung", "Löschen kann nicht ungeschehen gemacht werden. Fortfahren?");
|
||||
if (answer) {
|
||||
//Löschen
|
||||
await _hoursService.DeleteEntryAsync(DayTime);
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
} else {
|
||||
//nicht Löschen
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exceptionOccurred) {
|
||||
if (DayTime.Id != null) {
|
||||
await Shell.Current.GoToAsync($"..?saved={DayTime.Id}");
|
||||
} else {
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Anwenden der Query-Parameter
|
||||
/// </summary>
|
||||
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) {
|
||||
//load beinhaltet die ID: Eintrag bearbeiten
|
||||
//date beinhaltet einen Tag: Neuen Eintrag erstellen
|
||||
if (query.ContainsKey("load")) {
|
||||
/// <summary>
|
||||
/// Löschen ohne Bestätigung
|
||||
/// </summary>
|
||||
private async Task Delete() {
|
||||
await _hoursService.DeleteEntryAsync(DayTime);
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
}
|
||||
|
||||
//DateTime heute = DateTime.Now;
|
||||
try {
|
||||
var entry = await _hoursService.GetEntryAsync(Convert.ToInt32(query["load"]));
|
||||
var settings = await _hoursService.GetSettingsAsync();
|
||||
GlobalVar.Settings = settings;
|
||||
GemeindeAktivSet = settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = settings.ProjektAktivSet;
|
||||
/// <summary>
|
||||
/// Löschen mit Bestätigung
|
||||
/// </summary>
|
||||
private async Task DeleteConfirm() {
|
||||
if (ConfirmEvent != null) {
|
||||
bool answer =
|
||||
await ConfirmEvent.Invoke("Achtung", "Löschen kann nicht ungeschehen gemacht werden. Fortfahren?");
|
||||
if (answer) {
|
||||
//Löschen
|
||||
await _hoursService.DeleteEntryAsync(DayTime);
|
||||
await Shell.Current.GoToAsync($"..?date={DayTime.Day.ToString("yyyy-MM-dd")}");
|
||||
} else {
|
||||
//nicht Löschen
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DayTime = entry;
|
||||
DayTime.TimeSpanVon = entry.Begin.ToTimeSpan();
|
||||
DayTime.TimeSpanBis = entry.End.ToTimeSpan();
|
||||
/// <summary>
|
||||
/// Anwenden der Query-Parameter
|
||||
/// </summary>
|
||||
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) {
|
||||
//load beinhaltet die ID: Eintrag bearbeiten
|
||||
//date beinhaltet einen Tag: Neuen Eintrag erstellen
|
||||
if (query.ContainsKey("load")) {
|
||||
//DateTime heute = DateTime.Now;
|
||||
try {
|
||||
var entry = await _hoursService.GetEntryAsync(Convert.ToInt32(query["load"]));
|
||||
var settings = await _hoursService.GetSettingsAsync();
|
||||
GlobalVar.Settings = settings;
|
||||
GemeindeAktivSet = settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = settings.ProjektAktivSet;
|
||||
|
||||
OptionsGemeinde = settings.Gemeinden ?? new List<Gemeinde>();
|
||||
OptionsProjekt = settings.Projekte ?? new List<Projekt>();
|
||||
OptionsFreistellung = settings.Freistellungen ?? new List<Freistellung>();
|
||||
DayTime = entry;
|
||||
DayTime.TimeSpanVon = entry.Begin.ToTimeSpan();
|
||||
DayTime.TimeSpanBis = entry.End.ToTimeSpan();
|
||||
|
||||
DayTime.GemeindeAktiv = OptionsGemeinde.FirstOrDefault(Gemeinde => Gemeinde.Id == DayTime.Gemeinde) ?? new Gemeinde();
|
||||
DayTime.ProjektAktiv = OptionsProjekt.FirstOrDefault(Projekt => Projekt.Id == DayTime.Projekt) ?? new Projekt();
|
||||
DayTime.FreistellungAktiv = OptionsFreistellung.FirstOrDefault(Freistellung => Freistellung.Id == DayTime.Free) ?? new Freistellung();
|
||||
OptionsGemeinde = settings.Gemeinden ?? new List<Gemeinde>();
|
||||
OptionsProjekt = settings.Projekte ?? new List<Projekt>();
|
||||
OptionsFreistellung = settings.Freistellungen ?? new List<Freistellung>();
|
||||
|
||||
//Evtl. noch die anderen Zeiten des gleichen Tages holen
|
||||
var day = await _hoursService.GetDayWithSettingsAsync(DayTime.Day);
|
||||
DayTimes = day.dayTimes;
|
||||
OnPropertyChanged(nameof(DayTime));
|
||||
OnPropertyChanged(nameof(DayTimes));
|
||||
DayTime.GemeindeAktiv = OptionsGemeinde.FirstOrDefault(Gemeinde => Gemeinde.Id == DayTime.Gemeinde) ??
|
||||
new Gemeinde();
|
||||
DayTime.ProjektAktiv = OptionsProjekt.FirstOrDefault(Projekt => Projekt.Id == DayTime.Projekt) ??
|
||||
new Projekt();
|
||||
DayTime.FreistellungAktiv =
|
||||
OptionsFreistellung.FirstOrDefault(Freistellung => Freistellung.Id == DayTime.Free) ??
|
||||
new Freistellung();
|
||||
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
} finally {
|
||||
//Evtl. noch die anderen Zeiten des gleichen Tages holen
|
||||
var day = await _hoursService.GetDayWithSettingsAsync(DayTime.Day);
|
||||
DayTimes = day.dayTimes;
|
||||
OnPropertyChanged(nameof(DayTime));
|
||||
OnPropertyChanged(nameof(DayTimes));
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
} finally {
|
||||
}
|
||||
|
||||
}
|
||||
if (System.String.IsNullOrEmpty(DayTime.Description)) {
|
||||
InfoEvent?.Invoke(this, "Eintrag hat keinen Beschreibungstext");
|
||||
}
|
||||
|
||||
if (System.String.IsNullOrEmpty(DayTime.Description)) {
|
||||
InfoEvent?.Invoke(this, "Eintrag hat keinen Beschreibungstext");
|
||||
}
|
||||
SubTitle = DayTime.Day.ToString("dddd, d. MMMM yyyy");
|
||||
OnPropertyChanged(nameof(SubTitle));
|
||||
SubTitle = DayTime.Day.ToString("dddd, d. MMMM yyyy");
|
||||
OnPropertyChanged(nameof(SubTitle));
|
||||
|
||||
FreistellungEnabled = !DayTime.Approved;
|
||||
//OnPropertyChanged(nameof(DayTime));
|
||||
FreistellungEnabled = !DayTime.Approved;
|
||||
//OnPropertyChanged(nameof(DayTime));
|
||||
} else if (query.ContainsKey("date")) {
|
||||
Title = "Neuer Eintrag";
|
||||
OnPropertyChanged(nameof(Title));
|
||||
|
||||
} else if (query.ContainsKey("date")) {
|
||||
Title = "Neuer Eintrag";
|
||||
OnPropertyChanged(nameof(Title));
|
||||
DateTime _date = DateTime.ParseExact((string)query["date"], "yyyy-MM-dd",
|
||||
System.Globalization.CultureInfo.InvariantCulture);
|
||||
//Bei neuem Eintrag die vorhandenen des gleichen Tages anzeigen
|
||||
try {
|
||||
var (list, settings) = await _hoursService.GetDayWithSettingsAsync(_date);
|
||||
GlobalVar.Settings = settings;
|
||||
DayTimes = list;
|
||||
|
||||
DateTime _date = DateTime.ParseExact((string)query["date"], "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
//Bei neuem Eintrag die vorhandenen des gleichen Tages anzeigen
|
||||
try {
|
||||
var (list, settings) = await _hoursService.GetDayWithSettingsAsync(_date);
|
||||
GlobalVar.Settings = settings;
|
||||
DayTimes = list;
|
||||
OptionsGemeinde = settings.Gemeinden;
|
||||
OptionsProjekt = settings.Projekte;
|
||||
OptionsFreistellung = settings.Freistellungen;
|
||||
|
||||
OptionsGemeinde = settings.Gemeinden;
|
||||
OptionsProjekt = settings.Projekte;
|
||||
OptionsFreistellung = settings.Freistellungen;
|
||||
GemeindeAktivSet = settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = settings.ProjektAktivSet;
|
||||
} catch (Exception) {
|
||||
//Ein Tag ohne Einträge gibt eine Fehlermeldung,
|
||||
//die soll aber ignoriert werden, weil beim Neueintrag ist das ja Wurscht
|
||||
//In dem Fall müssen die Settings aber nochmal geholt werden, weil die dann nicht geladen wurden
|
||||
LoadSettingsAsync();
|
||||
} finally {
|
||||
DayTime = new DayTime();
|
||||
DayTime.Day = _date;
|
||||
DayTime.EmployeeId = GlobalVar.EmployeeId;
|
||||
DayTime.GemeindeAktiv = new Gemeinde();
|
||||
DayTime.ProjektAktiv = new Projekt();
|
||||
DayTime.FreistellungAktiv = new Freistellung();
|
||||
|
||||
GemeindeAktivSet = settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = settings.ProjektAktivSet;
|
||||
SubTitle = _date.ToString("dddd, d. MMMM yyyy");
|
||||
FreistellungEnabled = true;
|
||||
|
||||
} catch (Exception) {
|
||||
//Ein Tag ohne Einträge gibt eine Fehlermeldung,
|
||||
//die soll aber ignoriert werden, weil beim Neueintrag ist das ja Wurscht
|
||||
//In dem Fall müssen die Settings aber nochmal geholt werden, weil die dann nicht geladen wurden
|
||||
LoadSettingsAsync();
|
||||
} finally {
|
||||
DayTime = new DayTime();
|
||||
DayTime.Day = _date;
|
||||
DayTime.EmployeeId = GlobalVar.EmployeeId;
|
||||
DayTime.GemeindeAktiv = new Gemeinde();
|
||||
DayTime.ProjektAktiv = new Projekt();
|
||||
DayTime.FreistellungAktiv = new Freistellung();
|
||||
|
||||
SubTitle = _date.ToString("dddd, d. MMMM yyyy");
|
||||
FreistellungEnabled = true;
|
||||
|
||||
OnPropertyChanged(nameof(SubTitle));
|
||||
//OnPropertyChanged(nameof(DayTime));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
OnPropertyChanged(nameof(SubTitle));
|
||||
//OnPropertyChanged(nameof(DayTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,325 +5,366 @@ using Jugenddienst_Stunden.Types;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Input;
|
||||
using CommunityToolkit.Maui.Alerts;
|
||||
using CommunityToolkit.Maui.Core;
|
||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
using Jugenddienst_Stunden.Interfaces;
|
||||
using Jugenddienst_Stunden.Repositories;
|
||||
using Jugenddienst_Stunden.Services;
|
||||
|
||||
|
||||
namespace Jugenddienst_Stunden.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// ViewModel für die Stundenliste
|
||||
/// </summary>
|
||||
internal partial class StundenViewModel : ObservableObject, IQueryAttributable, INotifyPropertyChanged {
|
||||
private readonly IHoursService _hoursService;
|
||||
public partial class StundenViewModel : ObservableObject, IQueryAttributable, INotifyPropertyChanged {
|
||||
private readonly IHoursService _hoursService;
|
||||
|
||||
public ICommand NewEntryCommand { get; }
|
||||
public ICommand SelectEntryCommand { get; }
|
||||
public ICommand LoadDataCommand { get; private set; }
|
||||
public ICommand LoadDayCommand { get; private set; }
|
||||
public ICommand RefreshListCommand { get; }
|
||||
public ICommand RefreshCommand { get; }
|
||||
public ICommand NewEntryCommand { get; }
|
||||
public ICommand SelectEntryCommand { get; }
|
||||
public ICommand LoadDataCommand { get; private set; }
|
||||
public ICommand LoadDayCommand { get; private set; }
|
||||
public ICommand RefreshListCommand { get; }
|
||||
public ICommand RefreshCommand { get; }
|
||||
|
||||
public event EventHandler<string> AlertEvent;
|
||||
public event EventHandler<string> InfoEvent;
|
||||
public event EventHandler<string> AlertEvent;
|
||||
public event EventHandler<string> InfoEvent;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Beschriftung Button Monatsübersicht
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private string loadOverview;
|
||||
/// <summary>
|
||||
/// Beschriftung Button Monatsübersicht
|
||||
/// </summary>
|
||||
[ObservableProperty] private string loadOverview;
|
||||
|
||||
//private HoursBase HoursBase = new HoursBase();
|
||||
internal Settings Settings = new Settings();
|
||||
//private HoursBase HoursBase = new HoursBase();
|
||||
internal Settings Settings = new Settings();
|
||||
|
||||
/// <summary>
|
||||
/// Zu leistende Stunden
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private TimeOnly sollstunden;
|
||||
/// <summary>
|
||||
/// Zu leistende Stunden
|
||||
/// </summary>
|
||||
[ObservableProperty] private TimeOnly sollstunden;
|
||||
|
||||
/// <summary>
|
||||
/// Geleistete Stunden an einem Tag
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private TimeOnly dayTotal;
|
||||
/// <summary>
|
||||
/// Geleistete Stunden an einem Tag
|
||||
/// </summary>
|
||||
[ObservableProperty] private TimeOnly dayTotal;
|
||||
|
||||
/// <summary>
|
||||
/// Liste der Tageszeiten
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private List<DayTime> dayTimes = new List<DayTime>();
|
||||
/// <summary>
|
||||
/// Liste der Tageszeiten
|
||||
/// </summary>
|
||||
[ObservableProperty] private List<DayTime> dayTimes = new List<DayTime>();
|
||||
|
||||
public string Title { get; set; } = GlobalVar.Name + " " + GlobalVar.Surname;
|
||||
public string Title { get; set; } = GlobalVar.Name + " " + GlobalVar.Surname;
|
||||
|
||||
[ObservableProperty]
|
||||
private Hours hours;
|
||||
[ObservableProperty] private Hours hours;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Mindest-Datum für den Datepicker
|
||||
/// </summary>
|
||||
public DateTime MinimumDate {
|
||||
get => DateTime.Today.AddDays(-365);
|
||||
}
|
||||
/// <summary>
|
||||
/// Mindest-Datum für den Datepicker
|
||||
/// </summary>
|
||||
public DateTime MinimumDate {
|
||||
get => DateTime.Today.AddDays(-365);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Höchst-Datum für den Datepicker
|
||||
/// </summary>
|
||||
public DateTime MaximumDate {
|
||||
get => DateTime.Today.AddDays(60);
|
||||
}
|
||||
/// <summary>
|
||||
/// Höchst-Datum für den Datepicker
|
||||
/// </summary>
|
||||
public DateTime MaximumDate {
|
||||
get => DateTime.Today.AddDays(60);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Heutiges Datum, wenn das Datum geändert wird, wird auch der Tag geladen
|
||||
/// </summary>
|
||||
private DateTime dateToday = DateTime.Today;
|
||||
public DateTime DateToday {
|
||||
get => dateToday;
|
||||
set {
|
||||
if (dateToday != value) {
|
||||
dateToday = value;
|
||||
LoadOverview = "Lade Summen für " + dateToday.ToString("MMMM yy");
|
||||
//OnPropertyChanged();
|
||||
Task.Run(() => LoadDay(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Heutiges Datum, wenn das Datum geändert wird, wird auch der Tag geladen
|
||||
/// </summary>
|
||||
private DateTime dateToday = DateTime.Today;
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Geleistete Stunden
|
||||
/// </summary>
|
||||
public double? ZeitCalculated {
|
||||
get => Hours.Zeit_total;
|
||||
}
|
||||
public DateTime DateToday {
|
||||
get => dateToday;
|
||||
set {
|
||||
if (dateToday != value) {
|
||||
dateToday = value;
|
||||
LoadOverview = "Lade Summen für " + dateToday.ToString("MMMM yy");
|
||||
// Task.Run(() => LoadDay(value));
|
||||
// NICHT Task.Run: LoadDay aktualisiert UI-gebundene Properties
|
||||
MainThread.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadDay(dateToday);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AlertEvent?.Invoke(this, ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Sollstunden
|
||||
/// </summary>
|
||||
public double? Nominal {
|
||||
get => Hours.Nominal;
|
||||
}
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Geleistete Stunden
|
||||
/// </summary>
|
||||
public double? ZeitCalculated {
|
||||
get => Hours.Zeit_total;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Differenz zwischen Soll und geleisteten Stunden
|
||||
/// </summary>
|
||||
public double? Overtime {
|
||||
get => Hours.overtime;
|
||||
}
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Sollstunden
|
||||
/// </summary>
|
||||
public double? Nominal {
|
||||
get => Hours.Nominal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Restüberstunden insgesamt
|
||||
/// </summary>
|
||||
public double OvertimeMonth {
|
||||
get => Hours.overtime_month;
|
||||
}
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Differenz zwischen Soll und geleisteten Stunden
|
||||
/// </summary>
|
||||
public double? Overtime {
|
||||
get => Hours.overtime;
|
||||
}
|
||||
|
||||
public double Zeitausgleich {
|
||||
get => Hours.zeitausgleich;
|
||||
}
|
||||
public double ZeitausgleichMonth {
|
||||
get => Hours.zeitausgleich_month;
|
||||
}
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Restüberstunden insgesamt
|
||||
/// </summary>
|
||||
public double OvertimeMonth {
|
||||
get => Hours.overtime_month;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Resturlaub
|
||||
/// </summary>
|
||||
public double Holiday {
|
||||
get => Hours.holiday;
|
||||
}
|
||||
public double Zeitausgleich {
|
||||
get => Hours.zeitausgleich;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seite neu laden
|
||||
/// </summary>
|
||||
[ObservableProperty]
|
||||
private bool isRefreshing;
|
||||
public double ZeitausgleichMonth {
|
||||
get => Hours.zeitausgleich_month;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Monatsübersicht: Resturlaub
|
||||
/// </summary>
|
||||
public double Holiday {
|
||||
get => Hours.holiday;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seite neu laden
|
||||
/// </summary>
|
||||
[ObservableProperty] private bool isRefreshing;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Dürfen Gemeinden verwendet werden?
|
||||
/// </summary>
|
||||
public bool GemeindeAktivSet { get; set; }
|
||||
/// <summary>
|
||||
/// Dürfen Gemeinden verwendet werden?
|
||||
/// </summary>
|
||||
public bool GemeindeAktivSet { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Dürfen Projekte verwendet werden?
|
||||
/// </summary>
|
||||
public bool ProjektAktivSet { get; set; }
|
||||
/// <summary>
|
||||
/// Dürfen Projekte verwendet werden?
|
||||
/// </summary>
|
||||
public bool ProjektAktivSet { get; set; }
|
||||
|
||||
private bool doContinue = true;
|
||||
private bool doContinue = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CTOR (DI)
|
||||
/// </summary>
|
||||
public StundenViewModel(IHoursService hoursService) {
|
||||
_hoursService = hoursService;
|
||||
Hours = new Hours();
|
||||
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public StundenViewModel() : this(GetServiceOrCreate()) {
|
||||
}
|
||||
LoadOverview = "Lade Summen für " + DateToday.ToString("MMMM");
|
||||
|
||||
private static IHoursService GetServiceOrCreate() => new HoursService(new HoursRepository());
|
||||
LoadDataCommand = new AsyncRelayCommand(LoadData);
|
||||
NewEntryCommand = new AsyncRelayCommand(NewEntryAsync);
|
||||
SelectEntryCommand = new AsyncRelayCommand<DayTime>(SelectEntryAsync);
|
||||
RefreshListCommand = new AsyncRelayCommand(RefreshList);
|
||||
RefreshCommand = new Command(async () => await RefreshItemsAsync());
|
||||
|
||||
internal StundenViewModel(IHoursService hoursService) {
|
||||
_hoursService = hoursService;
|
||||
Hours = new Hours();
|
||||
|
||||
LoadOverview = "Lade Summen für " + DateToday.ToString("MMMM");
|
||||
|
||||
LoadDataCommand = new AsyncRelayCommand(LoadData);
|
||||
NewEntryCommand = new AsyncRelayCommand(NewEntryAsync);
|
||||
SelectEntryCommand = new AsyncRelayCommand<DayTime>(SelectEntryAsync);
|
||||
RefreshListCommand = new AsyncRelayCommand(RefreshList);
|
||||
RefreshCommand = new Command(async () => await RefreshItemsAsync());
|
||||
|
||||
Task task = LoadDay(DateTime.Today);
|
||||
}
|
||||
// Task task = LoadDay(DateTime.Today);
|
||||
// Beim Startup NICHT direkt im CTOR laden (kann Startup/Navigation blockieren)
|
||||
// Stattdessen via Dispatcher "nach" dem Aufbau starten:
|
||||
MainThread.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
await LoadDay(DateTime.Today);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AlertEvent?.Invoke(this, ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Öffnet eine neue Stundeneingabe
|
||||
/// </summary>
|
||||
private async Task NewEntryAsync() {
|
||||
//Hier muss das Datum übergeben werden
|
||||
//await Shell.Current.GoToAsync(nameof(Views.StundePage));
|
||||
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?date={dateToday:yyyy-MM-dd}");
|
||||
}
|
||||
/// <summary>
|
||||
/// Öffnet eine neue Stundeneingabe
|
||||
/// </summary>
|
||||
private async Task NewEntryAsync() {
|
||||
//Hier muss das Datum übergeben werden
|
||||
//await Shell.Current.GoToAsync(nameof(Views.StundePage));
|
||||
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?date={dateToday:yyyy-MM-dd}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Öffnet eine bestehende Stundeneingabe
|
||||
/// </summary>
|
||||
private async Task SelectEntryAsync(DayTime entry) {
|
||||
if (entry != null && entry.Id != null) {
|
||||
//var navigationParameters = new Dictionary<string, object> { { "load", entry.id } };
|
||||
//await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}", navigationParameters);
|
||||
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?load={entry.Id}");
|
||||
} else AlertEvent?.Invoke(this, "Auswahl enthält keine Daten");
|
||||
}
|
||||
/// <summary>
|
||||
/// Öffnet eine bestehende Stundeneingabe
|
||||
/// </summary>
|
||||
private async Task SelectEntryAsync(DayTime entry) {
|
||||
if (entry != null && entry.Id != null) {
|
||||
//var navigationParameters = new Dictionary<string, object> { { "load", entry.id } };
|
||||
//await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}", navigationParameters);
|
||||
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?load={entry.Id}");
|
||||
} else AlertEvent?.Invoke(this, "Auswahl enthält keine Daten");
|
||||
}
|
||||
|
||||
private async Task RefreshList() {
|
||||
OnPropertyChanged(nameof(DayTimes));
|
||||
}
|
||||
private async Task RefreshList() {
|
||||
OnPropertyChanged(nameof(DayTimes));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lädt die Monatssummen für die Übersicht
|
||||
/// </summary>
|
||||
private async Task LoadData() {
|
||||
try {
|
||||
var (hours, settings) = await _hoursService.GetMonthSummaryAsync(DateToday);
|
||||
Hours = hours;
|
||||
Settings = settings;
|
||||
/// <summary>
|
||||
/// Lädt die Monatssummen für die Übersicht
|
||||
/// </summary>
|
||||
private async Task LoadData() {
|
||||
try {
|
||||
var (hours, settings) = await _hoursService.GetMonthSummaryAsync(DateToday);
|
||||
Hours = hours;
|
||||
Settings = settings;
|
||||
|
||||
if (Settings.Version != AppInfo.Current.VersionString.Substring(0, 5)) {
|
||||
InfoEvent?.Invoke(this, "Version: " + Settings.Version + " verfügbar (" + AppInfo.Current.VersionString.Substring(0, 5) + " installiert)");
|
||||
}
|
||||
//_hour = await HoursBase.LoadData();
|
||||
RefreshProperties();
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
}
|
||||
}
|
||||
if (Settings.Version != AppInfo.Current.VersionString.Substring(0, 5)) {
|
||||
InfoEvent?.Invoke(this,
|
||||
"Version: " + Settings.Version + " verfügbar (" + AppInfo.Current.VersionString.Substring(0, 5) +
|
||||
" installiert)");
|
||||
}
|
||||
|
||||
//_hour = await HoursBase.LoadData();
|
||||
RefreshProperties();
|
||||
} catch (Exception e) {
|
||||
AlertEvent?.Invoke(this, e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Lädt die Arbeitszeiten für einen Tag
|
||||
/// </summary>
|
||||
public async Task LoadDay(DateTime date) {
|
||||
DayTotal = new TimeOnly(0);
|
||||
Sollstunden = new TimeOnly(0);
|
||||
try {
|
||||
var (dayTimes, settings) = await _hoursService.GetDayWithSettingsAsync(date);
|
||||
/// <summary>
|
||||
/// Lädt die Arbeitszeiten für einen Tag
|
||||
/// </summary>
|
||||
public async Task LoadDay(DateTime date) {
|
||||
// kleine Initialwerte sind ok, aber UI-Thread sicher setzen:
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
DayTotal = new TimeOnly(0);
|
||||
Sollstunden = new TimeOnly(0);
|
||||
});
|
||||
try {
|
||||
var (dayTimes, settings) = await _hoursService.GetDayWithSettingsAsync(date);
|
||||
|
||||
DayTimes = dayTimes;
|
||||
Settings = settings;
|
||||
GemeindeAktivSet = Settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = Settings.ProjektAktivSet;
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
DayTimes = dayTimes;
|
||||
Settings = settings;
|
||||
GemeindeAktivSet = Settings.GemeindeAktivSet;
|
||||
ProjektAktivSet = Settings.ProjektAktivSet;
|
||||
|
||||
OnPropertyChanged(nameof(GemeindeAktivSet));
|
||||
OnPropertyChanged(nameof(ProjektAktivSet));
|
||||
OnPropertyChanged(nameof(GemeindeAktivSet));
|
||||
OnPropertyChanged(nameof(ProjektAktivSet));
|
||||
});
|
||||
|
||||
List<Sollstunden> _soll;
|
||||
TimeSpan span = TimeSpan.Zero;
|
||||
bool merker = false;
|
||||
foreach (DayTime dt in DayTimes) {
|
||||
span += dt.End - dt.Begin;
|
||||
//Nachtstunden dazurechnen
|
||||
if (dt.Night.Ticks > 0 && !merker) {
|
||||
span += dt.Night.ToTimeSpan() * .5;
|
||||
merker = true;
|
||||
}
|
||||
_soll = Settings.Nominal.Where(w => w.Timetable == dt.TimeTable && w.Wochentag == dt.Wday).ToList();
|
||||
if (_soll.Count > 0)
|
||||
Sollstunden = TimeOnly.FromTimeSpan(TimeSpan.FromHours(_soll[0].Zeit));
|
||||
}
|
||||
DayTotal = TimeOnly.FromTimeSpan(span);
|
||||
List<Sollstunden> _soll;
|
||||
TimeSpan span = TimeSpan.Zero;
|
||||
bool merker = false;
|
||||
foreach (DayTime dt in DayTimes) {
|
||||
span += dt.End - dt.Begin;
|
||||
//Nachtstunden dazurechnen
|
||||
if (dt.Night.Ticks > 0 && !merker) {
|
||||
span += dt.Night.ToTimeSpan() * .5;
|
||||
merker = true;
|
||||
}
|
||||
|
||||
//Nach der Tagessumme die anderen Tage anhängen
|
||||
if (DayTimes != null) {
|
||||
var more = await _hoursService.GetDayRangeAsync(date, date.AddDays(3));
|
||||
if (more != null && more.Count > 0)
|
||||
DayTimes = DayTimes.Concat(more).ToList();
|
||||
}
|
||||
_soll = Settings.Nominal.Where(w => w.Timetable == dt.TimeTable && w.Wochentag == dt.Wday).ToList();
|
||||
if (_soll.Count > 0)
|
||||
{
|
||||
var soll = TimeOnly.FromTimeSpan(TimeSpan.FromHours(_soll[0].Zeit));
|
||||
await MainThread.InvokeOnMainThreadAsync(() => Sollstunden = soll);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
DayTimes = new List<DayTime>();
|
||||
//TODO: hier könnte auch ein Fehler kommen, dann wäre InfoEvent falsch.
|
||||
var total = TimeOnly.FromTimeSpan(span);
|
||||
await MainThread.InvokeOnMainThreadAsync(() => DayTotal = total);
|
||||
|
||||
if (Settings.Version != null && Settings.Version != AppInfo.Current.VersionString.Substring(0, 5)) {
|
||||
InfoEvent?.Invoke(this, "Version: " + Settings.Version + " verfügbar (" + AppInfo.Current.VersionString.Substring(0, 5) + " installiert)");
|
||||
} else { InfoEvent?.Invoke(this, e.Message); }
|
||||
} finally {
|
||||
OnPropertyChanged(nameof(DayTotal));
|
||||
OnPropertyChanged(nameof(Sollstunden));
|
||||
OnPropertyChanged(nameof(DateToday));
|
||||
OnPropertyChanged(nameof(LoadOverview));
|
||||
//OnPropertyChanged(nameof(DayTimes));
|
||||
}
|
||||
}
|
||||
//Nach der Tagessumme die anderen Tage anhängen
|
||||
if (DayTimes != null) {
|
||||
var more = await _hoursService.GetDayRangeAsync(date, date.AddDays(3));
|
||||
if (more != null && more.Count > 0)
|
||||
{
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
DayTimes = DayTimes.Concat(more).ToList()
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
DayTimes = new List<DayTime>();
|
||||
//TODO: hier könnte auch ein Fehler kommen, dann wäre InfoEvent falsch.
|
||||
|
||||
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) {
|
||||
if (query.ContainsKey("date")) {
|
||||
await LoadDay(Convert.ToDateTime(query["date"]));
|
||||
}
|
||||
}
|
||||
if (Settings.Version != null && Settings.Version != AppInfo.Current.VersionString.Substring(0, 5)) {
|
||||
InfoEvent?.Invoke(this,
|
||||
"Version: " + Settings.Version + " verfügbar (" + AppInfo.Current.VersionString.Substring(0, 5) +
|
||||
" installiert)");
|
||||
} else {
|
||||
InfoEvent?.Invoke(this, e.Message);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Seite aktualisieren
|
||||
/// </summary>
|
||||
private async Task RefreshItemsAsync() {
|
||||
IsRefreshing = true;
|
||||
|
||||
} finally {
|
||||
await MainThread.InvokeOnMainThreadAsync(() =>
|
||||
{
|
||||
OnPropertyChanged(nameof(DayTotal));
|
||||
OnPropertyChanged(nameof(Sollstunden));
|
||||
OnPropertyChanged(nameof(DateToday));
|
||||
OnPropertyChanged(nameof(LoadOverview));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//await Task.Delay(2000); // Simuliert eine Datenaktualisierung
|
||||
await LoadDay(DateToday);
|
||||
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) {
|
||||
if (query.ContainsKey("date")) {
|
||||
await LoadDay(Convert.ToDateTime(query["date"]));
|
||||
}
|
||||
}
|
||||
|
||||
IsRefreshing = false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Seite aktualisieren
|
||||
/// </summary>
|
||||
private async Task RefreshItemsAsync() {
|
||||
IsRefreshing = true;
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes all properties
|
||||
/// </summary>
|
||||
private void RefreshProperties() {
|
||||
OnPropertyChanged(nameof(Hours));
|
||||
OnPropertyChanged(nameof(Title));
|
||||
OnPropertyChanged(nameof(Nominal));
|
||||
OnPropertyChanged(nameof(Overtime));
|
||||
OnPropertyChanged(nameof(OvertimeMonth));
|
||||
OnPropertyChanged(nameof(Zeitausgleich));
|
||||
OnPropertyChanged(nameof(ZeitCalculated));
|
||||
OnPropertyChanged(nameof(Holiday));
|
||||
OnPropertyChanged(nameof(MinimumDate));
|
||||
OnPropertyChanged(nameof(MaximumDate));
|
||||
OnPropertyChanged(nameof(LoadOverview));
|
||||
}
|
||||
//await Task.Delay(2000); // Simuliert eine Datenaktualisierung
|
||||
await LoadDay(DateToday);
|
||||
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) {
|
||||
try {
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
|
||||
} catch (Exception ex) {
|
||||
AlertEvent?.Invoke(this, ex.Message);
|
||||
//Console.WriteLine($"Fehler bei OnPropertyChanged: {ex.Message}");
|
||||
}
|
||||
}
|
||||
IsRefreshing = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refreshes all properties
|
||||
/// </summary>
|
||||
private void RefreshProperties() {
|
||||
OnPropertyChanged(nameof(Hours));
|
||||
OnPropertyChanged(nameof(Title));
|
||||
OnPropertyChanged(nameof(Nominal));
|
||||
OnPropertyChanged(nameof(Overtime));
|
||||
OnPropertyChanged(nameof(OvertimeMonth));
|
||||
OnPropertyChanged(nameof(Zeitausgleich));
|
||||
OnPropertyChanged(nameof(ZeitCalculated));
|
||||
OnPropertyChanged(nameof(Holiday));
|
||||
OnPropertyChanged(nameof(MinimumDate));
|
||||
OnPropertyChanged(nameof(MaximumDate));
|
||||
OnPropertyChanged(nameof(LoadOverview));
|
||||
}
|
||||
|
||||
}
|
||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) {
|
||||
try {
|
||||
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
|
||||
} catch (Exception ex) {
|
||||
AlertEvent?.Invoke(this, ex.Message);
|
||||
//Console.WriteLine($"Fehler bei OnPropertyChanged: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
@@ -13,17 +14,18 @@
|
||||
|
||||
<!-- Add an item to the toolbar -->
|
||||
<ContentPage.ToolbarItems>
|
||||
<ToolbarItem Text="Neue Notiz" Command="{Binding NewCommand}" IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" />
|
||||
<ToolbarItem Text="Neue Notiz" Command="{Binding NewCommand}"
|
||||
IconImageSource="{FontImage Glyph='+', Color=Black, Size=22}" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<ContentPage.Behaviors>
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
StatusBarStyle="LightContent" />
|
||||
</ContentPage.Behaviors>
|
||||
|
||||
<VerticalStackLayout Margin="20,0,0,0">
|
||||
<Label Text="Werden nur lokal gespeichert"/>
|
||||
<Label Text="Werden nur lokal gespeichert" />
|
||||
|
||||
<!-- Display notes in a list -->
|
||||
<CollectionView x:Name="notesCollection"
|
||||
@@ -31,7 +33,7 @@
|
||||
Margin="0,20,0,0"
|
||||
SelectionMode="Single"
|
||||
SelectionChangedCommand="{Binding SelectNoteCommand}"
|
||||
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
|
||||
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
|
||||
|
||||
<!-- Designate how the collection of items is laid out -->
|
||||
<CollectionView.ItemsLayout>
|
||||
@@ -42,8 +44,8 @@
|
||||
<CollectionView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackLayout>
|
||||
<Label Text="{Binding Text}" FontSize="22"/>
|
||||
<Label Text="{Binding Date}" FontSize="14" TextColor="Silver"/>
|
||||
<Label Text="{Binding Text}" FontSize="22" />
|
||||
<Label Text="{Binding Date}" FontSize="14" TextColor="Silver" />
|
||||
</StackLayout>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
namespace Jugenddienst_Stunden.Views;
|
||||
|
||||
public partial class AllNotesPage : ContentPage
|
||||
{
|
||||
public AllNotesPage()
|
||||
{
|
||||
public partial class AllNotesPage : ContentPage {
|
||||
public AllNotesPage() {
|
||||
InitializeComponent();
|
||||
|
||||
}
|
||||
|
||||
private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
@@ -20,8 +21,8 @@
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.Behaviors>
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
StatusBarStyle="LightContent" />
|
||||
</ContentPage.Behaviors>
|
||||
|
||||
@@ -29,31 +30,33 @@
|
||||
<VerticalStackLayout Spacing="10" Margin="15,0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<HorizontalStackLayout Spacing="5" HorizontalOptions="Start" Grid.Column="0">
|
||||
<Label FontSize="20" FontAttributes="Bold" Text="{Binding AppTitle}" Margin="0,7,0,0" />
|
||||
<Label FontSize="16" Text="{Binding Version}" Margin="0,11,0,0" />
|
||||
</HorizontalStackLayout>
|
||||
<Grid Grid.Column="1" ColumnDefinitions="*,50" ColumnSpacing="10">
|
||||
<Label Text="Login QR/manuell" VerticalOptions="Center" Grid.Column="0"/>
|
||||
<Switch x:Name="LoginSwitch" IsToggled="False" Toggled="Switch_Toggled" VerticalOptions="Center" Grid.Column="1"/>
|
||||
<Label Text="Login QR/manuell" VerticalOptions="Center" Grid.Column="0" />
|
||||
<Switch x:Name="LoginSwitch" IsToggled="False" Toggled="Switch_Toggled" VerticalOptions="Center"
|
||||
Grid.Column="1" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Label x:Name="ServerLabel" Text="{Binding Server}" IsVisible="{Binding Server, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
<Label x:Name="ServerLabel" Text="{Binding Server}"
|
||||
IsVisible="{Binding Server, Converter={StaticResource StringVisibilityConverter}}" />
|
||||
|
||||
<VerticalStackLayout x:Name="LoginQR" Margin="0,20,0,0">
|
||||
<Label Text="Login mit QR-Code" FontSize="32" HorizontalOptions="Start" />
|
||||
<Label x:Name="Message" Text="{Binding Message}" Margin="0,15" />
|
||||
|
||||
<Border HeightRequest="300" Padding="0">
|
||||
<zxing:CameraBarcodeReaderView
|
||||
x:Name="barcodeScannerView"
|
||||
BarcodesDetected="BarcodesDetected"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand"/>
|
||||
<zxing:CameraBarcodeReaderView
|
||||
x:Name="barcodeScannerView"
|
||||
BarcodesDetected="BarcodesDetected"
|
||||
HorizontalOptions="FillAndExpand"
|
||||
VerticalOptions="FillAndExpand" />
|
||||
</Border>
|
||||
</VerticalStackLayout>
|
||||
|
||||
@@ -62,7 +65,8 @@
|
||||
<Entry x:Name="UsernameEntry" Placeholder="Benutzername (Mailadresse)" Keyboard="Email" />
|
||||
<Entry x:Name="PasswordEntry" Placeholder="Passwort" IsPassword="True" />
|
||||
<Entry x:Name="ServerEntry" Placeholder="Server (gleich wie im Browser)" Keyboard="Url" />
|
||||
<Button Text="Login" Clicked="OnLoginButtonClicked" TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}" />
|
||||
<Button Text="Login" Clicked="OnLoginButtonClicked"
|
||||
TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}" />
|
||||
</VerticalStackLayout>
|
||||
</VerticalStackLayout>
|
||||
|
||||
|
||||
@@ -9,197 +9,195 @@ namespace Jugenddienst_Stunden.Views;
|
||||
/// Die Loginseite mit dem Barcodescanner
|
||||
/// </summary>
|
||||
public partial class LoginPage : ContentPage {
|
||||
|
||||
private DateTime _lastDetectionTime;
|
||||
private readonly TimeSpan _detectionInterval = TimeSpan.FromSeconds(5);
|
||||
private DateTime _lastDetectionTime;
|
||||
private readonly TimeSpan _detectionInterval = TimeSpan.FromSeconds(5);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public LoginPage() {
|
||||
InitializeComponent();
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public LoginPage() {
|
||||
InitializeComponent();
|
||||
|
||||
barcodeScannerView.Options = new BarcodeReaderOptions {
|
||||
Formats = BarcodeFormat.QrCode,
|
||||
AutoRotate = true,
|
||||
Multiple = false
|
||||
};
|
||||
barcodeScannerView.Options =
|
||||
new BarcodeReaderOptions { Formats = BarcodeFormat.QrCode, AutoRotate = true, Multiple = false };
|
||||
|
||||
//if (BindingContext is LoginViewModel vm) {
|
||||
// vm.AlertEvent += Vm_AlertEvent;
|
||||
// vm.InfoEvent += Vm_InfoEvent;
|
||||
// vm.MsgEvent += Vm_MsgEvent;
|
||||
//}
|
||||
//if (BindingContext is LoginViewModel vm) {
|
||||
// vm.AlertEvent += Vm_AlertEvent;
|
||||
// vm.InfoEvent += Vm_InfoEvent;
|
||||
// vm.MsgEvent += Vm_MsgEvent;
|
||||
//}
|
||||
|
||||
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
|
||||
bool sqr = true;
|
||||
bool sma = false;
|
||||
if (Preferences.Default.Get("logintype", "") == "manual") {
|
||||
sqr = false;
|
||||
sma = true;
|
||||
LoginSwitch.IsToggled = true;
|
||||
Message.IsVisible = false;
|
||||
} else {
|
||||
LoginSwitch.IsToggled = false;
|
||||
Message.IsVisible = true;
|
||||
}
|
||||
LoginQR.IsVisible = sqr;
|
||||
LoginManual.IsVisible = sma;
|
||||
}
|
||||
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
|
||||
bool sqr = true;
|
||||
bool sma = false;
|
||||
if (Preferences.Default.Get("logintype", "") == "manual") {
|
||||
sqr = false;
|
||||
sma = true;
|
||||
LoginSwitch.IsToggled = true;
|
||||
Message.IsVisible = false;
|
||||
} else {
|
||||
LoginSwitch.IsToggled = false;
|
||||
Message.IsVisible = true;
|
||||
}
|
||||
|
||||
LoginQR.IsVisible = sqr;
|
||||
LoginManual.IsVisible = sma;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Nach der Erkennung des Barcodes wird der Benutzer eingeloggt
|
||||
/// ZXing.Net.Maui.Controls 0.4.4
|
||||
/// </summary>
|
||||
private void BarcodesDetected(object sender, BarcodeDetectionEventArgs e) {
|
||||
/// <summary>
|
||||
/// Nach der Erkennung des Barcodes wird der Benutzer eingeloggt
|
||||
/// ZXing.Net.Maui.Controls 0.4.4
|
||||
/// </summary>
|
||||
private void BarcodesDetected(object sender, BarcodeDetectionEventArgs e) {
|
||||
var currentTime = DateTime.Now;
|
||||
if ((currentTime - _lastDetectionTime) > _detectionInterval) {
|
||||
_lastDetectionTime = currentTime;
|
||||
foreach (var barcode in e.Results) {
|
||||
if (GlobalVar.ApiKey != barcode.Value) {
|
||||
_ = MainThread.InvokeOnMainThreadAsync(async () => {
|
||||
//await DisplayAlert("Barcode erkannt", $"Barcode: {barcode.Format} - {barcode.Value}", "OK");
|
||||
|
||||
var currentTime = DateTime.Now;
|
||||
if ((currentTime - _lastDetectionTime) > _detectionInterval) {
|
||||
_lastDetectionTime = currentTime;
|
||||
foreach (var barcode in e.Results) {
|
||||
if (GlobalVar.ApiKey != barcode.Value) {
|
||||
_ = MainThread.InvokeOnMainThreadAsync(async () => {
|
||||
//await DisplayAlert("Barcode erkannt", $"Barcode: {barcode.Format} - {barcode.Value}", "OK");
|
||||
try {
|
||||
var tokendata = new TokenData(barcode.Value);
|
||||
GlobalVar.ApiUrl = tokendata.Url;
|
||||
User user = await HoursBase.LoadUser(barcode.Value);
|
||||
|
||||
try {
|
||||
var tokendata = new TokenData(barcode.Value);
|
||||
GlobalVar.ApiUrl = tokendata.Url;
|
||||
User user = await HoursBase.LoadUser(barcode.Value);
|
||||
GlobalVar.ApiKey = barcode.Value;
|
||||
GlobalVar.Name = user.Name;
|
||||
GlobalVar.Surname = user.Surname;
|
||||
GlobalVar.EmployeeId = user.Id;
|
||||
|
||||
GlobalVar.ApiKey = barcode.Value;
|
||||
GlobalVar.Name = user.Name;
|
||||
GlobalVar.Surname = user.Surname;
|
||||
GlobalVar.EmployeeId = user.Id;
|
||||
|
||||
Title = user.Name + " " + user.Surname;
|
||||
//Auf der Loginseite wird der Server als Info ohne Protokoll und ohne /appapi angezeigt
|
||||
ServerLabel.Text = "Server: " + tokendata.Url.Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
|
||||
Title = user.Name + " " + user.Surname;
|
||||
//Auf der Loginseite wird der Server als Info ohne Protokoll und ohne /appapi angezeigt
|
||||
ServerLabel.Text = "Server: " + tokendata.Url.Replace("/appapi", "").Replace("https://", "")
|
||||
.Replace("http://", "");
|
||||
|
||||
|
||||
await DisplayAlert("Login erfolgreich", user.Name + " " + user.Surname, "OK");
|
||||
if (Navigation.NavigationStack.Count > 1) {
|
||||
//Beim ersten Start ohne Login, wird man automatisch auf die Loginseite geleitet. Danach in der History zur<75>ck
|
||||
await Navigation.PopAsync();
|
||||
} else {
|
||||
//Beim manuellen Wechsel auf die Loginseite leiten wir nach erfolgreichem Login auf die Stunden<65>bersicht
|
||||
await Shell.Current.GoToAsync($"//StundenPage");
|
||||
}
|
||||
await DisplayAlert("Login erfolgreich", user.Name + " " + user.Surname, "OK");
|
||||
if (Navigation.NavigationStack.Count > 1) {
|
||||
//Beim ersten Start ohne Login, wird man automatisch auf die Loginseite geleitet. Danach in der History zur<75>ck
|
||||
await Navigation.PopAsync();
|
||||
} else {
|
||||
//Beim manuellen Wechsel auf die Loginseite leiten wir nach erfolgreichem Login auf die Stunden<65>bersicht
|
||||
await Shell.Current.GoToAsync($"//StundenPage");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
await DisplayAlert("Fehler", e.Message, "OK");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
MainThread.InvokeOnMainThreadAsync(() => {
|
||||
DisplayAlert("Bereits eingeloggt",
|
||||
Preferences.Default.Get("name", "") + " " + Preferences.Default.Get("surname", ""),
|
||||
"OK");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
await DisplayAlert("Fehler", e.Message, "OK");
|
||||
}
|
||||
protected override void OnDisappearing() {
|
||||
base.OnDisappearing();
|
||||
|
||||
});
|
||||
} else {
|
||||
MainThread.InvokeOnMainThreadAsync(() => {
|
||||
DisplayAlert("Bereits eingeloggt",
|
||||
Preferences.Default.Get("name", "") + " " + Preferences.Default.Get("surname", ""),
|
||||
"OK");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
barcodeScannerView.CameraLocation = CameraLocation.Front;
|
||||
barcodeScannerView.IsDetecting = false;
|
||||
}
|
||||
|
||||
}
|
||||
protected override void OnAppearing() {
|
||||
base.OnAppearing();
|
||||
|
||||
protected override void OnDisappearing() {
|
||||
base.OnDisappearing();
|
||||
barcodeScannerView.IsDetecting = true;
|
||||
barcodeScannerView.CameraLocation = CameraLocation.Rear;
|
||||
}
|
||||
|
||||
barcodeScannerView.CameraLocation = CameraLocation.Front;
|
||||
barcodeScannerView.IsDetecting = false;
|
||||
}
|
||||
public bool IsCameraAvailable() {
|
||||
var status = Permissions.CheckStatusAsync<Permissions.Camera>().Result;
|
||||
if (status != PermissionStatus.Granted) {
|
||||
status = Permissions.RequestAsync<Permissions.Camera>().Result;
|
||||
}
|
||||
|
||||
protected override void OnAppearing() {
|
||||
base.OnAppearing();
|
||||
return status != PermissionStatus.Granted;
|
||||
}
|
||||
|
||||
barcodeScannerView.IsDetecting = true;
|
||||
barcodeScannerView.CameraLocation = CameraLocation.Rear;
|
||||
}
|
||||
|
||||
public bool IsCameraAvailable() {
|
||||
var status = Permissions.CheckStatusAsync<Permissions.Camera>().Result;
|
||||
if (status != PermissionStatus.Granted) {
|
||||
status = Permissions.RequestAsync<Permissions.Camera>().Result;
|
||||
}
|
||||
return status != PermissionStatus.Granted;
|
||||
}
|
||||
|
||||
private async void OnLoginButtonClicked(object sender, EventArgs e) {
|
||||
var username = UsernameEntry.Text;
|
||||
var password = PasswordEntry.Text;
|
||||
var server = ServerEntry.Text;
|
||||
private async void OnLoginButtonClicked(object sender, EventArgs e) {
|
||||
var username = UsernameEntry.Text;
|
||||
var password = PasswordEntry.Text;
|
||||
var server = ServerEntry.Text;
|
||||
|
||||
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(server)) {
|
||||
await DisplayAlert("Fehler", "Bitte alle Felder ausf<73>llen", "OK");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
Uri uri = new Uri(InputUrlWithSchema(server));
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || string.IsNullOrEmpty(server)) {
|
||||
await DisplayAlert("Fehler", "Bitte alle Felder ausf<73>llen", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
Types.User response = await BaseFunc.AuthUserPass(username, password, uri.Scheme + "://" + uri.Authority + "/appapi");
|
||||
try {
|
||||
Uri uri = new Uri(InputUrlWithSchema(server));
|
||||
|
||||
GlobalVar.ApiKey = response.Token;
|
||||
GlobalVar.Name = response.Name;
|
||||
GlobalVar.Surname = response.Surname;
|
||||
GlobalVar.EmployeeId = response.Id;
|
||||
GlobalVar.ApiUrl = uri.Scheme + "://" + uri.Authority + "/appapi";
|
||||
Types.User response =
|
||||
await BaseFunc.AuthUserPass(username, password, uri.Scheme + "://" + uri.Authority + "/appapi");
|
||||
|
||||
Title = response.Name + " " + response.Surname;
|
||||
//ServerLabel.Text = "Server: " + server.Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
|
||||
ServerLabel.Text = "Server: " + uri.Authority;
|
||||
GlobalVar.ApiKey = response.Token;
|
||||
GlobalVar.Name = response.Name;
|
||||
GlobalVar.Surname = response.Surname;
|
||||
GlobalVar.EmployeeId = response.Id;
|
||||
GlobalVar.ApiUrl = uri.Scheme + "://" + uri.Authority + "/appapi";
|
||||
|
||||
await DisplayAlert("Login erfolgreich", response.Name + " " + response.Surname, "OK");
|
||||
if (Navigation.NavigationStack.Count > 1)
|
||||
await Navigation.PopAsync();
|
||||
else {
|
||||
await Shell.Current.GoToAsync($"//StundenPage");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
await DisplayAlert("Fehler", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
Title = response.Name + " " + response.Surname;
|
||||
//ServerLabel.Text = "Server: " + server.Replace("/appapi", "").Replace("https://", "").Replace("http://", "");
|
||||
ServerLabel.Text = "Server: " + uri.Authority;
|
||||
|
||||
/// <summary>
|
||||
/// Aus einer URL ohne Schema eine URL mit Schema machen
|
||||
/// </summary>
|
||||
private static string InputUrlWithSchema(string url) {
|
||||
if (!url.StartsWith("http://") && !url.StartsWith("https://")) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
if (url.StartsWith("http://")) {
|
||||
url = url.Replace("http://", "https://");
|
||||
}
|
||||
return url;
|
||||
}
|
||||
await DisplayAlert("Login erfolgreich", response.Name + " " + response.Surname, "OK");
|
||||
if (Navigation.NavigationStack.Count > 1)
|
||||
await Navigation.PopAsync();
|
||||
else {
|
||||
await Shell.Current.GoToAsync($"//StundenPage");
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
await DisplayAlert("Fehler", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
|
||||
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
|
||||
private void Switch_Toggled(object sender, ToggledEventArgs e) {
|
||||
var switcher = (Switch)sender;
|
||||
/// <summary>
|
||||
/// Aus einer URL ohne Schema eine URL mit Schema machen
|
||||
/// </summary>
|
||||
private static string InputUrlWithSchema(string url) {
|
||||
if (!url.StartsWith("http://") && !url.StartsWith("https://")) {
|
||||
url = "https://" + url;
|
||||
}
|
||||
|
||||
if (switcher.IsToggled) {
|
||||
LoginQR.IsVisible = false;
|
||||
LoginManual.IsVisible = true;
|
||||
Message.IsVisible = false;
|
||||
Preferences.Default.Set("logintype", "manual");
|
||||
} else {
|
||||
LoginQR.IsVisible = true;
|
||||
LoginManual.IsVisible = false;
|
||||
Message.IsVisible = true;
|
||||
Preferences.Default.Set("logintype", "qr");
|
||||
}
|
||||
if (url.StartsWith("http://")) {
|
||||
url = url.Replace("http://", "https://");
|
||||
}
|
||||
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
//private void Vm_AlertEvent(object? sender, string e) {
|
||||
// DisplayAlert("Fehler:", e, "OK");
|
||||
//}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// DisplayAlert("Information:", e, "OK");
|
||||
//}
|
||||
//private async Task Vm_MsgEvent(string title, string message) {
|
||||
// await DisplayAlert(title, message, "OK");
|
||||
//}
|
||||
}
|
||||
//Zwischen manuellem und automatischem Login (mit QR-Code) umschalten und die Schalterstellung merken
|
||||
private void Switch_Toggled(object sender, ToggledEventArgs e) {
|
||||
var switcher = (Switch)sender;
|
||||
|
||||
if (switcher.IsToggled) {
|
||||
LoginQR.IsVisible = false;
|
||||
LoginManual.IsVisible = true;
|
||||
Message.IsVisible = false;
|
||||
Preferences.Default.Set("logintype", "manual");
|
||||
} else {
|
||||
LoginQR.IsVisible = true;
|
||||
LoginManual.IsVisible = false;
|
||||
Message.IsVisible = true;
|
||||
Preferences.Default.Set("logintype", "qr");
|
||||
}
|
||||
}
|
||||
|
||||
//private void Vm_AlertEvent(object? sender, string e) {
|
||||
// DisplayAlert("Fehler:", e, "OK");
|
||||
//}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// DisplayAlert("Information:", e, "OK");
|
||||
//}
|
||||
//private async Task Vm_MsgEvent(string title, string message) {
|
||||
// await DisplayAlert(title, message, "OK");
|
||||
//}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
@@ -11,8 +12,8 @@
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.Behaviors>
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
StatusBarStyle="LightContent" />
|
||||
</ContentPage.Behaviors>
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ namespace Jugenddienst_Stunden.Views;
|
||||
/// Einzelne Notiz
|
||||
/// </summary>
|
||||
public partial class NotePage : ContentPage {
|
||||
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
@@ -19,8 +20,8 @@
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.Behaviors>
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
StatusBarStyle="LightContent" />
|
||||
</ContentPage.Behaviors>
|
||||
|
||||
@@ -31,20 +32,26 @@
|
||||
<Border>
|
||||
<Border.Padding>
|
||||
<OnPlatform x:TypeArguments="Thickness" Default="0,15,10,0">
|
||||
<On Platform="Android" Value="0,4,10,8"/>
|
||||
<On Platform="WPF" Value="0,15,10,0"/>
|
||||
<On Platform="Android" Value="0,4,10,8" />
|
||||
<On Platform="WPF" Value="0,15,10,0" />
|
||||
</OnPlatform>
|
||||
</Border.Padding>
|
||||
|
||||
<FlexLayout Direction="Row" AlignItems="Start" Wrap="Wrap" JustifyContent="SpaceBetween">
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="Beginn" VerticalTextAlignment="Center" HorizontalTextAlignment="End" MinimumWidthRequest="60"></Label>
|
||||
<TimePicker x:Name="TimeBegin" HorizontalOptions="Center" Format="HH:mm" MinimumWidthRequest="80" Time="{Binding DayTime.TimeSpanVon}" />
|
||||
<Label Text="Beginn" VerticalTextAlignment="Center" HorizontalTextAlignment="End"
|
||||
MinimumWidthRequest="60">
|
||||
</Label>
|
||||
<TimePicker x:Name="TimeBegin" HorizontalOptions="Center" Format="HH:mm" MinimumWidthRequest="80"
|
||||
Time="{Binding DayTime.TimeSpanVon}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<HorizontalStackLayout Spacing="10">
|
||||
<Label Text="Ende" VerticalTextAlignment="Center" HorizontalTextAlignment="End" MinimumWidthRequest="60"></Label>
|
||||
<TimePicker x:Name="TimeEnd" Format="HH:mm" MinimumWidthRequest="80" Time="{Binding DayTime.TimeSpanBis}" />
|
||||
<Label Text="Ende" VerticalTextAlignment="Center" HorizontalTextAlignment="End"
|
||||
MinimumWidthRequest="60">
|
||||
</Label>
|
||||
<TimePicker x:Name="TimeEnd" Format="HH:mm" MinimumWidthRequest="80"
|
||||
Time="{Binding DayTime.TimeSpanBis}" />
|
||||
</HorizontalStackLayout>
|
||||
</FlexLayout>
|
||||
</Border>
|
||||
@@ -52,8 +59,8 @@
|
||||
<Border>
|
||||
<Border.Padding>
|
||||
<OnPlatform x:TypeArguments="Thickness" Default="5">
|
||||
<On Platform="Android" Value="5,4,5,8"/>
|
||||
<On Platform="WPF" Value="5"/>
|
||||
<On Platform="Android" Value="5,4,5,8" />
|
||||
<On Platform="WPF" Value="5" />
|
||||
</OnPlatform>
|
||||
</Border.Padding>
|
||||
<!--<Grid ColumnDefinitions="*,*,*">
|
||||
@@ -65,35 +72,45 @@
|
||||
</Picker>
|
||||
</Grid>-->
|
||||
<HorizontalStackLayout>
|
||||
<Picker x:Name="pick_gemeinde" Title="Gemeinde" ItemsSource="{Binding OptionsGemeinde}" SelectedItem="{Binding DayTime.GemeindeAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" IsVisible="{Binding GemeindeAktivSet}">
|
||||
<Picker x:Name="pick_gemeinde" Title="Gemeinde" ItemsSource="{Binding OptionsGemeinde}"
|
||||
SelectedItem="{Binding DayTime.GemeindeAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}"
|
||||
IsVisible="{Binding GemeindeAktivSet}">
|
||||
</Picker>
|
||||
<Picker x:Name="pick_projekt" Title="Projekt" ItemsSource="{Binding OptionsProjekt}" SelectedItem="{Binding DayTime.ProjektAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" IsVisible="{Binding ProjektAktivSet}">
|
||||
<Picker x:Name="pick_projekt" Title="Projekt" ItemsSource="{Binding OptionsProjekt}"
|
||||
SelectedItem="{Binding DayTime.ProjektAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}"
|
||||
IsVisible="{Binding ProjektAktivSet}">
|
||||
</Picker>
|
||||
<Picker x:Name="pick_freistellung" Title="Freistellung" ItemsSource="{Binding OptionsFreistellung}" SelectedItem="{Binding DayTime.FreistellungAktiv, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" IsEnabled="{Binding FreistellungEnabled}">
|
||||
<Picker x:Name="pick_freistellung" Title="Freistellung" ItemsSource="{Binding OptionsFreistellung}"
|
||||
SelectedItem="{Binding DayTime.FreistellungAktiv, Mode=TwoWay}"
|
||||
ItemDisplayBinding="{Binding Name}" IsEnabled="{Binding FreistellungEnabled}">
|
||||
</Picker>
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
|
||||
<Editor Placeholder="Beschreibung" Text="{Binding DayTime.Description}" MinimumHeightRequest="40" AutoSize="TextChanges" FontSize="18" />
|
||||
<Editor Placeholder="Beschreibung" Text="{Binding DayTime.Description}" MinimumHeightRequest="40"
|
||||
AutoSize="TextChanges" FontSize="18" />
|
||||
|
||||
<Grid ColumnDefinitions="*,*" ColumnSpacing="4">
|
||||
<Button Grid.Column="1" Text="Speichern"
|
||||
TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}"
|
||||
Command="{Binding SaveCommand}" />
|
||||
<Button Grid.Column="0" Text="Löschen"
|
||||
Command="{Binding DeleteConfirmCommand}"
|
||||
IsEnabled="{Binding DayTime.Id, Converter={StaticResource IntBoolConverter}}"
|
||||
<Button Grid.Column="0" Text="Löschen"
|
||||
Command="{Binding DeleteConfirmCommand}"
|
||||
IsEnabled="{Binding DayTime.Id, Converter={StaticResource IntBoolConverter}}"
|
||||
IsVisible="{Binding FreistellungEnabled}"
|
||||
BackgroundColor="{StaticResource Gray500}"
|
||||
TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}"/>
|
||||
TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}" />
|
||||
</Grid>
|
||||
|
||||
|
||||
<BoxView HeightRequest="1" Margin="3,10" />
|
||||
|
||||
<Label Text="Noch keine Einträge vorhanden" IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}, ConverterParameter=Invert}" Margin="6,0,0,0"/>
|
||||
<Label Text="Noch keine Einträge vorhanden"
|
||||
IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}, ConverterParameter=Invert}"
|
||||
Margin="6,0,0,0" />
|
||||
|
||||
<StackLayout Margin="6,0,0,0" IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}}">
|
||||
<StackLayout Margin="6,0,0,0"
|
||||
IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}}">
|
||||
<Label>
|
||||
<Label.FormattedText>
|
||||
<FormattedString>
|
||||
@@ -105,13 +122,13 @@
|
||||
</StackLayout>
|
||||
|
||||
<ScrollView IsVisible="{Binding DayTimes, Converter={StaticResource CollectionVisibilityConverter}}">
|
||||
<CollectionView
|
||||
ItemsSource="{Binding DayTimes}"
|
||||
x:Name="stundeItems" Margin="0"
|
||||
HeightRequest="350"
|
||||
SelectionMode="Single"
|
||||
SelectionChangedCommand="{Binding SelectEntryCommand}"
|
||||
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
|
||||
<CollectionView
|
||||
ItemsSource="{Binding DayTimes}"
|
||||
x:Name="stundeItems" Margin="0"
|
||||
HeightRequest="350"
|
||||
SelectionMode="Single"
|
||||
SelectionChangedCommand="{Binding SelectEntryCommand}"
|
||||
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}">
|
||||
|
||||
<CollectionView.ItemsLayout>
|
||||
<LinearItemsLayout Orientation="Vertical" ItemSpacing="0" />
|
||||
@@ -129,15 +146,18 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<HorizontalStackLayout Grid.Row="0" Grid.Column="0">
|
||||
<Label Grid.Column="0" Text="{Binding Begin}"/>
|
||||
<Label Text="bis" Padding="5,0,5,0"/>
|
||||
<Label Text="{Binding End}"/>
|
||||
<Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}"/>
|
||||
<Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}"/>
|
||||
<Label Text="{Binding FreistellungAktiv.Name}" Margin="10,0,0,0"/>
|
||||
<Label Grid.Column="0" Text="{Binding Begin}" />
|
||||
<Label Text="bis" Padding="5,0,5,0" />
|
||||
<Label Text="{Binding End}" />
|
||||
<Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0"
|
||||
IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}" />
|
||||
<Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0"
|
||||
IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}" />
|
||||
<Label Text="{Binding FreistellungAktiv.Name}" Margin="10,0,0,0" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Description}" Padding="0,0,0,15"/>
|
||||
<Label Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Description}"
|
||||
Padding="0,0,0,15" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
|
||||
@@ -10,45 +10,43 @@ namespace Jugenddienst_Stunden.Views;
|
||||
/// Einzelner Stundeneintrag
|
||||
/// </summary>
|
||||
public partial class StundePage : ContentPage {
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public StundePage() {
|
||||
InitializeComponent();
|
||||
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public StundePage() {
|
||||
InitializeComponent();
|
||||
if (BindingContext is StundeViewModel vm) {
|
||||
vm.AlertEvent += Vm_AlertEvent;
|
||||
vm.InfoEvent += Vm_InfoEvent;
|
||||
vm.ConfirmEvent += ShowConfirm;
|
||||
}
|
||||
}
|
||||
|
||||
if (BindingContext is StundeViewModel vm) {
|
||||
vm.AlertEvent += Vm_AlertEvent;
|
||||
vm.InfoEvent += Vm_InfoEvent;
|
||||
vm.ConfirmEvent += ShowConfirm;
|
||||
}
|
||||
}
|
||||
private void Vm_AlertEvent(object? sender, string e) {
|
||||
DisplayAlert("Fehler:", e, "OK");
|
||||
}
|
||||
|
||||
private void Vm_AlertEvent(object? sender, string e) {
|
||||
DisplayAlert("Fehler:", e, "OK");
|
||||
}
|
||||
private async Task<bool> ShowConfirm(string title, string message) {
|
||||
return await DisplayAlert(title, message, "Passt!", "Na, nor decht nit.");
|
||||
}
|
||||
|
||||
private async Task<bool> ShowConfirm(string title, string message) {
|
||||
return await DisplayAlert(title, message, "Passt!", "Na, nor decht nit.");
|
||||
}
|
||||
private void Vm_InfoEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => {
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
ToastDuration duration = ToastDuration.Short;
|
||||
double fontSize = 20;
|
||||
var toast = Toast.Make(e, duration, fontSize);
|
||||
await toast.Show(cts.Token);
|
||||
});
|
||||
}
|
||||
|
||||
private void Vm_InfoEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => {
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
ToastDuration duration = ToastDuration.Short;
|
||||
double fontSize = 20;
|
||||
var toast = Toast.Make(e, duration, fontSize);
|
||||
await toast.Show(cts.Token);
|
||||
});
|
||||
}
|
||||
|
||||
//private async Task<bool> ShowConfirm(string title, string message, string ok, string not_ok) {
|
||||
// return await DisplayAlert(title, message, ok, not_ok);
|
||||
//}
|
||||
|
||||
//private async void ShowConfirm(object? sender, ConfirmEventArgs e) {
|
||||
// bool result = await DisplayAlert(e.Title, e.Message, e.Ok, e.NotOk);
|
||||
// e.Result = result;
|
||||
//}
|
||||
//private async Task<bool> ShowConfirm(string title, string message, string ok, string not_ok) {
|
||||
// return await DisplayAlert(title, message, ok, not_ok);
|
||||
//}
|
||||
|
||||
//private async void ShowConfirm(object? sender, ConfirmEventArgs e) {
|
||||
// bool result = await DisplayAlert(e.Title, e.Message, e.Ok, e.NotOk);
|
||||
// e.Result = result;
|
||||
//}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
|
||||
@@ -8,52 +9,51 @@
|
||||
x:Class="Jugenddienst_Stunden.Views.StundenPage"
|
||||
Title="{Binding Title}">
|
||||
|
||||
<ContentPage.BindingContext>
|
||||
<models:StundenViewModel />
|
||||
</ContentPage.BindingContext>
|
||||
|
||||
<ContentPage.Resources>
|
||||
<ResourceDictionary>
|
||||
<conv:SecondsTimeConverter x:Key="secToTime" />
|
||||
<FontImageSource x:Key="ToolbarIcon"
|
||||
Glyph="+"
|
||||
Size="22"
|
||||
Color="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}"/>
|
||||
Glyph="+"
|
||||
Size="22"
|
||||
Color="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
</ResourceDictionary>
|
||||
</ContentPage.Resources>
|
||||
|
||||
<ContentPage.Behaviors>
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
<toolkit:StatusBarBehavior
|
||||
StatusBarColor="{AppThemeBinding Dark={StaticResource OffBlack}, Light={StaticResource Primary}}"
|
||||
StatusBarStyle="LightContent" />
|
||||
</ContentPage.Behaviors>
|
||||
|
||||
<ContentPage.ToolbarItems>
|
||||
<!--<ToolbarItem Text="Lade Liste" Command="{Binding RefreshListCommand}"/>-->
|
||||
<ToolbarItem Text="Neuer Eintrag" IconImageSource="{StaticResource ToolbarIcon}" Command="{Binding NewEntryCommand}" />
|
||||
<ToolbarItem Text="Neuer Eintrag" IconImageSource="{StaticResource ToolbarIcon}"
|
||||
Command="{Binding NewEntryCommand}" />
|
||||
</ContentPage.ToolbarItems>
|
||||
|
||||
<RefreshView x:Name="MyRefreshView" Command="{Binding RefreshCommand}" IsRefreshing="{Binding IsRefreshing}" Margin="10" Padding="10">
|
||||
<RefreshView x:Name="MyRefreshView" Command="{Binding RefreshCommand}" IsRefreshing="{Binding IsRefreshing}"
|
||||
Margin="10" Padding="10">
|
||||
<Grid RowDefinitions="50,*,Auto,80">
|
||||
<!--<VerticalStackLayout Spacing="10" Margin="10">-->
|
||||
<!--<VerticalStackLayout Spacing="10" Margin="10">-->
|
||||
|
||||
<Grid RowDefinitions="Auto" ColumnDefinitions="Auto,*" HeightRequest="50" Grid.Row="0">
|
||||
<DatePicker Grid.Column="0" MinimumDate="{Binding MinimumDate}"
|
||||
MaximumDate="{Binding MaximumDate}"
|
||||
Date="{Binding DateToday}" Format="dddd, d. MMMM yyyy" />
|
||||
MaximumDate="{Binding MaximumDate}"
|
||||
Date="{Binding DateToday}" Format="dddd, d. MMMM yyyy" />
|
||||
<Border Grid.Column="1" Margin="15,0,0,0" Padding="15,0,0,0" ToolTipProperties.Text="Tagessumme">
|
||||
<HorizontalStackLayout>
|
||||
<Label Text="{Binding DayTotal,StringFormat='{}{0:HH:mm}'}" VerticalOptions="Center"></Label>
|
||||
<Label Text="/" VerticalOptions="Center" Margin="3,0"/>
|
||||
<Label Text="/" VerticalOptions="Center" Margin="3,0" />
|
||||
<Label Text="{Binding Sollstunden,StringFormat='{}{0:HH:mm}'}" VerticalOptions="Center"></Label>
|
||||
</HorizontalStackLayout>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<CollectionView
|
||||
ItemsSource="{Binding DayTimes}"
|
||||
<CollectionView
|
||||
ItemsSource="{Binding DayTimes}"
|
||||
x:Name="stundeItems" Margin="0,0,0,20"
|
||||
|
||||
|
||||
SelectionMode="Single"
|
||||
SelectionChangedCommand="{Binding SelectEntryCommand}"
|
||||
SelectionChangedCommandParameter="{Binding Source={RelativeSource Self}, Path=SelectedItem}"
|
||||
@@ -72,27 +72,29 @@
|
||||
<VisualStateGroup Name="CommonStates">
|
||||
<VisualState Name="Normal">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource TransparentColor}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource White}, Dark={StaticResource TransparentColor}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
<VisualState Name="Selected">
|
||||
<VisualState.Setters>
|
||||
<Setter Property="BackgroundColor" Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
<Setter Property="BackgroundColor"
|
||||
Value="{AppThemeBinding Light={StaticResource Primary}, Dark={StaticResource PrimaryDark}}" />
|
||||
</VisualState.Setters>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
|
||||
|
||||
|
||||
<HorizontalStackLayout>
|
||||
<HorizontalStackLayout.Triggers>
|
||||
<DataTrigger TargetType="HorizontalStackLayout" Binding="{Binding Approved}" Value="True">
|
||||
<DataTrigger TargetType="HorizontalStackLayout" Binding="{Binding Approved}"
|
||||
Value="True">
|
||||
<Setter Property="BackgroundColor" Value="LightCoral" />
|
||||
<Setter Property="Padding" Value="4"/>
|
||||
<Setter Property="Padding" Value="4" />
|
||||
</DataTrigger>
|
||||
</HorizontalStackLayout.Triggers>
|
||||
<Label Text="{Binding Day, StringFormat='{0:dddd, dd. MMMM}'}"/>
|
||||
<Label Text="{Binding Day, StringFormat='{0:dddd, dd. MMMM}'}" />
|
||||
<Label Text="von" Padding="5,0,5,0" />
|
||||
<Label Text="{Binding Begin}" />
|
||||
<Label Text="bis" Padding="5,0,5,0" />
|
||||
@@ -101,27 +103,30 @@
|
||||
|
||||
<HorizontalStackLayout HorizontalOptions="FillAndExpand">
|
||||
<HorizontalStackLayout.Triggers>
|
||||
<DataTrigger TargetType="HorizontalStackLayout" Binding="{Binding Approved}" Value="True">
|
||||
<DataTrigger TargetType="HorizontalStackLayout" Binding="{Binding Approved}"
|
||||
Value="True">
|
||||
<Setter Property="BackgroundColor" Value="LightCoral" />
|
||||
<Setter Property="Padding" Value="4"/>
|
||||
<Setter Property="Padding" Value="4" />
|
||||
</DataTrigger>
|
||||
</HorizontalStackLayout.Triggers>
|
||||
<Label Text="{Binding GemeindeAktiv.Name}" Margin="0,0,10,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}" />
|
||||
<Label Text="{Binding ProjektAktiv.Name}" Margin="0,0,10,0" IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}" />
|
||||
<Label Text="{Binding GemeindeAktiv.Name}" Margin="0,0,10,0"
|
||||
IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.GemeindeAktivSet}" />
|
||||
<Label Text="{Binding ProjektAktiv.Name}" Margin="0,0,10,0"
|
||||
IsVisible="{Binding Source={RelativeSource AncestorType={x:Type ContentPage}}, Path=BindingContext.ProjektAktivSet}" />
|
||||
<Label Text="{Binding FreistellungAktiv.Name}" IsVisible="{Binding Approved}" />
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<Label Text="{Binding Description}" Padding="0,0,0,15"/>
|
||||
<Label Text="{Binding Description}" Padding="0,0,0,15" />
|
||||
</VerticalStackLayout>
|
||||
</DataTemplate>
|
||||
</CollectionView.ItemTemplate>
|
||||
|
||||
</CollectionView>
|
||||
|
||||
|
||||
<!--<BoxView HeightRequest="1" Grid.Row="2" Margin="0,5,0,15" />-->
|
||||
<Button Text="{Binding LoadOverview}"
|
||||
Grid.Row="2"
|
||||
Command="{Binding LoadDataCommand}"
|
||||
<Button Text="{Binding LoadOverview}"
|
||||
Grid.Row="2"
|
||||
Command="{Binding LoadDataCommand}"
|
||||
TextColor="{AppThemeBinding Dark={StaticResource White}, Light={StaticResource White}}" />
|
||||
<Border Padding="2" Grid.Row="3" Margin="0,10,0,0">
|
||||
<Grid RowDefinitions="Auto,Auto,Auto,Auto,Auto,*" ColumnDefinitions="Auto,Auto,*,Auto" Margin="10,2">
|
||||
@@ -133,15 +138,23 @@
|
||||
<Label Grid.Row="1" Grid.Column="2" Text="Zeitausgleich:" Margin="15,0,0,0" />
|
||||
<Label Grid.Row="2" Grid.Column="2" Text="Resturlaub:" Margin="15,0,0,0" />
|
||||
|
||||
<Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding Nominal, Converter={StaticResource secToTime}}" ToolTipProperties.Text="Sollstunden" />
|
||||
<Label Grid.Row="1" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding ZeitCalculated, Converter={StaticResource secToTime}}" ToolTipProperties.Text="Geleistete Stunden" />
|
||||
<Label Grid.Row="2" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding OvertimeMonth, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="0" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding Overtime, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="1" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding Zeitausgleich, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="2" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0" Text="{Binding Holiday, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding Nominal, Converter={StaticResource secToTime}}"
|
||||
ToolTipProperties.Text="Sollstunden" />
|
||||
<Label Grid.Row="1" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding ZeitCalculated, Converter={StaticResource secToTime}}"
|
||||
ToolTipProperties.Text="Geleistete Stunden" />
|
||||
<Label Grid.Row="2" Grid.Column="1" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding OvertimeMonth, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="0" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding Overtime, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="1" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding Zeitausgleich, Converter={StaticResource secToTime}}" />
|
||||
<Label Grid.Row="2" Grid.Column="3" HorizontalTextAlignment="End" Padding="0,0,5,0"
|
||||
Text="{Binding Holiday, Converter={StaticResource secToTime}}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
|
||||
</Grid>
|
||||
<!--</VerticalStackLayout>-->
|
||||
|
||||
|
||||
@@ -9,60 +9,80 @@ namespace Jugenddienst_Stunden.Views;
|
||||
/// Code-Behind f<>r die Stunden-<2D>bersicht
|
||||
/// </summary>
|
||||
public partial class StundenPage : ContentPage {
|
||||
/// <summary>
|
||||
/// CTOR (f<>r Shell/XAML DataTemplate erforderlich)
|
||||
/// </summary>
|
||||
public StundenPage() : this(
|
||||
(Application.Current?.Handler?.MauiContext?.Services
|
||||
?? throw new InvalidOperationException("DI container ist nicht verf<72>gbar."))
|
||||
.GetRequiredService<StundenViewModel>()) {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// CTOR
|
||||
/// </summary>
|
||||
public StundenPage() {
|
||||
InitializeComponent();
|
||||
/// <summary>
|
||||
/// CTOR (DI)
|
||||
/// </summary>
|
||||
public StundenPage(StundenViewModel vm) {
|
||||
InitializeComponent();
|
||||
BindingContext = vm;
|
||||
|
||||
if (BindingContext is StundenViewModel vm) {
|
||||
vm.AlertEvent += Vm_AlertEvent;
|
||||
vm.InfoEvent += Vm_InfoEvent;
|
||||
}
|
||||
if (!CheckLogin()) {
|
||||
NavigateToTargetPage();
|
||||
}
|
||||
vm.AlertEvent += Vm_AlertEvent;
|
||||
vm.InfoEvent += Vm_InfoEvent;
|
||||
|
||||
}
|
||||
// Navigation NICHT im CTOR ausf<73>hren (Shell/Navigation-Stack ist hier oft noch nicht ?ready?)
|
||||
// if (!CheckLogin()) {
|
||||
// NavigateToTargetPage();
|
||||
// }
|
||||
}
|
||||
|
||||
private void Vm_AlertEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => {
|
||||
await DisplayAlert("Fehler:", e, "OK");
|
||||
});
|
||||
}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// DisplayAlert("Information:", e, "OK");
|
||||
//}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// MainThread.BeginInvokeOnMainThread(async () => {
|
||||
// await DisplayAlert("Information:", e, "OK");
|
||||
// });
|
||||
//}
|
||||
private void Vm_InfoEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => {
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
ToastDuration duration = ToastDuration.Short;
|
||||
double fontSize = 16;
|
||||
var toast = Toast.Make(e, duration, fontSize);
|
||||
await toast.Show(cts.Token);
|
||||
});
|
||||
}
|
||||
private void Vm_AlertEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => { await DisplayAlert("Fehler:", e, "OK"); });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Beim Laden der Seite den Titel setzen
|
||||
/// </summary>
|
||||
protected override void OnAppearing() {
|
||||
base.OnAppearing();
|
||||
Title = Preferences.Default.Get("name", "Nicht") + " " + Preferences.Default.Get("surname", "eingeloggt");
|
||||
}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// DisplayAlert("Information:", e, "OK");
|
||||
//}
|
||||
//private void Vm_InfoEvent(object? sender, string e) {
|
||||
// MainThread.BeginInvokeOnMainThread(async () => {
|
||||
// await DisplayAlert("Information:", e, "OK");
|
||||
// });
|
||||
//}
|
||||
private void Vm_InfoEvent(object? sender, string e) {
|
||||
MainThread.BeginInvokeOnMainThread(async () => {
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
ToastDuration duration = ToastDuration.Short;
|
||||
double fontSize = 16;
|
||||
var toast = Toast.Make(e, duration, fontSize);
|
||||
await toast.Show(cts.Token);
|
||||
});
|
||||
}
|
||||
|
||||
private bool CheckLogin() {
|
||||
return Preferences.Default.Get("apiKey", "") != "";
|
||||
}
|
||||
/// <summary>
|
||||
/// Beim Laden der Seite den Titel setzen
|
||||
/// </summary>
|
||||
protected override async void OnAppearing() {
|
||||
base.OnAppearing();
|
||||
Title = Preferences.Default.Get("name", "Nicht") + " " + Preferences.Default.Get("surname", "eingeloggt");
|
||||
|
||||
private async void NavigateToTargetPage() {
|
||||
await Navigation.PushAsync(new LoginPage());
|
||||
}
|
||||
if (!CheckLogin()) {
|
||||
try {
|
||||
await NavigateToTargetPage();
|
||||
} catch (Exception ex) {
|
||||
await DisplayAlert("Fehler:", ex.Message, "OK");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool CheckLogin() {
|
||||
return Preferences.Default.Get("apiKey", "") != "";
|
||||
}
|
||||
|
||||
// private async void NavigateToTargetPage() {
|
||||
// await Navigation.PushAsync(new LoginPage());
|
||||
// }
|
||||
|
||||
private Task NavigateToTargetPage() {
|
||||
// Shell-Navigation statt Navigation.PushAsync
|
||||
// Voraussetzung: LoginPage-Route ist in AppShell registriert (Routing.RegisterRoute(...))
|
||||
return Shell.Current.GoToAsync(nameof(Views.LoginPage));
|
||||
}
|
||||
}
|
||||