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
73 changes: 22 additions & 51 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,59 +1,30 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Build outputs
bin/
obj/

# Coverage directory used by tools like istanbul
coverage
# User-specific files
*.user
*.suo
*.userosscache
*.sln.docstates

# nyc test coverage
.nyc_output
# Rider
.idea/

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# VS Code
.vscode/

# Bower dependency directory (https://bower.io/)
bower_components
# ASP.NET Core secrets
appsettings.*.json
!appsettings.json
!appsettings.Development.json

# node-waf configuration
.lock-wscript
# Logs
*.log

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# OS
.DS_Store
Thumbs.db

# Dependency directories
# Node (optional tooling)
node_modules/
jspm_packages/

# Typescript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

7 changes: 7 additions & 0 deletions ExpenseTracker.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>
114 changes: 114 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
using System.Globalization;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseDefaultFiles();
app.UseStaticFiles();

var transactions = new List<Transaction>
{
new(
Id: Guid.NewGuid(),
Title: "Morning coffee",
Amount: -4.5m,
Category: "Food & Drink",
Tags: new List<string> { "Cafe", "Daily" },
Location: "Downtown",
Date: DateTimeOffset.Now.AddHours(-5),
PaymentMethod: "Card"
),
new(
Id: Guid.NewGuid(),
Title: "Metro pass",
Amount: -22m,
Category: "Transport",
Tags: new List<string> { "Commute" },
Location: "Central Station",
Date: DateTimeOffset.Now.AddDays(-1),
PaymentMethod: "Card"
),
new(
Id: Guid.NewGuid(),
Title: "Freelance invoice",
Amount: 540m,
Category: "Income",
Tags: new List<string> { "Client" },
Location: "Remote",
Date: DateTimeOffset.Now.AddDays(-2),
PaymentMethod: "Bank"
)
};

app.MapGet("/api/transactions", () => Results.Ok(transactions.OrderByDescending(t => t.Date)));

app.MapPost("/api/transactions", (TransactionInput input) =>
{
var transaction = new Transaction(
Id: Guid.NewGuid(),
Title: input.Title,
Amount: input.Amount,
Category: input.Category,
Tags: input.Tags ?? new List<string>(),
Location: input.Location ?? "",
Date: input.Date ?? DateTimeOffset.Now,
PaymentMethod: input.PaymentMethod ?? ""
);

transactions.Add(transaction);

return Results.Created($"/api/transactions/{transaction.Id}", transaction);
});

app.MapPost("/api/transcribe", (TranscriptionRequest request) =>
{
if (string.IsNullOrWhiteSpace(request.Transcript))
{
return Results.BadRequest(new { message = "No audio transcript received." });
}

var normalized = request.Transcript.Trim();
var suggestion = normalized.Contains("coffee", StringComparison.OrdinalIgnoreCase)
? new TransactionSuggestion("Coffee", -4.5m, "Food & Drink")
: new TransactionSuggestion("Voice entry", -12m, "Everyday");

return Results.Ok(new
{
transcript = normalized,
suggestion,
confidence = 0.82
});
});

app.MapFallbackToFile("/index.html");

app.Run();

record Transaction(
Guid Id,
string Title,
decimal Amount,
string Category,
List<string> Tags,
string Location,
DateTimeOffset Date,
string PaymentMethod
)
{
public string DateLabel => Date.ToString("MMM d, yyyy", CultureInfo.InvariantCulture);
}

record TransactionInput(
string Title,
decimal Amount,
string Category,
List<string>? Tags,
string? Location,
DateTimeOffset? Date,
string? PaymentMethod
);

record TranscriptionRequest(string Transcript);

record TransactionSuggestion(string Title, decimal Amount, string Category);
20 changes: 16 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
## Try it
# Everyday Expense Tracker (.NET)

[https://my-json-server.typicode.com/typicode/demo](https://my-json-server.typicode.com/typicode/demo)
A minimal, voice-forward expense tracker prototype built with ASP.NET Core and a lightweight HTML/CSS/JS UI.

## Use your own data
## Features
- Period stats for today, week, and month
- Transaction history with quick filters
- Add expense/income with categories, tags, and location
- Voice capture flow with confirmation + retry

Fork it and change `db.json` values or create a repo with a `db.json` file.
## Run locally
```bash
dotnet run
```
Then open `http://localhost:5000` (or the URL shown in the console).

## Notes
- Voice capture uses the browser Speech Recognition API when available.
- `/api/transcribe` is a minimal stub that returns a suggestion from the transcript.
9 changes: 9 additions & 0 deletions appsettings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}
12 changes: 0 additions & 12 deletions db.json

This file was deleted.

Loading