Skip to content

Commit

Permalink
✨ implement add time record functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
russkyc committed May 27, 2024
1 parent ca71706 commit 7e64e51
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 54 deletions.
22 changes: 17 additions & 5 deletions Timely/Components/TimeRecord.razor
Original file line number Diff line number Diff line change
@@ -1,29 +1,41 @@
@using Timely.Models
@using Humanizer
@using Microsoft.EntityFrameworkCore
@using Timely.Services.Data
<MudPaper Elevation="0" Class="rounded-lg bg-[#f3f2fc] p-3">
<MudStack Row="true">
<MudPaper Elevation="0" Class="flex flex-col p-0 flex-shrink-0 w-20 h-20 rounded-lg bg-[--mud-palette-primary]">
<MudStack Spacing="0" Class="m-1 m-auto">
<MudText Class="text-3xl md:text-4xl font-bold text-white mx-auto">@Shift.ShiftStart.ToString("dd")</MudText>
<MudText Class="text-xl font-medium text-white mx-auto">@Shift.ShiftStart.ToString("MMMM")</MudText>
<MudText Class="text-3xl md:text-4xl font-bold text-white mx-auto">@Shift.Date.ToString("dd")</MudText>
<MudText Class="text-xl font-medium text-white mx-auto">@Shift.Date.ToString("MMMM")</MudText>
</MudStack>
</MudPaper>
<MudStack Spacing="2">
<MudText Class="text-2xl md:text-4xl font-bold text-[--mud-palette-primary]">@((Shift.ShiftEnd - Shift.ShiftStart).ToString("hh\\:mm\\:ss")) Shift</MudText>
<MudText Class="text-2xl md:text-4xl font-bold text-[--mud-palette-primary]">@((Shift.ShiftEnd.Value - Shift.ShiftStart.Value).ToString("hh\\:mm\\:ss")) Shift</MudText>

Check warning on line 14 in Timely/Components/TimeRecord.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.

Check warning on line 14 in Timely/Components/TimeRecord.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.
<MudChipSet ReadOnly="true">
<MudChip Color="Color.Primary" Size="Size.Small" Class="p-2">
<MudText Class="text-md font-medium mx-auto">@Shift.ShiftStart.ToString("t")</MudText>
<MudText Class="text-md font-medium mx-auto">@((DateTime.Today + Shift.ShiftStart.Value).ToString("t"))</MudText>

Check warning on line 17 in Timely/Components/TimeRecord.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.
</MudChip>
-
<MudChip Color="Color.Primary" Size="Size.Small" Class="p-2">
<MudText Class="text-md font-medium mx-auto">@Shift.ShiftEnd.ToString("t")</MudText>
<MudText Class="text-md font-medium mx-auto">@((DateTime.Today + Shift.ShiftEnd.Value).ToString("t"))</MudText>

Check warning on line 21 in Timely/Components/TimeRecord.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.
</MudChip>
</MudChipSet>
</MudStack>
<MudButton OnClick="@Delete" StartIcon="@Icons.Material.Rounded.Delete" Color="Color.Primary" Variant="Variant.Text" DisableElevation="true">
</MudButton>
</MudStack>
</MudPaper>

@code {

[Parameter] public Shift Shift { get; set; }

Check warning on line 32 in Timely/Components/TimeRecord.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Non-nullable property 'Shift' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.

private async Task Delete()
{
await using var db = new AppDbContext();
await db.Shifts.Where(shift => shift.Id == Shift.Id).ExecuteDeleteAsync();
await db.SaveToCacheAsync();
}

}
38 changes: 38 additions & 0 deletions Timely/Layout/AddRecordDialog.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
@using Timely.Models
<MudDialog>
<TitleContent>
<MudText Typo="Typo.h6">
<MudIcon Icon="@Icons.Material.Filled.AddAlarm" Class="mr-3 mb-n1"/>
Add Record
</MudText>
</TitleContent>
<DialogContent>
<MudDatePicker bind-Date="@_date" Label="Date"></MudDatePicker>
<MudTimePicker AmPm="true" @bind-Time="@Shift.ShiftStart" Label="Shift Start"/>
<MudTimePicker AmPm="true" @bind-Time="@Shift.ShiftEnd" Label="Shift End"/>
</DialogContent>
<DialogActions>
<MudButton OnClick="Cancel">Cancel</MudButton>
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AddRecord">Add</MudButton>
</DialogActions>
</MudDialog>

@code {

private DateTime? _date = DateTime.Today;

[CascadingParameter] MudDialogInstance MudDialog { get; set; }
[Parameter] public Shift Shift { get; set; } = new();

private void Cancel()
{
MudDialog.Cancel();
}

private void AddRecord()
{
Shift.Date = _date is not null ? DateTime.Today : _date!.Value;
MudDialog.Close(DialogResult.Ok(Shift));
}

}
7 changes: 5 additions & 2 deletions Timely/Models/Shift.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

public record Shift
{
public DateTime ShiftStart { get; set; }
public DateTime ShiftEnd { get; set; }
public int Id { get; set; }
public DateTime Date { get; set; }
public TimeSpan? ShiftStart { get; set; }
public TimeSpan? ShiftEnd { get; set; }
public bool Active { get; set; }
}
38 changes: 28 additions & 10 deletions Timely/Pages/Home.razor
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<MudPaper Elevation="0" Class="rounded-lg bg-[--mud-palette-primary]">
<MudStack Spacing="0" Class="m-4">
<MudText Class="text-lg md:text-xl font-medium text-white mx-auto">@_currentTime.ToString("dddd, MMMM dd - hh\\:mm\\:ss tt")</MudText>
<MudText Class="text-4xl mt-4 md:text-6xl font-bold text-white mx-auto">@((DateTime.Now - _currentShift.ShiftStart).ToString(@"hh\:mm\:ss"))</MudText>
<MudText Class="text-4xl mt-4 md:text-6xl font-bold text-white mx-auto">@((DateTime.Now - _currentShift.ShiftStart.Value).ToString(@"hh\:mm\:ss"))</MudText>

Check warning on line 15 in Timely/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Dereference of a possibly null reference.

Check warning on line 15 in Timely/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.
<MudText Class="mt-4 text-xl md:text-2xl font-bold text-[--mud-palette-primary] mx-auto bg-white p-2 px-4 rounded-lg">Shift Ongoing</MudText>
</MudStack>
</MudPaper>
Expand Down Expand Up @@ -64,8 +64,8 @@
<MudText Class="text-4xl md:text-6xl font-bold text-white mx-auto">@($"{_timeLogged.Value.TotalHours:0.00} Hours")</MudText>
</MudStack>
</MudPaper>
<MudButton OnClick="@ClearRecords" StartIcon="@Icons.Material.Rounded.Delete" Color="Color.Primary" Variant="Variant.Text" DisableElevation="true">
<MudText Class="text-xl font-medium">Clear Records</MudText>
<MudButton OnClick="@AddRecord" StartIcon="@Icons.Material.Filled.AddAlarm" Color="Color.Primary" Variant="Variant.Text" DisableElevation="true">
<MudText Class="text-xl font-medium">Add Record</MudText>
</MudButton>
}

Expand All @@ -77,11 +77,17 @@
{
<TimeRecord Shift="shift"/>
}
<MudButton OnClick="@ClearRecords" StartIcon="@Icons.Material.Rounded.Delete" Color="Color.Primary" Variant="Variant.Text" DisableElevation="true">
<MudText Class="text-xl font-medium">Clear Records</MudText>
</MudButton>
</MudStack>
</div>
}
else
{
<MudButton OnClick="@AddRecord" StartIcon="@Icons.Material.Filled.AddAlarm" Color="Color.Primary" Variant="Variant.Text" DisableElevation="true">
<MudText Class="text-xl font-medium">Add Record</MudText>
</MudButton>
<div class="flex h-full w-full">
<MudText Class="m-auto">No data recorded.</MudText>
</div>
Expand All @@ -107,7 +113,7 @@
private DateTime _currentTime = DateTime.Now;
private TimeSpan? _timeLogged;

private LinkedList<Shift>? _timeRecords = new();
private IEnumerable<Shift>? _timeRecords;

protected override async Task OnInitializedAsync()

Check warning on line 118 in Timely/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
{
Expand All @@ -118,7 +124,7 @@
{
_currentShift = new Shift
{
ShiftStart = DateTime.Now
ShiftStart = DateTime.Now.TimeOfDay
};

await ShiftManager.StartShift();
Expand All @@ -135,6 +141,21 @@
Snackbar.Add("Shift ended, record saved");
}

async Task AddRecord()
{
var dialog = await DialogService.ShowAsync<AddRecordDialog>("Add Record");
var result = await dialog.Result;

if (result.Canceled)
{
return;
}

if (result.Data is not Shift shift) return;

await ShiftManager.AddTimeRecord(shift);
}

async Task ClearRecords()
{
var result = await DialogService.ShowMessageBox("Clear Time Records", "This action cannot be undone, continue?");
Expand All @@ -145,7 +166,6 @@
}

await ShiftManager.ClearTimeRecords();
await ShiftManager.GetDataFromLocalStorage();
_timeRecords = await ShiftManager.GetTimeRecords();
Snackbar.Add("Time records cleared");
}
Expand All @@ -154,8 +174,6 @@
{
_currentTime = DateTime.Now;

await ShiftManager.GetDataFromLocalStorage();

_timeRecords = await ShiftManager.GetTimeRecords();
_currentShift = await ShiftManager.GetCurrentShift();

Expand All @@ -167,8 +185,8 @@
if (_timeRecords is not null && _timeRecords.Any())
{
var totalTimeRecordTicks = _timeRecords.Select(
timeRecord => new TimeSpan(timeRecord.ShiftEnd.Ticks - timeRecord.ShiftStart.Ticks))
.Sum(timeSpans => timeSpans.Ticks);
timeRecord => timeRecord.ShiftEnd - timeRecord.ShiftStart)
.Sum(timerecord => timerecord.Value.Ticks);

Check warning on line 189 in Timely/Pages/Home.razor

View workflow job for this annotation

GitHub Actions / deploy-to-github-pages

Nullable value type may be null.

_timeLogged = new TimeSpan(totalTimeRecordTicks);
}
Expand Down
7 changes: 6 additions & 1 deletion Timely/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BlazorDB;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
Expand All @@ -19,4 +20,8 @@
// App Services
builder.Services.AddScoped<ShiftManager>();

await builder.Build().RunAsync();
var app = builder.Build();

await app.Services.ConfigureBlazorDBAsync<AppDbContext>();

await app.RunAsync();
10 changes: 10 additions & 0 deletions Timely/Services/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using BlazorDB;
using Microsoft.EntityFrameworkCore;
using Timely.Models;

namespace Timely.Services.Data;

public class AppDbContext : BlazorDBContext
{
public DbSet<Shift> Shifts { get; set; }
}
94 changes: 58 additions & 36 deletions Timely/Services/Data/ShiftManager.cs
Original file line number Diff line number Diff line change
@@ -1,75 +1,97 @@
using Blazored.LocalStorage;
using Microsoft.EntityFrameworkCore;
using Timely.Models;

namespace Timely.Services.Data;

public class ShiftManager
{
private Shift? _currentShift;
private LinkedList<Shift>? _timeRecords;
private ILocalStorageService _localStorage;

public ShiftManager(ILocalStorageService localStorage)
{
_localStorage = localStorage;
_timeRecords = new LinkedList<Shift>();
}

public async Task GetDataFromLocalStorage()
{
_currentShift = await _localStorage.GetItemAsync<Shift?>("current-shift");

var timeRecords = await _localStorage.GetItemAsync<LinkedList<Shift>?>("time-records");
_timeRecords = timeRecords ?? new LinkedList<Shift>(Enumerable.Empty<Shift>());

await UpdateTimeRecords();
}
private Shift? _activeShift;

public async Task UpdateTimeRecords()
public ShiftManager()
{
await _localStorage.SetItemAsync("time-records", _timeRecords);
using var db = new AppDbContext();
db.Database.EnsureCreated();
}

public async Task AddTimeRecord(Shift shift)
{
_timeRecords.AddFirst(shift);
await UpdateTimeRecords();
await GetDataFromLocalStorage();
await using var db = new AppDbContext();
await db.Shifts.AddAsync(shift);
await db.SaveToCacheAsync();
}

public async Task<LinkedList<Shift>?> GetTimeRecords()
public async Task<IEnumerable<Shift>?> GetTimeRecords()
{
return await _localStorage.GetItemAsync<LinkedList<Shift>?>("time-records");
await using var db = new AppDbContext();
return await db.Shifts.Where(shift => !shift.Active).ToListAsync();
}

public async Task ClearTimeRecords()
{
await _localStorage.RemoveItemAsync("time-records");
await GetDataFromLocalStorage();
await using var db = new AppDbContext();
await db.Shifts.AsQueryable().ExecuteDeleteAsync();
await db.SaveToCacheAsync();
}

public async Task<Shift?> GetCurrentShift()
{
return await _localStorage.GetItemAsync<Shift>("current-shift");
if (_activeShift is not null) return _activeShift;

await using var db = new AppDbContext();
_activeShift = await db.Shifts.FirstOrDefaultAsync(shift => shift.Active);

return _activeShift;
}

public async Task AddShift(TimeSpan start, TimeSpan end, DateTime? date = null)
{

var shift = new Shift()
{
ShiftStart = start,
ShiftEnd = end,
Date = date ?? DateTime.Today
};

await using var db = new AppDbContext();
await db.Shifts.AddAsync(shift);
await db.SaveToCacheAsync();

}

public async Task StartShift()
{
_currentShift ??= new Shift()
if (_activeShift is not null) return;

var shift = new Shift()
{
ShiftStart = DateTime.Now
Date = DateTime.Today,
ShiftStart = DateTime.Now.TimeOfDay,
Active = true
};

await _localStorage.SetItemAsync("current-shift",_currentShift);
await using var db = new AppDbContext();
var entry = await db.Shifts.AddAsync(shift);
await db.SaveToCacheAsync();

_activeShift = entry.Entity;

}

public async Task EndShift()
{
_currentShift.ShiftEnd = DateTime.Now;
await GetCurrentShift();

await using var db = new AppDbContext();
if (_activeShift is null) return;

_activeShift.Active = false;
_activeShift.ShiftEnd = DateTime.Now.TimeOfDay;

await AddTimeRecord(_currentShift);
db.Shifts.Update(_activeShift);
await db.SaveToCacheAsync();

await _localStorage.RemoveItemAsync("current-shift");
await GetDataFromLocalStorage();
_activeShift = null;
}
}
1 change: 1 addition & 0 deletions Timely/Timely.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Blazor.DB" Version="1.2.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.5.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Majorsoft.Blazor.Components.Timer" Version="1.5.0" />
Expand Down

0 comments on commit 7e64e51

Please sign in to comment.