Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions CafeTracker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# =========================
# .NET
# =========================
bin/
obj/

# User-specific files
*.user
*.suo
appsettings.Development.json

# Visual Studio
.vs/

# Rider
.idea/

# =========================
# Angular / Node
# =========================
node_modules/
dist/

# Logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Environment files
.env

# =========================
# VS Code
# =========================
.vscode/

# =========================
# OS Files
# =========================
.DS_Store
Thumbs.db
24 changes: 24 additions & 0 deletions CafeTracker/backend/CafeTracker.API/CafeTracker.API.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.6"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.1" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.7" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions CafeTracker/backend/CafeTracker.API/CafeTracker.API.http
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@CafeTracker.API_HostAddress = http://localhost:5171

GET {{CafeTracker.API_HostAddress}}/weatherforecast/
Accept: application/json

###
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using CafeTracker.API.Data;
using CafeTracker.API.Models;
using CafeTracker.API.Models.Dtos;
using CafeTracker.API.Services;
using Microsoft.AspNetCore.Mvc;

namespace CafeTracker.API.Controllers;
[ApiController]
[Route("api/cafe-records")]
public class CafeRecordController: ControllerBase
{
private readonly ILogger<CafeRecordController> _logger;

private readonly ICafeRecordService _cafeRecordService;
// GET
public CafeRecordController(ILogger<CafeRecordController> logger, ICafeRecordService cafeRecordService)
{
_logger = logger;
_cafeRecordService = cafeRecordService;
}

[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] string? drinkName, [FromQuery] DateOnly? date, [FromQuery] ProductCategory? category)
{
var result = await _cafeRecordService.GetCafeRecords(drinkName, date, category);
return Ok(result);
}

[HttpGet("{id:int}")]
public async Task<IActionResult> GetById(int id)
{
var result = await _cafeRecordService.GetCafeRecordById(id);
return Ok(result);
}

[HttpPost]
public async Task<IActionResult> Create([FromBody]CreateCafeRecord record)
{
var result = await _cafeRecordService.CreateCafeRecord(record);
return CreatedAtAction(nameof(GetById), new { id = result.Id }, result);
}

[HttpPut("{id:int}")]
public async Task<IActionResult> Update(int id, UpdateCafeRecord record)
{
var result = await _cafeRecordService.UpdateCafeRecord(id, record);
return Ok(result);
}

[HttpDelete("{id:int}")]
public async Task<IActionResult> Delete(int id)
{
await _cafeRecordService.DeleteCafeRecord(id);
return NoContent();
}
}
201 changes: 201 additions & 0 deletions CafeTracker/backend/CafeTracker.API/Data/AppDbContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
using CafeTracker.API.Models;
using Microsoft.EntityFrameworkCore;

namespace CafeTracker.API.Data;

public class AppDbContext : DbContext
{


public AppDbContext(DbContextOptions options) : base(options)
{
}
public DbSet<CafeRecord> CafeRecords { get; set; } = null!;

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CafeRecord>(entity =>
{
entity.HasKey(c => c.Id);
entity.Property(c => c.ProductName).IsRequired().HasMaxLength(100);
entity.Property(c => c.Quantity).IsRequired().HasDefaultValue(1);
entity.Property(c=> c.Notes).HasMaxLength(500);
entity.Property(c => c.DateConsumed).IsRequired();
entity.Property(c => c.DateCreated).IsRequired();
entity.Property(c => c.DateModified).IsRequired();
entity.HasIndex(c => c.ProductName);
entity.Property(c => c.Category).IsRequired();
});

modelBuilder.Entity<CafeRecord>().HasData(SeedCafeRecords);
base.OnModelCreating(modelBuilder);
}


public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
var entries = ChangeTracker.Entries<CafeRecord>();

foreach (var entry in entries)
{
if (entry.State == EntityState.Added)
{
entry.Entity.DateCreated = DateTimeOffset.UtcNow;
entry.Entity.DateModified = DateTimeOffset.UtcNow;
}

if (entry.State == EntityState.Modified)
{
entry.Entity.DateModified = DateTimeOffset.UtcNow;
}
}

return base.SaveChangesAsync(cancellationToken);
}
private static readonly CafeRecord[] SeedCafeRecords =
[
new CafeRecord
{
Id = 1,
ProductName = "Cappuccino",
Category = ProductCategory.Coffee,
Quantity = 5,
DateConsumed = new DateTime(2026, 5, 1, 0, 0, 0, DateTimeKind.Utc),
Notes = "Had with breakfast",
DateCreated = new DateTimeOffset(2026, 5, 2, 15, 45, 20, 391, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 2, 15, 45, 20, 391, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 2,
ProductName = "Cappuccino",
Category = ProductCategory.Coffee,
Quantity = 2,
DateConsumed = new DateTime(2026, 5, 6, 11, 30, 19, 672, DateTimeKind.Utc),
Notes = "Served with breakfast",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 29, 3, 739, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 29, 3, 739, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 3,
ProductName = "Latte",
Category = ProductCategory.Coffee,
Quantity = 3,
DateConsumed = new DateTime(2026, 5, 2, 0, 0, 0, DateTimeKind.Utc),
Notes = "Had with dinner",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 29, 18, 170, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 29, 18, 170, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 4,
ProductName = "Americano",
Category = ProductCategory.Coffee,
Quantity = 1,
DateConsumed = new DateTime(2026, 5, 3, 0, 0, 0, DateTimeKind.Utc),
Notes = "Early morning meeting prep",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 29, 30, 65, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 29, 30, 65, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 5,
ProductName = "Mocha",
Category = ProductCategory.Coffee,
Quantity = 2,
DateConsumed = new DateTime(2026, 5, 4, 0, 0, 0, DateTimeKind.Utc),
Notes = "Coffee break with friends",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 29, 40, 838, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 29, 40, 838, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 6,
ProductName = "Flat White",
Category = ProductCategory.Coffee,
Quantity = 2,
DateConsumed = new DateTime(2026, 5, 5, 0, 0, 0, DateTimeKind.Utc),
Notes = "Needed energy for coding session",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 29, 55, 56, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 29, 55, 56, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 7,
ProductName = "Macchiato",
Category = ProductCategory.Coffee,
Quantity = 1,
DateConsumed = new DateTime(2026, 5, 5, 0, 0, 0, DateTimeKind.Utc),
Notes = "Short coffee break",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 30, 4, 288, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 30, 4, 288, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 8,
ProductName = "Cold Brew",
Category = ProductCategory.ColdDrink,
Quantity = 3,
DateConsumed = new DateTime(2026, 5, 6, 0, 0, 0, DateTimeKind.Utc),
Notes = "Very hot afternoon",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 30, 13, 238, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 30, 13, 238, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 9,
ProductName = "Irish Coffee",
Category = ProductCategory.Coffee,
Quantity = 1,
DateConsumed = new DateTime(2026, 5, 6, 0, 0, 0, DateTimeKind.Utc),
Notes = "Relaxing evening drink",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 30, 21, 227, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 30, 21, 227, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 10,
ProductName = "Vanilla Latte",
Category = ProductCategory.Coffee,
Quantity = 2,
DateConsumed = new DateTime(2026, 5, 7, 0, 0, 0, DateTimeKind.Utc),
Notes = "Breakfast companion",
DateCreated = new DateTimeOffset(2026, 5, 4, 11, 30, 28, 810, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 4, 11, 30, 28, 810, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 11,
ProductName = "Espresso",
Category = ProductCategory.Coffee,
Quantity = 10,
DateConsumed = new DateTime(2010, 10, 10, 0, 0, 0, DateTimeKind.Utc),
Notes = "Delicious and healthy",
DateCreated = new DateTimeOffset(2026, 5, 6, 11, 45, 8, 57, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 6, 11, 45, 8, 57, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 13,
ProductName = "Hollandia Yoghurt",
Category = ProductCategory.Other,
Quantity = 2,
DateConsumed = new DateTime(2010, 10, 10, 0, 0, 0, DateTimeKind.Utc),
Notes = "Cold, creamy and tasty",
DateCreated = new DateTimeOffset(2026, 5, 9, 14, 12, 43, 178, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 9, 14, 12, 43, 178, TimeSpan.FromHours(1))
},
new CafeRecord
{
Id = 15,
ProductName = "Hot Choco",
Category = ProductCategory.HotChocolate,
Quantity = 10,
DateConsumed = new DateTime(2001, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Notes = "Cold beverage for everyday nourishment",
DateCreated = new DateTimeOffset(2026, 5, 9, 14, 24, 10, 550, TimeSpan.FromHours(1)),
DateModified = new DateTimeOffset(2026, 5, 9, 14, 24, 10, 550, TimeSpan.FromHours(1))
}
];
}
18 changes: 18 additions & 0 deletions CafeTracker/backend/CafeTracker.API/Extensions/EnumExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.ComponentModel;
using System.Reflection;

namespace CafeTracker.API.Extensions;

public static class EnumExtensions

{
public static string GetDescription(this Enum value)
{
var field = value.GetType().GetField(value.ToString());

var attribute = field?.GetCustomAttribute<DescriptionAttribute>();

return attribute?.Description ?? value.ToString();
}
}

Loading