Finished Settings page
This commit is contained in:
23
WorkTime.Database/DatabaseContext.cs
Normal file
23
WorkTime.Database/DatabaseContext.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using WorkTime.Models;
|
||||
|
||||
namespace WorkTime.Database;
|
||||
|
||||
internal sealed class DatabaseContext(DbContextOptions<DatabaseContext> options) : DbContext(options) {
|
||||
|
||||
public DbSet<TimeEntry> Entries { get; set; }
|
||||
|
||||
}
|
||||
|
||||
internal sealed class DbMigrationService(IServiceProvider provider) : IHostedService {
|
||||
public async Task StartAsync(CancellationToken cancellationToken) {
|
||||
await using var scope = provider.CreateAsyncScope();
|
||||
var context = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
|
||||
|
||||
await context.Database.MigrateAsync(cancellationToken);
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
|
||||
}
|
||||
42
WorkTime.Database/Migrations/20251112121330_Initial.Designer.cs
generated
Normal file
42
WorkTime.Database/Migrations/20251112121330_Initial.Designer.cs
generated
Normal file
@@ -0,0 +1,42 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WorkTime.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace WorkTime.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20251112121330_Initial")]
|
||||
partial class Initial
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("WorkTime.Models.TimeEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Entries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
35
WorkTime.Database/Migrations/20251112121330_Initial.cs
Normal file
35
WorkTime.Database/Migrations/20251112121330_Initial.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace WorkTime.Database.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class Initial : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Entries",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "TEXT", nullable: false),
|
||||
Type = table.Column<int>(type: "INTEGER", nullable: false),
|
||||
Timestamp = table.Column<DateTime>(type: "TEXT", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Entries", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Entries");
|
||||
}
|
||||
}
|
||||
}
|
||||
39
WorkTime.Database/Migrations/DatabaseContextModelSnapshot.cs
Normal file
39
WorkTime.Database/Migrations/DatabaseContextModelSnapshot.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WorkTime.Database;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace WorkTime.Database.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
partial class DatabaseContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||
|
||||
modelBuilder.Entity("WorkTime.Models.TimeEntry", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<DateTime>("Timestamp")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<int>("Type")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Entries");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
28
WorkTime.Database/Repositories/SettingsRepository.cs
Normal file
28
WorkTime.Database/Repositories/SettingsRepository.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using WorkTime.Models;
|
||||
using WorkTime.Models.Repositories;
|
||||
|
||||
namespace WorkTime.Database.Repositories;
|
||||
|
||||
internal sealed class SettingsRepository(string filePath) : ISettingsRepository {
|
||||
public async Task<Settings> LoadSettings() {
|
||||
if (!File.Exists(filePath))
|
||||
return new Settings();
|
||||
|
||||
var raw = await File.ReadAllTextAsync(filePath);
|
||||
return JsonSerializer.Deserialize<Settings>(raw, SettingsSerializer.Default.Settings) ?? new Settings();
|
||||
}
|
||||
|
||||
public async Task SaveSettings(Settings settings) {
|
||||
if (File.Exists(filePath))
|
||||
File.Delete(filePath);
|
||||
|
||||
var json = JsonSerializer.Serialize(settings, SettingsSerializer.Default.Settings);
|
||||
await File.WriteAllTextAsync(filePath, json);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonSourceGenerationOptions(WriteIndented = true)]
|
||||
[JsonSerializable(typeof(Settings))]
|
||||
internal partial class SettingsSerializer : JsonSerializerContext {}
|
||||
22
WorkTime.Database/Repositories/TimeEntryRepository.cs
Normal file
22
WorkTime.Database/Repositories/TimeEntryRepository.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using WorkTime.Models;
|
||||
using WorkTime.Models.Repositories;
|
||||
|
||||
namespace WorkTime.Database.Repositories;
|
||||
|
||||
internal sealed class TimeEntryRepository : ITimeEntryRepository {
|
||||
public Task<IEnumerable<TimeEntry>> GetTimeEntries() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IEnumerable<TimeEntry>> GetTimeEntries(DateOnly date) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task AddTimeEntry(TimeEntry entry) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task DeleteTimeEntry(Guid id) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
16
WorkTime.Database/ServiceCollectionExtensions.cs
Normal file
16
WorkTime.Database/ServiceCollectionExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using WorkTime.Database.Repositories;
|
||||
using WorkTime.Models.Repositories;
|
||||
|
||||
namespace WorkTime.Database;
|
||||
|
||||
public static class ServiceCollectionExtensions {
|
||||
|
||||
public static void AddDatabase(this IServiceCollection services, string basePath) {
|
||||
services.AddSqlite<DatabaseContext>($"Filename={Path.Combine(basePath, "data.db")}");
|
||||
services.AddHostedService<DbMigrationService>();
|
||||
services.AddTransient<ITimeEntryRepository, TimeEntryRepository>();
|
||||
services.AddTransient<ISettingsRepository, SettingsRepository>(_ => new SettingsRepository(Path.Combine(basePath, "settings.json")));
|
||||
}
|
||||
|
||||
}
|
||||
18
WorkTime.Database/WorkTime.Database.csproj
Normal file
18
WorkTime.Database/WorkTime.Database.csproj
Normal file
@@ -0,0 +1,18 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkTime.Models\WorkTime.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
8
WorkTime.Migrator/Program.cs
Normal file
8
WorkTime.Migrator/Program.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using WorkTime.Database;
|
||||
|
||||
var builder = Host.CreateApplicationBuilder(args);
|
||||
|
||||
builder.Services.AddDatabase(Environment.CurrentDirectory);
|
||||
|
||||
var host = builder.Build();
|
||||
host.Run();
|
||||
12
WorkTime.Migrator/Properties/launchSettings.json
Normal file
12
WorkTime.Migrator/Properties/launchSettings.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"WorkTime.Migrator": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"environmentVariables": {
|
||||
"DOTNET_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
WorkTime.Migrator/WorkTime.Migrator.csproj
Normal file
21
WorkTime.Migrator/WorkTime.Migrator.csproj
Normal file
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>dotnet-WorkTime.Migrator-77e5f218-8552-46f7-943e-28675db76107</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkTime.Database\WorkTime.Database.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
8
WorkTime.Migrator/appsettings.Development.json
Normal file
8
WorkTime.Migrator/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
WorkTime.Migrator/appsettings.json
Normal file
8
WorkTime.Migrator/appsettings.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using MauiIcons.Core;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace WorkTime.Mobile;
|
||||
|
||||
public partial class App : Application {
|
||||
public App() {
|
||||
InitializeComponent();
|
||||
_ = new MauiIcon();
|
||||
}
|
||||
|
||||
protected override Window CreateWindow(IActivationState? activationState) {
|
||||
|
||||
@@ -3,12 +3,35 @@
|
||||
x:Class="WorkTime.Mobile.AppShell"
|
||||
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:local="clr-namespace:WorkTime.Mobile"
|
||||
Title="WorkTime.Mobile">
|
||||
xmlns:pages="clr-namespace:WorkTime.Mobile.Pages"
|
||||
xmlns:icons="http://www.aathifmahir.com/dotnet/2022/maui/icons"
|
||||
Title="Zeiterfassung">
|
||||
|
||||
<Shell.Resources>
|
||||
<ResourceDictionary>
|
||||
<FontImageSource x:Key="CaptureIcon" Glyph="{icons:Material Schedule}" />
|
||||
<FontImageSource x:Key="AnalysisIcon" Glyph="{icons:Material Schedule}" />
|
||||
<FontImageSource x:Key="SettingsIcon" Glyph="{icons:Material Settings}" />
|
||||
</ResourceDictionary>
|
||||
</Shell.Resources>
|
||||
|
||||
<ShellContent
|
||||
Title="Home"
|
||||
ContentTemplate="{DataTemplate local:MainPage}"
|
||||
Route="MainPage" />
|
||||
<TabBar>
|
||||
|
||||
<ShellContent
|
||||
Title="Erfassen"
|
||||
Icon="{StaticResource CaptureIcon}"
|
||||
ContentTemplate="{DataTemplate pages:CapturePage}" />
|
||||
|
||||
<ShellContent
|
||||
Title="Auswertung"
|
||||
Icon="{StaticResource AnalysisIcon}"
|
||||
ContentTemplate="{DataTemplate pages:AnalysisPage}" />
|
||||
|
||||
<ShellContent
|
||||
Title="Einstellungen"
|
||||
Icon="{StaticResource SettingsIcon}"
|
||||
ContentTemplate="{DataTemplate pages:SettingsPage}" />
|
||||
|
||||
</TabBar>
|
||||
|
||||
</Shell>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CommunityToolkit.Maui;
|
||||
using MauiIcons.Material;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WorkTime.Database;
|
||||
using WorkTime.Mobile.Pages;
|
||||
|
||||
namespace WorkTime.Mobile;
|
||||
|
||||
@@ -7,11 +11,17 @@ public static class MauiProgram {
|
||||
var builder = MauiApp.CreateBuilder();
|
||||
builder
|
||||
.UseMauiApp<App>()
|
||||
.UseMauiCommunityToolkit()
|
||||
.UseMaterialMauiIcons()
|
||||
.ConfigureFonts(fonts => {
|
||||
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
|
||||
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
|
||||
});
|
||||
|
||||
builder.Services.AddDatabase(FileSystem.AppDataDirectory);
|
||||
|
||||
builder.Services.AddTransient<SettingsPageModel>();
|
||||
|
||||
#if DEBUG
|
||||
builder.Logging.AddDebug();
|
||||
#endif
|
||||
|
||||
9
WorkTime.Mobile/Pages/AnalysisPage.xaml
Normal file
9
WorkTime.Mobile/Pages/AnalysisPage.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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"
|
||||
x:Class="WorkTime.Mobile.Pages.AnalysisPage">
|
||||
<ContentPage.Content>
|
||||
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
||||
13
WorkTime.Mobile/Pages/AnalysisPage.xaml.cs
Normal file
13
WorkTime.Mobile/Pages/AnalysisPage.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkTime.Mobile.Pages;
|
||||
|
||||
public partial class AnalysisPage : ContentPage {
|
||||
public AnalysisPage() {
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
9
WorkTime.Mobile/Pages/CapturePage.xaml
Normal file
9
WorkTime.Mobile/Pages/CapturePage.xaml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?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"
|
||||
x:Class="WorkTime.Mobile.Pages.CapturePage">
|
||||
<ContentPage.Content>
|
||||
<Label Text="Hello from Capture Page" />
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
||||
13
WorkTime.Mobile/Pages/CapturePage.xaml.cs
Normal file
13
WorkTime.Mobile/Pages/CapturePage.xaml.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WorkTime.Mobile.Pages;
|
||||
|
||||
public partial class CapturePage : ContentPage {
|
||||
public CapturePage() {
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
24
WorkTime.Mobile/Pages/Components/SettingComponent.xaml
Normal file
24
WorkTime.Mobile/Pages/Components/SettingComponent.xaml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||
xmlns:components="clr-namespace:WorkTime.Mobile.Pages.Components"
|
||||
x:Class="WorkTime.Mobile.Pages.Components.SettingComponent"
|
||||
x:DataType="components:SettingComponent"
|
||||
x:Name="Component">
|
||||
<Grid ColumnDefinitions="*,Auto,80" ColumnSpacing="5">
|
||||
<Label Text="{Binding Title, Source={x:Reference Component}}"
|
||||
VerticalTextAlignment="Center"
|
||||
Grid.Column="0"/>
|
||||
|
||||
<Entry Placeholder="{Binding Placeholder, Source={x:Reference Component}}"
|
||||
Keyboard="Numeric"
|
||||
Grid.Column="1"
|
||||
TextChanged="OnValueChanged"
|
||||
x:Name="EntryField"/>
|
||||
|
||||
<Label Text="{Binding Unit, Source={x:Reference Component}}"
|
||||
VerticalTextAlignment="Center"
|
||||
Grid.Column="2"/>
|
||||
</Grid>
|
||||
</ContentView>
|
||||
59
WorkTime.Mobile/Pages/Components/SettingComponent.xaml.cs
Normal file
59
WorkTime.Mobile/Pages/Components/SettingComponent.xaml.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
namespace WorkTime.Mobile.Pages.Components;
|
||||
|
||||
public partial class SettingComponent : ContentView {
|
||||
public static readonly BindableProperty TitleProperty =
|
||||
BindableProperty.Create(nameof(Title), typeof(string), typeof(SettingComponent), string.Empty);
|
||||
|
||||
public static readonly BindableProperty UnitProperty =
|
||||
BindableProperty.Create(nameof(Unit), typeof(string), typeof(SettingComponent), string.Empty);
|
||||
|
||||
public static readonly BindableProperty PlaceholderProperty =
|
||||
BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(SettingComponent), string.Empty);
|
||||
|
||||
public static readonly BindableProperty ValueProperty =
|
||||
BindableProperty.Create(nameof(Value), typeof(TimeSpan), typeof(SettingComponent), TimeSpan.Zero,
|
||||
BindingMode.TwoWay);
|
||||
|
||||
public string Title {
|
||||
get => (string)GetValue(TitleProperty);
|
||||
set => SetValue(TitleProperty, value);
|
||||
}
|
||||
|
||||
public string Unit {
|
||||
get => (string)GetValue(UnitProperty);
|
||||
set => SetValue(UnitProperty, value);
|
||||
}
|
||||
|
||||
public string Placeholder {
|
||||
get => (string)GetValue(PlaceholderProperty);
|
||||
set => SetValue(PlaceholderProperty, value);
|
||||
}
|
||||
|
||||
public TimeSpan Value {
|
||||
get => (TimeSpan)GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
|
||||
public SettingComponent() {
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnValueChanged(object? sender, TextChangedEventArgs e) {
|
||||
if (!int.TryParse(e.NewTextValue, out var number)) return;
|
||||
|
||||
switch (Unit) {
|
||||
case "Stunden":
|
||||
case "Uhr":
|
||||
Value = TimeSpan.FromHours(number);
|
||||
break;
|
||||
|
||||
case "Minuten":
|
||||
Value = TimeSpan.FromMinutes(number);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearInput() {
|
||||
Content.FindByName<Entry>(nameof(EntryField)).Text = string.Empty;
|
||||
}
|
||||
}
|
||||
97
WorkTime.Mobile/Pages/SettingsPage.xaml
Normal file
97
WorkTime.Mobile/Pages/SettingsPage.xaml
Normal file
@@ -0,0 +1,97 @@
|
||||
<?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:components="clr-namespace:WorkTime.Mobile.Pages.Components"
|
||||
xmlns:pages="clr-namespace:WorkTime.Mobile.Pages"
|
||||
xmlns:icons="http://www.aathifmahir.com/dotnet/2022/maui/icons"
|
||||
x:Class="WorkTime.Mobile.Pages.SettingsPage"
|
||||
x:DataType="pages:SettingsPageModel">
|
||||
|
||||
<ContentPage.Content>
|
||||
<Grid Margin="20,10" RowDefinitions="*,Auto">
|
||||
<VerticalStackLayout Grid.Row="0" x:Name="SettingsStack">
|
||||
<HorizontalStackLayout Margin="0, 0, 0, 5" Spacing="15">
|
||||
<icons:MauiIcon Icon="{icons:Material Work}"
|
||||
IconSize="30"
|
||||
IconColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
|
||||
<Label Text="Zeiten"
|
||||
FontSize="20"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Arbeitszeit"
|
||||
Unit="Stunden"
|
||||
Placeholder="{Binding Settings.WorkTime.TotalHours}"
|
||||
Value="{Binding Settings.WorkTime, Mode=OneWayToSource}"/>
|
||||
|
||||
|
||||
|
||||
<HorizontalStackLayout Margin="0, 10, 0, 5" Spacing="15">
|
||||
<icons:MauiIcon Icon="{icons:Material LocalPizza}"
|
||||
IconSize="30"
|
||||
IconColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
|
||||
<Label Text="Pause"
|
||||
FontSize="20"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Nach 0 Stunden"
|
||||
Unit="Minuten"
|
||||
Placeholder="{Binding Settings.BreakAfter0.TotalMinutes}"
|
||||
Value="{Binding Settings.BreakAfter0, Mode=OneWayToSource}"/>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Nach 6 Stunden"
|
||||
Unit="Minuten"
|
||||
Placeholder="{Binding Settings.BreakAfter6.TotalMinutes}"
|
||||
Value="{Binding Settings.BreakAfter6, Mode=OneWayToSource}"/>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Nach 9 Stunden"
|
||||
Unit="Minuten"
|
||||
Placeholder="{Binding Settings.BreakAfter9.TotalMinutes}"
|
||||
Value="{Binding Settings.BreakAfter9, Mode=OneWayToSource}"/>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Nicht tracken nach"
|
||||
Unit="Uhr"
|
||||
Placeholder="{Binding Settings.DontTrackBreakAfter.TotalHours}"
|
||||
Value="{Binding Settings.DontTrackBreakAfter, Mode=OneWayToSource}"/>
|
||||
|
||||
|
||||
|
||||
<HorizontalStackLayout Margin="0, 10, 0, 5" Spacing="15">
|
||||
<icons:MauiIcon Icon="{icons:Material CreditCard}"
|
||||
IconSize="30"
|
||||
IconColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource White}}" />
|
||||
|
||||
<Label Text="Überstunden"
|
||||
FontSize="20"
|
||||
HorizontalTextAlignment="Center"/>
|
||||
</HorizontalStackLayout>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Optimal"
|
||||
Unit="Minuten"
|
||||
Placeholder="{Binding Settings.IdealOvertime.TotalMinutes}"
|
||||
Value="{Binding Settings.IdealOvertime, Mode=OneWayToSource}"/>
|
||||
|
||||
<components:SettingComponent
|
||||
Title="Maximal"
|
||||
Unit="Minuten"
|
||||
Placeholder="{Binding Settings.MaxOvertime.TotalMinutes}"
|
||||
Value="{Binding Settings.MaxOvertime, Mode=OneWayToSource}"/>
|
||||
</VerticalStackLayout>
|
||||
|
||||
<Button Grid.Row="1"
|
||||
Text="Speichern"
|
||||
HorizontalOptions="Center"
|
||||
Command="{Binding SaveSettingsCommand}"/>
|
||||
</Grid>
|
||||
</ContentPage.Content>
|
||||
</ContentPage>
|
||||
50
WorkTime.Mobile/Pages/SettingsPage.xaml.cs
Normal file
50
WorkTime.Mobile/Pages/SettingsPage.xaml.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using CommunityToolkit.Maui.Alerts;
|
||||
using CommunityToolkit.Maui.Core;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using WorkTime.Mobile.Pages.Components;
|
||||
using WorkTime.Models;
|
||||
using WorkTime.Models.Repositories;
|
||||
|
||||
namespace WorkTime.Mobile.Pages;
|
||||
|
||||
public partial class SettingsPage : ContentPage {
|
||||
private readonly SettingsPageModel _model;
|
||||
|
||||
public SettingsPage(SettingsPageModel model) {
|
||||
InitializeComponent();
|
||||
BindingContext = model;
|
||||
_model = model;
|
||||
}
|
||||
|
||||
protected override void OnAppearing() {
|
||||
base.OnAppearing();
|
||||
|
||||
if (_model.LoadSettingsCommand.CanExecute(null))
|
||||
_model.LoadSettingsCommand.Execute(null);
|
||||
|
||||
foreach (var child in Content.FindByName<VerticalStackLayout>(nameof(SettingsStack)).Children) {
|
||||
if (child is SettingComponent component)
|
||||
component.ClearInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class SettingsPageModel(ISettingsRepository settingsRepository) : ObservableObject {
|
||||
|
||||
[ObservableProperty]
|
||||
public partial Settings Settings { get; set; }
|
||||
|
||||
[RelayCommand]
|
||||
public async Task LoadSettings() {
|
||||
Settings = await settingsRepository.LoadSettings();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
public async Task SaveSettings() {
|
||||
await settingsRepository.SaveSettings(Settings);
|
||||
|
||||
await Toast.Make("Einstellungen gespeichert!").Show();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#512BD4</color>
|
||||
<color name="colorPrimary">#1F1F1F</color>
|
||||
<color name="colorPrimaryDark">#2B0B98</color>
|
||||
<color name="colorAccent">#2B0B98</color>
|
||||
</resources>
|
||||
BIN
WorkTime.Mobile/Resources/AppIcon/appicon.png
Normal file
BIN
WorkTime.Mobile/Resources/AppIcon/appicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 406 B |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="456" height="456" viewBox="0 0 456 456" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="0" y="0" width="456" height="456" fill="#512BD4" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 228 B |
BIN
WorkTime.Mobile/Resources/AppIcon/appiconfg.png
Normal file
BIN
WorkTime.Mobile/Resources/AppIcon/appiconfg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -1,8 +0,0 @@
|
||||
<?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="456" height="456" 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;">
|
||||
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
BIN
WorkTime.Mobile/Resources/Splash/splash.png
Normal file
BIN
WorkTime.Mobile/Resources/Splash/splash.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -1,8 +0,0 @@
|
||||
<?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="456" height="456" 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;">
|
||||
<path d="m 105.50037,281.60863 c -2.70293,0 -5.00091,-0.90042 -6.893127,-2.70209 -1.892214,-1.84778 -2.837901,-4.04181 -2.837901,-6.58209 0,-2.58722 0.945687,-4.80389 2.837901,-6.65167 1.892217,-1.84778 4.190197,-2.77167 6.893127,-2.77167 2.74819,0 5.06798,0.92389 6.96019,2.77167 1.93749,1.84778 2.90581,4.06445 2.90581,6.65167 0,2.54028 -0.96832,4.73431 -2.90581,6.58209 -1.89221,1.80167 -4.212,2.70209 -6.96019,2.70209 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 213.56111,280.08446 H 195.99044 L 149.69953,207.0544 c -1.17121,-1.84778 -2.14037,-3.76515 -2.90581,-5.75126 h -0.40578 c 0.36051,2.12528 0.54076,6.67515 0.54076,13.6496 v 65.13172 h -15.54349 v -99.36009 h 18.71925 l 44.7374,71.29798 c 1.89222,2.95695 3.1087,4.98917 3.64945,6.09751 h 0.26996 c -0.45021,-2.6325 -0.67573,-7.09015 -0.67573,-13.37293 v -64.02256 h 15.47557 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="m 289.25134,280.08446 h -54.40052 v -99.36009 h 52.23835 v 13.99669 h -36.15411 v 28.13085 h 33.31621 v 13.9271 h -33.31621 v 29.37835 h 38.31628 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
<path d="M 366.56466,194.72106 H 338.7222 v 85.3634 h -16.08423 v -85.3634 h -27.77455 v -13.99669 h 71.70124 z" style="fill:#ffffff;fill-rule:nonzero;stroke-width:0.838376" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -19,10 +19,10 @@
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<!-- Display name -->
|
||||
<ApplicationTitle>WorkTime.Mobile</ApplicationTitle>
|
||||
<ApplicationTitle>Zeiterfassung</ApplicationTitle>
|
||||
|
||||
<!-- App Identifier -->
|
||||
<ApplicationId>com.companyname.worktime.mobile</ApplicationId>
|
||||
<ApplicationId>de.leon-hoppe.worktime</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
|
||||
@@ -36,18 +36,19 @@
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'android'">21.0</SupportedOSPlatformVersion>
|
||||
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</SupportedOSPlatformVersion>
|
||||
<TargetPlatformMinVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'windows'">10.0.17763.0</TargetPlatformMinVersion>
|
||||
<LangVersion>preview</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- App Icon -->
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4"/>
|
||||
<MauiIcon Include="Resources\AppIcon\appicon.png" ForegroundFile="Resources\AppIcon\appiconfg.png" Color="#FFFFFF"/>
|
||||
|
||||
<!-- Splash Screen -->
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128"/>
|
||||
<MauiSplashScreen Include="Resources\Splash\splash.png" Color="#FFFFFF" BaseSize="128,128"/>
|
||||
|
||||
<!-- Images -->
|
||||
<MauiImage Include="Resources\Images\*"/>
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="300,185"/>
|
||||
<MauiImage Update="Resources\Images\dotnet_bot.png" Resize="True" BaseSize="512,512"/>
|
||||
|
||||
<!-- Custom Fonts -->
|
||||
<MauiFont Include="Resources\Fonts\*"/>
|
||||
@@ -57,9 +58,17 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AathifMahir.Maui.MauiIcons.Material" Version="5.0.0" />
|
||||
<PackageReference Include="CommunityToolkit.Maui" Version="12.3.0" />
|
||||
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="$(MauiVersion)"/>
|
||||
<PackageReference Include="Microsoft.Maui.Essentials" Version="$(MauiVersion)"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="10.0.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WorkTime.Database\WorkTime.Database.csproj" />
|
||||
<ProjectReference Include="..\WorkTime.Models\WorkTime.Models.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
6
WorkTime.Models/Repositories/ISettingsRepository.cs
Normal file
6
WorkTime.Models/Repositories/ISettingsRepository.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace WorkTime.Models.Repositories;
|
||||
|
||||
public interface ISettingsRepository {
|
||||
Task<Settings> LoadSettings();
|
||||
Task SaveSettings(Settings settings);
|
||||
}
|
||||
9
WorkTime.Models/Repositories/ITimeEntryRepository.cs
Normal file
9
WorkTime.Models/Repositories/ITimeEntryRepository.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace WorkTime.Models.Repositories;
|
||||
|
||||
public interface ITimeEntryRepository {
|
||||
Task<IEnumerable<TimeEntry>> GetTimeEntries();
|
||||
Task<IEnumerable<TimeEntry>> GetTimeEntries(DateOnly date);
|
||||
|
||||
Task AddTimeEntry(TimeEntry entry);
|
||||
Task DeleteTimeEntry(Guid id);
|
||||
}
|
||||
13
WorkTime.Models/Settings.cs
Normal file
13
WorkTime.Models/Settings.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace WorkTime.Models;
|
||||
|
||||
public sealed class Settings {
|
||||
public TimeSpan WorkTime { get; set; } = TimeSpan.FromHours(7);
|
||||
|
||||
public TimeSpan BreakAfter0 { get; set; } = TimeSpan.Zero;
|
||||
public TimeSpan BreakAfter6 { get; set; } = TimeSpan.FromMinutes(30);
|
||||
public TimeSpan BreakAfter9 { get; set; } = TimeSpan.FromMinutes(45);
|
||||
public TimeSpan DontTrackBreakAfter { get; set; } = TimeSpan.FromHours(14);
|
||||
|
||||
public TimeSpan IdealOvertime { get; set; } = TimeSpan.FromMinutes(30);
|
||||
public TimeSpan MaxOvertime { get; set; } = TimeSpan.FromMinutes(60);
|
||||
}
|
||||
21
WorkTime.Models/TimeEntry.cs
Normal file
21
WorkTime.Models/TimeEntry.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WorkTime.Models;
|
||||
|
||||
public sealed class TimeEntry {
|
||||
[Key]
|
||||
public Guid Id { get; init; } = Guid.CreateVersion7();
|
||||
|
||||
public required EntryType Type { get; init; }
|
||||
|
||||
public required DateTime Timestamp { get; set; }
|
||||
}
|
||||
|
||||
public enum EntryType {
|
||||
Login,
|
||||
LoginTrip,
|
||||
LoginHome,
|
||||
Logout,
|
||||
LogoutTrip,
|
||||
LogoutHome
|
||||
}
|
||||
9
WorkTime.Models/WorkTime.Models.csproj
Normal file
9
WorkTime.Models/WorkTime.Models.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,3 +1,8 @@
|
||||
<Solution>
|
||||
<Folder Name="/Modules/">
|
||||
<Project Path="WorkTime.Database/WorkTime.Database.csproj" />
|
||||
<Project Path="WorkTime.Migrator/WorkTime.Migrator.csproj" />
|
||||
<Project Path="WorkTime.Models/WorkTime.Models.csproj" />
|
||||
</Folder>
|
||||
<Project Path="WorkTime.Mobile/WorkTime.Mobile.csproj" />
|
||||
</Solution>
|
||||
|
||||
Reference in New Issue
Block a user