[{"data":1,"prerenderedAt":304},["ShallowReactive",2],{"projects":3},[4,111,201],{"id":5,"title":6,"architecture":7,"body":17,"challenge":24,"code":27,"cover":31,"demo":34,"description":36,"extension":37,"featured":38,"lessons":39,"meta":54,"navigation":38,"path":55,"problem":56,"screenshots":64,"seo":79,"solution":80,"stem":89,"tags":90,"technicalAnalysis":94,"type":109,"__hash__":110},"projects\u002Fprojects\u002Ffix-my-city.md","FixMyCity",{"title":8,"description":9,"tags":10},"Full-Stack Application Flow","The application consists of a Vue frontend, a Spring Boot REST API, and a MySQL database. Users can create reports, upload images, track issue statuses, and receive notifications when administrators update their reports. JWT authentication secures API access while Docker is used for deployment.",[11,12,13,14,15,16],"Vue","REST API","JWT Auth","MySQL","S3","Docker",{"type":18,"value":19,"toc":20},"minimark",[],{"title":21,"searchDepth":22,"depth":22,"links":23},"",2,[],{"title":25,"description":26},"Challenges","The main challenge in this project was not one single difficult algorithm, but the amount of connected application features. Authentication affects protected routes, image access, user profiles, issue ownership, admin permissions, and notifications. The project needed enough structure to keep those parts separated while still making them work together as one application.",{"filename":28,"language":29,"content":30},"NotificationService.java","java","public void notificationAfterUpdate(\n    Issue issue,\n    Status issueStatus\n) {\n    StringBuilder message = new StringBuilder();\n\n    message.append(\"The status of the post with title: \\\"\")\n        .append(issue.getTitle())\n        .append(\"\\\" was changed to \");\n\n    switch (issueStatus) {\n        case OPEN -> message.append(\"open.\");\n        case IN_PROGRESS -> message.append(\"in progress.\");\n        case COMPLETED -> message.append(\"completed.\");\n        default -> message.append(\"unknown status.\");\n    }\n\n    NotificationRequest request = new NotificationRequest();\n    request.setMessage(message.toString());\n    request.setRead(false);\n\n    saveNotification(request, issue.getUser(), issue);\n}\n",{"src":32,"alt":33},"\u002Fimages\u002Fprojects\u002Ffixmycity\u002Ffeed.png","FixMyCity issue reporting dashboard",{"source":35},"https:\u002F\u002Fgithub.com\u002Fdamienhensen\u002Ffix-my-city","A full-stack issue reporting platform for municipalities, built with Vue, Spring Boot, MySQL, JWT authentication, secure image handling, Cypress tests, and Docker-based deployment.","md",true,{"title":40,"items":41},"What This Project Demonstrates",[42,45,48,51],{"title":43,"description":44},"Full-Stack Application Development","The project combines frontend views, backend APIs, database persistence, authentication, image uploads, notifications, map integration, and deployment into one complete application.",{"title":46,"description":47},"Practical Backend Structure","The backend is organized into controllers, DTOs, entities, repositories, services, filters, configuration classes, and seeders. This keeps request handling, business logic, persistence, security, and setup code separated.",{"title":49,"description":50},"Frontend Reuse","Reusable services and composables handle repeated frontend concerns such as sessions, authenticated requests, secure image loading, image uploads, deletion, flash messages, and availability checks.",{"title":52,"description":53},"Production-Oriented Workflow","The project goes beyond local development by including Docker, environment configuration, CI\u002FCD, automated testing, test deployment, and production deployment.",{},"\u002Fprojects\u002Ffix-my-city",{"title":57,"text":58,"points":59},"The Problem","Public space problems are easy to notice but not always easy to report or follow up on. Citizens need a simple way to submit reports, while administrators need a clear workflow for reviewing issues and keeping users updated.",[60,61,62,63],"Citizens need a simple way to report local problems.","Reports need useful context such as images, categories, urgency, and location.","Users need feedback after submitting a report.","Administrators need a central place to manage and update reports.",[65,68,70,73,76],{"src":66,"alt":67},"\u002Fimages\u002Fprojects\u002Ffixmycity\u002Fhome.png","FixMyCity landing page",{"src":32,"alt":69},"Issue feed with filtering and status tracking",{"src":71,"alt":72},"\u002Fimages\u002Fprojects\u002Ffixmycity\u002Fmap.png","Interactive map showing issue locations",{"src":74,"alt":75},"\u002Fimages\u002Fprojects\u002Ffixmycity\u002Fprofile.png","User profile and submitted reports",{"src":77,"alt":78},"\u002Fimages\u002Fprojects\u002Ffixmycity\u002Fadmin.png","Administrative issue management dashboard",{"title":6,"description":36},{"title":81,"text":82,"points":83},"The Solution","FixMyCity is a full-stack platform where users can create issue reports, upload images, view reports on a map, track their own submissions, and receive notifications when administrators update the status of an issue.",[84,85,86,87,88],"Users can submit public space reports with images and location data.","Reports can be filtered by tag, urgency, and status.","Issues can be viewed in both a feed and an interactive map.","Admins can update issue statuses from a dedicated dashboard.","Users receive notifications when their reports are updated.","projects\u002Ffix-my-city",[11,91,14,16,92,93,15],"Spring Boot","JWT","Cypress",{"title":95,"decisions":96},"Technical Analysis",[97,100,103,106],{"title":98,"description":99},"Frontend And Backend Separation","The application was built as a separate Vue frontend and Spring Boot backend communicating through a REST API. This allowed both applications to be developed, tested, and deployed independently while maintaining a clear separation of responsibilities.",{"title":101,"description":102},"Authentication And Authorization","JWT access tokens and refresh tokens were used to secure the API. Protected endpoints validate authenticated users before allowing access to features such as issue creation, profile management, notifications, and administrative actions.",{"title":104,"description":105},"Issue Management Workflow","The core of the application revolves around issue reports. Users can create reports with location data and images, while administrators review submissions, update statuses, and trigger notifications that keep users informed about progress.",{"title":107,"description":108},"Deployment And Quality Assurance","The project includes Dockerized services, automated deployments, and Cypress end-to-end tests. This helped ensure that important user flows continued to work correctly as new features were added.","School Assignment","usTWNLc5JtDpZRveCwz9VnEcbR-lJ9CvRT2y4GjaWSo",{"id":112,"title":113,"architecture":114,"body":122,"challenge":126,"code":128,"cover":132,"demo":135,"description":137,"extension":37,"featured":138,"lessons":139,"meta":152,"navigation":38,"path":153,"problem":154,"screenshots":161,"seo":173,"solution":174,"stem":181,"tags":182,"technicalAnalysis":186,"type":109,"__hash__":200},"projects\u002Fprojects\u002Fgame-backlog.md","Game Backlog",{"title":8,"description":115,"tags":116},"The application is built around a repository abstraction that separates the Flutter frontend from the underlying storage implementation. Instead of communicating directly with a database or API, the UI interacts with a common interface that can delegate requests to either a local SQLite data source or a Laravel REST API. The backend exposes CRUD endpoints, processes background enrichment jobs, and persists data, allowing the application to switch data sources without changing the rest of the codebase.",[117,118,119,120,121,16],"Flutter","Repository Pattern","SQLite","Laravel REST API","Background Jobs",{"type":18,"value":123,"toc":124},[],{"title":21,"searchDepth":22,"depth":22,"links":125},[],{"title":25,"description":127},"The biggest challenge was designing an architecture that could support multiple storage implementations without affecting the rest of the application. Building clear abstractions between the UI, repository layer, and backend services made the project significantly more flexible and easier to maintain.",{"filename":129,"language":130,"content":131},"game_data_source.dart","dart","abstract class GameDataSource {\n    Future\u003CList\u003CGame>> retrieveGames();\n    Future\u003CGame?> getGame(int id);\n    Future\u003CGame> insertGame(Game game);\n    Future\u003CGame> updateGame(Game game);\n    Future\u003Cvoid> removeGame(int id);\n    Future\u003Cvoid> deleteAllGames();\n}\n",{"src":133,"alt":134},"\u002Fimages\u002Fprojects\u002Fgamebacklog\u002Fhome.jpg","Game Backlog application overview",{"source":136},"https:\u002F\u002Fgithub.com\u002Fdamienhensen\u002Fgame-backlog","A full-stack game backlog application built with Flutter and Laravel, featuring interchangeable local and remote data sources, automatic game metadata enrichment, CI\u002FCD, and Docker deployment.",false,{"title":40,"items":140},[141,144,147,150],{"title":142,"description":143},"Cross-Platform Development","The project demonstrates building a mobile application using Flutter while integrating it with a custom backend.",{"title":145,"description":146},"Clean Application Architecture","The application is organized into separate layers for presentation, repositories, services, and data sources, reducing coupling between components.",{"title":148,"description":149},"Full-Stack Engineering","The project combines mobile development, backend APIs, databases, background jobs, CI\u002FCD, and Docker into a single application.",{"title":52,"description":151},"Beyond local development, the project includes automated deployment, containerization, environment configuration, and testing practices that mirror real-world software development.",{},"\u002Fprojects\u002Fgame-backlog",{"title":57,"text":155,"points":156},"The goal of this project was not simply to build another game tracker, but to explore how a mobile application can be designed around interchangeable data sources and a clean separation between presentation, business logic, and persistence.",[157,158,159,160],"Mobile applications should not depend on a single storage implementation.","Business logic should remain independent from the UI.","Additional game information should be retrieved automatically instead of entered manually.","The architecture should be maintainable and easy to extend.",[162,164,167,170],{"src":133,"alt":163},"Game Backlog overview showing tracked games",{"src":165,"alt":166},"\u002Fimages\u002Fprojects\u002Fgamebacklog\u002Fcreate.jpg","Creating a new game with play and ownership status",{"src":168,"alt":169},"\u002Fimages\u002Fprojects\u002Fgamebacklog\u002Fdetail.jpg","Game detail page with automatically enriched metadata",{"src":171,"alt":172},"\u002Fimages\u002Fprojects\u002Fgamebacklog\u002Fedit.jpg","Editing an existing game using the management interface",{"title":113,"description":137},{"title":81,"text":175,"points":176},"The application uses Flutter as the client and communicates through a repository abstraction instead of directly accessing storage. Depending on the implementation, requests can be handled by a local SQLite database or a Laravel REST API. The backend enriches games with additional metadata, reducing manual input while demonstrating a scalable full-stack architecture.",[177,178,179,180],"Manage a personal game backlog and wishlist.","Track play and ownership status.","Retrieve additional game metadata automatically.","Switch between local and remote data sources through a shared abstraction.","projects\u002Fgame-backlog",[117,183,184,119,12,16,185],"Dart","Laravel","GitLab CI\u002FCD",{"title":95,"decisions":187},[188,191,194,197],{"title":189,"description":190},"Repository Abstraction","Rather than tightly coupling the UI to a specific storage solution, the application communicates with a repository interface. This makes it possible to switch between local SQLite storage and a REST API without changing the rest of the application.",{"title":192,"description":193},"Separation Of Responsibilities","The project follows a layered architecture where presentation, business logic, and data access are separated. This keeps widgets lightweight and makes the application easier to maintain and extend.",{"title":195,"description":196},"Automatic Metadata Enrichment","Newly added games are enriched by backend jobs that retrieve additional information such as artwork and metadata from an external source. This improves the user experience while demonstrating asynchronous backend processing.",{"title":198,"description":199},"Deployment And Automation","The project includes a GitLab CI\u002FCD pipeline and Dockerized services for reproducible deployments. Automated testing helps verify that important functionality continues to work as new features are introduced.","ksyXsTLmF6CNoxaqBYd4UWHhK3aPEY_M_1IYphnbRv8",{"id":202,"title":203,"architecture":204,"body":214,"challenge":218,"code":220,"cover":223,"demo":226,"description":228,"extension":37,"featured":138,"lessons":229,"meta":243,"navigation":38,"path":244,"problem":245,"screenshots":252,"seo":267,"solution":268,"stem":277,"tags":278,"technicalAnalysis":283,"type":109,"__hash__":303},"projects\u002Fprojects\u002Freceipt-splitter.md","Receipt Splitter",{"title":205,"description":206,"tags":207},"Local-First Mobile Application Flow","The application uses a layered Flutter architecture. The UI layer is split into pages, widgets, and ViewModels. ViewModels expose screen state and delegate persistence to a repository. The repository uses Drift tables and mappers to translate database rows into UI domain models. OCR follows a separate pipeline where an image is picked or captured, cropped, processed by ML Kit text recognition, parsed into receipt items, and inserted as draft expenses in the local database.",[208,209,210,211,212,213],"Flutter UI","Provider ViewModels","Drift Repository","SQLite Persistence","OCR Pipeline","Receipt Parsing",{"type":18,"value":215,"toc":216},[],{"title":21,"searchDepth":22,"depth":22,"links":217},[],{"title":25,"description":219},"The main challenge was connecting OCR input to a real expense-splitting workflow. The application has to deal with unreliable OCR text, editable receipt items, participants, payers, split validation, draft bills, open bills, and settlement status. The technical difficulty is not one algorithm, but keeping those concerns separated while still making them work as one mobile flow.",{"filename":221,"language":130,"content":222},"receipt_parser.dart","static List\u003CReceiptItem> parse(String rawText) {\n  final lines = rawText\n      .split('\\n')\n      .map(_normalizeLine)\n      .where((line) => line.isNotEmpty)\n      .toList();\n\n  final separatedItems = _parseSeparatedItemsAndPrices(lines);\n  if (separatedItems.isNotEmpty) {\n    return separatedItems;\n  }\n\n  final inlineItems = _parseInlineItems(lines);\n  return inlineItems;\n}\n",{"src":224,"alt":225},"\u002Fimages\u002Fprojects\u002Freceiptsplitter\u002Fdashboard.jpg","Receipt Splitter dashboard showing open settlements and bill summaries",{"source":227},"https:\u002F\u002Fgithub.com\u002Fdamienhensen\u002Freceipt-splitter","A Flutter mobile application for scanning receipts, creating shared bill drafts, splitting expenses between participants, and tracking open settlements using local Drift persistence.",{"title":40,"items":230},[231,234,237,240],{"title":232,"description":233},"Mobile Application Architecture","The project separates screens, reusable widgets, ViewModels, services, repositories, mappers, database tables, and domain models.",{"title":235,"description":236},"Local Data Modeling","The Drift database models bills, people, participants, expenses, and expense splits as separate relational concepts instead of storing everything as one flat object.",{"title":238,"description":239},"OCR Integration","The application integrates camera\u002Fgallery selection, image cropping, ML Kit text recognition, receipt parsing, and bill creation into one workflow.",{"title":241,"description":242},"Business Logic Outside The UI","Payment validation, debt aggregation, settlement status updates, and receipt parsing are handled outside presentation widgets, making the application easier to reason about and extend.",{},"\u002Fprojects\u002Freceipt-splitter",{"title":57,"text":246,"points":247},"Splitting receipts between multiple people becomes messy when a bill contains several items, different payers, partial payments, and changing participants. A simple receipt scanner is not enough; the application also needs to turn scanned items into a bill that can be edited, split, finalized, and tracked until everyone has paid.",[248,249,250,251],"Receipt items should not have to be entered manually one by one.","A bill can contain multiple expenses, participants, payers, and debt splits.","Users need to know which bills are still open and who still owes money.","Bill, expense, participant, and payment data should remain available locally on the device.",[253,255,258,261,264],{"src":224,"alt":254},"Dashboard showing open settlements, latest draft bills, and the largest outstanding shared expenses",{"src":256,"alt":257},"\u002Fimages\u002Fprojects\u002Freceiptsplitter\u002Fsettled.jpg","Bills overview organized into draft, open, and settled categories",{"src":259,"alt":260},"\u002Fimages\u002Fprojects\u002Freceiptsplitter\u002Fbill-draft.jpg","Bill creation screen with participants and imported expenses ready for review before finalization",{"src":262,"alt":263},"\u002Fimages\u002Fprojects\u002Freceiptsplitter\u002Fbill.jpg","Bill detail page displaying participants, expenses, payers, and remaining outstanding balances",{"src":265,"alt":266},"\u002Fimages\u002Fprojects\u002Freceiptsplitter\u002Fdebtors.jpg","Debtors overview aggregating outstanding balances across multiple bills for each participant",{"title":203,"description":228},{"title":81,"text":269,"points":270},"Receipt Splitter combines OCR-assisted receipt scanning with a local shared-expense workflow. Users can scan or import a receipt image, crop it for better recognition, convert detected receipt lines into draft expenses, add participants, assign payers, split costs, finalize bills, and track outstanding payments from a dashboard.",[271,272,273,274,275,276],"Scan receipt images using the device camera or gallery.","Convert OCR output into structured receipt items.","Create editable bill drafts from detected receipt items.","Add participants and split each expense between selected people.","Track draft, open, and settled bills.","View dashboard summaries for open settlements, biggest bills, and largest debtors.","projects\u002Freceipt-splitter",[117,183,279,119,280,281,282],"Drift","Provider","OCR","ML Kit",{"title":95,"decisions":284},[285,288,291,294,297,300],{"title":286,"description":287},"OCR As An Input Pipeline","The OCR feature is not treated as a standalone demo. It feeds directly into the bill workflow by converting recognized receipt text into structured receipt items and creating a bill draft from the result.",{"title":289,"description":290},"Local-First Persistence With Drift","Bills, participants, persons, expenses, and expense splits are stored locally using Drift. This makes the application usable without a backend while still keeping the data model relational and structured.",{"title":292,"description":293},"Repository And Mapper Layer","Database access is kept behind a BillRepository. Drift rows are converted into UI models through mapper classes, which prevents screens from depending directly on database tables.",{"title":295,"description":296},"ViewModel-Driven Screens","Screens use Provider-backed ViewModels to handle state, subscriptions, validation, and user actions. This keeps UI widgets focused on rendering instead of containing bill and payment logic.",{"title":298,"description":299},"Derived Settlement Data","Dashboard statistics such as open settlement value, top bills, and largest debtors are calculated from bill, expense, and split data instead of being stored separately. This reduces duplicated state and keeps summaries tied to the source data.",{"title":301,"description":302},"Bill Lifecycle Validation","A bill starts as a draft, can only be opened after expenses have a payer and valid splits, and becomes settled when all expense splits are fully paid. This gives the application a clear workflow instead of treating bills as simple CRUD records.","m8j_RrTM8i4WvQ13f6P1sc816gm4znxDjBM_hlanHq4",1780768957005]