Settings geht noch nicht ganz

This commit is contained in:
2024-10-18 18:42:58 +02:00
parent b27e8ffcce
commit fbd650c174
8 changed files with 623 additions and 481 deletions

View File

@@ -37,11 +37,6 @@ class Auth {
/// <summary> /// <summary>
/// Stundeneintrag speichern /// Stundeneintrag speichern
/// </summary> /// </summary>
/// <param name="url"></param>
/// <param name="token"></param>
/// <param name="item"></param>
/// <param name="isNewItem"></param>
/// <returns></returns>
public static async Task SaveItemAsync(string url, string token, DayTime item, bool isNewItem = false) { public static async Task SaveItemAsync(string url, string token, DayTime item, bool isNewItem = false) {
//using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) { //using (HttpClient client = new HttpClient() { Timeout = TimeSpan.FromSeconds(15) }) {

View File

@@ -10,7 +10,6 @@ internal class Stunde : ObservableObject {
public DateTime Date { get; set; } public DateTime Date { get; set; }
//Default-Werte zum Testen
public static string apiKey = Preferences.Default.Get("apiKey", ""); public static string apiKey = Preferences.Default.Get("apiKey", "");
public static int EmployeeId = Preferences.Default.Get("employeeId", 0); public static int EmployeeId = Preferences.Default.Get("employeeId", 0);
public static string name = Preferences.Default.Get("name", ""); public static string name = Preferences.Default.Get("name", "");
@@ -21,8 +20,6 @@ internal class Stunde : ObservableObject {
public static async Task<Hours> LoadData() { public static async Task<Hours> LoadData() {
if (string.IsNullOrEmpty(apiKey)) { if (string.IsNullOrEmpty(apiKey)) {
throw new Exception("Kein APIKEY, bitte zuerst Login durchführen"); throw new Exception("Kein APIKEY, bitte zuerst Login durchführen");
} }
@@ -31,9 +28,6 @@ internal class Stunde : ObservableObject {
if (Connectivity.Current.NetworkAccess == NetworkAccess.None) { if (Connectivity.Current.NetworkAccess == NetworkAccess.None) {
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut."); throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
//await App.Current.MainPage.DisplayAlert("Keine Internetverbindung",
// "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.",
// "OK");
} else { } else {
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
@@ -56,20 +50,51 @@ internal class Stunde : ObservableObject {
return hours; return hours;
} }
/// <summary> /// <summary>
/// Basisdaten: Mitarbeiterdaten, Projekte, Gemeinden, Freistellungen. /// Einstellungen
/// </summary> /// </summary>
/// <returns></returns> public static async Task<Settings> LoadSettings()
/// <exception cref="Exception"></exception> {
public static async Task<Hours> LoadBasicData() {
Settings settings;
if (Connectivity.Current.NetworkAccess == NetworkAccess.None)
{
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
}
else
{
var tokendata = new TokenData(apiKey);
string? data = await Auth.GetApiDataWithAuthAsync(tokendata.url + "?settings", tokendata.apiKey);
if (data == "null")
{
throw new Exception("Keine Daten erhalten");
}
if (data == "\"Lalala\"")
{
throw new Exception("Problem mit Token");
}
if (data == null)
{
throw new Exception("Keine Daten erhalten");
}
settings = JsonConvert.DeserializeObject<Settings>(data) ?? throw new Exception("Fehler beim Deserialisieren der Daten");
}
return settings;
}
/// <summary>
/// Basisdaten: Mitarbeiterdaten, Projekte, Gemeinden, Freistellungen.
/// </summary>
public static async Task<Hours> LoadBasicData() {
Hours hours = new Hours(); Hours hours = new Hours();
if (Connectivity.Current.NetworkAccess == NetworkAccess.None) { if (Connectivity.Current.NetworkAccess == NetworkAccess.None) {
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut."); throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
//await App.Current.MainPage.DisplayAlert("Keine Internetverbindung",
// "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.",
// "OK");
} else { } else {
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
@@ -94,9 +119,6 @@ internal class Stunde : ObservableObject {
/// <summary> /// <summary>
/// Zeiten eines Tages holen /// Zeiten eines Tages holen
/// </summary> /// </summary>
/// <param name="date"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static async Task<ObservableCollection<DayTime>> LoadDay(DateTime date) { public static async Task<ObservableCollection<DayTime>> LoadDay(DateTime date) {
if (string.IsNullOrEmpty(apiKey)) { if (string.IsNullOrEmpty(apiKey)) {
throw new Exception("Kein APIKEY, bitte zuerst Login durchführen"); throw new Exception("Kein APIKEY, bitte zuerst Login durchführen");
@@ -104,9 +126,6 @@ internal class Stunde : ObservableObject {
if (Connectivity.Current.NetworkAccess == NetworkAccess.None) { if (Connectivity.Current.NetworkAccess == NetworkAccess.None) {
throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut."); throw new Exception("Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.");
//await App.Current.MainPage.DisplayAlert("Keine Internetverbindung",
// "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut.",
// "OK");
} }
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
@@ -134,8 +153,6 @@ internal class Stunde : ObservableObject {
/// <summary> /// <summary>
/// Einzelnen Stundeneintrag holen /// Einzelnen Stundeneintrag holen
/// </summary> /// </summary>
/// <param name="id"></param>
/// <returns></returns>
public static async Task<DayTime> LoadEntry(int id) { public static async Task<DayTime> LoadEntry(int id) {
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
@@ -171,8 +188,6 @@ internal class Stunde : ObservableObject {
/// <summary> /// <summary>
/// Eintrag speichern /// Eintrag speichern
/// </summary> /// </summary>
/// <param name="stunde"></param>
/// <returns></returns>
public static async Task<DayTime> SaveEntry(DayTime stunde) { //, string begin, string end, string freistellung, string bemerkung) { public static async Task<DayTime> SaveEntry(DayTime stunde) { //, string begin, string end, string freistellung, string bemerkung) {
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
@@ -187,14 +202,9 @@ internal class Stunde : ObservableObject {
/// <summary> /// <summary>
/// Eintrag löschen /// Eintrag löschen
/// </summary> /// </summary>
/// <param name="stunde"></param> public static async Task DeleteEntry(DayTime stunde) {
/// <returns></returns>
public static async Task<DayTime> DeleteEntry(DayTime stunde) { //, string begin, string end, string freistellung, string bemerkung) {
var tokendata = new TokenData(apiKey); var tokendata = new TokenData(apiKey);
await Auth.DeleteItemAsync(tokendata.url + "/entry/" + stunde.id, tokendata.apiKey); await Auth.DeleteItemAsync(tokendata.url + "/entry/" + stunde.id, tokendata.apiKey);
return stunde;
} }

View File

@@ -4,103 +4,107 @@ namespace Jugenddienst_Stunden.Types;
/// <summary> /// <summary>
/// Represents a day time entry for an employee. /// Represents a day time entry for an employee.
/// </summary> /// </summary>
public class DayTime { public class DayTime
/// <summary> {
/// ID des Stundeneintrages /// <summary>
/// </summary> /// ID des Stundeneintrages
public int? id { get; set; } /// </summary>
public int? id { get; set; }
/// <summary> /// <summary>
/// Mitarbeiter-ID /// Mitarbeiter-ID
/// </summary> /// </summary>
public int EmployeeId { get; set; } public int EmployeeId { get; set; }
/// <summary> /// <summary>
/// Der betreffende Tag /// Der betreffende Tag
/// </summary> /// </summary>
public DateTime day { get; set; } public DateTime day { get; set; }
/// <summary> /// <summary>
/// Der Wochentag /// Der Wochentag
/// </summary> /// </summary>
public int wday { get; set; } public int wday { get; set; }
/// <summary> /// <summary>
/// Arbeitsbeginn /// Arbeitsbeginn
/// </summary> /// </summary>
public TimeOnly begin { get; set; } public TimeOnly begin { get; set; }
/// <summary> /// <summary>
/// Arbeitsende /// Arbeitsende
/// </summary> /// </summary>
public TimeOnly end { get; set; } public TimeOnly end { get; set; }
/// <summary> /// <summary>
/// Beschreibung der Arbeit /// Beschreibung der Arbeit
/// </summary> /// </summary>
public string description { get; set; } public string description { get; set; }
/// <summary> /// <summary>
/// Freistellung /// Freistellung
/// </summary> /// </summary>
public string? free { get; set; } public string? free { get; set; }
/// <summary> /// <summary>
/// Freisetellung genehmigt? /// Freisetellung genehmigt?
/// </summary> /// </summary>
public bool? approved { get; set; } public bool? approved { get; set; }
/// <summary> /// <summary>
/// Sollte nix sein /// Sollte nix sein
/// </summary> /// </summary>
public int? type { get; set; } public int? type { get; set; }
/// <summary> /// <summary>
/// Das gewählte Projekt /// Das gewählte Projekt
/// </summary> /// </summary>
public int? projekt { get; set; } public int? projekt { get; set; }
/// <summary> /// <summary>
/// Die gewählte Gemeinde /// Die gewählte Gemeinde
/// </summary> /// </summary>
public int? gemeinde { get; set; } public int? gemeinde { get; set; }
/// <summary> /// <summary>
/// Nachtstunden /// Nachtstunden
/// </summary> /// </summary>
public TimeOnly night { get; set; } public TimeOnly night { get; set; }
/// <summary> /// <summary>
/// Summe Arbeitszeit (inklusive Nachstunden mit Faktor) /// Summe Arbeitszeit (inklusive Nachstunden mit Faktor)
/// </summary> /// </summary>
public Dictionary<string, TimeOnly> total { get; set; } public Dictionary<string, TimeOnly> total { get; set; }
public TimeOnly end_print { get; set; } public TimeOnly end_print { get; set; }
public TimeSpan TimeSpanVon { get; set; } public TimeSpan TimeSpanVon { get; set; }
public TimeSpan TimeSpanBis { get; set; } public TimeSpan TimeSpanBis { get; set; }
/// <summary> /// <summary>
/// Projekte für die Auswahlliste /// Projekte für die Auswahlliste
/// </summary> /// </summary>
public Collection<Projekt> Projekte { get; set; } public Collection<Projekt> Projekte { get; set; }
/// <summary> /// <summary>
/// Gemeinden für die Auswahlliste /// Gemeinden für die Auswahlliste
/// </summary> /// </summary>
public Collection<Gemeinde> Gemeinden { get; set; } public Collection<Gemeinde> Gemeinden { get; set; }
public Collection<Freistellung> Freistellungen { get; set; } public Collection<Freistellung> Freistellungen { get; set; }
/// <summary> /// <summary>
/// Gets the active Gemeinde based on the gemeinde ID. /// Gets the active Gemeinde based on the gemeinde ID.
/// </summary> /// </summary>
public Gemeinde GemeindeAktiv { get; set; } public Gemeinde GemeindeAktiv { get; set; }
/// <summary> /// <summary>
/// Gets the active Projekt based on the projekt ID. /// Gets the active Projekt based on the projekt ID.
/// </summary> /// </summary>
public Projekt ProjektAktiv { get; set; } public Projekt ProjektAktiv { get; set; }
/// <summary> /// <summary>
/// Gets the active Freistellung based on the Freistellung ID /// Gets the active Freistellung based on the Freistellung ID
/// </summary> /// </summary>
public Freistellung FreistellungAktiv { get; set; } public Freistellung FreistellungAktiv { get; set; }
public bool ProjektAktivSet { get; set; } = false;
public bool GemeindeAktivSet { get; set; } = false;
} }

View File

@@ -0,0 +1,15 @@
using System.Collections.ObjectModel;
namespace Jugenddienst_Stunden.Types;
/// <summary>
/// Einstellungen
/// </summary>
public class Settings
{
public bool ProjektAktivSet { get; set; }
public bool GemeindeAktivSet { get; set; }
public Collection<Projekt> Projekte { get; set; }
public Collection<Gemeinde> Gemeinden { get; set; }
public Collection<Freistellung> Freistellungen { get; set; }
}

View File

@@ -6,197 +6,263 @@ using System.Windows.Input;
using static System.Runtime.InteropServices.JavaScript.JSType; using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Jugenddienst_Stunden.ViewModels; namespace Jugenddienst_Stunden.ViewModels;
internal class StundeViewModel : ObservableObject, IQueryAttributable { internal class StundeViewModel : ObservableObject, IQueryAttributable
{
public int id { get; set; } public int id { get; set; }
public string SubTitle { get; set; } = DateTime.Today.ToString("dddd, d. MMMM yyyy"); public string SubTitle { get; set; } = DateTime.Today.ToString("dddd, d. MMMM yyyy");
private Settings _settings;
public Settings Settings
{
get => _settings; set
{
if (_settings != value)
{
_settings = value;
}
}
}
private DayTime _stunde;
public DayTime Stunde
{
get => _stunde;
set
{
if (_stunde != value)
{
_stunde = value;
OnPropertyChanged(nameof(Stunde));
}
}
}
public string Title { get; set; } = "Eintrag bearbeiten";
public event EventHandler<string> AlertEvent;
public event EventHandler<string> InfoEvent;
public event Func<string, string, Task<bool>> ConfirmEvent;
private DayTime _stunde; public ObservableCollection<Gemeinde> OptionsGemeinde { get; private set; }
public DayTime Stunde { get => _stunde; } public ObservableCollection<Projekt> OptionsProjekt { get; private set; }
public ObservableCollection<Freistellung> OptionsFreistellung { get; private set; }
public ObservableCollection<DayTime> DayTimes { get; set; }
public string Title { get; set; } = "Eintrag bearbeiten"; //private Gemeinde _selectedGemeinde;
public Gemeinde SelectedOptionGemeinde
{
get => _stunde.GemeindeAktiv;
set
{
if (_stunde.GemeindeAktiv != value)
{
//_selectedGemeinde = value;
_stunde.GemeindeAktiv = value;
OnPropertyChanged(nameof(SelectedOptionGemeinde));
}
}
}
public event EventHandler<string> AlertEvent; //private Projekt _selectedProjekt;
public event EventHandler<string> InfoEvent; public Projekt SelectedOptionProjekt
public event Func<string, string, Task<bool>> ConfirmEvent; {
get => _stunde.ProjektAktiv;
set
{
if (_stunde.ProjektAktiv != value)
{
//_selectedProjekt = value;
_stunde.ProjektAktiv = value;
OnPropertyChanged(nameof(SelectedOptionProjekt));
}
}
}
//private Freistellung _selectedFreistellung;
public ObservableCollection<Gemeinde> OptionsGemeinde { get; private set; } public Freistellung SelectedOptionFreistellung
public ObservableCollection<Projekt> OptionsProjekt { get; private set; } {
public ObservableCollection<Freistellung> OptionsFreistellung { get; private set; } get => _stunde.FreistellungAktiv;
public ObservableCollection<DayTime> DayTimes { get; set; } set
{
//private Gemeinde _selectedGemeinde; if (_stunde.FreistellungAktiv != value)
public Gemeinde SelectedOptionGemeinde { {
get => _stunde.GemeindeAktiv; _stunde.FreistellungAktiv = value;
set { OnPropertyChanged(nameof(SelectedOptionFreistellung));
if (_stunde.GemeindeAktiv != value) { }
//_selectedGemeinde = value; }
_stunde.GemeindeAktiv = value; }
OnPropertyChanged(nameof(SelectedOptionGemeinde));
}
}
}
//private Projekt _selectedProjekt;
public Projekt SelectedOptionProjekt {
get => _stunde.ProjektAktiv;
set {
if (_stunde.ProjektAktiv != value) {
//_selectedProjekt = value;
_stunde.ProjektAktiv = value;
OnPropertyChanged(nameof(SelectedOptionProjekt));
}
}
}
//private Freistellung _selectedFreistellung;
public Freistellung SelectedOptionFreistellung {
get => _stunde.FreistellungAktiv;
set {
if (_stunde.FreistellungAktiv != value) {
_stunde.FreistellungAktiv = value;
OnPropertyChanged(nameof(SelectedOptionFreistellung));
}
}
}
public ICommand SaveCommand { get; private set; } public ICommand SaveCommand { get; private set; }
public ICommand DeleteCommand { get; private set; } public ICommand DeleteCommand { get; private set; }
public ICommand DeleteConfirmCommand { get; private set; } public ICommand DeleteConfirmCommand { get; private set; }
//public ICommand LoadDataCommand { get; private set; } //public ICommand LoadDataCommand { get; private set; }
public StundeViewModel() { public StundeViewModel()
_stunde = new DayTime(); {
_stunde = new DayTime();
SaveCommand = new AsyncRelayCommand(Save); SaveCommand = new AsyncRelayCommand(Save);
//DeleteCommand = new AsyncRelayCommand(Delete); //DeleteCommand = new AsyncRelayCommand(Delete);
DeleteConfirmCommand = new Command(async () => await DeleteConfirm()); DeleteConfirmCommand = new Command(async () => await DeleteConfirm());
} }
public StundeViewModel(DayTime stunde) { public StundeViewModel(DayTime stunde)
_stunde = stunde; {
_stunde = stunde;
SaveCommand = new AsyncRelayCommand(Save); SaveCommand = new AsyncRelayCommand(Save);
DeleteConfirmCommand = new AsyncRelayCommand(DeleteConfirm); DeleteConfirmCommand = new AsyncRelayCommand(DeleteConfirm);
} }
private async Task LoadData() { private async Task LoadData()
try { {
Hours _hours = await Models.Stunde.LoadBasicData(); try
OptionsProjekt = new ObservableCollection<Projekt>(_hours.Projekte); {
OptionsGemeinde = new ObservableCollection<Gemeinde>(_hours.Gemeinden); Hours _hours = await Models.Stunde.LoadBasicData();
OptionsFreistellung = new ObservableCollection<Freistellung>(_hours.Freistellungen); OptionsProjekt = new ObservableCollection<Projekt>(_hours.Projekte);
OnPropertyChanged(nameof(OptionsGemeinde)); OptionsGemeinde = new ObservableCollection<Gemeinde>(_hours.Gemeinden);
OnPropertyChanged(nameof(OptionsProjekt)); OptionsFreistellung = new ObservableCollection<Freistellung>(_hours.Freistellungen);
OnPropertyChanged(nameof(OptionsFreistellung)); OnPropertyChanged(nameof(OptionsGemeinde));
_stunde.EmployeeId = _hours.EmployeeId; OnPropertyChanged(nameof(OptionsProjekt));
} catch (Exception e) { OnPropertyChanged(nameof(OptionsFreistellung));
AlertEvent?.Invoke(this, e.Message); _stunde.EmployeeId = _hours.EmployeeId;
} }
} catch (Exception e)
{
AlertEvent?.Invoke(this, e.Message);
}
}
async Task Save() { async Task Save()
bool exceptionOccurred = false; {
try { bool exceptionOccurred = false;
await Models.Stunde.SaveEntry(_stunde); try
} catch (Exception e) { {
AlertEvent?.Invoke(this, e.Message); await Models.Stunde.SaveEntry(_stunde);
exceptionOccurred = true; }
} catch (Exception e)
if (!exceptionOccurred) { {
if (_stunde.id != null) { AlertEvent?.Invoke(this, e.Message);
await Shell.Current.GoToAsync($"..?saved={_stunde.id}"); exceptionOccurred = true;
} else { }
await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}"); if (!exceptionOccurred)
} {
} if (_stunde.id != null)
} {
await Shell.Current.GoToAsync($"..?saved={_stunde.id}");
}
else
{
await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}");
}
}
}
private async Task Delete() { private async Task Delete()
await Models.Stunde.DeleteEntry(_stunde); {
await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}"); await Models.Stunde.DeleteEntry(_stunde);
} await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}");
}
private async Task DeleteConfirm() { private async Task DeleteConfirm()
if (ConfirmEvent != null) { {
bool answer = await ConfirmEvent.Invoke("Achtung", "Löschen kann nicht ungeschehen gemacht werden. Fortfahren?"); if (ConfirmEvent != null)
if (answer) { {
//Löschen bool answer = await ConfirmEvent.Invoke("Achtung", "Löschen kann nicht ungeschehen gemacht werden. Fortfahren?");
await Models.Stunde.DeleteEntry(_stunde); if (answer)
await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}"); {
} else { //nicht Löschen //Löschen
} await Models.Stunde.DeleteEntry(_stunde);
} await Shell.Current.GoToAsync($"..?date={_stunde.day.ToString("yyyy-MM-dd")}");
}
else
{ //nicht Löschen
}
}
} }
private async Task LoadSettings()
{
Settings = await Models.Stunde.LoadSettings();
}
/// <summary>
/// Anwenden der Query-Parameter
/// </summary>
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
{
await LoadSettings();
var probe = query;
if (query.ContainsKey("load"))
{
//DateTime heute = DateTime.Now;
Stunde = await Models.Stunde.LoadEntry(Convert.ToInt32(query["load"]));
if (System.String.IsNullOrEmpty(Stunde.description))
{
AlertEvent?.Invoke(this, "Eintrag hat keine Daten zurückgegeben");
}
SubTitle = Stunde.day.ToString("dddd, d. MMMM yyyy");
OptionsProjekt = new ObservableCollection<Projekt>(Stunde.Projekte);
OptionsGemeinde = new ObservableCollection<Gemeinde>(Stunde.Gemeinden);
OptionsFreistellung = new ObservableCollection<Freistellung>(Stunde.Freistellungen);
OnPropertyChanged(nameof(OptionsGemeinde));
OnPropertyChanged(nameof(OptionsProjekt));
OnPropertyChanged(nameof(OptionsFreistellung));
//OptionsProjekt.FirstOrDefault(x => x.Id == _stunde.projekt);
SelectedOptionGemeinde = OptionsGemeinde.FirstOrDefault(item => item.Id == Stunde.gemeinde) ?? new Gemeinde();
OnPropertyChanged(nameof(SelectedOptionGemeinde));
SelectedOptionProjekt = OptionsProjekt.FirstOrDefault(Projekt => Projekt.Id == Stunde.projekt) ?? new Projekt();
OnPropertyChanged(nameof(SelectedOptionProjekt));
SelectedOptionFreistellung = OptionsFreistellung.FirstOrDefault(Freistellung => Freistellung.Id == Stunde.free) ?? new Freistellung();
OnPropertyChanged(nameof(SelectedOptionFreistellung));
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) { OnPropertyChanged(nameof(SubTitle));
var probe = query;
if (query.ContainsKey("load")) {
//DateTime heute = DateTime.Now; }
_stunde = await Models.Stunde.LoadEntry(Convert.ToInt32(query["load"])); if (query.ContainsKey("date"))
if (System.String.IsNullOrEmpty(_stunde.description)) { {
AlertEvent?.Invoke(this, "Eintrag hat keine Daten zurückgegeben"); Title = "Neuer Eintrag";
}
SubTitle = _stunde.day.ToString("dddd, d. MMMM yyyy");
OptionsProjekt = new ObservableCollection<Projekt>(_stunde.Projekte); DateTime _date = DateTime.ParseExact((string)query["date"], "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
OptionsGemeinde = new ObservableCollection<Gemeinde>(_stunde.Gemeinden);
OptionsFreistellung = new ObservableCollection<Freistellung>(_stunde.Freistellungen);
OnPropertyChanged(nameof(OptionsGemeinde));
OnPropertyChanged(nameof(OptionsProjekt));
OnPropertyChanged(nameof(OptionsFreistellung));
//OptionsProjekt.FirstOrDefault(x => x.Id == _stunde.projekt); //Bei neuem Eintrag die vorhandenen des gleichen Tages anzeigen
try
{
DayTimes = await Models.Stunde.LoadDay(_date);
}
catch (Exception)
{
//Ein Tag ohne Einträge gibt eine Fehlermeldung,
//die soll aber ignoriert werden, weil beim Neueintrag ist das ja Wurscht
}
SelectedOptionGemeinde = OptionsGemeinde.FirstOrDefault(item => item.Id == _stunde.gemeinde) ?? new Gemeinde(); _stunde.day = _date;
OnPropertyChanged(nameof(SelectedOptionGemeinde)); SubTitle = _date.ToString("dddd, d. MMMM yyyy");
SelectedOptionProjekt = OptionsProjekt.FirstOrDefault(Projekt => Projekt.Id == _stunde.projekt) ?? new Projekt(); _ = LoadData();
OnPropertyChanged(nameof(SelectedOptionProjekt)); OnPropertyChanged(nameof(Title));
OnPropertyChanged(nameof(SubTitle));
OnPropertyChanged(nameof(DayTimes));
}
SelectedOptionFreistellung = OptionsFreistellung.FirstOrDefault(Freistellung => Freistellung.Id == _stunde.free) ?? new Freistellung(); }
OnPropertyChanged(nameof(SelectedOptionFreistellung));
OnPropertyChanged(nameof(Stunde));
OnPropertyChanged(nameof(SubTitle));
}
if (query.ContainsKey("date")) {
Title = "Neuer Eintrag";
DateTime _date = DateTime.ParseExact((string)query["date"], "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
//Bei neuem Eintrag die vorhandenen des gleichen Tages anzeigen
try {
DayTimes = await Models.Stunde.LoadDay(_date);
} catch (Exception) {
//Ein Tag ohne Einträge gibt eine Fehlermeldung,
//die soll aber ignoriert werden, weil beim Neueintrag ist das ja Wurscht
}
_stunde.day = _date;
SubTitle = _date.ToString("dddd, d. MMMM yyyy");
_ = LoadData();
OnPropertyChanged(nameof(Title));
OnPropertyChanged(nameof(SubTitle));
OnPropertyChanged(nameof(DayTimes));
}
}
} }

View File

@@ -5,34 +5,41 @@ using Newtonsoft.Json.Linq;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
namespace Jugenddienst_Stunden.ViewModels; namespace Jugenddienst_Stunden.ViewModels;
internal class StundenViewModel : ObservableObject, IQueryAttributable, INotifyPropertyChanged { internal class StundenViewModel : ObservableObject, IQueryAttributable, INotifyPropertyChanged
public string Name => AppInfo.Name; {
public string Surname => AppInfo.VersionString; public string Name => AppInfo.Name;
public string MoreInfoUrl => "https://aka.ms/maui"; public string Surname => AppInfo.VersionString;
public string Message => "Hier werden deine geleisteten Arbeitsstunden aufgelistet"; public string MoreInfoUrl => "https://aka.ms/maui";
public string LoadOverview => "Lade Summen für " + DateTime.Today.ToString("MMMM"); public string Message => "Hier werden deine geleisteten Arbeitsstunden aufgelistet";
//public static DateTime GetDay = DateTime.Today; public string LoadOverview => "Lade Summen für " + DateTime.Today.ToString("MMMM");
//public string ShowDay => "Zeit an Tag " + GetDay.ToString("ddd d. MMM") + ": "; //public static DateTime GetDay = DateTime.Today;
//public string ShowDay => "Zeit an Tag " + GetDay.ToString("ddd d. MMM") + ": ";
public ICommand NewEntryCommand { get; } public ICommand NewEntryCommand { get; }
public ICommand SelectEntryCommand { get; } public ICommand SelectEntryCommand { get; }
public ICommand LoadDataCommand { get; private set; } public ICommand LoadDataCommand { get; private set; }
public ICommand LoadDayCommand { get; private set; } public ICommand LoadDayCommand { get; private set; }
public ICommand RefreshListCommand { get; } public ICommand RefreshListCommand { get; }
public ICommand RefreshCommand { get; } public ICommand RefreshCommand { get; }
public event EventHandler<string> AlertEvent; public event EventHandler<string> AlertEvent;
public event EventHandler<string> InfoEvent; public event EventHandler<string> InfoEvent;
private Settings Settings { get; set; }
private bool isRefreshing; private bool isRefreshing;
public bool IsRefreshing { public bool IsRefreshing
{
get => isRefreshing; get => isRefreshing;
set { set
if (isRefreshing != value) { {
if (isRefreshing != value)
{
isRefreshing = value; isRefreshing = value;
OnPropertyChanged(); OnPropertyChanged();
} }
@@ -40,95 +47,108 @@ internal class StundenViewModel : ObservableObject, IQueryAttributable, INotifyP
} }
private string _title = Preferences.Default.Get("name", "") + " " + Preferences.Default.Get("surname", ""); private string _title = Preferences.Default.Get("name", "") + " " + Preferences.Default.Get("surname", "");
public string Title { public string Title
get => _title; {
set => SetProperty(ref _title, value); get => _title;
} set => SetProperty(ref _title, value);
}
private Hours _hour; private Hours _hour;
public Hours Hours { public Hours Hours
get => _hour; {
} get => _hour;
}
/// <summary> /// <summary>
/// Gesamtstunden an einem Tag /// Gesamtstunden an einem Tag
/// </summary> /// </summary>
public TimeOnly DayTotal { get; set; } public TimeOnly DayTotal { get; set; }
/// <summary> /// <summary>
/// Liste der Tageszeiten /// Liste der Tageszeiten
/// </summary> /// </summary>
private ObservableCollection<DayTime> _dayTimes = new ObservableCollection<DayTime>(); private ObservableCollection<DayTime> _dayTimes = new ObservableCollection<DayTime>();
public ObservableCollection<DayTime> DayTimes { public ObservableCollection<DayTime> DayTimes
get => _dayTimes; {
set => SetProperty(ref _dayTimes, value); get => _dayTimes;
} set => SetProperty(ref _dayTimes, value);
}
/// <summary> /// <summary>
/// Mindest-Datum für den Datepicker /// Mindest-Datum für den Datepicker
/// </summary> /// </summary>
public DateTime MinimumDate { public DateTime MinimumDate
get => DateTime.Today.AddDays(-365); {
} get => DateTime.Today.AddDays(-365);
}
/// <summary> /// <summary>
/// Höchst-Datum für den Datepicker /// Höchst-Datum für den Datepicker
/// </summary> /// </summary>
public DateTime MaximumDate { public DateTime MaximumDate
get => DateTime.Today.AddDays(60); {
} get => DateTime.Today.AddDays(60);
}
/// <summary> /// <summary>
/// Heutiges Datum, wenn das Datum geändert wird, wird auch der Tag geladen /// Heutiges Datum, wenn das Datum geändert wird, wird auch der Tag geladen
/// </summary> /// </summary>
private DateTime dateToday = DateTime.Today; private DateTime dateToday = DateTime.Today;
public DateTime DateToday { public DateTime DateToday
get => dateToday; {
set { get => dateToday;
if (dateToday != value) { set
dateToday = value; {
//GetDay = value; if (dateToday != value)
//OnPropertyChanged(); {
Task.Run(() => LoadDay(value)); dateToday = value;
//GetDay = value;
//OnPropertyChanged();
Task.Run(() => LoadDay(value));
} }
} }
} }
/// <summary> /// <summary>
/// Monatsübersicht: Geleistete Stunden /// Monatsübersicht: Geleistete Stunden
/// </summary> /// </summary>
public string? ZeitCalculated { public string? ZeitCalculated
get => _hour.zeit_total; {
} get => _hour.zeit_total;
}
/// <summary> /// <summary>
/// Monatsübersicht: Sollstunden /// Monatsübersicht: Sollstunden
/// </summary> /// </summary>
public string? Nominal { public string? Nominal
get => _hour.nominal; {
} get => _hour.nominal;
}
/// <summary> /// <summary>
/// Monatsübersicht: Differenz zwischen Soll und geleisteten Stunden /// Monatsübersicht: Differenz zwischen Soll und geleisteten Stunden
/// </summary> /// </summary>
public string? Overtime { public string? Overtime
get => _hour.overtime; {
} get => _hour.overtime;
}
/// <summary> /// <summary>
/// Monatsübersicht: Restüberstunden insgesamt /// Monatsübersicht: Restüberstunden insgesamt
/// </summary> /// </summary>
public string OvertimeMonth { public string OvertimeMonth
get => _hour.overtime_month; {
} get => _hour.overtime_month;
}
/// <summary> /// <summary>
/// Monatsübersicht: Resturlaub /// Monatsübersicht: Resturlaub
/// </summary> /// </summary>
public string Holiday { public string Holiday
get => _hour.holiday; {
} get => _hour.holiday;
}
@@ -136,107 +156,134 @@ internal class StundenViewModel : ObservableObject, IQueryAttributable, INotifyP
/// <summary> /// <summary>
/// CTOR /// CTOR
/// </summary> /// </summary>
public StundenViewModel() { public StundenViewModel()
{
_hour = new Types.Hours(); _hour = new Types.Hours();
LoadDataCommand = new AsyncRelayCommand(LoadData);
NewEntryCommand = new AsyncRelayCommand(NewEntryAsync);
SelectEntryCommand = new AsyncRelayCommand<DayTime>(SelectEntryAsync);
RefreshListCommand = new AsyncRelayCommand(RefreshList);
Task task = LoadDay(DateTime.Today);
LoadDataCommand = new AsyncRelayCommand(LoadData);
NewEntryCommand = new AsyncRelayCommand(NewEntryAsync);
SelectEntryCommand = new AsyncRelayCommand<DayTime>(SelectEntryAsync);
RefreshListCommand = new AsyncRelayCommand(RefreshList);
RefreshCommand = new Command(async () => await RefreshItemsAsync()); RefreshCommand = new Command(async () => await RefreshItemsAsync());
Task task = LoadDay(DateTime.Today);
} }
/// <summary> /// <summary>
/// Öffnet eine neue Stundeneingabe /// Öffnet eine neue Stundeneingabe
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task NewEntryAsync() { private async Task NewEntryAsync()
//Hier muss das Datum übergeben werden {
//await Shell.Current.GoToAsync(nameof(Views.StundePage)); //Hier muss das Datum übergeben werden
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?date={dateToday:yyyy-MM-dd}"); //await Shell.Current.GoToAsync(nameof(Views.StundePage));
} await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?date={dateToday:yyyy-MM-dd}");
}
/// <summary> /// <summary>
/// Öffnet eine bestehende Stundeneingabe /// Öffnet eine bestehende Stundeneingabe
/// </summary> /// </summary>
/// <param name="entry"></param> /// <param name="entry"></param>
/// <returns></returns> /// <returns></returns>
private async Task SelectEntryAsync(DayTime entry) { private async Task SelectEntryAsync(DayTime entry)
if (entry != null && entry.id != null) { {
//var navigationParameters = new Dictionary<string, object> { { "load", entry.id } }; if (entry != null && entry.id != null)
//await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}", navigationParameters); {
await Shell.Current.GoToAsync($"{nameof(Views.StundePage)}?load={entry.id}"); //var navigationParameters = new Dictionary<string, object> { { "load", entry.id } };
} else AlertEvent?.Invoke(this, "Auswahl enthält keine Daten"); //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() { private async Task RefreshList()
OnPropertyChanged(nameof(DayTimes)); {
} OnPropertyChanged(nameof(DayTimes));
}
/// <summary> /// <summary>
/// Lädt die Monatssummen für die Übersicht /// Lädt die Monatssummen für die Übersicht
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
private async Task LoadData() { private async Task LoadData()
try { {
_hour = await Models.Stunde.LoadData(); try
RefreshProperties(); {
} catch (Exception e) {
AlertEvent?.Invoke(this, e.Message);
}
}
/// <summary> _hour = await Models.Stunde.LoadData();
/// Lädt die Arbeitszeiten für einen Tag RefreshProperties();
/// </summary> }
/// <param name="date"></param> catch (Exception e)
/// <returns></returns> {
public async Task LoadDay(DateTime date) { AlertEvent?.Invoke(this, e.Message);
DayTotal = new TimeOnly(0); }
}
try { /// <summary>
DayTimes = await Models.Stunde.LoadDay(date); /// Lädt die Arbeitszeiten für einen Tag
/// </summary>
/// <param name="date"></param>
/// <returns></returns>
public async Task LoadDay(DateTime date)
{
DayTotal = new TimeOnly(0);
//TODO: Hier muss noch die Berechnung der Stunden erfolgen try
//Hier werden im Moment noch nur die eingetragenen Stunden gezählt {
//Auf der Website bekommt der Benutzer die berechneten Stunden angezeigt (Nachstunden außerhalb des Stundenplanes zählen mehr ...) await LoadSettings();
DayTimes = await Models.Stunde.LoadDay(date);
TimeSpan span = TimeSpan.Zero; //TODO: Hier muss noch die Berechnung der Stunden erfolgen
foreach (DayTime dt in DayTimes) { //Es werden im Moment nur die eingetragenen Stunden gezählt
span += dt.end - dt.begin; //Auf der Website bekommt der Benutzer hingegen die berechneten Stunden angezeigt (Nachstunden außerhalb des Stundenplanes zählen mehr ...)
}
DayTotal = TimeOnly.FromTimeSpan(span);
} catch (Exception e) { TimeSpan span = TimeSpan.Zero;
DayTimes = new ObservableCollection<DayTime>(); foreach (DayTime dt in DayTimes)
//TODO: hier könnte auch ein Fehler kommen, dann wäre InfoEvent falsch. {
InfoEvent?.Invoke(this, e.Message); span += dt.end - dt.begin;
} finally { }
OnPropertyChanged(nameof(DayTotal)); DayTotal = TimeOnly.FromTimeSpan(span);
//OnPropertyChanged(nameof(DayTimes));
}
} }
catch (Exception e)
{
DayTimes = new ObservableCollection<DayTime>();
//TODO: hier könnte auch ein Fehler kommen, dann wäre InfoEvent falsch.
InfoEvent?.Invoke(this, e.Message);
}
finally
{
OnPropertyChanged(nameof(DayTotal));
//OnPropertyChanged(nameof(DayTimes));
}
}
private async Task LoadSettings()
{
Settings = await Models.Stunde.LoadSettings();
}
async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query) { async void IQueryAttributable.ApplyQueryAttributes(IDictionary<string, object> query)
if (query.ContainsKey("date")) { {
await LoadDay(Convert.ToDateTime(query["date"])); if (query.ContainsKey("date"))
} {
} await LoadDay(Convert.ToDateTime(query["date"]));
}
}
private async Task RefreshItemsAsync() { private async Task RefreshItemsAsync()
{
IsRefreshing = true; IsRefreshing = true;
// Fügen Sie hier die Logik zum Aktualisieren der Daten hinzu // Fügen Sie hier die Logik zum Aktualisieren der Daten hinzu
@@ -249,26 +296,31 @@ internal class StundenViewModel : ObservableObject, IQueryAttributable, INotifyP
/// <summary> /// <summary>
/// Refreshes all properties /// Refreshes all properties
/// </summary> /// </summary>
private void RefreshProperties() { private void RefreshProperties()
OnPropertyChanged(nameof(Nominal)); {
OnPropertyChanged(nameof(Overtime)); OnPropertyChanged(nameof(Nominal));
OnPropertyChanged(nameof(OvertimeMonth)); OnPropertyChanged(nameof(Overtime));
OnPropertyChanged(nameof(ZeitCalculated)); OnPropertyChanged(nameof(OvertimeMonth));
OnPropertyChanged(nameof(Holiday)); OnPropertyChanged(nameof(ZeitCalculated));
OnPropertyChanged(nameof(Hours)); OnPropertyChanged(nameof(Holiday));
OnPropertyChanged(nameof(Title)); OnPropertyChanged(nameof(Hours));
OnPropertyChanged(nameof(MinimumDate)); OnPropertyChanged(nameof(Title));
OnPropertyChanged(nameof(MaximumDate)); OnPropertyChanged(nameof(MinimumDate));
} OnPropertyChanged(nameof(MaximumDate));
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
try { {
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); try
} catch (Exception ex) { {
AlertEvent?.Invoke(this, ex.Message); base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
//Console.WriteLine($"Fehler bei OnPropertyChanged: {ex.Message}"); }
} catch (Exception ex)
} {
AlertEvent?.Invoke(this, ex.Message);
//Console.WriteLine($"Fehler bei OnPropertyChanged: {ex.Message}");
}
}
} }

View File

@@ -38,9 +38,9 @@
<Frame Padding="5,2,5,10"> <Frame Padding="5,2,5,10">
<Grid ColumnDefinitions="*,*,*"> <Grid ColumnDefinitions="*,*,*">
<Picker x:Name="pick_gemeinde" Title="Gemeinde" ItemsSource="{Binding OptionsGemeinde}" SelectedItem="{Binding SelectedOptionGemeinde, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="0" > <Picker x:Name="pick_gemeinde" Title="Gemeinde" ItemsSource="{Binding OptionsGemeinde}" SelectedItem="{Binding SelectedOptionGemeinde, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="0" IsVisible="{Binding Settings.GemeindeAktivSet}">
</Picker> </Picker>
<Picker x:Name="pick_projekt" Title="Projekt" ItemsSource="{Binding OptionsProjekt}" SelectedItem="{Binding SelectedOptionProjekt, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="1" > <Picker x:Name="pick_projekt" Title="Projekt" ItemsSource="{Binding OptionsProjekt}" SelectedItem="{Binding SelectedOptionProjekt, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="1" IsVisible="{Binding Settings.ProjektAktivSet}">
</Picker> </Picker>
<Picker x:Name="pick_freistellung" Title="Freistellung" ItemsSource="{Binding OptionsFreistellung}" SelectedItem="{Binding SelectedOptionFreistellung, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="2" > <Picker x:Name="pick_freistellung" Title="Freistellung" ItemsSource="{Binding OptionsFreistellung}" SelectedItem="{Binding SelectedOptionFreistellung, Mode=TwoWay}" ItemDisplayBinding="{Binding Name}" Grid.Column="2" >
</Picker> </Picker>

View File

@@ -77,8 +77,8 @@
<Label Grid.Column="0" Text="{Binding begin}" /> <Label Grid.Column="0" Text="{Binding begin}" />
<Label Text="bis" Padding="5,0,5,0" /> <Label Text="bis" Padding="5,0,5,0" />
<Label Text="{Binding end}" /> <Label Text="{Binding end}" />
<Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0" /> <Label Text="{Binding GemeindeAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding GemeindeAktivSet}" />
<Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0" /> <Label Text="{Binding ProjektAktiv.Name}" Margin="10,0,0,0" IsVisible="{Binding ProjektAktivSet}" />
<Label Text="{Binding FreistellungAktiv.Id}" Margin="10,0,0,0" /> <Label Text="{Binding FreistellungAktiv.Id}" Margin="10,0,0,0" />
</HorizontalStackLayout> </HorizontalStackLayout>