Add finance tracker design spec and update .gitignore
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c9b40f980a
commit
74f924c715
831
.gitignore
vendored
831
.gitignore
vendored
@ -1,414 +1,417 @@
|
|||||||
# ---> VisualStudio
|
# ---> VisualStudio
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
*.suo
|
*.suo
|
||||||
*.user
|
*.user
|
||||||
*.userosscache
|
*.userosscache
|
||||||
*.sln.docstates
|
*.sln.docstates
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
*.userprefs
|
*.userprefs
|
||||||
|
|
||||||
# Mono auto generated files
|
# Mono auto generated files
|
||||||
mono_crash.*
|
mono_crash.*
|
||||||
|
|
||||||
# Build results
|
# Build results
|
||||||
[Dd]ebug/
|
[Dd]ebug/
|
||||||
[Dd]ebugPublic/
|
[Dd]ebugPublic/
|
||||||
[Rr]elease/
|
[Rr]elease/
|
||||||
[Rr]eleases/
|
[Rr]eleases/
|
||||||
x64/
|
x64/
|
||||||
x86/
|
x86/
|
||||||
[Ww][Ii][Nn]32/
|
[Ww][Ii][Nn]32/
|
||||||
[Aa][Rr][Mm]/
|
[Aa][Rr][Mm]/
|
||||||
[Aa][Rr][Mm]64/
|
[Aa][Rr][Mm]64/
|
||||||
bld/
|
bld/
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
[Ll]og/
|
[Ll]og/
|
||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
#wwwroot/
|
#wwwroot/
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
# Visual Studio 2017 auto generated files
|
||||||
Generated\ Files/
|
Generated\ Files/
|
||||||
|
|
||||||
# MSTest test Results
|
# MSTest test Results
|
||||||
[Tt]est[Rr]esult*/
|
[Tt]est[Rr]esult*/
|
||||||
[Bb]uild[Ll]og.*
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
# NUnit
|
# NUnit
|
||||||
*.VisualState.xml
|
*.VisualState.xml
|
||||||
TestResult.xml
|
TestResult.xml
|
||||||
nunit-*.xml
|
nunit-*.xml
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
# Build Results of an ATL Project
|
||||||
[Dd]ebugPS/
|
[Dd]ebugPS/
|
||||||
[Rr]eleasePS/
|
[Rr]eleasePS/
|
||||||
dlldata.c
|
dlldata.c
|
||||||
|
|
||||||
# Benchmark Results
|
# Benchmark Results
|
||||||
BenchmarkDotNet.Artifacts/
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
# .NET Core
|
# .NET Core
|
||||||
project.lock.json
|
project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
# ASP.NET Scaffolding
|
||||||
ScaffoldingReadMe.txt
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
# StyleCop
|
# StyleCop
|
||||||
StyleCopReport.xml
|
StyleCopReport.xml
|
||||||
|
|
||||||
# Files built by Visual Studio
|
# Files built by Visual Studio
|
||||||
*_i.c
|
*_i.c
|
||||||
*_p.c
|
*_p.c
|
||||||
*_h.h
|
*_h.h
|
||||||
*.ilk
|
*.ilk
|
||||||
*.meta
|
*.meta
|
||||||
*.obj
|
*.obj
|
||||||
*.iobj
|
*.iobj
|
||||||
*.pch
|
*.pch
|
||||||
*.pdb
|
*.pdb
|
||||||
*.ipdb
|
*.ipdb
|
||||||
*.pgc
|
*.pgc
|
||||||
*.pgd
|
*.pgd
|
||||||
*.rsp
|
*.rsp
|
||||||
*.sbr
|
*.sbr
|
||||||
*.tlb
|
*.tlb
|
||||||
*.tli
|
*.tli
|
||||||
*.tlh
|
*.tlh
|
||||||
*.tmp
|
*.tmp
|
||||||
*.tmp_proj
|
*.tmp_proj
|
||||||
*_wpftmp.csproj
|
*_wpftmp.csproj
|
||||||
*.log
|
*.log
|
||||||
*.tlog
|
*.tlog
|
||||||
*.vspscc
|
*.vspscc
|
||||||
*.vssscc
|
*.vssscc
|
||||||
.builds
|
.builds
|
||||||
*.pidb
|
*.pidb
|
||||||
*.svclog
|
*.svclog
|
||||||
*.scc
|
*.scc
|
||||||
|
|
||||||
# Chutzpah Test files
|
# Chutzpah Test files
|
||||||
_Chutzpah*
|
_Chutzpah*
|
||||||
|
|
||||||
# Visual C++ cache files
|
# Visual C++ cache files
|
||||||
ipch/
|
ipch/
|
||||||
*.aps
|
*.aps
|
||||||
*.ncb
|
*.ncb
|
||||||
*.opendb
|
*.opendb
|
||||||
*.opensdf
|
*.opensdf
|
||||||
*.sdf
|
*.sdf
|
||||||
*.cachefile
|
*.cachefile
|
||||||
*.VC.db
|
*.VC.db
|
||||||
*.VC.VC.opendb
|
*.VC.VC.opendb
|
||||||
|
|
||||||
# Visual Studio profiler
|
# Visual Studio profiler
|
||||||
*.psess
|
*.psess
|
||||||
*.vsp
|
*.vsp
|
||||||
*.vspx
|
*.vspx
|
||||||
*.sap
|
*.sap
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
# Visual Studio Trace Files
|
||||||
*.e2e
|
*.e2e
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
# TFS 2012 Local Workspace
|
||||||
$tf/
|
$tf/
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
# Guidance Automation Toolkit
|
||||||
*.gpState
|
*.gpState
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
# ReSharper is a .NET coding add-in
|
||||||
_ReSharper*/
|
_ReSharper*/
|
||||||
*.[Rr]e[Ss]harper
|
*.[Rr]e[Ss]harper
|
||||||
*.DotSettings.user
|
*.DotSettings.user
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
# TeamCity is a build add-in
|
||||||
_TeamCity*
|
_TeamCity*
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
# DotCover is a Code Coverage Tool
|
||||||
*.dotCover
|
*.dotCover
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
# AxoCover is a Code Coverage Tool
|
||||||
.axoCover/*
|
.axoCover/*
|
||||||
!.axoCover/settings.json
|
!.axoCover/settings.json
|
||||||
|
|
||||||
# Coverlet is a free, cross platform Code Coverage Tool
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
coverage*.json
|
coverage*.json
|
||||||
coverage*.xml
|
coverage*.xml
|
||||||
coverage*.info
|
coverage*.info
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
# Visual Studio code coverage results
|
||||||
*.coverage
|
*.coverage
|
||||||
*.coveragexml
|
*.coveragexml
|
||||||
|
|
||||||
# NCrunch
|
# NCrunch
|
||||||
_NCrunch_*
|
_NCrunch_*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
nCrunchTemp_*
|
nCrunchTemp_*
|
||||||
|
|
||||||
# MightyMoose
|
# MightyMoose
|
||||||
*.mm.*
|
*.mm.*
|
||||||
AutoTest.Net/
|
AutoTest.Net/
|
||||||
|
|
||||||
# Web workbench (sass)
|
# Web workbench (sass)
|
||||||
.sass-cache/
|
.sass-cache/
|
||||||
|
|
||||||
# Installshield output folder
|
# Installshield output folder
|
||||||
[Ee]xpress/
|
[Ee]xpress/
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
# DocProject is a documentation generator add-in
|
||||||
DocProject/buildhelp/
|
DocProject/buildhelp/
|
||||||
DocProject/Help/*.HxT
|
DocProject/Help/*.HxT
|
||||||
DocProject/Help/*.HxC
|
DocProject/Help/*.HxC
|
||||||
DocProject/Help/*.hhc
|
DocProject/Help/*.hhc
|
||||||
DocProject/Help/*.hhk
|
DocProject/Help/*.hhk
|
||||||
DocProject/Help/*.hhp
|
DocProject/Help/*.hhp
|
||||||
DocProject/Help/Html2
|
DocProject/Help/Html2
|
||||||
DocProject/Help/html
|
DocProject/Help/html
|
||||||
|
|
||||||
# Click-Once directory
|
# Click-Once directory
|
||||||
publish/
|
publish/
|
||||||
|
|
||||||
# Publish Web Output
|
# Publish Web Output
|
||||||
*.[Pp]ublish.xml
|
*.[Pp]ublish.xml
|
||||||
*.azurePubxml
|
*.azurePubxml
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
*.pubxml
|
*.pubxml
|
||||||
*.publishproj
|
*.publishproj
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
# in these scripts will be unencrypted
|
# in these scripts will be unencrypted
|
||||||
PublishScripts/
|
PublishScripts/
|
||||||
|
|
||||||
# NuGet Packages
|
# NuGet Packages
|
||||||
*.nupkg
|
*.nupkg
|
||||||
# NuGet Symbol Packages
|
# NuGet Symbol Packages
|
||||||
*.snupkg
|
*.snupkg
|
||||||
# The packages folder can be ignored because of Package Restore
|
# The packages folder can be ignored because of Package Restore
|
||||||
**/[Pp]ackages/*
|
**/[Pp]ackages/*
|
||||||
# except build/, which is used as an MSBuild target.
|
# except build/, which is used as an MSBuild target.
|
||||||
!**/[Pp]ackages/build/
|
!**/[Pp]ackages/build/
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
#!**/[Pp]ackages/repositories.config
|
#!**/[Pp]ackages/repositories.config
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
*.nuget.props
|
*.nuget.props
|
||||||
*.nuget.targets
|
*.nuget.targets
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
# Microsoft Azure Build Output
|
||||||
csx/
|
csx/
|
||||||
*.build.csdef
|
*.build.csdef
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
# Microsoft Azure Emulator
|
||||||
ecf/
|
ecf/
|
||||||
rcf/
|
rcf/
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
# Windows Store app package directories and files
|
||||||
AppPackages/
|
AppPackages/
|
||||||
BundleArtifacts/
|
BundleArtifacts/
|
||||||
Package.StoreAssociation.xml
|
Package.StoreAssociation.xml
|
||||||
_pkginfo.txt
|
_pkginfo.txt
|
||||||
*.appx
|
*.appx
|
||||||
*.appxbundle
|
*.appxbundle
|
||||||
*.appxupload
|
*.appxupload
|
||||||
|
|
||||||
# Visual Studio cache files
|
# Visual Studio cache files
|
||||||
# files ending in .cache can be ignored
|
# files ending in .cache can be ignored
|
||||||
*.[Cc]ache
|
*.[Cc]ache
|
||||||
# but keep track of directories ending in .cache
|
# but keep track of directories ending in .cache
|
||||||
!?*.[Cc]ache/
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
# Others
|
# Others
|
||||||
ClientBin/
|
ClientBin/
|
||||||
~$*
|
~$*
|
||||||
*~
|
*~
|
||||||
*.dbmdl
|
*.dbmdl
|
||||||
*.dbproj.schemaview
|
*.dbproj.schemaview
|
||||||
*.jfm
|
*.jfm
|
||||||
*.pfx
|
*.pfx
|
||||||
*.publishsettings
|
*.publishsettings
|
||||||
orleans.codegen.cs
|
orleans.codegen.cs
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
# Including strong name files can present a security risk
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
#*.snk
|
#*.snk
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
#bower_components/
|
#bower_components/
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
# RIA/Silverlight projects
|
||||||
Generated_Code/
|
Generated_Code/
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
# Backup & report files from converting an old project file
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
# because we have git ;-)
|
# because we have git ;-)
|
||||||
_UpgradeReport_Files/
|
_UpgradeReport_Files/
|
||||||
Backup*/
|
Backup*/
|
||||||
UpgradeLog*.XML
|
UpgradeLog*.XML
|
||||||
UpgradeLog*.htm
|
UpgradeLog*.htm
|
||||||
ServiceFabricBackup/
|
ServiceFabricBackup/
|
||||||
*.rptproj.bak
|
*.rptproj.bak
|
||||||
|
|
||||||
# SQL Server files
|
# SQL Server files
|
||||||
*.mdf
|
*.mdf
|
||||||
*.ldf
|
*.ldf
|
||||||
*.ndf
|
*.ndf
|
||||||
|
|
||||||
# Business Intelligence projects
|
# Business Intelligence projects
|
||||||
*.rdl.data
|
*.rdl.data
|
||||||
*.bim.layout
|
*.bim.layout
|
||||||
*.bim_*.settings
|
*.bim_*.settings
|
||||||
*.rptproj.rsuser
|
*.rptproj.rsuser
|
||||||
*- [Bb]ackup.rdl
|
*- [Bb]ackup.rdl
|
||||||
*- [Bb]ackup ([0-9]).rdl
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
# Microsoft Fakes
|
# Microsoft Fakes
|
||||||
FakesAssemblies/
|
FakesAssemblies/
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
# GhostDoc plugin setting file
|
||||||
*.GhostDoc.xml
|
*.GhostDoc.xml
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
# Node.js Tools for Visual Studio
|
||||||
.ntvs_analysis.dat
|
.ntvs_analysis.dat
|
||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
# Visual Studio 6 build log
|
||||||
*.plg
|
*.plg
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
# Visual Studio 6 workspace options file
|
||||||
*.opt
|
*.opt
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
*.vbw
|
*.vbw
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
*.vbp
|
*.vbp
|
||||||
|
|
||||||
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
*.dsw
|
*.dsw
|
||||||
*.dsp
|
*.dsp
|
||||||
|
|
||||||
# Visual Studio 6 technical files
|
# Visual Studio 6 technical files
|
||||||
*.ncb
|
*.ncb
|
||||||
*.aps
|
*.aps
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
# Visual Studio LightSwitch build output
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
**/*.Server/GeneratedArtifacts
|
**/*.Server/GeneratedArtifacts
|
||||||
**/*.Server/ModelManifest.xml
|
**/*.Server/ModelManifest.xml
|
||||||
_Pvt_Extensions
|
_Pvt_Extensions
|
||||||
|
|
||||||
# Paket dependency manager
|
# Paket dependency manager
|
||||||
.paket/paket.exe
|
.paket/paket.exe
|
||||||
paket-files/
|
paket-files/
|
||||||
|
|
||||||
# FAKE - F# Make
|
# FAKE - F# Make
|
||||||
.fake/
|
.fake/
|
||||||
|
|
||||||
# CodeRush personal settings
|
# CodeRush personal settings
|
||||||
.cr/personal
|
.cr/personal
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
# Python Tools for Visual Studio (PTVS)
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
# Cake - Uncomment if you are using it
|
||||||
# tools/**
|
# tools/**
|
||||||
# !tools/packages.config
|
# !tools/packages.config
|
||||||
|
|
||||||
# Tabs Studio
|
# Tabs Studio
|
||||||
*.tss
|
*.tss
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
# Telerik's JustMock configuration file
|
||||||
*.jmconfig
|
*.jmconfig
|
||||||
|
|
||||||
# BizTalk build output
|
# BizTalk build output
|
||||||
*.btp.cs
|
*.btp.cs
|
||||||
*.btm.cs
|
*.btm.cs
|
||||||
*.odx.cs
|
*.odx.cs
|
||||||
*.xsd.cs
|
*.xsd.cs
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
# OpenCover UI analysis results
|
||||||
OpenCover/
|
OpenCover/
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
# Azure Stream Analytics local run output
|
||||||
ASALocalRun/
|
ASALocalRun/
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
# MSBuild Binary and Structured Log
|
||||||
*.binlog
|
*.binlog
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
# NVidia Nsight GPU debugger configuration file
|
||||||
*.nvuser
|
*.nvuser
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
.mfractor/
|
.mfractor/
|
||||||
|
|
||||||
# Local History for Visual Studio
|
# Local History for Visual Studio
|
||||||
.localhistory/
|
.localhistory/
|
||||||
|
|
||||||
# Visual Studio History (VSHistory) files
|
# Visual Studio History (VSHistory) files
|
||||||
.vshistory/
|
.vshistory/
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
# BeatPulse healthcheck temp database
|
||||||
healthchecksdb
|
healthchecksdb
|
||||||
|
|
||||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
MigrationBackup/
|
MigrationBackup/
|
||||||
|
|
||||||
# Ionide (cross platform F# VS Code tools) working folder
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
.ionide/
|
.ionide/
|
||||||
|
|
||||||
# Fody - auto-generated XML schema
|
# Fody - auto-generated XML schema
|
||||||
FodyWeavers.xsd
|
FodyWeavers.xsd
|
||||||
|
|
||||||
# VS Code files for those working on multiple tools
|
# VS Code files for those working on multiple tools
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/settings.json
|
!.vscode/settings.json
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/launch.json
|
!.vscode/launch.json
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
*.code-workspace
|
*.code-workspace
|
||||||
|
|
||||||
# Local History for Visual Studio Code
|
# Local History for Visual Studio Code
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
# Windows Installer files from build outputs
|
# Windows Installer files from build outputs
|
||||||
*.cab
|
*.cab
|
||||||
*.msi
|
*.msi
|
||||||
*.msix
|
*.msix
|
||||||
*.msm
|
*.msm
|
||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
|
||||||
# ---> VisualStudioCode
|
# Superpowers brainstorming sessions
|
||||||
.vscode/*
|
.superpowers/
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
# ---> VisualStudioCode
|
||||||
!.vscode/launch.json
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/settings.json
|
||||||
!.vscode/*.code-snippets
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
# Local History for Visual Studio Code
|
!.vscode/extensions.json
|
||||||
.history/
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
# Built Visual Studio Code Extensions
|
# Local History for Visual Studio Code
|
||||||
*.vsix
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
|||||||
279
docs/superpowers/specs/2026-03-19-finance-tracker-design.md
Normal file
279
docs/superpowers/specs/2026-03-19-finance-tracker-design.md
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
# Finance Tracker — Design Spec
|
||||||
|
|
||||||
|
**Date:** 2026-03-19
|
||||||
|
**Stack:** .NET 8 · MySQL 8 · Vue.js 3 · Vuetify 3 · NSwag · ApexCharts · Docker
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
A personal web app for tracking finances. The primary workflow is importing CSV bank exports (ČSOB format) and visualising spending over time. Single-user with a login screen for security. Deployed via docker-compose on a home server behind an existing Nginx reverse proxy.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Three docker-compose services sharing an internal network:
|
||||||
|
|
||||||
|
| Service | Image | Exposed Port | Role |
|
||||||
|
|---------|-------|-------------|------|
|
||||||
|
| `api` | .NET 8 (custom) | 5000 | REST API + business logic |
|
||||||
|
| `web` | Nginx (custom, serves Vue build) | 3000 | Frontend SPA |
|
||||||
|
| `db` | MySQL 8 | 3306 (internal only) | Persistence |
|
||||||
|
|
||||||
|
**Host Nginx routing** — the .NET API mounts all routes under `/api/...`, so Nginx passes the full URI through without stripping the prefix:
|
||||||
|
```nginx
|
||||||
|
location /api/ { proxy_pass http://localhost:5000; }
|
||||||
|
location / { proxy_pass http://localhost:3000; }
|
||||||
|
```
|
||||||
|
|
||||||
|
**CORS** — the API enables CORS for `http://localhost:3000` (dev) and the production frontend origin via the `ALLOWED_ORIGIN` env var.
|
||||||
|
|
||||||
|
Secrets provided via `.env` file (not committed to git):
|
||||||
|
- `DB_CONNECTION` — MySQL connection string
|
||||||
|
- `JWT_SECRET` — 32-byte random value, Base64-encoded (e.g. `openssl rand -base64 32`)
|
||||||
|
- `APP_USERNAME` / `APP_PASSWORD` — single user credentials (password stored as bcrypt hash in `.env`)
|
||||||
|
- `ALLOWED_ORIGIN` — frontend origin for CORS
|
||||||
|
|
||||||
|
**DB persistence & migrations** — a named Docker volume persists MySQL data across restarts. The `db` service has a health check (`mysqladmin ping`). The `api` service depends on `db` with `condition: service_healthy`. Schema is managed via EF Core migrations, applied automatically at API startup using Polly retry: 5 attempts, exponential backoff (1s, 2s, 4s, 8s, 16s). If all attempts fail, the container exits.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## NSwag Client Generation
|
||||||
|
|
||||||
|
The TypeScript API client is generated from the .NET OpenAPI spec at build time — no running API or database is needed.
|
||||||
|
|
||||||
|
**Mechanism:**
|
||||||
|
1. `AccountTracking.Api` uses the `NSwag.MSBuild` NuGet package, which generates `openapi.json` into the project output directory as part of `dotnet build`.
|
||||||
|
2. The `AccountTracking.Web` Dockerfile is a two-stage build:
|
||||||
|
- **Stage 1** (`sdk`): runs `dotnet build` on the API project (no DB needed) — produces `openapi.json`.
|
||||||
|
- **Stage 2** (`node`): copies `openapi.json` from stage 1, runs `nswag run nswag.json` to generate the TypeScript client into `src/api/`, then runs `npm run build`.
|
||||||
|
3. The final image is Nginx serving the Vue build output.
|
||||||
|
|
||||||
|
**NSwag client base URL:** configured as `/api` (relative), so it works identically in development (through Vite's dev server proxy) and production (through Nginx proxy). No build-time URL argument needed.
|
||||||
|
|
||||||
|
**Vite dev proxy** — `vite.config.ts` proxies `/api` → `http://localhost:5000` with no path rewriting (since the API mounts all routes under `/api/...`):
|
||||||
|
```ts
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
'/api': { target: 'http://localhost:5000', changeOrigin: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Data Model
|
||||||
|
|
||||||
|
All `DATETIME` columns are stored as UTC. EF Core models use `DateTimeKind.Utc` on datetime properties (required when using Pomelo's MySQL EF Core provider to avoid implicit UTC conversion issues).
|
||||||
|
|
||||||
|
### `transactions`
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|--------|------|-------|
|
||||||
|
| id | INT PK AUTO_INCREMENT | |
|
||||||
|
| account_number | VARCHAR(30) | e.g. `216868554/0300` |
|
||||||
|
| booking_date | DATE | |
|
||||||
|
| amount | DECIMAL(15,2) | negative = outgoing |
|
||||||
|
| currency | VARCHAR(3) | CZK |
|
||||||
|
| balance | DECIMAL(15,2) | account balance after transaction |
|
||||||
|
| counter_party_name | VARCHAR(255) | merchant or sender name |
|
||||||
|
| operation_description | VARCHAR(255) | e.g. "Transakce platební kartou" |
|
||||||
|
| message | TEXT | full description from CSV |
|
||||||
|
| category | VARCHAR(100) | from bank CSV, used as-is |
|
||||||
|
| variable_symbol | VARCHAR(30) | |
|
||||||
|
| bank_note | VARCHAR(255) | "vlastní poznámka" from CSV (read-only, not user-editable) |
|
||||||
|
| transaction_id | VARCHAR(512) UNIQUE, collation utf8mb4_bin | case-sensitive; format `transhist-v-YYYY-MM_<sha>` |
|
||||||
|
|
||||||
|
Additional CSV columns (counter bank code, constant symbol, specific symbol, order name, exchange rate, E2E id, payer reference, original payer, final recipient, original transaction) stored as nullable VARCHAR(255) but not used in UI.
|
||||||
|
|
||||||
|
### `import_logs`
|
||||||
|
|
||||||
|
| Column | Type | Notes |
|
||||||
|
|--------|------|-------|
|
||||||
|
| id | INT PK AUTO_INCREMENT | |
|
||||||
|
| imported_at | DATETIME | stored as UTC |
|
||||||
|
| filename | VARCHAR(255) | original filename |
|
||||||
|
| records_imported | INT | new transactions saved |
|
||||||
|
| records_skipped | INT | duplicates ignored |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ČSOB CSV Format
|
||||||
|
|
||||||
|
The CSV is semicolon-delimited, UTF-8. Lines 1–2 are a bank-generated title. The parser locates the header row by scanning for the first line that starts with `číslo účtu` (case-insensitive) rather than hardcoding a line number.
|
||||||
|
|
||||||
|
Column mapping:
|
||||||
|
|
||||||
|
| CSV Column (Czech) | Model Field |
|
||||||
|
|-------------------|-------------|
|
||||||
|
| číslo účtu | account_number |
|
||||||
|
| datum zaúčtování | booking_date |
|
||||||
|
| částka | amount |
|
||||||
|
| měna | currency |
|
||||||
|
| zůstatek | balance |
|
||||||
|
| jméno protistrany | counter_party_name |
|
||||||
|
| označení operace | operation_description |
|
||||||
|
| zpráva | message |
|
||||||
|
| kategorie | category |
|
||||||
|
| variabilní symbol | variable_symbol |
|
||||||
|
| vlastní poznámka | bank_note |
|
||||||
|
| ID transakce | transaction_id |
|
||||||
|
|
||||||
|
Amount and balance values use Czech locale formatting (comma as decimal separator, space as thousands separator) — normalise before parsing (remove spaces, replace comma with dot).
|
||||||
|
|
||||||
|
**Error handling:**
|
||||||
|
- Header row not found, or required columns (`částka`, `ID transakce`, `datum zaúčtování`) missing → HTTP 400 `{ error: string }`
|
||||||
|
- File exceeds 10 MB → HTTP 400 `{ error: "File too large" }` (Kestrel `MaxRequestBodySize` set to 10 MB; framework error caught and returned as structured response)
|
||||||
|
- Zero new transactions and zero duplicates → HTTP 200 `{ recordsImported: 0, recordsSkipped: 0 }`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoints
|
||||||
|
|
||||||
|
All endpoints except login require `Authorization: Bearer <token>`. JWT tokens expire after **30 days**; `expiresAt` is an ISO 8601 UTC string (e.g. `"2026-04-18T10:00:00Z"`). The frontend stores both `token` and `expiresAt` in `localStorage`. The Vue Router navigation guard checks `Date.now() < Date.parse(expiresAt)` to detect expiry locally. On any 401 API response, the auth store clears both values and redirects to `/login`.
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/auth/login
|
||||||
|
Body: { username, password }
|
||||||
|
Returns 200: { token, expiresAt }
|
||||||
|
Returns 401: wrong credentials
|
||||||
|
|
||||||
|
POST /api/transactions/import
|
||||||
|
Body: multipart/form-data (CSV file, max 10 MB)
|
||||||
|
Returns 200: { recordsImported, recordsSkipped }
|
||||||
|
Returns 400: { error: string }
|
||||||
|
|
||||||
|
GET /api/transactions
|
||||||
|
Query: year (required), month (optional), category (optional),
|
||||||
|
search (optional), page (default 1), pageSize (default 50, max 200)
|
||||||
|
— search: case-insensitive contains match (%term%) on counter_party_name
|
||||||
|
OR message; uses database collation (utf8mb4_unicode_ci)
|
||||||
|
Returns 200: { items: [...], totalCount, page, pageSize }
|
||||||
|
|
||||||
|
GET /api/transactions/categories
|
||||||
|
No query params — returns all-time distinct categories (not period-filtered)
|
||||||
|
Returns 200: string[] sorted alphabetically
|
||||||
|
|
||||||
|
GET /api/dashboard/summary
|
||||||
|
Query: year (required), month (required)
|
||||||
|
Returns 200: { totalSpent, totalIncome, currentBalance }
|
||||||
|
— totalSpent: absolute value of sum of negative amounts WHERE booking_date
|
||||||
|
falls within the given year+month
|
||||||
|
— totalIncome: sum of positive amounts within the given year+month
|
||||||
|
— currentBalance: balance from the transaction with the globally latest
|
||||||
|
booking_date (highest id as tiebreaker), ignoring year/month
|
||||||
|
filter; null if no transactions exist at all
|
||||||
|
|
||||||
|
GET /api/dashboard/spending-by-category
|
||||||
|
Query: year (required), month (required)
|
||||||
|
Returns 200: [{ category, total }]
|
||||||
|
— total: absolute value of sum of negative amounts for that category in period
|
||||||
|
— positive amounts within any category are excluded from totals
|
||||||
|
— sorted descending by total
|
||||||
|
|
||||||
|
GET /api/dashboard/balance-trend
|
||||||
|
Query: year (required)
|
||||||
|
Returns 200: [{ month, closingBalance }]
|
||||||
|
— one entry per month that has transactions (months with no transactions omitted)
|
||||||
|
— closingBalance: balance from the transaction with the latest booking_date
|
||||||
|
in that month (highest id as tiebreaker for same-date rows)
|
||||||
|
— always year-scoped; ignores any month selection
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Frontend
|
||||||
|
|
||||||
|
### Pages
|
||||||
|
|
||||||
|
| Page | Route | Auth Required |
|
||||||
|
|------|-------|--------------|
|
||||||
|
| Login | `/login` | No |
|
||||||
|
| Dashboard | `/` | Yes |
|
||||||
|
| Transactions | `/transactions` | Yes |
|
||||||
|
| Import | `/import` | Yes |
|
||||||
|
|
||||||
|
JWT and `expiresAt` stored in `localStorage`. Vue Router navigation guard checks local expiry and redirects unauthenticated/expired users to `/login`. On 401 API response, auth store clears storage and redirects to `/login`.
|
||||||
|
|
||||||
|
### Dashboard Layout
|
||||||
|
|
||||||
|
Three KPI cards across the top (data from `/api/dashboard/summary`):
|
||||||
|
- **Total Spent** — absolute value of negative amounts for selected month/year
|
||||||
|
- **Total Income** — sum of positive amounts for selected month/year
|
||||||
|
- **Current Balance** — globally most recent balance (not period-filtered); shows `—` if null
|
||||||
|
|
||||||
|
Cards show a skeleton loader while fetching and an error state (red border, retry button) on failure.
|
||||||
|
|
||||||
|
Two charts below, side by side:
|
||||||
|
- **Spending by Category** (ApexCharts horizontal bar) — category totals for selected month/year
|
||||||
|
- **Balance Trend** (ApexCharts line) — closing balance per month for selected year; chart title includes year (e.g. "Balance trend — 2025") to communicate it is not filtered by month
|
||||||
|
|
||||||
|
Period picker (month + year dropdowns) in the app bar updates KPI cards and spending chart. Balance trend updates only when the year changes.
|
||||||
|
|
||||||
|
### Transactions Page
|
||||||
|
|
||||||
|
Vuetify data table:
|
||||||
|
- Columns: Date, Counter Party, Category, Amount, Balance
|
||||||
|
- Filters: year (required), month (optional), category dropdown — server-side query params
|
||||||
|
- Search box — server-side (`search` query param, debounced 300 ms)
|
||||||
|
- Server-side pagination (page, pageSize=50)
|
||||||
|
|
||||||
|
### Import Page
|
||||||
|
|
||||||
|
File input (`.csv` only), upload button. On success: "Imported X transactions, skipped Y duplicates." On error: shows the `error` string from the API response.
|
||||||
|
|
||||||
|
### State Management (Pinia)
|
||||||
|
|
||||||
|
- `auth` — token, expiresAt, login/logout actions, 401 handler
|
||||||
|
- `period` — selected year/month, shared across dashboard and transactions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
account-tracking/
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── .env # secrets (gitignored)
|
||||||
|
├── .env.example # template with placeholder values
|
||||||
|
├── src/
|
||||||
|
│ ├── AccountTracking.Api/ # .NET 8 Web API
|
||||||
|
│ │ ├── Controllers/
|
||||||
|
│ │ ├── Services/
|
||||||
|
│ │ ├── Models/
|
||||||
|
│ │ ├── Data/ # EF Core DbContext + migrations
|
||||||
|
│ │ └── Dockerfile
|
||||||
|
│ └── AccountTracking.Web/ # Vue 3 + Vuetify frontend
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── api/ # NSwag-generated client (do not edit manually)
|
||||||
|
│ │ ├── stores/ # Pinia: auth, period
|
||||||
|
│ │ ├── views/ # Login, Dashboard, Transactions, Import
|
||||||
|
│ │ └── components/ # KpiCard, SpendingChart, BalanceTrendChart
|
||||||
|
│ ├── nswag.json # NSwag config (input: openapi.json, output: src/api/)
|
||||||
|
│ ├── vite.config.ts # includes /api proxy to http://localhost:5000
|
||||||
|
│ └── Dockerfile
|
||||||
|
└── docs/
|
||||||
|
└── superpowers/specs/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Decisions
|
||||||
|
|
||||||
|
- **Single user from config** — no user table; credentials in `.env`; password stored as bcrypt hash.
|
||||||
|
- **JWT lifetime 30 days** — long-lived for convenience; Base64-encoded 32-byte secret; expiry in ISO 8601 UTC; checked locally in router guard; redirect on expiry or 401.
|
||||||
|
- **Bank categories used as-is** — no re-categorisation UI needed.
|
||||||
|
- **Deduplication via `transaction_id`** — format `transhist-v-YYYY-MM_<sha>`; VARCHAR(512) utf8mb4_bin (case-sensitive) for correctness and future-proofing.
|
||||||
|
- **CSV header detection** — scan for line starting with `číslo účtu`; do not hardcode line number.
|
||||||
|
- **Spending totals exclude positive amounts per category** — refunds within a spending category are excluded from totals.
|
||||||
|
- **`currentBalance` is not period-filtered** — always the globally latest transaction balance; clearly documented in API spec and shown as "not filtered" in UI.
|
||||||
|
- **Balance trend omits months with no transactions** — ApexCharts handles sparse data; no zero-fill.
|
||||||
|
- **Search is contains-match (OR)** — `%term%` on counter_party_name OR message; case-insensitive via utf8mb4_unicode_ci.
|
||||||
|
- **Server-side search and pagination** — no client-side filtering of paginated data.
|
||||||
|
- **NSwag via MSBuild** — `openapi.json` generated at `dotnet build`; web Dockerfile copies it; no running API needed.
|
||||||
|
- **NSwag client base URL: `/api`** — relative; Vite dev proxy and Nginx prod proxy both resolve it correctly.
|
||||||
|
- **EF Core migrations at startup** — Polly retry: 5 attempts, exponential backoff; container exits on all-fail.
|
||||||
|
- **EF Core UTC datetime** — `DateTimeKind.Utc` on all datetime model properties (Pomelo requirement).
|
||||||
|
- **`bank_note` is read-only** — maps to ČSOB's "vlastní poznámka"; no user-edit UI.
|
||||||
|
- **ApexCharts** — Vue 3 compatible, dark theme compatible with Vuetify.
|
||||||
Loading…
Reference in New Issue
Block a user